Esiste un modo per unire ogni riga di TableA a una riga del TableB più piccolo ripetendo TableB tuttavia sono necessarie molte volte?


8

Scusate il titolo confuso, non ero sicuro di cosa scrivere lì.

Ho una tabella di poche centinaia di dischi. Devo assegnare ciascun record di questa tabella a una tabella dinamica di utenti molto più piccola e gli utenti dovrebbero alternare i record assegnati.

Ad esempio, se TableA lo è

ID numero riga ()
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10

e TableB lo è

ID numero riga ()
1 1
2 2
3 3

Ho bisogno di un set di risultati finali che sia

UserId RecordId
1 1
2 2
3 3
1 4
2 5
3 6
1 7
2 8
3 9
1 10

Sono riuscito a fare qualcosa di un po 'complicato usando l'operatore mod, ma ero curioso di sapere se questa stessa query potesse essere eseguita senza la tabella temporanea e la variabile.

La tabella temporanea viene utilizzata perché TableA è in realtà una funzione definita dall'utente che converte una stringa delimitata da virgole in una tabella e ho bisogno del conteggio degli oggetti dall'UDF.

-- Converts a comma-delimited string into a table
SELECT Num as [UserId], Row_Number() OVER (ORDER BY (SELECT 1)) as [RowNo]
INTO #tmpTest
FROM dbo.StringToNumSet('2,3,1', ',') 

DECLARE @test int
SELECT @test = Count(*) FROM #tmpTest

SELECT *
FROM #tmpTest as T1
INNER JOIN (
    SELECT Top 10 Id, Row_Number() OVER (ORDER BY SomeDateTime) as [RowNo]
    FROM TableA WITH (NOLOCK)
) as T2 ON T1.RowNo = (T2.RowNo % @test) + 1

È importante che anche gli UserId si alternino. Non riesco ad assegnare il primo 1/3 dei record a User1, il secondo 1/3 dei record a User2 e il terzo 1/3 dei record a User3.

Inoltre, gli UserId devono mantenere l'ordine in cui sono stati originariamente inseriti, motivo per cui ne ho uno Row_Number() OVER (ORDER BY (SELECT 1))nella tabella User

Esiste un modo per unire queste tabelle in una singola query, quindi non avrò bisogno di usare una tabella temporanea e una variabile?

Sto usando SQL Server 2005

Risposte:


12

Un altro modo per evitare le tabelle temporanee sarebbe questo:

;WITH tmpTest AS
(
    SELECT  Num as [UserId]
            , Row_Number() OVER (ORDER BY (SELECT 1)) as [RowNo]
            , COUNT(*) OVER() AS Quant
    FROM dbo.StringToNumSet('2,3,1', ',') 
)
SELECT *
FROM tmpTest as T1
INNER JOIN 
    (
        SELECT Top 10 Id
            , Row_Number() OVER (ORDER BY SomeDateTime) as [RowNo]
        FROM TableA WITH (NOLOCK)
    ) as T2 ON T1.RowNo = (T2.RowNo % Quant) + 1;

Ahhh non mi rendo conto che potrei usare Count(*)con OVER()per ottenere un numero di record senza realmente raggruppare i record. Questo mi permette di eliminare sia la tabella temporanea che la variabile, grazie :)
Rachel,

6

Sì, puoi farlo senza tabelle temporanee:

with w as (select usr_id, row_number() over (order by usr_id) as usr_ordinal from usr)
select record_id, ( select usr_id
                    from w
                    where usr_ordinal=1+(record_ordinal%(select count(*) from w))
                  ) as usr_id
from ( select record_id, row_number() over (order by record_id) as record_ordinal 
       from record ) as z;

Vedi qui per una demo di SQLFiddle


Mentre questo elimina la tabella temporanea e la variabile, non mi piace il fatto che è necessario eseguire una query secondaria per ogni record :)
Rachel

4
@ Rachel solo perché è così che viene dichiarato non significa che sia così che viene eseguito.
Aaron Bertrand

@AaronBertrand È vero, ma non mi piace fidarmi del compilatore di SQL per determinare un buon piano di esecuzione per me più di quanto sia necessario. Mi ha causato problemi in passato :)
Rachel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.