【问题标题】:每组优化的最大 n
【发布时间】:2022-01-06 21:22:39
【问题描述】:

如何优化这个 SQLite 查询?目前运行需要 31 秒。我想在 Web 应用程序上显示前 10 名股票上涨者和前 10 名股票下跌者。该表有 200 万行,并且每天都有新的价格数据可用时会略微增加。

如果这不可能,我可以创建一个计划任务来将这些结果缓存到临时数据库表或临时文件中。看起来像是额外的工作,但如果需要也可以。

WITH todayPrices AS (
    SELECT * FROM (
        SELECT *, row_number() OVER (
        PARTITION BY CompanyID 
        ORDER BY Date DESC
        ) AS rn
    FROM DimCompanyPrice
    ) a
    WHERE rn = 1 
    ORDER BY CompanyID ASC
    ),
    yestPrices AS (
    SELECT * FROM (
        SELECT *, row_number() OVER (
        PARTITION BY CompanyID 
        ORDER BY Date DESC
        ) AS rn
    FROM DimCompanyPrice
    ) a
    WHERE rn = 2 
    ORDER BY CompanyID ASC
    )
    SELECT todayPrices.CompanyID, 100.0 * (todayPrices.CloseAdjusted-yestPrices.CloseAdjusted) / yestPrices.CloseAdjusted AS gain
    FROM todayPrices
    INNER JOIN yestPrices on todayPrices.CompanyID=yestPrices.CompanyID
    ORDER BY gain DESC
    LIMIT 10

我想要一些关于什么是让它表现更好的最佳方法的意见。任何意见将不胜感激。

EXPLAIN QUERY PLAN的结果:

id  parent  notused  detail
3   0       0   MATERIALIZE 2
5   3       0   CO-ROUTINE 1
8   5       0   CO-ROUTINE 6
11  8       0   SCAN TABLE DimCompanyPrice
36  8       0   USE TEMP B-TREE FOR ORDER BY
62  5       0   SCAN SUBQUERY 6
134 3       0   SCAN SUBQUERY 1 AS a
163 3       0   USE TEMP B-TREE FOR ORDER BY
174 0       0   MATERIALIZE 4
176 174     0   CO-ROUTINE 3
179 176     0   CO-ROUTINE 7
182 179     0   SCAN TABLE DimCompanyPrice
207 179     0   USE TEMP B-TREE FOR ORDER BY
233 176     0   SCAN SUBQUERY 7
305 174     0   SCAN SUBQUERY 3 AS a
334 174     0   USE TEMP B-TREE FOR ORDER BY
345 0       0   SCAN SUBQUERY 4
357 0       0   SEARCH SUBQUERY 2 USING AUTOMATIC COVERING INDEX (CompanyID=?)
382 0       0   USE TEMP B-TREE FOR ORDER BY

【问题讨论】:

  • 并从 CTE 中删除 ORDER BYs。
  • 有一个 CTE,其中 rn = 1 或 rn = 2,然后在最终 SELECT 中自行加入该 CTE
  • 放弃ORDER BY 或使用单个CTE 的建议都没有提高性能。
  • 您需要使用为您需要帮助的 SQL 语句运行 EXPLAIN QUERY PLAN 的结果来更新您的问题。如果 SQL 语句不是您的问题中已经存在的语句(因为您已使用之前的建议之一对其进行了更新),那么请包含与 EXPLAIN QUERY PLAN 对应的 SQL 语句

标签: sql sqlite greatest-n-per-group window-functions conditional-aggregation


【解决方案1】:

我建议使用条件聚合来计算列 gain 的查询。
为此,您将需要一次表扫描以根据日期对每个公司的行进行排名,然后过滤掉排名大于 2 的行,最后进行聚合:

SELECT CompanyId,
       100 * (MAX(CASE WHEN rn = 1 THEN CloseAdjusted END) / MAX(CASE WHEN rn = 2 THEN CloseAdjusted END) - 1) gain
FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY CompanyId ORDER BY Date DESC) rn
  FROM DimCompanyPrice
)
WHERE rn <= 2
GROUP BY CompanyId
ORDER BY gain DESC;

但是如果 todayyesterday 实际上是指当前日期和上一个日期,您也可以使用以下查询:

SELECT CompanyId,
       100 * (MAX(CASE WHEN Date = CURRENT_DATE THEN CloseAdjusted END) / MAX(CASE WHEN Date = Date(CURRENT_DATE, '-1 day') THEN CloseAdjusted END) - 1) gain
FROM DimCompanyPrice
WHERE Date >= Date(CURRENT_DATE, '-1 day')
GROUP BY CompanyId;

【讨论】:

  • 这将运行时间减少了 100%!由于与我的查询中的两次相比,这仅使用了一次表扫描,因此我倾向于相信长杆正在扫描表。我可以使用一些日期索引来加快扫描速度吗?
  • @sat1017 值得在 Date 上尝试索引。
  • 对不起,我对 SQL 很陌生,所以我正在快速学习。 MAX 有什么意义? CASE 不应该返回正值的CloseAdjusted 价格吗?
  • @sat1017 这称为条件聚合。 MAX 中的 CASE 表达式仅根据 rn 选取 1 个 CloseAdjusted,此值由 MAX 返回。
  • 这是否有效,因为在第一种情况下,当 rn 不等于 1 null 或返回 0 时?因此,MAX 将返回 rn =1 的关联值?
猜你喜欢
  • 2018-12-12
  • 2013-04-16
  • 2017-03-02
  • 1970-01-01
  • 1970-01-01
  • 2013-05-27
  • 2023-04-08
  • 2018-06-02
  • 1970-01-01
相关资源
最近更新 更多