简体   繁体   中英

SQL Recursive CTE using two tables

I have two tables, one is a Product Customer mapping and another is a Product sharing table. Using SQL recursive CTE, given a product, I am trying to find all the products that are linked as a chain, mapped across the customers. In addition, if a Product is shared with another Product, I need to include it in the chain too. Hopefully, my example make more sense than the description

Product Customer Table
Product    Customer
  Milk     Illinois
  Milk     Michigan
  Butter   Michigan
  Cream    Wisconsin
  Honey    Wisconsin
  Cheese   Minnesota

Product Sharing Table
Product    SharedProduct
 Butter     Cream
 Cream      Cheese

For the above data, let's say my input Product is Milk, then the result set should include all the products - Milk, Butter, Cream, Honey & Cheese. Here Butter-Cream & Cream-Cheese are linked through the Product Sharing table.

My current SQL looks like this but doesn't really work more than one level deep.

WITH Product_CTE AS
(
  SELECT DISTINCT [Product] FROM ProductCustomer
  WHERE [Product] IN (SELECT DISTINCT p2.[Product]
  FROM ProductCustomer p1 INNER JOIN ProductCustomer p2
  ON p1.[Customer] = p2.[Customer] WHERE p1.[Product] = 'Milk')
  UNION ALL
  SELECT [SharedProduct] FROM ProductSharing b
  INNER JOIN Product_CTE p ON p.[Product] = b.[Product]
)
Select [Product] from Product_CTE

A CTE has issues with multiple UNION s. Although it may be possible, it wasn't working for me.

An alternative is to use a loop that stops when there are no more rows added to a working table:

declare @ProductCustomers as Table ( Product VarChar(16), Customer VarChar(16) )
insert into @ProductCustomers ( Product, Customer ) values
  ( 'Milk', 'Illinois' ),
  ( 'Milk', 'Michigan ' ),
  ( 'Butter', 'Michigan ' ),
  ( 'Cream', 'Wisconsin' ),
  ( 'Honey', 'Wisconsin' ),
  ( 'Cheese', 'Minnesota' )

declare @ProductSharing as Table ( Product VarChar(16), SharedProduct VarChar(16) )
insert into @ProductSharing ( Product, SharedProduct ) values
  ( 'Butter', 'Cream ' ),
  ( 'Cream', 'Cheese ' )

declare @TargetProduct as VarChar(16) = 'Milk'

declare @ProductChain as Table ( Product VarChar(16) )
insert into @ProductChain ( Product ) values ( @TargetProduct )
declare @NewRows as Int = 1

while @NewRows > 0
  begin
  set @NewRows = 0
  -- Add products shared by the same customer.
  insert into @ProductChain
    select PCR.Product
      from @ProductCustomers as PCL inner join
        @ProductCustomers as PCR on
          -- Shared customer.
          PCR.Customer = PCL.Customer and
          -- Different product.
          PCR.Product <> PCL.Product
      where not exists ( select 42 from @ProductChain where Product = PCR.Product )
  set @NewRows = @NewRows + @@RowCount
  -- Add products linked through the product sharing table.
  insert into @ProductChain
    select PS.SharedProduct
      from @ProductSharing as PS inner join
        @ProductChain as PC on PC.Product = PS.Product
      where not exists ( select 42 from @ProductChain where Product = PS.SharedProduct )
  set @NewRows = @NewRows + @@RowCount 
  end

select Product
  from @ProductChain
  order by Product

Assumed herein is that the @ProductSharing table is unidirectional.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM