2009-06-24 6 views
13

Я хочу добавить переменное количество записей в таблице (дней)Вставка п количество записей с T-SQL

И я видел изящное решение для этого:

SET @nRecords=DATEDIFF(d,'2009-01-01',getdate()) 
SET ROWCOUNT @nRecords 
INSERT int(identity,0,1) INTO #temp FROM sysobjects a,sysobjects b 
SET ROWCOUNT 0 

Но к сожалению, это не работает в UDF (потому что #temp и SET ROWCOUNT). Любая идея, как это может быть достигнуто?

В настоящий момент я делаю это с переменной WHILE и переменной таблицы, но с точки зрения производительности это не очень хорошее решение.

ответ

3

это подход, который я Я использую и лучше всего использую свои цели и использую SQL 2000. Поскольку в моем случае это внутри UDF, я не могу использовать ## или # временные таблицы, поэтому я использую переменную таблицы. я делаю:

DECLARE @tblRows TABLE (pos int identity(0,1), num int) 
DECLARE @numRows int,@i int 


SET @numRows = DATEDIFF(dd,@start,@end) + 1 
SET @i=1 

WHILE @i<@numRows 
begin 
    INSERT @tblRows SELECT TOP 1 1 FROM sysobjects a 

    SET @[email protected]+1 
end 
0

Вы могли бы сделать то, что PinalDave предлагает:

INSERT INTO MyTable (FirstCol, SecondCol) 
SELECT 'First' ,1 
UNION ALL 
SELECT 'Second' ,2 
UNION ALL 
SELECT 'Third' ,3 
UNION ALL 
SELECT 'Fourth' ,4 
UNION ALL 
SELECT 'Fifth' ,5 
GO 
+0

, но мне нужно добавить n строк ... это может быть 2, это может быть 2 000! Это может привести к созданию динамического запроса, все еще использующего цикл while, а затем сделать вставку в конце, но это не будет работать в UDF anywyay. –

+2

Пожалуйста, не цитируйте PinalDave ... – gbn

2

вы можете использовать перекрестное соединение

select top 100000 row_number() over(order by t1.number)-- here you can change 100000 to a number you want or a variable 
from master.dbo.spt_values t1 
     cross join master.dbo.spt_values t2 
11

Вы можете использовать WHILE заявление о том, что:

declare @i int 
declare @rows_to_insert int 
set @i = 0 
set @rows_to_insert = 1000 

while @i < @rows_to_insert 
    begin 
    INSERT INTO #temp VALUES (@i) 
    set @i = @i + 1 
    end 
+0

Это подход, который я использую и наилучшим образом использую для своих целей и использую SQL 2000. Так как в моем случае это внутри UDF, я не могу использовать ## или # временные таблицы поэтому я использую переменную таблицы, как показано в моем ответе –

16

Если вы» используя SQL 2005 или новее, вы можете использовать рекурсивный CTE для получения списка дат или цифр ...

with MyCte AS 
    (select MyCounter = 0 
    UNION ALL 
    SELECT MyCounter + 1 
    FROM  MyCte 
    where MyCounter < DATEDIFF(d,'2009-01-01',getdate())) 
select MyCounter, DATEADD(d, MyCounter, '2009-01-01') 
from MyCte 
option (maxrecursion 0) 


/* output... 
MyCounter MyDate 
----------- ----------------------- 
0   2009-01-01 00:00:00.000 
1   2009-01-02 00:00:00.000 
2   2009-01-03 00:00:00.000 
3   2009-01-04 00:00:00.000 
4   2009-01-05 00:00:00.000 
5   2009-01-06 00:00:00.000 
.... 
170   2009-06-20 00:00:00.000 
171   2009-06-21 00:00:00.000 
172   2009-06-22 00:00:00.000 
173   2009-06-23 00:00:00.000 
174   2009-06-24 00:00:00.000 

(175 row(s) affected) 

*/ 
+3

Я думаю, что этот подход должен быть ограничен относительно небольшими наборами результатов, например, 1000 или менее. Рекурсивные операции начинают заметно завышать даже за 100 000 результатов. Тем не менее, он все еще должен быть самым опрятным. – SurroundedByFish

+0

Несмотря на отрицательную производительность рекурсивного CTE, если вы используете это для генерации строк для вставки, это позволит вам сделать набор, основанный на вставке. Использование вышеприведенного для вставки на основе набора, вероятно, будет намного быстрее, чем вставка одной записи за раз в цикле. – AaronLS

2

Когда у вас есть таблица преднастроенным номера, просто использовать, что:

SELECT * 
FROM numbers 
WHERE number <= DATEDIFF(d,'2009-01-01',getdate()) 

Есть любое количество методов для построения таблицы чисел в первую очередь (с использованием методов здесь), но как только он будет создан и проиндексирован, вы не создадите его снова.

+0

Большинство людей, особенно из кодеров, смеялись над идеей иметь таблицу, которая просто заполнена миллионом или более последовательными номерами. Однако, я думаю, это на самом деле лучшее решение здесь. То есть, это решение, которое наименее разрывает намерение операций на основе набора и не является взломом. Кодеры думают в итерации и рекурсии, но у них есть проблемы с пониманием операций на основе набора. Таблица «числа» будет занимать постоянный, относительно небольшой объем пространства и возвращать чрезвычайно быстрые результаты даже для массивных наборов результатов. Но эй, это не сексуально. – SurroundedByFish

+0

Независимо от того, строите ли вы его на лету, и как бы вы его не построили, это все равно таблица чисел (даже если она замаскирована под серию дат). –

0

Как насчет:

DECLARE @nRecords INT 

SET @nRecords=DATEDIFF(d,'2009-01-01',getdate()) 

SELECT TOP (@nRecords) 
    ROW_NUMBER() OVER (ORDER BY a.object_id, b.object_id) - 1 
FROM sys.objects a, sys.objects b 

Если вы не хотите его нулевой индекс, удалите "- 1"

Требуется по крайней мере SQL Server 2005.

+0

Извините, забыли упомянуть, что я на SQL 2000 –

+0

В этом случае вы строго ограничены в том, что вы можете сделать, и почти ни одно из предлагаемых здесь решений не будет работать. Cade Roux's, вероятно, ваш лучший выбор. – GalacticCowboy

3

В целом гораздо быстрее, чтобы удвоить количество строк на каждой итерации

CREATE TABLE dbo.Numbers(n INT NOT NULL PRIMARY KEY) 
GO 
DECLARE @i INT; 
SET @i = 1; 
INSERT INTO dbo.Numbers(n) SELECT 1; 
WHILE @i<128000 BEGIN 
    INSERT INTO dbo.Numbers(n) 
    SELECT n + @i FROM dbo.Numbers; 
    SET @i = @i * 2; 
END; 

Я намеренно не SET NOCOUNT ON, так что вы видите, как он вставляет 1,2 , 4,8 строки

Смежные вопросы