Salida de columna variable

¿Puedo usar un bucle para crear un número variable de columnas en función de un cálculo?

Tabla A: tiene columnas id, nombre, fecha, artículo. Muestra quién compró un artículo en qué día. El nombre aparece varias veces cada vez que se compra un artículo. la identificación es única.

Tabla B: tiene nombre, fecha de inicio. Muestra cuando el nombre comenzó a funcionar.

(Sé cómo asociar el nombre con la fecha de inicio, por lo que no está realmente en el pseudocódigo a continuación).

La tabla C es como quiero que se vean mis resultados:

name   itemsday1 itemsday2 itemsday3... itemsdayn

la segunda columna corresponde a artículos comprados hoy, la última columna a artículos comprados antes de la fecha de inicio del nombre. El número de columnas debe variar según la persona que comenzó primero. Quiero que mi consulta haga un bucle y cree una columna para cada día desde la fecha de inicio hasta hoy, donde cuente la cantidad de artículos comprados por Nombre ese día.

Código Psuedo:

declare mostd INT
select datediff(dd, begindate, getdate()) as spans  into #span from B
set mostd = select max(spans) from #span

select
    name
    ,while mostd !=0
        begin
            case when???
                count(items)
            end as column.mostd
            mostd=mostd-1
        end
    end
from A (join B blah blah)
group by name

Results
NAME    column4    column3    column2    column1    column0
James   9          8          7          6          NULL
Wade    5          4          3          NULL       NULL
Bosh    2          1          NULL       NULL       NULL
Durant  0          9          8          NULL       NULL

¿Puede TSQL hacer esto? ¿Puedes ayudarme a solidificar esto en código real? Gracias.

preguntado el 12 de junio de 12 a las 19:06

3 Respuestas

Necesitará usar SQL dinámico para hacer esto. Empezar con

DECLARE @SelectSQL varchar(max) = "SELECT name";

Luego haga su bucle while, rastreando un contador de enteros para el nombre del campo (si realmente quiere hacer esto en orden inverso, necesitará usar DATEDIFF(day, getdate(), begindate) y contar hacia atrás). Para cada día que desee que represente una columna, genere lo siguiente:

SELECT @SelectSQL = @SelectSQL + ', (SELECT COUNT(id) FROM TableA where name = TableB.name AND [date] = ''' + CONVERT(varchar, @mostd) + ''') AS column' + str(@ColumnCounter)

Luego, fuera del bucle, pospone el resto de la consulta:

@SelectSQL = @SelectSQL + ' FROM TableB';

Tenga en cuenta que no se unirá a TableA porque todo eso se soluciona en las subconsultas. Además, por la misma razón, no necesita GROUP BY.

Edit: Vaya, se me olvidó la última parte.

Para ejecutar esto, simplemente ejecute:

EXEC (@SelectSQL);

Respondido el 12 de junio de 12 a las 20:06

Guau. Nunca he oído hablar de SQL dinámico. Veré que puedo leer sobre esto. Mi habilidad actual no está en ese nivel. Sin embargo, parece una solución realmente compacta. Gracias. - VISQL

El código anterior no es la única forma de hacer esto: @Tisho propuso el método PIVOT, que también puede crear columnas a partir de datos de fila. Pero incluso eso no funciona en un número arbitrario de columnas o para valores arbitrarios. Entonces, para que eso funcione, aún necesita recurrir a SQL dinámico para generar el PIVOT. Básicamente, a SQL simplemente no le gusta (por una buena razón, en general) devolver datos sin una forma predefinida (número/tipo de columnas, etc.), y generalmente necesita recurrir a SQL dinámico para que lo haga. . - Domingo P

Esto es posible usando el operador PIVOT http://msdn.microsoft.com/en-us/library/ms177410(v=sql.105).aspx

ACTUALIZACIÓN: Un ejemplo. Está inspirado en este gran artículo: http://dotnetgalactics.wordpress.com/2009/10/23/using-sql-server-20052008-pivot-on-unknown-number-of-columns-dynamic-pivot/:

-- create table #items (id int, name varchar(40), date datetime, userid int)
-- create table #users (id int, name varchar(40), begindate datetime)

declare @dates varchar(4000)
declare @query varchar(4000)

SELECT  @dates = STUFF(( SELECT DISTINCT
                        '],[' + cast(date as varchar(30))
                        FROM    #items
                        ORDER BY '],[' + cast(date as varchar(30))
                        FOR XML PATH('')
                        ), 1, 2, '') + ']'

set @query = 'SELECT * FROM (SELECT #users.name, date FROM #items
        INNER JOIN #users ON #items.userid = #users.id) items 
        PIVOT (COUNT(date) FOR date IN (' + @dates + ')) pvt'

EXECUTE(@query)

Resultado:

name  2012-10-01 2012-10-02 2012-10-03 2012-10-04 2012-10-05 2012-10-06 2012-10-07

user1     0          0           1          0          0          1         0
user2     1          6           0          0          0          1         1
user3     1          1           7          0          0          3         0
user4     0          0           0          1          1          1         1

Respondido el 12 de junio de 12 a las 21:06

¿Podrías ayudarme a visualizar esto? ¿Está diciendo que un PIVOT en mi tabla original (Tabla A: id, nombre, fecha, artículo) puede rotar para hacer c columnas para cada día, filas para el nombre y agregados donde se encuentran? Por alguna razón, basándome en el ejemplo del enlace, creo que el formato podría no encajar. Aunque intentaré esto gracias. - VISQL

Hecho. Solo necesitas hacer coincidir las uniones. - tisho

Hay algunas respuestas sobre SO relacionadas con datos dinámicamente dinámicos: esta es una que proporcioné, y es útil saberla.

SQL Server 2008 dinámico pivote basado en valor ordinal

Al usar el operador XML PATH, también obtiene nombres de columna decentes en su conjunto de resultados.

contestado el 23 de mayo de 17 a las 11:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.