2011-01-26 17 views
27

Sto cercando di inserire da una tabella in un altro utilizzandoSQL Server 2005 ROW_NUMBER() senza ORDER BY

DECLARE @IDOffset int; 
SELECT @IDOffset = MAX(ISNULL(ID,0)) FROM TargetTable 

INSERT INTO TargetTable(ID, FIELD) 
SELECT [Increment] + @IDOffset ,FeildValue 
FROM SourceTable 
WHERE [somecondition] 

TargetTable.ID non è una colonna di identità, che è il motivo per cui devo trovare un modo per auto -incremento me stesso

So che posso usare un cursore, o creare una variabile di tabella con una colonna Identity e un campo FieldValue, popolarlo, quindi usarlo nel mio insert into...select, ma non è molto efficiente. Ho provato a utilizzare la funzione ROW_NUMBER per incrementare, ma in realtà non ho un campo ORDER BY legittimo in SourceTable che posso utilizzare e vorrei mantenere l'ordine originale di SourceTable (se possibile).

Qualcuno può suggerire qualcosa?

+2

Qual è l'indice cluster della tabella di origine? Suppongo che sia di questo che stai parlando quando dici "l'ordine originale della SourceTable"? Se è un mucchio non c'è un ordine particolare. –

risposta

70

È possibile evitare di specificare un ordinamento esplicito come segue:

INSERT dbo.TargetTable (ID, FIELD) 
SELECT 
    Row_Number() OVER (ORDER BY (SELECT 1)) 
     + Coalesce(
     (SELECT Max(ID) FROM dbo.TargetTable WITH (TABLOCKX, HOLDLOCK)), 
     0 
    ), 
    FieldValue 
FROM dbo.SourceTable 
WHERE {somecondition}; 

Tuttavia, si ricorda che è solo un modo per evitare di specificare un ordinamento e non garantisce che qualsiasi ordinamento dei dati originali saranno conservati. Esistono altri fattori che possono causare l'ordinamento dei risultati, ad esempio un ORDER BY nella query esterna. Per comprendere appieno questo, bisogna rendersi conto che il concetto "non ordinato (in un modo particolare)" non è lo stesso di "mantenere l'ordine originale" (che è ordinato in un modo particolare!). Credo che da un punto di vista del database relazionale puro, il secondo concetto non esiste, per definizione (anche se potrebbero esserci implementazioni di database che violano questo, SQL Server non è tra queste).

Il motivo per i suggerimenti di blocco è impedire il caso in cui alcuni altri processi inseriscono utilizzando il valore che si intende utilizzare, tra le parti dell'esecuzione della query.

Nota: Molte persone usano (SELECT NULL) per aggirare la restrizione "nessuna costante consentita nella clausola ORDER BY di una funzione di finestratura". Per qualche ragione, preferisco 1 su NULL.

Inoltre: Penso che una colonna di identità sia di gran lunga superiore e dovrebbe essere utilizzata al suo posto. Non è positivo per la concorrenza bloccare esclusivamente tabelle intere. Understatement.

+0

@Emtucifor: il primo funziona correttamente. Per quanto riguarda il secondo, il fatto è che restituisce un risultato, una riga, in ogni caso. Se la tabella è vuota, restituisce NULL, quindi non c'è bisogno di sottoselezione, basta usare ISNULL() su MAX(), ma non al suo interno. In qualche modo, in un modo o nell'altro, sono riuscito a respingerlo, anzi, forse non avrei dovuto pensarci molto. Una semplice coincidenza lascia che impari diversamente quando leggo il post di qualcuno qui su SO (non ricordo ora quale). L'ho controllato prima di postare la mia correzione. Perché è meglio? Beh, penso che sia più semplice e più facile da leggere. –

+0

Questo non è necessariamente corretto, se la query esterna ha una clausola 'ORDER BY', o in altri casi limite come può essere visto qui: http://stackoverflow.com/q/18961789/521799 –

+0

@LukasEder La mia risposta era corretta , ma forse non era così completo come avrebbe potuto essere.Se qualcuno cercava "preservare l'ordinamento originale" e non sapesse che questo è un concetto senza senso in SQL Server, potrebbe tentare di usare questo codice per ottenerlo, ma fallirebbe. Ho ora aggiornato la mia risposta per affrontare questo aspetto. – ErikE

2

È possibile ignorare l'ordinamento utilizzando order by (select null) come questo:

declare @IDOffset int; 
select @IDOffset = max(isnull(ID, 0)) from TargetTable 

insert into TargetTable(ID, FIELD) 
select row_number() over (order by (select null)) + @IDOffset, FeildValue 
    from SourceTable 
where [somecondition] 
+1

Rispetto alla risposta che era già stata qui per cinque anni, cosa aggiunge? Non fornisce alcun nuovo approccio. Non fornisce alcuna nuova spiegazione. Non fornisce nulla di nuovo. – hvd