Быстрая вставка большого количества записей

DrVavilon
Дата: 10.06.2011 18:07:02
Здравствуйте!

Нужно сгенерировать большой объём тестовых данных - больше 600 000 000 записей. Если делать обычным INSERT INTO ... VALUES, то это займет больше 50 часов. Каким образом можно существенно сократить время выполнения данной процедуры?
Гавриленко Сергей Алексеевич
Дата: 10.06.2011 18:10:03
Сделать файло соответвующего формата и залить bcp.

Еще можно генерировать и заливать разом по 100к - 1кк записей, но все зависит от того, что и чем вы генерируете.
DrVavilon
Дата: 10.06.2011 18:26:21
Гавриленко Сергей Алексеевич
Сделать файло соответвующего формата и залить bcp.


Не совсем понял, как это реализовать. Мне нужно забить тестовыми данными, в которых должна сохраняться определенная логика. Файл же нужно заполнить, чтобы копировать из него.
Maxx
Дата: 10.06.2011 18:33:14
ну так возьмите свой запрос когторый генерит ети миллиона записей - и вылете его в файл тем де bcp ,потом подымите файл в таблицу как вам поповетовали
DrVavilon
Дата: 10.06.2011 19:00:11
Честно говоря, никогда такого не делал( А можно какой-то пример привести? Или пошаговый алгоритм. Был бы очень благодарен
kDnZP
Дата: 10.06.2011 19:23:55
DrVavilon, вот только совсем недавно эксперементировал...

insert into...
select ...

работает медленнее чем

select ...
into ...

Правда данных генерерировал что-то около 5 записей по 6 байт (30000000 штук). Заняло это где-то 15-25 секунд, балк вычитка с KILOBYTES_PER_BATCH = 64 заняла около 63569 ms, тобишь минуту с копейками.
kDnZP
Дата: 10.06.2011 19:25:38
Собственно, вот че делалось:
--CREATE DATABASE tst
--go
--USE tst
--go
--DROP  TABLE tempTbl

SET STATISTICS TIME ON
SELECT  t.a , t.b , t.с , t.d , t.e
INTO tempTbl
FROM (
select a='123456',b='789012',с='345678',d='901234',e='567890'
from
(
select 1 n union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10
) a1
CROSS JOIN
(
select 1 n union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10
) a2
CROSS JOIN
(
select 1 n union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10
) a3
) t CROSS JOIN (
select a=1
from
(
select 1 n union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10
) a1
CROSS JOIN
(
select 1 n union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10
) a2
CROSS JOIN
(
select 1 n union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10
) a3
) t1 CROSS JOIN 
(
SELECT a1.n FROM (
select 1 n union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 10
) a1
CROSS JOIN (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3) a3
) t2
SET STATISTICS TIME OFF

-- bcp "SELECT  a + ',' + b + ',' + с + ',' + d + ',' + e FROM tst.dbo.tempTbl" queryout e:\bulk.txt -c -T -S .\SQLEXPRESS >nul
--TRUNCATE TABLE dbo.tempTbl
SET STATISTICS TIME ON
BULK INSERT dbo.tempTbl FROM 'e:\bulk.txt' WITH (TABLOCK, KILOBYTES_PER_BATCH = 64, ROWTERMINATOR='\n', FIELDTERMINATOR=',')
SET STATISTICS TIME OFF
DrVavilon
Дата: 10.06.2011 20:44:12
kDnZP, Огромное спасибо за пример. Теперь понятно каким путем нужно идти)
Только не понял, как Вы заполняете таблицу tempTbl. Я как раз не могу придумать, как в моем случае её заполнять; Схематически, мне нужно сделать приблизительно следующее:

DECLARE @Index int
SET @Index = 1
WHILE @Index < 100000000
BEGIN
	INSERT INTO Form VALUES (@Index, 'val' + CAST(@Index as varchar(10)), null, null)
	SET @Index = @Index + 1
END

Но, так как SELECT INTO <new table> здесь в цикле использовать нельзя, то нужно эти данные (которые, по сути, получаються в результате циклического обхода) получить одним запросом, как это сделали Вы.

Увы, пока не представляю как...(
NoLock
Дата: 10.06.2011 21:35:05
В одном из моих проектов BULK работал в 20 раз быстрее, чем C# программа, которая разбирала файл и вставляла по одной записи:
insert into T
	(a,b,c)
select 
	a,b,c
from openrowset
(
	bulk 'e:\test.csv',
	FIRSTROW = 2,
	FORMATFILE='e:\testFormat.xml'
) as [src]
GO

Формат файл:
<?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="CharTerm" TERMINATOR="," MAX_LENGTH="12"/>
		<FIELD ID="2" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="2"/>
		<FIELD ID="3" xsi:type="CharTerm" TERMINATOR="," MAX_LENGTH="16"/>
	</RECORD>
	<ROW>
		<COLUMN SOURCE="1" NAME="a" xsi:type="SQLINT"/>
		<COLUMN SOURCE="2" NAME="b" xsi:type="SQLNVARCHAR"/>
		<COLUMN SOURCE="3" NAME="c" xsi:type="SQLNVARCHAR"/>
	</ROW>
</BCPFORMAT>
DrVavilon
Дата: 10.06.2011 21:58:32
Интересно) Правда, у меня сейчас проблема другая. С тем, чтобы прочитать и вставить записи из файла посредством BULK проблем нет. Вопрос в том, как эти данные в таком количестве и формируемые по вышеописанной схеме в этот файл впихнуть?