【问题标题】:What is the most performant way to rewrite a correlated subquery in the SELECT clause?在 SELECT 子句中重写相关子查询的最高效方法是什么?
【发布时间】:2018-05-01 15:44:08
【问题描述】:

我正在尝试计算用户是否在三个时间范围内访问过网站:

  • 过去 30 天
  • 31 到 60 天之间
  • 61 到 90 天之间

我正在使用 Netezza,它不支持 SELECT 子句中的相关子查询。有关必须重写以不使用相关子查询的成功查询,请参阅 Rextester:http://rextester.com/JGR62033

样本数据:

| user_id | last_visit | num_days_since_2017117 |
|---------|------------|------------------------|
| 1234    | 2017-11-02 | 15.6                   |
| 1234    | 2017-09-30 | 48.6                   |
| 1234    | 2017-09-03 | 75.0                   |
| 1234    | 2017-08-21 | 88.0                   |
| 9876    | 2017-10-03 | 45.0                   |
| 9876    | 2017-07-20 | 120.0                  |
| 5545    | 2017-09-15 | 63.0                   |

所需的输出:

| user_id | last_30 | btwn_31_60 | btwn_61_90 |
|---------|---------|------------|------------|
| 1234    | 1       | 1          | 1          |
| 5545    | 0       | 0          | 1          |
| 9876    | 0       | 1          | 0          |

【问题讨论】:

  • 如果 DBMS 支持 CASE 或类似的东西,这里不需要相关子查询。
  • 为什么投反对票? @a_horse_with_no_name?如果我可以在问题中提供额外的清晰度,请发表评论。我觉得我很清楚并提供了足够的信息来回答这个问题,不是吗?
  • 我没有投反对票。我评论了。
  • @KenWhite 您能否举例说明使用示例数据会是什么样子?
  • 我不确切知道 DBMS 支持什么,但您可以使用 SELECT user_id, SUM(CASE WHEN num_days <= 30 then 1 else 0 end) as last_30, SUM(CASE WHEN numdays > 30 AND numdays <=60 then 1 else 0 end) as btwn_31_60, 等在 user_id 上带有 GROUP BY 的东西。

标签: sql database-performance netezza correlated-subquery


【解决方案1】:

这是条件聚合的一种方式,Rextester

select 
    user_id
    ,MAX(case when '2017-11-17'-visit_date <=30
          then 1
          else 0
     end) as last_30
    ,MAX(case when '2017-11-17'-visit_date >=31
               and '2017-11-17'-visit_date <=60
          then 1
          else 0
     end) as between_31_60
    ,MAX(case when '2017-11-17'-visit_date >=61
              and '2017-11-17'-visit_date <=90
          then 1
          else 0
     end) as between_61_90
from 
    visits
group by user_id
order by user_id

【讨论】:

  • 由于这个问题的上下文是“最高性能”,重要的是要注意,在原始“rextester”示例中建议的相关子查询可能比这种基于案例的解决方案慢得多。任何“selfjoin”都可能导致基表被多次扫描,并且查询计划被案例构造/单表扫描完全抛出的可能性极低:)
  • @Lars,我一定误解了相关子查询的效率,以及何时使用它们。这是真的吗:有条件地检查单个表中是否存在条件最好使用 CASE 语句执行,但有条件地检查单独表中是否存在条件最好使用相关子查询?
  • @psrpsrpsr 1.) 检查 存在 通常比其他子查询快速且首选 2.) 选择中的任何类型的子查询都会增加扫描子查询表的风险逐行进行 3.) JOINs 通常比选择子查询更受欢迎,因为您避免了 #2 4.) 查询优化器可能足够聪明,可以创建相同的计划,无论您使用子查询还是 JOIN
【解决方案2】:

我不知道您正在使用的具体 DBMS,但如果它支持 CASE 或等效项,则您不需要相关子查询;您可以结合使用SUM()CASE

当然,在您的 DBMS 中未经测试,但它应该为您提供一个起点:

SELECT 
  user_id, 
  SUM(CASE WHEN num_days <= 30 then 1 else 0 end) as last_30, 
  SUM(CASE WHEN num_days > 30 AND numdays < 61 then 1 else 0 end) as btwn_31_60,
  SUM(CASE WHEN num_days >= 61 then 1 else 0 end) as btwn_61_90
FROM 
  YourTableName  -- You didn't provide a tablename
GROUP BY 
  user_id 

由于您的值是浮点数而不是整数,您可能需要调整用于日期范围的值以满足您的特定要求。

【讨论】:

  • 仅供参考,表名和示例数据可在问题的 Rextester 链接中找到。我能够通过将 SUM(CASE) 包装在另一个 CASE 中来获得所需的输出,例如: CASE WHEN SUM(CASE WHEN num_days =1 THEN 1 ELSE 0 END
  • @psrpsrpsr:我看到了示例数据,但没有看到表名(除非它在您提供的外部链接中,我的公司代理不允许我访问 - 另一个不使用外部链接的理由您帖子中的网站)。你不应该需要外部 CASE(你添加的那个);我首先在 SQL Server 中进行了测试,没有它也可以正常工作,尽管我的测试数据使用日期值和 DateDiff() 而不是您的数据,因为那是我当时可用的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-15
  • 1970-01-01
  • 1970-01-01
  • 2011-07-12
  • 2021-01-06
  • 1970-01-01
相关资源
最近更新 更多