Как запустить большой сценарий со многими вставками без исчерпания памяти?

28

Вопрос:

У меня есть скрипт с около 45 тысяч вставок из отборных высказываний. Когда я пытаюсь запустить его, я получаю сообщение об ошибке, в котором говорится, что у меня недостаточно памяти. Как мне запустить этот скрипт?

Контекст:

  1. Добавлены некоторые новые поля данных, чтобы приложение работало с другим приложением, которое использует клиент.
  2. Получил электронную таблицу данных от клиента, полную данных, которые отображали текущие элементы данных в значения для этих новых полей.
  3. Преобразованная электронная таблица для вставки выписок.
  4. Если я только запускаю некоторые операторы, это работает, но весь сценарий - нет.
  5. Нет. Опечаток нет.

Если есть другой способ, которым я должен загружать эти данные, не стесняйтесь наказать меня и сообщить мне.

spaghetticowboy
источник
Аналогичный вопрос для SO: ( stackoverflow.com/questions/222442/… ) Не уверен, что ответ поможет
jumpdart

Ответы:

17

Максимальный размер пакета для SQL Server 2005 составляет 65 536 * Размер сетевого пакета (NPS), где NPS обычно составляет 4 КБ. Это работает до 256 МБ. Это будет означать, что ваши операторы вставки будут в среднем 5,8 КБ каждый. Это кажется неправильным, но, возможно, там есть посторонние места или что-то необычное.

Моим первым предложением было бы ставить оператор «GO» после каждого оператора INSERT. Это разделит ваш отдельный пакет из 45 000 операторов INSERT на 45 000 отдельных пакетов. Это должно быть легче переварить. Будьте осторожны, если одна из этих вставок даст сбой, вам может быть трудно найти виновного. Возможно, вы захотите защитить себя с помощью транзакции. Вы можете быстро добавить эти операторы, если у вашего редактора есть хороший поиск и замена (который позволит вам искать и заменять возвращаемые символы, такие как \ r \ n) или макрос.

Второе предложение - использовать мастер для импорта данных прямо из Excel. Мастер создает небольшой пакет служб SSIS для вас за кулисами, а затем запускает его. У него не будет этой проблемы.

пролив дарина
источник
2
А GOпосле каждого заявления? Ну, я думаю, если вы генерируете их, используя другой скрипт, это нормально. В противном случае я бы просто ставил один раз в 1000 INSERTс. Что касается того, чтобы сделать транзакцию атомарной и минимизировать размер транзакции, почему бы не загрузить все строки во временную таблицу или переменную таблицы, а затем загрузить их одним выстрелом оттуда в целевую таблицу?
Ник Чаммас
1000 так же хорош, как 1, но сложнее сосчитать. Честно говоря, он мог бы сойти с рук только с одним заявлением GO, на полпути, около заявления 21,500. Мне нравится исправление GO, потому что оно не требует сложного редактирования текущего скрипта или подсчета операторов INSERT (которые могут не отображаться непосредственно в номера строк).
пролив
2
Конечно, даже плохое приближение 1000 утверждений достаточно хорошо. :)
Ник Чаммас
1
Добавление GO было быстрым и простым исправлением. 25-мегабайтный скрипт выполняется без проблем менее чем за 9 минут. Хотел иметь его в качестве скрипта, чтобы он оставался в рамках нашего стандартного процесса развертывания патчей, когда он выйдет.
spaghetticowboy
14

BULK INSERTили bcpкажутся более подходящими варианты, чем 45 000 операторов вставки.

Если вам нужно придерживаться операторов вставки, я бы рассмотрел несколько вариантов:

A: Используйте транзакции и упаковывайте пакеты по 100, 500 или 1000 операторов в каждом, чтобы минимизировать влияние на журнал и пакет. например

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

B: Вместо отдельных операторов вставки используйте UNION ALLпо 100 или 500 операторов за раз, например

INSERT dbo.table(a, ...)
SELECT 1, ...
UNION ALL SELECT 2, ...
...
UNION ALL SELECT 500, ...
GO

