2009-06-24 4 views
13

voglio aggiungere un numero variabile di record di una tabella (giorni)Inserimento di n numero di record con T-SQL

e ho visto una soluzione ordinata per questo:

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 

Ma purtroppo questo non funziona in una UDF (perché il #temp e il SET ROWCOUNT). Qualche idea su come ciò potrebbe essere realizzato?

Al momento lo sto facendo con una variabile WHILE e una variabile di tabella, ma in termini di prestazioni non è una buona soluzione.

risposta

3

questo è l'approccio che ho sto usando e funziona meglio per i miei scopi e usando SQL 2000. Perché nel mio caso si trova all'interno di una UDF, non posso usare le tabelle temporanee ## o # quindi uso una variabile di tabella. che sto facendo:

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

Si potrebbe fare ciò che PinalDave suggerisce:

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

ma ho bisogno di aggiungere n righe ... potrebbe essere 2 potrebbe essere 2.000! Questo potrebbe funzionare a costruire una query dinamica usando comunque il ciclo while e poi fare l'insert alla fine, ma non funzionerebbe in una UDF anywayay. –

+2

Si prega di non citare PinalDave ... – gbn

2

è possibile utilizzare un cross join

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

È possibile utilizzare l'istruzione while per questo:

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

questo è l'approccio che sto usando e funziona meglio per i miei scopi e utilizzando SQL 2000. Perché nel mio caso è all'interno di una UDF, non posso usare tabelle temporanee ## o # quindi uso una variabile di tabella come mostrato nella mia risposta –

16

Se si' utilizzando SQL 2005 o versioni successive, è possibile utilizzare un CTE ricorsivo per ottenere un elenco di date o numeri ...

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

Penso che questo approccio dovrebbe essere limitato a set di risultati relativamente piccoli come 1.000 o meno. Le operazioni ricorsive iniziano ad essere visibilmente costose anche per 100.000 risultati. Tuttavia, deve ancora essere la cosa più bella qui. – SurroundedByFish

+0

Nonostante le prestazioni negative del CTE ricorsivo, se si utilizza questo per generare righe per un inserto, ciò consentirà di eseguire un inserimento basato su set. L'utilizzo di quanto sopra per un inserto basato su serie sarà probabilmente molto più veloce dell'inserimento di un singolo record alla volta in un ciclo. – AaronLS

2

Quando si dispone di una tabella di numeri pre-costruiti, basta usare che:

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

Ci sono un certo numero di tecniche per costruire la tabella di numeri, in primo luogo (utilizzando tecniche qui), ma una volta che è stato creato e indicizzato, non lo ricostruisci.

+0

La maggior parte delle persone, in particolare dagli sfondi di coder, riderebbe dell'idea di avere un tavolo pieno di un milione o più numeri sequenziali. Tuttavia, penso che questa sia in realtà la soluzione migliore qui. Vale a dire, è la soluzione che rompe l'intenzione delle operazioni basate su set meno e non è un hack. I programmatori pensano in iterazione e ricorsione, ma hanno difficoltà a comprendere le operazioni basate su set. La tabella "numeri" occuperà una quantità di spazio costante e relativamente piccola e restituirà risultati estremamente rapidi anche per serie di risultati massicci. Ma hey, non è sexy. – SurroundedByFish

+0

Se lo costruisci al volo e comunque lo costruisci, è comunque una tabella di numeri (anche se è camuffato da una serie di date). –

0

ne dite:

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 

Se non si vuole che lo zero-indicizzato, rimuovere il "- 1"

Richiede almeno SQL Server 2005.

+0

scusate, ho dimenticato di dire che sono su SQL 2000 –

+0

In tal caso, sei * severamente * limitato in quello che puoi fare, e quasi nessuna delle soluzioni suggerite qui funzionerà. Cade Roux è probabilmente la scelta migliore. – GalacticCowboy

3

Nel complesso molto più veloce per raddoppiare la quantità di righe ad ogni iterazione

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; 

Ho deliberatamente non ha definito NOCOUNT ON, in modo che si vede come si inserisce 1,2 , 4,8 righe