递归选择使用T-SQL CTE

时间:2018-05-05 03:08:51

标签: sql-server tsql common-table-expression

我有一个递归选择问题,我无法工作,感觉就像在那里,但不一定。

我有两张表:

表1 - 产品清单

List    ProductCode    SortNumber
------------------------------------
1       A              1  
1       B              2  
1       F              3  
1       G              4  
1       K              5  
2       C              1  
2       A              2  
3       B              1  
3       K              2  
3       G              3  

表2 - 产品价格

ProductType     Client      ProductCode     Price
---------------------------------------------------
Type 1          1               A           100  
Type 1          1               A           150  
Type 1          1               B           200  
Type 1          1               B           120  
Type 1          1               F           150  
Type 2          1               A           200  
Type 2          1               A           300  
Type 2          1               B           300  
Type 2          1               F           400  
Type 2          1               G           125  
Type 2          1               G           75  
Type 1          2               A           190  
Type 1          2               A           130  
Type 1          2               A           200  
Type 1          2               B           270  
Type 1          2               B           180  
Type 1          2               F           130  
Type 2          2               A           210    
Type 2          2               A           100  
Type 2          2               B           350  
Type 2          2               F           200  
Type 2          2               G           175  
Type 2          2               G           95  
Type 2          2               K           65

我想要实现的是当我要求产品列表为1时,它将获得与产品代码相关的所有最高价格。如果产品类型1没有价格,它将检查产品类型2的最高价格。如果没有相应的类型2价格,则值为0.

因此,所需的输出是当我为所有客户请求价格表1时(比如客户1和2),所需的输出应为

Client  ProductCode     SortNumber  PriceToDisplay
---------------------------------------------------
1       A               1           150  
1       B               2           200
1       F               3           150
1       G               4           125     (No Type 1, so we get type 2 price)
1       K               5           0       (No Type 1, 2 Price for Product K) 
2       A               1           200
2       B               2           270
2       F               3           130
2       G               4           175
2       K               5           65

尝试的解决方案:CTE方法

由于我的印象是这可能是一个递归选择,我想到了使用CTE。我有这个查询 -

请注意评论代码 - - 和pp1.ProductCode = pp2.ProductCode

Declare @List int = 1

;With ClientCTE As (
Select Distinct Client From ClientsTable
),
RecursiveSelect As (
Select p1.Client
, l.ProductCode
, l.SortOrder
, p1.Price As P1Price
, p2.Price As P2Price
, Case when p1.Price Is Null Then Case When p2.Price Is Null Then 0 Else p2.Price End
    Else p1.Price End As PriceToDisplay
From ProductList l

Left Join (
    Select Distinct pp.Client, pp.ProductCode, Max(pp.Price) As ItemPrice From ProductPrice pp
    Left Join ClientCTE c On c.Client = pp.Client
    Where pp.ProductType = 1
    Group By pp.Client, pp.ProductCode) p1 On p1.ProductCode = l.ProductCode

Left Join (
    Select Distinct pp.Client, pp.ProductCode, Max(pp.Price) As ItemPrice From ProductPrice pp
    Left Join ClientCTE c On c.Client = pp.Client
    Where pp.ProductType = 2
    Group By pp.Client, pp.ProductCode) p2 On p2.Client = p1.Client

Where pp1.Client = pp2.Client
-- And pp1.ProductCode = pp2.ProductCode **this commented code**
And l.List = 1

)

Select Distinct Client, ProductCode, SortOrder, Max(P1Price), Max(P2Price)   
From RecursiveSelect  
Group By Client, ProductCode, SortOrder

调查结果 - CTE

如果代码被注释,它将:

  • 正确获取价格
  • 但是,由于“产品价格”表中的“产品代码G”和“类型1”的K没有价格,因此无法正确显示

结果

Client  ProductCode     SortNumber  PriceToDisplay
---------------------------------------------------
1       A               1           150  
1       B               2           200
1       F               3           150
(missing G and K product codes)
2       A               1           200
2       B               2           270
2       F               3           130
2       G               4           175
2       K               5           65

如果没有注释代码,它将:

  • 将显示所有产品代码但价格错误
  • 显示的价格是Price1表中的最高价格,无论客户如何

结果

Client  ProductCode     SortNumber  PriceToDisplay
---------------------------------------------------
1       A               1           WRONG PRICE  
1       B               2           WRONG PRICE
1       F               3           WRONG PRICE
1       G               4           WRONG PRICE
1       K               5           WRONG PRICE 
2       A               1           WRONG PRICE
2       B               2           WRONG PRICE
2       F               3           WRONG PRICE
2       G               4           WRONG PRICE
2       K               5           WRONG PRICE

我想我的查询只需要进行一次调整,但我无法准确指出确切的位置。未注释的代码返回正确的值,但为什么它不返回丢失的左表值,因为它是左连接?

4 个答案:

答案 0 :(得分:0)

您可以使用OUTER APPLY获取最高价格,您不需要递归CTE