INSERT dbo.table(a, ...)
SELECT 501, ...
UNION ALL SELECT 502, ...
...
UNION ALL SELECT 1000, ...
GO

Для краткости я оставил обработку ошибок, но суть в том, что я никогда не буду пытаться отправить в SQL Server один пакет из 45 000 отдельных операторов.

Аарон Бертран
источник
1
Жаль, что OP не может использовать конструкторы табличных значений , особенность 2008+. Ему все равно придется группировать вставки в группы по 1000 строк, что является максимумом, который вы можете сгруппировать вместе с TVC.
Ник Чаммас
Это было мое первое предложение, пока я не увидел тег версии.
Аарон Бертран
2
@NickChammas - их производительность нелинейно падает с количеством значений в терминах . Я отправил элемент подключения с повторением вставки 1000 строк с 10 VARCHAR(800)столбцами в 2008 году со временем компиляции 12,5 минут на моем экземпляре разработчика 2008 года, поскольку он выполняет много ненужной работы, сравнивая значения, а не просто вставляя их (выполняет много быстрее при параметризации и нет значений для просмотра). Несмотря на значительное улучшение в 2012 году, нелинейный шаблон все еще существует и должен быть исправлен в версии после.
Мартин Смит
9

Я не уверен, почему вы получаете ошибку нехватки памяти, но есть более простой подход.

Если вы можете экспортировать данные из электронной таблицы в формат с разделителями (например, csv), вы можете использовать мастер импорта данных в SSMS, чтобы вставить данные для вас:

Задача импорта данных SSMS.

datagod
источник
это полезно, но у меня нет доступа к базам данных клиентов. Я должен подготовить патчи и данные в сценариях
spaghetticowboy
0

Используя несколько SqlBulkCopy, создайте временную таблицу. Вставьте новые данные во временную таблицу, затем объедините данные из временной таблицы в существующую. Пример использования метода C # SqlBulkCopy.WriteToServer (DataTable) . Надеюсь, это поможет

Хунг Ву
источник
0

Да, мы могли бы сделать это, я попытался использовать подход BCP (Bulk Copy Program), чтобы избежать проблемы OutOfMemory .

Примечание . Пробовал на SQL Server 2014.

В BCP сначала нам нужно экспортировать данные базы данных источника в файл bcp (в папке локального каталога), а затем импортировать этот файл bcp в базу данных назначения.

введите описание изображения здесь

Ниже приведены шаги по пирогу:

Заметка:

а) Убедитесь, что пустая таблица присутствует в базе данных назначения

б) Убедитесь, что папка Temp присутствует на диске C

  1. Создайте файл bat с именем Export_Data.bat с помощью команды, показанной ниже:

    bcp.exe [Source_DataBase_Name].[dbo].[TableName] OUT "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    Пауза

  2. Запустите этот bat-файл, в результате чего в папке Temp будет сгенерирован bcp- файл.

  3. Затем создайте другой файл bat с именем Import_Data.bat с помощью следующей команды:

    bcp.exe [Destination_DataBase_Name].[dbo].[TableName] IN "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    Пауза

И здесь мы идем!

Kms
источник
Получение ошибки «Для параметров in, out или format требуется правильное имя таблицы». когда пытался экспортировать данные.
Сен Джейкоб
1
Не могли бы вы вставить команду, которую вы пробовали, со всеми значениями атрибута. Пожалуйста, следуйте приведенному ниже примеру: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q В этом [ExportDB -> Исходная БД, AddressCountry-> Таблица, присутствующая в Исходной БД, IN-L20054 -> Имя машины, "sa" - это имя пользователя / pwd БД]
Kms
У меня его сейчас нет. Я закончил тем, что использовал функцию импорта данных в SSMS. Затем подключил целевую БД (v14.0) к исходной БД (v.15.0), используя соединение MS OLE DB, и было достаточно быстро импортировать многомиллионные строки данных. Благодарность!
Сен Джейкоб