Как сохранить порядок строк с помощью SqlBulkCopy? - PullRequest
3 голосов
/ 10 октября 2008

Я экспортирую данные программно из Excel в SQL Server 2005 с помощью SqlBulkCopy. Он прекрасно работает, единственная проблема у меня заключается в том, что он не сохраняет последовательность строк, которую я имею в файле Excel. У меня нет столбца для сортировки, я просто хочу, чтобы записи были вставлены в том же порядке, в котором они отображаются в электронной таблице Excel.

Я не могу изменить файл Excel, и мне приходится работать с тем, что у меня есть. Сортировка по любому из существующих столбцов нарушит последовательность.

Пожалуйста, помогите.

P.S. Закончилось вставкой столбца идентификатора в электронную таблицу, похоже, что нет способа сохранить порядок во время экспорта / импорта

Ответы [ 4 ]

3 голосов
/ 10 октября 2008

Я не думаю, что порядок строк определяется или гарантируется SQL, если вы не используете предложение "ORDER BY".

Из поста Билла Вона (http://betav.com/blog/billva/2008/08/sql_server_indexing_tips_and_t.html):

Использование Order By: даже если таблица имеет кластерный индекс (который хранит данные в физическом порядке), SQL Server не гарантирует, что строки будут вернулся в этом (или любой конкретный) порядок, если условие ORDER BY б.

Другая ссылка с информацией:

http://sqlblogcasts.com/blogs/simons/archive/2007/08/21/What-is-the-position-of-a-row--.aspx

1 голос
/ 29 апреля 2017

После многих исследований кажется очевидным, что невозможно сохранить порядок строк с помощью команды «Массовая вставка», написанной Microsoft. Вы должны либо сами добавить столбец идентификатора непосредственно в файл импорта, либо использовать оболочку или другой внешний скрипт, либо без этого. Похоже, что это была бы необходимая (и простая) функция для Microsoft, но после более чем десятилетия ничего от них не произойдет.

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

Так что я пошел другим путем. Мои ограничения были:

  • Я вообще не мог изменить исходный файл. (и создать плохой прецедент!)
  • Я не мог использовать внешний скрипт. Слишком сложно. Это должно было быть простое решение на основе T-Sql, без выполнения CMD. Для этого нужно было выполнить одну процедуру, чтобы ее можно было автоматизировать.

Мне понравилась логика использования Powershell для создания упорядоченных операторов вставки для каждой строки с последующим запуском в Sql. По сути, это ставило в очередь каждую запись для отдельной вставки, а не для БОЛЬШОЙ вставки. Да, это будет работать, но это также будет очень медленно. У меня часто есть файлы с 500K + строк в них. Мне нужно что-то БЫСТРОЕ.

Итак, я столкнулся с XML. Массовая загрузка файла напрямую в одну переменную XML. Это сохранит порядок записей при добавлении каждой в XML. Затем проанализируйте переменную XML и вставьте результаты в таблицу, одновременно добавив столбец идентификаторов.

Существует предположение, что файл импорта является стандартным текстовым файлом, где каждая запись заканчивается строкой (Char (13) + Char (10))

Мой подход состоит из 2 шагов:

  1. Выполните инструкцию IMPORT SQL (используя OPENROWSET), инкапсулируя каждую запись с тегами XML. Захват результатов в переменную XML.

  2. Разобрать переменную по тегам XML в таблицу, добавив столбец [ID] с приращением.

    ---------------------------------
    Declare @X xml;
    ---------------------------------
    SELECT @X=Cast('<X>'+Replace([BulkColumn],Char(13)+Char(10),'</X><X>')+'</X>' as XML)
    FROM OPENROWSET (BULK N'\\FileServer\ImportFolder\ImportFile_20170120.csv',SINGLE_CLOB) T
    ---------------------------------
    SELECT [Record].[X].query('.').value('.','varchar(max)') [Record]
    ,ROW_NUMBER() OVER (ORDER BY (SELECT 100)) [ID]
    --Into #TEMP 
    FROM @X.nodes('X') [Record](X);
    ---------------------------------
    
    • Теги XML заменяют каждый перевод строки.

    • Если файл заканчивается переводом строки, это приведет к добавлению пустой строки в конце. Просто удалите последнюю строку.

Я записал это в свою процедуру, используя динамический sql, чтобы я мог передать FileName и установить идентификатор, начинающийся с 1 или 0 (в случае, если есть строка заголовка).

Мне удалось запустить это для файла с 300K-записями примерно за 5 секунд.

0 голосов
/ 10 октября 2008

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

0 голосов
/ 10 октября 2008

Если вы можете сохранить электронную таблицу Excel как CSV, очень легко создать список операторов INSERT с любым языком сценариев, который будет выполняться в том же порядке, что и электронная таблица. Вот быстрый пример в Groovy, но любой язык сценариев сделает это так же легко, если не проще:

def file1 = new File('c:\\temp\\yourSpreadsheet.csv')
def file2 = new File('c:\\temp\\yourInsertScript.sql')

def reader = new FileReader(file1)
def writer = new FileWriter(file2)

reader.transformLine(writer) { line ->
    fields =  line.split(',')

    text = """INSERT INTO table1 (col1, col2, col3) VALUES ('${fields[0]}', '${fields[1]}', '${fields[2]}');"""

}

Затем вы можете выполнить свой yourInsertScript.sql для вашей базы данных, и ваш порядок будет таким же, как у вашей электронной таблицы.

...