如何避免多个子查询作为表达式(SQL优化)

Posted

技术标签:

【中文标题】如何避免多个子查询作为表达式(SQL优化)【英文标题】:How to avoid multiple subquery as expression (SQL optimization) 【发布时间】:2017-11-03 09:27:30 【问题描述】:

我有两张桌子 表A

user_id             external_party_id
  1                        6

表 B

user_id    activated    user_mode         sale_mode
  1          false      'Customer'         'Web'          
  1          true       'Customer'         'Local'        
  1          true       'Partner'          'Web'         
  1          true       'Partner'          'Local'           

结果

user_id   external_party_id   customer_web   customer_local  partner_web   partner_local
  1         6                   false          true        true             true
  2         7                   false          false        true             false

现在我有以下这种查询

SELECT
  u.*,
  (SELECT activated
   FROM TABLE_B
   WHERE user_id = u.user_id AND user_mode = 'Customer' AND sale_mode = 'Web'
  ) AS customer_web,
  (SELECT activated
   FROM TABLE_B
   WHERE user_id = u.user_id AND user_mode = 'Customer' AND sale_mode = 'Local'
  ) AS customer_local,
  (SELECT activated
   FROM TABLE_B
   WHERE user_id = u.user_id AND user_mode = 'Partner' AND sale_mode = 'Web'
  ) AS partner_web,
  (SELECT activated
   FROM TABLE_B
   WHERE user_id = u.user_id AND user_mode = 'Partner' AND sale_mode = 'Local'
  ) AS partner_local,
FROM TABLE_A AS u;

我想知道是否有更好的方法来实现这一点。此查询是否尝试向 TABLE_B 查询四次以填充每个列的值?

【问题讨论】:

这是一个枢轴问题:modern-sql.com/use-case/pivot @MarkusWinand 谢谢,我一定会阅读的。只是好奇这种方式是真的效率低下还是只是涉及到更多的代码? 只访问一次表会更快。只需确保添加尽可能严格的全局where 子句(WHERE user_mode IN ('Partner', 'Customer') AND sale_mode('Web', 'Local')。根据您的数据,添加索引可能会有所帮助。在 PostgreSQL 中,您甚至可以为此使用部分索引:use-the-index-luke.com/sql/where-clause/… 在这种情况下使用 WITH 语句是否可以帮助我减少查询? 我没有看到在这里使用with 的理由。我会进行数据透视查询。与this answer 不同,我会先在子查询中执行数据透视部分,然后再将其加入另一个表。 【参考方案1】:

使用条件聚合

SELECT
  u.user_id,u.external_party_id,
  MAX (CASE WHEN user_mode = 'Customer' AND sale_mode = 'Web' THEN activated END) AS customer_web,
  MAX (CASE WHEN user_mode = 'Customer' AND sale_mode = 'Local' THEN activated END) AS customer_local,
  MAX (CASE WHEN user_mode = 'Partner' AND sale_mode = 'Web' THEN activated END) AS partner_web,
  MAX (CASE WHEN user_mode = 'Partner' AND sale_mode = 'Local' THEN activated END) AS partner_local
FROM TABLE_A AS u
LEFT JOIN TABLE_B b ON b.user_id = u.user_id
GROUP BY u.user_id,u.external_party_id

【讨论】:

嗨,MAX 在这种情况下是做什么的? @Zanko 因为user_modesale_mode 不是GROUP BY 的一部分,它们必须在聚合函数中。您也可以使用MIN,因为它是从一组值中选择的,其中所有值都是NULL,并且只有一个具有值(满足条件的那个)。【参考方案2】:

格式不完全相同,但您可以使用直接左连接重新表述:

SELECT
    a.*,
    b.activated,
    b.user_mode,
    b.sales_mode
FROM TABLE_A AS a
LEFT JOIN TABLE_B AS b
    ON a.user_id = b.user_id AND
       user_mode IN ('Customer', 'Partner') AND
       sale_mode IN ('Web', 'Local');

【讨论】:

以上是关于如何避免多个子查询作为表达式(SQL优化)的主要内容,如果未能解决你的问题,请参考以下文章

SQL之子查询

返回链接记录并避免多个子查询/循环

sql 问题子查询返回的值不止一个。

使用子查询优化 SQL 查询

从多个表中创建 Sql Server VIEW GROUPing BY,选择子查询作为别名

由一条sql语句想到的子查询优化