2010-08-12 14 views
7

Ho un insieme dinamico di dati X della forma:Effetto di separazione?

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

E devo ottenere un risultato di Y (ordine è irrilevante):

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

Ho una soluzione basata su UTF, ma sto cercando l'iper-efficienza, quindi mi chiedo se ci sia un modo basato sulle istruzioni, non procedurale per ottenere questo tipo di effetto "separa per"?

Sembra un po 'unpopot, ma il mio cervello non può arrivarci ora.

+1

Quale versione di SQL Server? – AakashM

+0

2K5 fww (omg padding) – annakata

risposta

6

utilizza SQL Server 2005, UNPIVOT, e CTE si può provare qualcosa di simile

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

Sapevo che era imperdibile :) Grazie. – annakata

7

Se nel database è presente una tabella di numeri, è possibile utilizzarla per ottenere i risultati. Nel mio database, ho una tabella chiamata Numbers con una colonna 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

Semplice, elegante, ma ovviamente suddivide a intervalli di numeri estremi a meno che non si desideri generare righe bigint.max nella tabella. Quasi certamente la soluzione più efficiente, ma non mi piace inquinare il mio DB con meccanismi di supporto. – annakata

+0

Inquinamento di un DB con meccanismi di supporto? Veramente? Penso che sia la definizione di un indice. Esistono indici per supportare le prestazioni di query del tuo DB. Scommetto che ne hai un sacco di quelli nel tuo database. –

+0

@annakata - Sei un vero jolly! Ti rendi conto che i numeri nella domanda dell'OP vengono effettivamente tradotti in file reali in un vero set di risultati? Qualunque sia il conteggio delle righe del set di risultati finale, se il server e l'applicazione sono in grado di gestirli, possono gestire una tabella di numeri per utilizzarli. Permettendo qualche improbabile sistema colossale, una tabella con 2^31-1 valori interi contiene solo circa 8 Gb di dati. Restituire 2.147.483.647 file non è quindi un problema e garantisco che questa soluzione supererà il tuo CTE di ordini di grandezza. – ErikE

0

Questa risposta è solo il ping di nuovo a G Mastros e non ha bisogno di upvotes. Ho pensato che avrebbe apprezzato un incremento delle prestazioni per la sua query già superiore.

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

'Numbers' è una tabella/vista di sistema? È disponibile in 'SQL Server 2005'? –

+1

È solo una tabella con i numeri da 1 a 100.000 o una quantità elevata. – ErikE