【问题标题】:Multiple CASE expressions in select, where and group by clauseselect、where 和 group by 子句中的多个 CASE 表达式
【发布时间】:2016-08-25 05:11:38
【问题描述】:

我正在尝试解决其中一个绝妙的问题。我想用sql中的case来优化SQL存储过程。

ProductMetrics 表格如下。此表包含有关产品销售价格和数量以及产品公司、销售区域、产品名称、市场部门的数据。

表 ProductMetrics 包含 10000 多行

ProductMetrics
| Day | CompanyId | RegionId | ProdId | DivId | Quantity | Sale

我们还有其他参考表 -

Company(CompanyId, CompanyName), 
Region(RegionId, RegionName),
Product(ProdId, ProductName),
Division(DivId, DivisionName) 

用户可以使用以下查询从该表中获取可读的统计信息。

Select m.Day, c.CompanyName, r.RegionName, p.ProductName, d.DivisionName, m.Quantity, m.Sale
from ProductMetrics m
left outer join Company on c.CompanyId = m.CompanyId
left outer join Region on r.RegionId = m.RegionId
left outer join Product on p.ProdId = m.ProdId
left outer join Division on d.DivId = m.DivId
where m.Day = '12-05-2015' and
      m.CompanyId= 15 and
      m.RegionId =10

我希望存储过程能够获得特定部门、产品或公司或两者的汇总统计数据。 SP 将根据我们传递的 queryParameter 返回。 getProductMetrics(queryParam, Day, CompanyId, RegionId, ProdId, DivId) QueryParameter 在以下示例中的括号中给出。 例如,

  1. (C) 获取特定公司的总数量和销售额。即按 CompanyId 分组
  2. (CR)按公司和地区分组的总数量和销售额。公司和地区的不同组合。
  3. (P) 按公司分组的总数量和销售额
  4. (D) 获取特定部门的总数量和销售额。

等等…… QueryParameter 可能是有价值的 - C, R, P, CRP, D, CR, CP, CD。对于每个查询参数,我都有选择语句。例如下面

IF @queryParameter IN ('C')
  select m.Day, c.CompanyName, 'ALL' as Region, 'ALL' as ProductName, 'ALL' as DivisionName, SUM(m.Quantity), SUM(m.Sale)
    from ProductMetrics m
    left outer join Company on c.CompanyId = m.CompanyId
    where m.Day = '12-05-2015' and
        c.CompanyId =23
    group by m.Day, c.CompanyName

等等.. 以上存储过程为我提供了所有可用部门、地区的特定产品和日期的总销售额。

对于存储过程,我需要编写 8 个带有 IF 条件的选择语句。

问题是而不是 8 个不同的 select 语句,我应该使用 CASE 语句来只有一个 storedProc 吗?

我在下面尝试过-

Select m.Day as Date,
       CASE @QueryParameter
          WHEN IN (C, CRP, CP, CR, CD) THEN c.CompanyName
          ELSE 'ALL'
        END as 'CompanyName',

        CASE @QueryParameter
          WHEN IN (R, CR) THEN r.RegionName
          ELSE 'ALL'
        END as 'RegionName',

        CASE @QueryParameter
          WHEN IN (P, CRP, CP) THEN p.ProductName
          ELSE 'ALL'
        END as 'ProductName',

        CASE @QueryParameter
          WHEN IN (D, CD) THEN d.DivisionName
          ELSE 'ALL'
        END as 'DivisionName',

        SUM(Quantity), SUM(Sale)
from ProductMetrics m,
left outer join Company on c.CompanyId = m.CompanyId
left outer join Region on r.RegionId = m.RegionId
left outer join Product on p.ProdId = m.ProdId
left outer join Division on d.DivId = m.DivId

where m.Day='12-5-2015' and
      r.Region = @region and
      p.ProdId = @product
      c.CompanyId = @company and
      d.DivId = @division
group by Day, CompanyName,RegionName, ProductName, DivisionName

