¿Hay alguna forma de unir cada fila de la Tabla A a una fila de la Tabla B más pequeña repitiendo la Tabla B sin importar cuántas veces sea necesario?


8

Perdón por el título confuso, no estaba seguro de qué escribir allí.

Tengo una tabla de unos cientos de registros. Necesito asignar cada registro de esta tabla a una tabla dinámica de usuarios mucho más pequeña, y los usuarios deben alternar en cuanto a qué registros se les asignan.

Por ejemplo, si TableA es

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

y TableB es

Id de Row_Number ()
1 1
2 2
3 3

Necesito un conjunto de resultados finales que sea

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

Me las arreglé para hacer algo un poco desordenado usando el operador mod, pero tenía curiosidad por saber si esta misma consulta podría ejecutarse sin la tabla temporal y la variable.

La tabla temporal se usa porque la Tabla A es en realidad una función definida por el usuario que convierte una cadena delimitada por comas en una tabla, y necesito el recuento de los objetos del 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

Es importante que los ID de usuario también se alternen. No puedo asignar el 1/3 superior de los registros al Usuario1, el segundo 1/3 de los registros al Usuario2 y el tercero 1/3 de los registros al Usuario3.

Además, los ID de usuario deben mantener el orden en el que se ingresaron originalmente, por lo que tengo un Row_Number() OVER (ORDER BY (SELECT 1))en la tabla del usuario

¿Hay alguna forma de unir estas tablas en una sola consulta para que no necesite usar una tabla temporal y una variable?

Estoy usando SQL Server 2005

Respuestas:


12

Otra forma de evitar tablas temporales sería esta:

;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 no me di cuenta que podía usar Count(*)con OVER()para obtener un número de registros sin tener que agrupar los registros. Esto me permite deshacerme tanto de la tabla temporal como de la variable, gracias :)
Rachel

6

Sí, puede hacer esto sin ninguna tabla temporal:

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;

Vea aquí para una demostración de SQLFiddle


Si bien esto elimina la tabla temporal y la variable, no me gusta el hecho de que deba ejecutar una subconsulta para cada registro :)
Rachel

44
@Rachel solo porque así se declare no significa que se ejecute así.
Aaron Bertrand

@AaronBertrand Eso es cierto, pero no me gusta confiar en el compilador de SQL para determinar un buen plan de ejecución para mí más de lo que debo. Me causó problemas en el pasado :)
Rachel
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.