select  c.Client, l.ProductCode, l.SortNumber, Price = isnull(p.Price, 0)
from    ProductList l
        cross join ClientCTE c
        outer apply
        (
            -- Top 1 to return only one row per client + Pproduct Code
            select  top 1 p.Price 
            from    ProductPrice p
            where   p.Client    = c.Client
            and     p.ProductCode   = l.ProductCode
            -- Type 1 will comes before Type 2, and higher price first
            -- If there isn't any rows for Type 1, Type 2 will be selected
            -- no rows return if no rows are found
            order by p.ProductType, p.Price desc
        ) p
where   l.List  = 1
order by Client, ProductCode

答案 1 :(得分:0)

您可以通过

加入和分组来完成此操作
declare @PL table (client int, ProductCode char(1), Sort int);
insert into @pl values
       (1, 'A', 1),
       (1, 'B', 2), 
       (1, 'F', 3), 
       (1, 'G', 4), 
       (1, 'K', 5), 
       (2, 'C', 1), 
       (2, 'A', 2), 
       (3, 'B', 1), 
       (3, 'K', 2), 
       (3, 'G', 3);
--select * from @PL;
declare @PP table (ProductType varchar(10), Client int, ProductCode char(1), Price int);
insert into @PP values
       ('Type 1', 1, 'A', 100), 
       ('Type 1', 1, 'A', 150), 
       ('Type 1', 1, 'B', 200), 
       ('Type 1', 1, 'B', 120), 
       ('Type 1', 1, 'F', 150), 
       ('Type 2', 1, 'A', 200), 
       ('Type 2', 1, 'A', 300), 
       ('Type 2', 1, 'B', 300), 
       ('Type 2', 1, 'F', 400), 
       ('Type 2', 1, 'G', 125), 
       ('Type 2', 1, 'G',  75), 
       ('Type 1', 2, 'A', 190), 
       ('Type 1', 2, 'A', 130), 
       ('Type 1', 2, 'A', 200), 
       ('Type 1', 2, 'B', 270), 
       ('Type 1', 2, 'B', 180), 
       ('Type 1', 2, 'F', 130), 
       ('Type 2', 2, 'A', 210), 
       ('Type 2', 2, 'A', 100), 
       ('Type 2', 2, 'B', 350), 
       ('Type 2', 2, 'F', 200), 
       ('Type 2', 2, 'G', 175), 
       ('Type 2', 2, 'G',  95), 
       ('Type 2', 2, 'K',  65);
--select * from @pp;
select pl.client, pl.ProductCode
   --, pp1.Price as Price1, pp2.Price as Price2
     , max(isnull(pp1.Price, isnull(pp2.Price, 0))) as Price12
from @PL pl 
left join @PP pp1
  on pp1.ProductCode = pl.ProductCode 
 and pp1.Client      = pl.client
 and pp1.ProductType = 'Type 1'
 left join @PP pp2
  on pp2.ProductCode = pl.ProductCode 
 and pp2.Client      = pl.client
 and pp2.ProductType = 'Type 2'
 where pl.client in (1, 2) 
 group by pl.client, pl.ProductCode 
 order by pl.client, pl.ProductCode;

答案 2 :(得分:0)

根据要求,这就是我要做的。

  1. 子查询或视图,例如vw_product_max_price

    select producttype, client, productcode, max(price) as maxprice group by producttype, client, productcode

  2. 查询请求产品清单

  3. select MP.client, MP.productcode, PL.sortnumber, case MP.maxprice when NULL then 0 else MP.maxprice end as PriceToDisplay from productlist PL left outer join vw_product_max_price MP on PL.productcode=MP.productcode where MP.client = ? and MP.productcode = ?

    我没有对此进行过测试..但我们的想法是首先列出每个产品代码的最高价格。

答案 3 :(得分:0)

所以我和一位朋友坐在一起工作。有效的解决方案(从原始数据库表名和列调整)如下:

Declare @List int = 1

;With ClientCTE (BuyGroup, CustomerPriceGroup, ProductListHeaderId) As (
Select Distinct Client From ClientsTable
), 

RecursiveSelect As (
Select x.Client, x.ProductCode
, Case When Max(Type1Price) Is Null Then 
    Case When Max(Type2Price) Is Null Then 0 
    Else Max(Type2Price) End 
    Else Max(Type1Price) End 
As PriceToDisplay

From 
    (Select h.Client, h.ProductCode,
        Case When h.ProductType = 'Type1' 
            Then 
                Max(h.UnitPrice) 
            Else 
                Null 
            End As Type1Price
        , Case When h.ProductType = 'Type2' 
            Then 
                Max(h.UnitPrice) 
            Else 
                Null 
            End As Type2Price
            , h.BuyGroup, h.ConditionScale          
        From ProductPrice h
             Inner Join ClientCTE c On c.Client = h.Client
        Where           
            h.ProductType In ('Type1', 'Type2')
        And h.ProductCode In (Select ProductCode From ProductList where List = @List)           
        Group by h.Client, h.ProductCode
        ) x
        Group by Client, ProductCode
        )

select * from RecursiveSelect

我们都发现在原始查询中,两个左连接只有一个条件(即ProductType)不同。一旦我们从查询中获得原始数据,我们就会分组并选择将要显示的价格。

CTE的原因是查询的初衷是获取N个客户的价格。这就是锚定查询获取客户端列表然后传递给第二个客户端列表的原因。此外,如果你也在处理X个列表,你可以在锚点查询中包含它并替换" @ List"与第一个列。