如您所知,join、where 和 group by 语句也应该存在 case 语句。因为对于 QueryParameter 'CR',divionId 和 ProdId 将作为 null 传递。 我将使用以下 exec 运行 storedProc(QueryParameter, CompID, RegionId, ProdId, DivId) EXEC getProductMetrics('CR',23, 39, '','') // CR requires only CompID andRegionId

EXEC getProductMetrics('CD',23, '', '',100) // CD requires only CompID and DivId

等等.. 您能帮我获得这个优化的查询吗?

【问题讨论】:

  • MySQL 还是 SQL Server? (下定决心)
  • SQL Server ;) 非常感谢您的建议
  • 你为什么坚持有一个select语句?我会使用 IF...ELSE 来评估哪些参数为 NULL,然后为它们生成单独的选择语句。查询优化器可能会更好地处理它们并且代码将更具可读性。
  • @PawełKucharski,看起来是个好主意 :)
  • 这些是 case 表达式,而不是 case 语句。

标签: sql sql-server stored-procedures


【解决方案1】:

要以简单易读的方式执行此操作,我将创建一些辅助逻辑变量,例如 @ByCompany,以检查我们是否应该按公司分组等等。然后我会解释输入并相应地设置这些变量,然后创建一个在 SELECTWHEREGROUP BY 中使用它们的语句(可能还有ORDER BY)部分。不过,我认为不需要在 FROM 部分中使用它们。

下面是一些示例代码——对于 TransactSQL 中的任何错误,我深表歉意——我有一段时间没有使用它了。 ;)

DECLARE @ByCompany BIT = 0;
DECLARE @ByRegion BIT = 0;
[..]
IF @QueryParameter LIKE ('%C%') @ByCompany = 1
IF @QueryParameter LIKE ('%R%') @ByRegion = 1
[..]
Select 
    m.Day as Date,
    CASE @ByCompany WHEN 1 THEN c.CompanyName ELSE 'ALL' END as 'CompanyName',
    CASE @ByRegion  WHEN 1 THEN r.RegionName  ELSE 'ALL' END as 'RegionName',
    [..]
    SUM(Quantity), 
    SUM(Sale)
from 
    ProductMetrics m,
left outer join Company on c.CompanyId = m.CompanyId
left outer join Region on r.RegionId = m.RegionId
[..]
where m.Day='12-5-2015'
and (c.CompanyId = @company OR @ByCompany = 0)
and (r.Region = @region OR @ByRegion = 0)
[..]
group by 
    Day, 
    CASE @ByCompany WHEN 1 THEN c.CompanyName ELSE 'ALL' END,
    CASE @ByRegion  WHEN 1 THEN r.RegionName  ELSE 'ALL' END,
    [..]

这样做,我们有一个简单易读的查询,当我们想添加其他列进行分组时,它将是可维护的,比如子区域。

【讨论】:

    【解决方案2】:
    SELECT m.Day AS Date,
    CASE @QueryParameter
          WHEN EXISTS (C, CRP, CP, CR, CD) THEN (select c.CompanyName from Company where c.CompanyId= @company)
          ELSE 'ALL'
        END as 'CompanyName',
    
        CASE @QueryParameter
          WHEN exists (R, CR) THEN (select r.RegionName from Region r where r.RegionId=@region)
          ELSE 'ALL'
        END as 'RegionName',
    
        CASE @QueryParameter
          WHEN exists (P, CRP, CP) THEN (select p.ProductName from Product p where p.ProdId =@product)
          ELSE 'ALL'
        END as 'ProductName',
        CASE @QueryParameter
          WHEN exists (D, CD) THEN (select d.DivisionName from  Division where d.DivId = @division)
          ELSE 'ALL'
        END as 'DivisionName',
        SUM(Quantity), SUM(Sale)
        from ProductMetrics m
        where m.Day='12-5-2015'
    

    已删除连接并添加了子查询,试试这个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-19
      相关资源
      最近更新 更多