Existe maneira de associar todas as linhas da Tabela A a uma linha da Tabela B menor, repetindo a Tabela B, quantas vezes forem necessárias?

8

Desculpe pelo título confuso, eu não tinha certeza do que escrever lá.

Eu tenho uma tabela de algumas centenas de registros. Preciso atribuir cada registro desta tabela a uma tabela dinâmica muito menor de usuários, e os usuários devem alternar quanto aos registros que são atribuídos.

Por exemplo, se TableA for

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

e TableB é

ID Row_Number ()
1 1
2 2
3 3

Eu preciso de um conjunto de resultados finais que seja

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

Eu consegui fazer algo um pouco confuso usando o operador mod, mas fiquei curioso para saber se essa mesma consulta poderia ser executada sem a tabela temporária e a variável.

A tabela temporária é usada porque TableA é na verdade uma função definida pelo usuário que converte uma cadeia de caracteres delimitada por vírgulas em uma tabela, e eu preciso da contagem dos objetos do 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 que os UserIds também se alternem. Não posso atribuir 1/3 dos registros principais ao Usuário1, 1/3 dos registros ao Usuário2 e 1/3 dos registros ao Usuário3.

Além disso, os UserIds precisam manter a ordem em que foram originalmente inseridos, e é por isso que tenho um Row_Number() OVER (ORDER BY (SELECT 1))na tabela do usuário

Existe uma maneira de associar essas tabelas em uma única consulta, para que eu não precise usar uma tabela e variável temporárias?

Estou usando o SQL Server 2005

Rachel
fonte

Respostas:

12

Outra maneira de evitar tabelas temporárias seria:

;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;
Lamak
fonte
Ahhh eu não sabia que eu poderia usar Count(*)com OVER()para obter uma contagem de registros sem realmente agrupando os registros. Isso me permite se livrar de tanto a tabela temporária e variável, obrigado :)
Rachel
6

Sim, você pode fazer isso sem nenhuma tabela temporária:

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;

Veja aqui uma demonstração do SQLFiddle

Jack diz que tenta topanswers.xyz
fonte
Enquanto isso não se livrar da tabela temporária e variável, eu não gosto do fato de que você precisa para executar uma consulta sub para cada registro :)
Rachel
4
@ Rachel só porque é assim que é declarada, não significa que é assim que é executada.
Aaron Bertrand
@AaronBertrand Isso é verdade, mas não gosto de confiar no compilador do SQL para determinar um bom plano de execução para mim mais do que eu preciso. É causado problemas para mim no :) passado
Rachel