2016-01-22 31 views
6

Ho una domanda circa l'uso di SQL ricorsivo in cui ho seguito la struttura della tabellaImplementazione di una query ricorsive in SQL

prodotti possono essere in più gruppi (per ragioni di chiarezza, non sto usando int)

CREATE TABLE ProductGroups(ProductName nvarchar(50), GroupName nvarchar(50)) 

INSERT INTO ProductGroups(ProductName, GroupName) values 
('Product 1', 'Group 1'), 
('Product 1', 'Group 2'), 
('Product 2', 'Group 1'), 
('Product 2', 'Group 6'), 
('Product 3', 'Group 7'), 
('Product 3', 'Group 8'), 
('Product 4', 'Group 6') 


+-----------+---------+ 
| Product | Group | 
+-----------+---------+ 
| Product 1 | Group 1 | 
| Product 1 | Group 2 | 
| Product 2 | Group 1 | 
| Product 2 | Group 6 | 
| Product 3 | Group 7 | 
| Product 3 | Group 8 | 
| Product 4 | Group 6 | 
+-----------+---------+ 

Ora la domanda è voglio scoprire tutti i relativi prodotti in modo cioè se mi passa prodotto 1 poi ho bisogno il seguente risultato

+-----------+---------+ 
| Product | Group | 
+-----------+---------+ 
| Product 1 | Group 1 | 
| Product 1 | Group 2 | 
| Product 2 | Group 1 | 
| Product 2 | Group 6 |  
| Product 4 | Group 6 | 
+-----------+---------+ 

Quindi, fondamentalmente voglio prima scoprire tutti i gruppi per il prodotto 1 e quindi per ogni gruppo voglio scoprire tutti i prodotti e così via ...

  1. prodotto 1 => Gruppo 1 , Gruppo 2;
  2. Gruppo 1 => Prodotto 1, Prodotto 2 (il Gruppo 1 e il Prodotto 1 esistono già, quindi dovrebbero essere evitati altrimenti andrebbero nel ciclo infinito );
  3. Gruppo 2 => Prodotto 1 (esiste già come sopra);
  4. prodotto 2 => Gruppo 1, Gruppo 6 (Gruppo 1 e 2 del prodotto già esistono)
  5. Gruppo 6 => prodotto 4
+1

Non credo che la sintassi dello snippet sia una buona idea in questo contesto, anche se l'output che si desidera è formattato come HTML. Ero molto tentato di rimuoverlo, ma non sono sicuro di quale significato attribuisca alla sezione CSS dell'output. Penso che dovrebbe essere omesso, ma è la tua domanda. –

+1

Suggerisco di creare tabelle ascii usando qualcosa come https://ozh.github.io/ascii-tables/ – Blorgbeard

+0

Spiacente che sia stato per errore appena modificato, quindi mostra ora l'output corretto ... –

risposta

3

non credo che questo è possibile con un CTE ricorsiva, perché ti è consentito solo un riferimento ricorsivo per definizione ricorsiva.

sono riuscito a realizzarlo con un while ciclo, che rischia di essere meno efficiente rispetto al CTE:

declare @related table (ProductName nvarchar(50), GroupName nvarchar(50)) 

-- base case 
insert @related select * from ProductGroups where ProductName='Product 1' 

-- recursive step 
while 1=1 
begin 

    -- select * from @related -- uncomment to see progress 

    insert @related select p.* 
    from @related r 
    join ProductGroups p on p.GroupName=r.GroupName or p.ProductName=r.ProductName 
    left join @related rr on rr.ProductName=p.ProductName and rr.GroupName=p.GroupName 
    where rr.ProductName is null   

    if @@ROWCOUNT = 0 
     break; 

end 

select * from @related 

Si dovrebbe fare attenzione a quanto sopra - punto di riferimento su dati reali dimensioni prima della distribuzione!

+1

Funzionerà finché la tabella è piccola. Le prestazioni saranno un problema. O (x^2) –

+0

Sì, certo. Spero che qualcuno arriverà con una soluzione "corretta". – Blorgbeard

+0

Penso che questo sia il miglior OP possibile. SQL non è la lingua giusta per questo, IMO –

4

Esso può essere fatto con una query ricorsive, ma non è ottimale perché SQL Server non consente di fare riferimento a la tabella ricorsiva come un insieme. Quindi finisci per dover mantenere una stringa di percorso per evitare loop infiniti. Se si utilizzano ints, è possibile sostituire la stringa del percorso con un hierarchyid.

with r as (
    select ProductName Root, ProductName, GroupName, convert(varchar(max), '/') Path from ProductGroups 
    union all 
    select r.Root, pg.ProductName, pg.GroupName, convert(varchar(max), r.Path + r.ProductName + ':' + r.GroupName + '/') 
    from r join ProductGroups pg on pg.GroupName=r.GroupName or pg.ProductName=r.ProductName 
    where r.Path not like '%' + pg.ProductName + ':' + pg.GroupName + '%' 
) 

select distinct ProductName, GroupName from r where Root='Product 1' 

http://sqlfiddle.com/#!3/a65d1/5/0

+0

Risposta intelligente. Il percorso può essere di qualsiasi formato ed è necessario per interrompere il ciclo ricorsivo. –

2

Si può fare questo.

DECLARE @ProductGroups AS TABLE (
     ProductName NVARCHAR(50) , 
     GroupName NVARCHAR(50) 
     ) 

INSERT INTO @ProductGroups 
     (ProductName, GroupName) 
VALUES ('Product 1', 'Group 1'), 
     ('Product 1', 'Group 2'), 
     ('Product 2', 'Group 1'), 
     ('Product 2', 'Group 6'), 
     ('Product 3', 'Group 7'), 
     ('Product 3', 'Group 8'), 
     ('Product 4', 'Group 6'); 
; 
WITH cte 
      AS (SELECT a.ProductName 
       FROM  @ProductGroups a 
       WHERE a.GroupName IN (SELECT x.GroupName 
             FROM @ProductGroups x 
             WHERE x.ProductName = 'Product 1') 
      ), 
     cte2 
      AS (SELECT GroupName 
       FROM  @ProductGroups 
       WHERE ProductName IN (SELECT x.ProductName 
             FROM cte x) 
      ) 
    SELECT * 
    FROM @ProductGroups 
    WHERE GroupName IN (SELECT x.GroupName 
          FROM  cte2 x) 
+0

Non funziona abbastanza con il seguente scenario + ----------- + --------- + | Prodotto | Gruppo | + ----------- + --------- + | Prodotto 1 | Gruppo 1 | | Prodotto 1 | Gruppo 2 | | Prodotto 2 | Gruppo 1 | | Prodotto 2 | Gruppo 6 | | Prodotto 3 | Gruppo 7 | | Prodotto 3 | Gruppo 8 | | Prodotto 4 | Gruppo 6 | | Prodotto 4 | Gruppo 7 | + ----------- + --------- + per lo scenario sopra, Dovrebbe fornire esattamente lo stesso output di input ... –

+0

Le mie scuse come sopra commento non è leggibile ma i commenti non consentono la tabella ascii. Quindi se aggiungiamo un altro record nella tabella di input come (Prodotto 4 e Gruppo 7), tutti gli 8 record dovrebbero essere mostrati in output ma la soluzione che hai fornito mostra solo 5 righe. http://sqlfiddle.com/#!6/c641f4/1 –