Использование bcp.exe для успешного импорта данных, даже если порядок столбцов в файле формата отличается от порядка столбцов в таблице

Я создавал систему для своей команды, которая позволяет нам автоматически генерировать большие объемы тестовых данных, чтобы мы могли возвращать нашу базу данных в согласованное состояние, когда нам это нужно. Это должно быть полностью автоматизировано, но в настоящее время у меня есть одна проблема, которая не позволяет этому произойти.

Наша база данных создается с помощью Entity Framework, и мы также используем миграции. Проблема в том, что, поскольку порядок столбцов может измениться из-за миграции, это означает, что тестовые данные, которые были экспортированы до миграции, могут больше не импортироваться правильно при использовании bcp.

Чтобы проиллюстрировать это более четко, вот шаги для воспроизведения поведения:

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

dbo.Person - исходный порядок столбцов

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

C:\bcp [BCPTest].[dbo].[Person] формат nul -f person.xml -x -N -T -S .\SQLExpress

Создайте бинарный выходной файл:

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

Измените порядок столбцов и сохраните:

dbo.Person — порядок миграции

Попробуйте импортировать:

C:\bcp [BCPTest].[dbo].[Person] в 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