2010-08-12 12 views
7

Tengo un conjunto dinámico de datos X de la forma:Efecto desagrupar?

---------------------------------- 
x.id | x.allocated | x.unallocated 
---------------------------------- 
foo | 2   | 0 
bar | 1   | 2 
---------------------------------- 

Y tengo que llegar a un resultado de Y (orden no es importante):

---------------------------------- 
y.id | y.state 
---------------------------------- 
foo | allocated 
foo | allocated 
bar | allocated 
bar | unallocated 
bar | unallocated 
---------------------------------- 

tengo una solución basada en UTF, pero estoy buscando una hiper-eficiencia así que me pregunto si hay una forma basada en declaraciones, no procedimental para obtener este tipo de efecto de "desagrupar por".

Se siente como un unpivot, pero mi cerebro no puede llegar allí en este momento.

+1

¿Qué versión de SQL Server? – AakashM

+0

2K5 fwiw (omg padding) – annakata

Respuesta

6

utilizando SQL Server 2005, UNPIVOT y CTE puede intentar algo así como

DECLARE @Table TABLE(
     id VARCHAR(20), 
     allocated INT, 
     unallocated INT 
) 

INSERT INTO @Table SELECT 'foo', 2, 0 
INSERT INTO @Table SELECT 'bar', 1, 2 

;WITH vals AS (
     SELECT * 
     FROM  
     (
      SELECT id, 
        allocated, 
        unallocated 
      FROM @Table 
     ) p 
     UNPIVOT (Cnt FOR Action IN (allocated, unallocated)) unpvt 
     WHERE Cnt > 0 
) 
, Recurs AS (
     SELECT id, 
       Action, 
       Cnt - 1 Cnt 
     FROM vals 
     UNION ALL 
     SELECT id, 
       Action, 
       Cnt - 1 Cnt 
     FROM Recurs 
     WHERE Cnt > 0 

) 
SELECT id, 
     Action 
FROM Recurs 
ORDER BY id, action 
+0

Sabía que era indestructible :) Gracias. – annakata

7

Si tiene una tabla de números en su base de datos, puede usarla para obtener sus resultados. En mi base de datos, tengo una tabla llamada Números con una columna Num.

Declare @Temp Table(id VarChar(10), Allocated Int, UnAllocated Int) 

Insert Into @Temp Values('foo', 2, 0) 
Insert Into @Temp Values('bar',1, 2) 

Select T.id,'Allocated' 
From @Temp T 
     Inner Join Numbers 
      On T.Allocated >= Numbers.Num 
Union All 
Select T.id,'Unallocated' 
From @Temp T 
     Inner Join Numbers 
      On T.unAllocated >= Numbers.Num 
+0

Simple, elegante, pero obviamente se descompone en rangos de números extremos a menos que desee generar filas bigint.max en su tabla. Casi con seguridad la solución más eficiente, pero no me gusta contaminar mi base de datos con mecanismos de soporte. – annakata

+0

¿Contaminación de DB con mecanismos de soporte? De Verdad? Creo que esa es la definición de un índice. Los índices existen para respaldar el rendimiento de consultas de su base de datos. Apuesto a que tienes un montón de esos en tu base de datos. –

+0

@annakata - ¡Eres un verdadero bromista! ¿Te das cuenta de que los números en la pregunta del OP en realidad se están traduciendo en filas reales en un conjunto de resultados real? Cualquiera que sea el recuento de filas del conjunto de resultados final, si el servidor y la aplicación pueden manejar eso, pueden manejar una tabla de números para acompañarlo. Permitiendo un sistema colosal poco probable, una tabla con valores enteros de 2^31-1 contiene solo 8 Gb de datos. Devolver 2.147.483.647 filas no es un problema, y ​​le garantizo que esta solución superará su CTE en órdenes de magnitud. – ErikE

0

Esta respuesta es simplemente para hacer ping a volver a G Mastros y no necesita ningún upvotes. Pensé que apreciaría un aumento en el rendimiento de su consulta ya superior.

SELECT 
    T.id, 
    CASE X.Which WHEN 1 THEN 'Allocated' ELSE 'Unallocated' END 
FROM 
    @Temp T 
    INNER JOIN Numbers N 
     On N.Num <= CASE X.Which WHEN 1 THEN T.Allocated ELSE T.Unallocated END 
    CROSS JOIN (SELECT 1 UNION ALL SELECT 2) X (Which) 
+0

¿Es 'Numbers' una tabla/vista del sistema? ¿Está disponible en 'SQL Server 2005'? –

+1

Es solo una tabla con los números de 1 a 100,000 o una cantidad alta. – ErikE