繁体   English   中英

优化MySQL子查询

[英]Optimize MySQL Sub-Query

有没有一种方法可以优化此查询? 它看起来很多余:

SELECT
        SUM((SELECT 
            IFNULL(SUM(trx.totalAmount), 0) 
            FROM trx
            WHERE 
            FIND_IN_SET (trx.clientOrderId, "B6A8DB9568,6E7705B487,59C4D4234D,1D9CD4EF96,4C373E8CDE,E818BEE48F,6610555669,ECF388E288,32FD93075C,B03417425B,18FD77061A,1C39E4BD04,C92B970E55,0920F06DFA,EEFB4AAADA,FC2D9FF9AD") > 0
            AND trx.txnType IN ('REFUND', 'VOID')
        )) as refunds,

      SUM((SELECT 
        IFNULL(SUM(trx.totalAmount), 0) 
        FROM trx
        WHERE 
            FIND_IN_SET (trx.clientOrderId, "B6A8DB9568,6E7705B487,59C4D4234D,1D9CD4EF96,4C373E8CDE,E818BEE48F,6610555669,ECF388E288,32FD93075C,B03417425B,18FD77061A,1C39E4BD04,C92B970E55,0920F06DFA,EEFB4AAADA,FC2D9FF9AD") > 0 
            AND trx.txnType = 'SALE'
            AND trx.billingCycleNumber != 1
      )) AS lifetimeRevenue

请注意,这只是查询的一部分,原始查询中还有10多个查询,因此确实需要知道是否可以对其进行优化。

谢谢大家

使用这样的子查询的问题是每个子查询都必须扫描整个表。 同样,使用FIND_IN_SET()的方式也会强制进行全表扫描,即使您有索引也是如此。 因此,您要进行12次完整的表格扫描。

这是根本不使用子查询的解决方案。 它会在表中扫描一次匹配的clientOrderId值,以获取与您需要的任何txType匹配的所有行的超集。

然后,如果txnType是某些类型之一,则totalAmount的每个和都是有条件的,否则对每行的totalAmount使用零,并且零对总和没有任何贡献,因此就像您跳过了不匹配txnType的行一样。

SELECT
  SUM(IF(trx.txnType IN ('REFUND', 'VOID'), trx.totalAmount, 0)) AS refunds,
  SUM(IF(trx.txnType = 'SALE' AND trx.billingCycleNumber != 1, trx.totalAmount, 0)) AS lifetimeRevenue
FROM trx
WHERE trx.clientOrderId IN (
    'B6A8DB9568', '6E7705B487', '59C4D4234D', '1D9CD4EF96', 
    '4C373E8CDE', 'E818BEE48F', '6610555669', 'ECF388E288',
    '32FD93075C', 'B03417425B', '18FD77061A', '1C39E4BD04',
    'C92B970E55', '0920F06DFA', 'EEFB4AAADA', 'FC2D9FF9AD')
  AND trx.txnType IN ('REFUND', 'VOID', 'SALE');

您应该在此查询的(clientOrderId)上有一个索引。 由于您有两个IN()谓词,因此WHERE子句将仅对索引中的第一列使用索引。

不要使用FIND_IN_SET()表达式,因为它不会在WHERE子句中使用索引。

您说查询中还有10个字词。 因此,我预计在这些术语中会有一些不同类型的表达式。 我不会回答任何“但是如果下一个术语看起来有些不同……该怎么办……”。 我已经向您展示了将子查询分解为一个单遍查询的方法。 您可以将其应用于查询中的其他术语。


这是我测试过的一个演示:

create table trx (
  clientOrderId char(10), 
  txnType enum('REFUND','VOID','SALE'), 
  totalAmount numeric(9,2), 
  billingCycleNumber int default 0,
  key (clientOrderId)
);

+---------------+---------+-------------+--------------------+
| clientOrderId | txnType | totalAmount | billingCycleNumber |
+---------------+---------+-------------+--------------------+
| B6A8DB9568    | REFUND  |       42.00 |                  0 |
| 59C4D4234D    | SALE    |       84.00 |                  0 |
+---------------+---------+-------------+--------------------+

这是您查询的解释:

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra          |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+
|  1 | PRIMARY     | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | No tables used |
|  3 | SUBQUERY    | trx   | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where    |
|  2 | SUBQUERY    | trx   | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |    50.00 | Using where    |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------+

请注意,每个术语都有一个子查询,每个子查询都以“ type = All”作为其表访问权限。

这是我的查询的解释:

+----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key           | key_len | ref  | rows | filtered | Extra                              |
+----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+------------------------------------+
|  1 | SIMPLE      | trx   | NULL       | range | clientOrderId | clientOrderId | 11      | NULL |   16 |    50.00 | Using index condition; Using where |
+----+-------------+-------+------------+-------+---------------+---------------+---------+------+------+----------+------------------------------------+

一个简单的表访问,使用索引。

给定示例数据,您的查询和我的查询的结果:

+---------+-----------------+
| refunds | lifetimeRevenue |
+---------+-----------------+
|   42.00 |           84.00 |
+---------+-----------------+

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM