【问题标题】:how can this query be optimized (n+1 and dense_rank after)如何优化这个查询(n+1 和dense_rank 之后)
【发布时间】:2021-05-12 21:40:05
【问题描述】:

如何优化这个查询

WITH stats AS (SELECT a.IntegratorSalesAssociateID,
                      a.AgentName,
                      (
                          SELECT COUNT(*)
                          FROM properties AS p
                          WHERE a.IntegratorSalesAssociateID = p.IntegratorSalesAssociateID
                            AND p.TransactionType = '2'
                            AND MONTH(p.OrigListingDate) = MONTH(CURRENT_DATE)
                            AND YEAR(p.OrigListingDate) = YEAR(CURRENT_DATE)
                      ) AS properties_this_month
               FROM agents AS a)
SELECT stats.*,
       DENSE_RANK() over (ORDER BY stats.properties_this_month DESC) AS 'rank'
from stats

我想如果我加入这两个表并以某种方式将它们分组,它会执行得更好,目前它运行了 17.5 秒,奇怪的是,添加dense_rank 根本不会影响性能。

相关表结构

CREATE TABLE `agents`
(
    `IntegratorSalesAssociateID` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL,
    `AgentName`                  varchar(100) COLLATE utf8mb4_unicode_ci     DEFAULT NULL,
    `created_at`                 timestamp                              NULL DEFAULT NULL,
    `updated_at`                 timestamp                              NULL DEFAULT NULL
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci;


CREATE TABLE `properties`
(
    `id`                         bigint(20) UNSIGNED                    NOT NULL,
    `IntegratorSalesAssociateID` varchar(13) COLLATE utf8mb4_unicode_ci NOT NULL,
    `TransactionType`            tinyint(4)                             NOT NULL,
    `OrigListingDate`            date                                        DEFAULT NULL,
    `created_at`                 timestamp                              NULL DEFAULT NULL,
    `updated_at`                 timestamp                              NULL DEFAULT NULL
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci;

【问题讨论】:

  • 我们对底层表结构、索引、数据或查询计划一无所知。您认为我们可以如何优化此查询?
  • 我认为实际的表结构并不重要,我的问题基本上是,如何通过加入表而不使用子查询来达到相同的结果。实际的表格非常大,会占据整个页面
  • AND MONTH(p.OrigListingDate) = MONTH(CURRENT_DATE) AND YEAR(p.OrigListingDate) = YEAR(CURRENT_DATE) 看起来很可疑。我会尝试将其重写为 p.OrigListingDate BETWEEN AND
  • 将相关子查询转换为 JOIN。删除 CTE 使用。
  • 您可以包含表格的相关部分。

标签: mysql sql mariadb query-optimization mariadb-10.5


【解决方案1】:

鉴于DENSE_RANK() 不影响性能,您要优化:

SELECT a.IntegratorSalesAssociateID,
       a.AgentName,
       (SELECT COUNT(*)
        FROM properties p
        WHERE a.IntegratorSalesAssociateID = p.IntegratorSalesAssociateID AND
              p.TransactionType = '2' AND
              MONTH(p.OrigListingDate) = MONTH(CURRENT_DATE) AND
              YEAR(p.OrigListingDate) = YEAR(CURRENT_DATE)
         ) AS properties_this_month
FROM agents a;

我会改写为:

SELECT a.IntegratorSalesAssociateID,
       a.AgentName,
       (SELECT COUNT(*)
        FROM properties p
        WHERE a.IntegratorSalesAssociateID = p.IntegratorSalesAssociateID AND
              p.TransactionType = 2 AND
              p.OrigListingDate >= CURRENT_DATE - INTERVAL (1 - DAY(CURRENT_DATE) DAY
         ) AS properties_this_month
FROM agents a;

两个变化是:

  • TransactionType 看起来像一个数字。假设是,我删除了单引号。不要混合数据类型!当然,如果列是字符串,那么使用单引号。
  • 我更改了日期逻辑以删除列上的所有功能。我假设没有未来的“原始”日期。如果是这样,您可以在月底添加另一个条件。

然后,对于这个查询,您需要一个索引:properties(IntegratorSalesAssociateID, TransactionType, OrigListingDate)。实际上,这个索引可能适用于原始版本的数据。

我真诚地怀疑使用显式聚合会提高性能。 GROUP BY——虽然非常强大——通常比相关子查询慢。使用正确的索引几乎总是更慢(或至少不会更快)。

【讨论】:

    【解决方案2】:

    你可以试试这个:

    ;WITH stats AS
    (
        SELECT 
           p.IntegratorSalesAssociateID
           , COUNT(*) AS properties_this_month
        FROM properties AS p
        WHERE p.TransactionType = '2'
            AND MONTH(p.OrigListingDate) = MONTH(CURRENT_DATE)
            AND YEAR(p.OrigListingDate) = YEAR(CURRENT_DATE)
        GROUP BY p.IntegratorSalesAssociateID
    )
    SELECT 
       a.IntegratorSalesAssociateID
       , a.AgentName
       , COALESCE(s.properties_this_month, 0) AS properties_this_month
    FROM agents AS a
    LEFT JOIN stats s ON a.IntegratorSalesAssociateID = s.IntegratorSalesAssociateID
    

    【讨论】:

    • 我们当然同意范围查询可能会更高效。
    • 您还删除了RANK()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-22
    • 1970-01-01
    • 2022-11-13
    • 1970-01-01
    相关资源
    最近更新 更多