【问题标题】:Recursive Select using T-SQL CTE使用 T-SQL CTE 进行递归选择
【发布时间】:2018-05-05 03:08:51
【问题描述】:

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

我有两张桌子,其中:

表 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

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

【问题讨论】:

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


    【解决方案1】:

    根据需求,我会这样做。

    1. 子查询或查看例如vw_product_max_price

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

    2. 查询请求产品列表

    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 = ?

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

    【讨论】:

      【解决方案2】:

      您可以使用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
      

      【讨论】:

        【解决方案3】:

        您可以通过加入和分组来做到这一点

        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;
        

        【讨论】:

          【解决方案4】:

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

          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”替换为第一个列表中的列。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-08-12
            • 1970-01-01
            • 1970-01-01
            • 2010-11-03
            • 2017-08-19
            • 2017-08-31
            • 2021-02-04
            相关资源
            最近更新 更多