Existe-t-il un moyen de joindre chaque ligne du tableau A à une ligne du petit tableau B en répétant le tableau B, même si plusieurs fois sont nécessaires?


8

Désolé pour le titre déroutant, je ne savais pas quoi écrire là-bas.

J'ai un tableau de quelques centaines d'enregistrements. J'ai besoin d'affecter chaque enregistrement de cette table à une table dynamique d'utilisateurs beaucoup plus petite, et les utilisateurs doivent alterner quant aux enregistrements qui leur sont attribués.

Par exemple, si TableA est

Row_Number () Id
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10

et TableB est

Row_Number () Id
1 1
2 2
3 3

J'ai besoin d'un jeu de résultats final qui soit

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

J'ai réussi à faire quelque chose d'un peu mal en utilisant l'opérateur mod, mais j'étais curieux de savoir si cette même requête pouvait être exécutée sans la table temporaire et la variable.

La table temporaire est utilisée car TableA est en fait une fonction définie par l'utilisateur qui convertit une chaîne délimitée par des virgules en table, et j'ai besoin du nombre d'objets de l'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

Il est important que les UserIds alternent également. Je ne peux pas affecter le tiers supérieur des enregistrements à User1, le deuxième tiers des enregistrements à User2 et le tiers 1/3 des enregistrements à User3.

De plus, les UserIds doivent maintenir l'ordre dans lequel ils ont été initialement entrés, c'est pourquoi j'ai un Row_Number() OVER (ORDER BY (SELECT 1))dans le tableau de l'utilisateur

Existe-t-il un moyen de joindre ces tables dans une seule requête, donc je n'aurai pas besoin d'utiliser une table temporaire et une variable?

J'utilise SQL Server 2005

Réponses:


12

Une autre façon d'éviter les tables temporaires serait la suivante:

;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, je ne savais pas que je pouvais utiliser Count(*)avec OVER()pour obtenir un nombre d'enregistrements sans vraiment regrouper les enregistrements. Cela me permet de me débarrasser à la fois de la table temporaire et de la variable, merci :)
Rachel

6

Oui, vous pouvez le faire sans aucune table temporaire:

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;

Voir ici pour une démonstration de SQLFiddle


Bien que cela supprime la table temporaire et la variable, je n'aime pas le fait que vous ayez besoin d'exécuter une sous-requête pour chaque enregistrement :)
Rachel

4
@Rachel simplement parce que c'est ainsi qu'il est déclaré ne signifie pas que c'est ainsi qu'il est exécuté.
Aaron Bertrand

@AaronBertrand C'est vrai, mais je n'aime pas faire plus confiance au compilateur SQL pour déterminer un bon plan d'exécution pour moi que je ne le dois. Cela m'a causé des problèmes dans le passé :)
Rachel
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.