Използване на bcp.exe за успешно импортиране на данни, дори ако редът на колоните във файла за форматиране се различава от този на таблицата

Създавам система за моя екип, която ни позволява автоматично да генерираме големи количества тестови данни, така че да можем да върнем нашата база данни в последователно състояние, когато имаме нужда от нея. Това трябва да бъде напълно автоматизирано, но в момента имам един проблем, който пречи това да се случи.

Нашата база данни е създадена с помощта на Entity Framework и ние също използваме миграции. Проблемът е, че тъй като редът на колоните може да се промени поради миграция, това означава, че тестовите данни, които са били експортирани преди миграция, може вече да не се импортират правилно при използване на bcp.

За да илюстрираме това по-ясно, ето стъпките за възпроизвеждане на поведението:

Създайте проста таблица:

dbo.Person - оригинален ред на колоните

Създайте файла с формат:

C:\bcp [BCPTest].[dbo].[Person] format nul -f person.xml -x -N -T -S .\SQLExpress

Създайте двоичен изходен файл:

C:\bcp [BCPTest].[dbo].[Person] out person.dat -N -T -S .\SQLExpress

Пренаредете колоните и запазете:

dbo.Person - поръчка за мигриране

Опитайте да импортирате:

C:\bcp [BCPTest].[dbo].[Person] in person.dat -f person.xml -N -T -S .\SQLExpress

Това ще доведе до много различни грешки. Понастоящем това може да бъде коригирано чрез пренареждане на колоните в неуспешните таблици ръчно, запазване и повторно изпълнение на импортирането, но, както казах по-рано, това трябва да бъде напълно автоматизирано.

Някой има ли някакви предложения как мога да постигна това?


person John H    schedule 23.05.2015    source източник


Отговори (1)


Опитайте да премахнете опцията -N от командата BCP in. Опцията -N ефективно заменя порядковите номера на полето за формат на файл. Форматният файл така или иначе определя собствените формати на полето, така че спецификацията -N е излишна.

РЕДАКТИРАНЕ:

По-долу е даден T-SQL пример, който променя реда на съпоставяне на колони на xml файл с оригинален формат, за да съответства на текущата таблица. Това може да се разшири, за да се справят с изпуснати колони и откривания на условия, които трябва да се обработват ръчно, като нови не-NULL колони.

Предлагам ви да извикате това от скрипт на Powershell, който итерира списъка ви с форматирани файлове и извиква процедурата, предавайки в оригиналния формат XML и име на таблица за всеки.

CREATE PROC dbo.usp_GetNewFormatFileXml
      @TableName nvarchar(261)
    , @OriginalFormatFileXml xml
    , @NewFormatFileXml xml OUTPUT
AS

DECLARE @NewFormatFileColumns xml;

--genrate new field/column mappings based in current column ordinal
WITH
    XMLNAMESPACES (
          'http://www.w3.org/2001/XMLSchema-instance' AS xsi
        ,  DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/bulkload/format'
        )
SELECT @NewFormatFileColumns = (
    SELECT
          RecordField.ID AS [@SOURCE]
        , NewTableColumn.Name AS [@NAME]
        , OldTableColumn.type AS [@xsi:type]
    FROM (
        SELECT 
            Field.value('@ID', 'int') AS ID
        FROM @OriginalFormatFileXml.nodes('/BCPFORMAT/RECORD/FIELD') AS Record(Field)) AS RecordField
    JOIN (
        SELECT 
              TableColumn.value('@SOURCE', 'int') AS FieldId
            , TableColumn.value('@NAME', 'sysname') AS Name
            , TableColumn.value('@xsi:type', 'sysname') AS type
        FROM @OriginalFormatFileXml.nodes('/BCPFORMAT/ROW/COLUMN') AS TableRow(TableColumn)) AS OldTableColumn ON
        OldTableColumn.FieldId = RecordField.ID
    JOIN (
        SELECT
              Name
            , column_id
        FROM sys.columns
        WHERE 
            object_id = OBJECT_ID(@TableName)
        ) AS NewTableColumn ON
            NewTableColumn.Name = OldTableColumn.Name
    ORDER BY NewTableColumn.column_id
FOR XML PATH('COLUMN'), ROOT('ROW'), TYPE);

SET @NewFormatFileXml = @OriginalFormatFileXml;

--remove old column mapping
SET @NewFormatFileXml.modify('
    declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/bulkload/format";
    delete
        (/BCPFORMAT/ROW[1] )[1] 
        ') ;

--add new column mapping
SET @NewFormatFileXml.modify('
    declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/bulkload/format";
    insert sql:variable("@NewFormatFileColumns")
        into (/BCPFORMAT )[1] 
        ') ;
GO

--example usage
DECLARE
    @TableName nvarchar(261) = 'dbo.Person'
    , @OriginalFormatFileXml xml = '<?xml version="1.0"?>
<BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <RECORD>
  <FIELD ID="1" xsi:type="NativeFixed" LENGTH="8"/>
  <FIELD ID="2" xsi:type="NCharPrefix" PREFIX_LENGTH="2" MAX_LENGTH="100" COLLATION="SQL_Latin1_General_CP1_CI_AS"/>
 </RECORD>
 <ROW>
  <COLUMN SOURCE="1" NAME="ID" xsi:type="SQLBIGINT"/>
  <COLUMN SOURCE="2" NAME="Name" xsi:type="SQLNVARCHAR"/>
 </ROW>
</BCPFORMAT>'
    , @NewFormatFileXml xml;

EXEC dbo.usp_GetNewFormatFileXml
      @TableName = @TableName
    , @OriginalFormatFileXml = @OriginalFormatFileXml
    , @NewFormatFileXml = @NewFormatFileXml OUTPUT;

SELECT @NewFormatFileXml;
person Dan Guzman    schedule 23.05.2015
comment
Благодаря за предложението, но, за съжаление, това не работи и все още генерира същия вид грешки в колоните, които виждаме сега. В опростения случай на примерната таблица по-горе това би довело до следната грешка за всяка колона: Invalid character value for cast specification. - person John H; 23.05.2015
comment
@JohnH, файлът с формат също трябва да бъде модифициран, ако целевите таблици имат различна схема. Достатъчно ли е решение, което модифицира форматиращите файлове, за да се справи с повторната последователност на колоните? Имайте предвид, че ще има случаи, в които това не може да бъде напълно автоматизирано, като например добавяне на нова колона, която не позволява нули. - person Dan Guzman; 24.05.2015
comment
В момента съм отворен за всички предложения, Дан. Частичната автоматизация пак би била по-добра от никаква. Всъщност се чудех дали е възможно да сравня реда на колоните на генерирана таблица, програмно, с този на форматиран файл и да коригирам, ако е необходимо. - person John H; 24.05.2015
comment
@JohnH, редактирах отговора си, за да добавя T-SQL частта от решение. Това може да бъде извикано от скрипт на Powershell, след като бъде инсталирана новата схема на база данни. - person Dan Guzman; 25.05.2015
comment
Благодаря ти много за това, Дан. В момента не съм на работа, а утре е национален празник, така че ще изпробвам това във вторник и ще се свържа с вас. Така или иначе, благодаря, че отделихте време да ми дадете толкова подробен отговор; Аз наистина го оценявам. - person John H; 25.05.2015
comment
Благодаря много за това. Това определено е подобрение на това, което имаме в момента, така че благодаря за времето, което отделихте. - person John H; 31.05.2015