需要一个高效的sql查询

Posted

技术标签:

【中文标题】需要一个高效的sql查询【英文标题】:Need a sql efficient query 【发布时间】:2020-04-08 06:44:13 【问题描述】:

我的表中有大约 600 万行,我正在使用以下查询查询该表。

SELECT * FROM FD_CPC_HISTORICAL_DATA WHERE id IN (SELECT MAX(id) FROM FD_CPC_HISTORICAL_DATA WHERE fb_ads_account_id=1462257067274960 AND created_at BETWEEN '2019-12-13 00:00:00' AND '2019-12-13 23:59:59' GROUP BY source_text) \G

我为 fb_ads_account_id、created_at、source_text 创建了索引。 id 是主键。

我的问题是为什么即使我已经创建了索引,这个查询也需要大约 9 秒才能得到结果?

还有其他方法可以更高效地创建此查询吗?

这里是mysql解释命令说明

【问题讨论】:

不清楚你有什么索引;请提供SHOW CREATE TABLE 【参考方案1】:

这个查询可能可以在没有子查询的情况下对同一个表执行,即:

SELECT * FROM FD_CPC_HISTORICAL_DATA WHERE fb_ads_account_id=1462257067274960 AND created_at BETWEEN '2019-12-13 00:00:00' AND '2019-12-13 23:59:59' ORDER BY id DESC LIMIT 1

如果你想要最大 ID。或类似的东西,我不确定您是否需要 GROUP BY 才能获得所需的结果。

【讨论】:

谢谢。 group by 应该在那里,因为有多个具有相同来源的行,因此需要获取最新的。【参考方案2】:

我认为索引正是您所需要的。 EXPLAIN 中让我感到困惑的部分是子查询中的(估计的?)行数与主查询中的行数大不相同。

说实话,我对 mysql 不是很熟悉,但在 MSSQL 中,我会尝试先将子查询的结果转储到临时表中,在其上放置一个唯一的聚集索引,然后从中选择所有内容原始表在 ID 列上连接到所述临时表。 (不要使用 IN,使用 JOIN,因为临时表中不能有任何双精度)

这也可能显示所有时间都花在了哪里。 我的猜测是,这主要是一个统计问题,但我真的不知道如何强制更新 MYSQL 中索引的统计信息。 (https://dzone.com/articles/updating-innodb-table-statistics-manually 中有一些关于 FLUSH TABLE 的讨论,但它似乎也有一些缺点,请谨慎使用)

【讨论】:

非常感谢您的回答。这对我帮助很大【参考方案3】:

这是您的查询:

SELECT hd.*
FROM FD_CPC_HISTORICAL_DATA hd
WHERE hd.id IN (SELECT MAX(hd2.id)
                FROM FD_CPC_HISTORICAL_DATA hd2
                WHERE hd2.fb_ads_account_id = 1462257067274960 AND
                      hd2.created_at >= '2019-12-13' AND 
                      hd2.created_at < '2019-12-14'
                GROUP BY source_text
               );

我建议这样写:

SELECT hd.*
FROM FD_CPC_HISTORICAL_DATA hd
WHERE hd.fb_ads_account_id = 1462257067274960 AND
      hd.id = (SELECT MAX(hd2.id)
               FROM FD_CPC_HISTORICAL_DATA hd2
               WHERE hd2.fb_ads_account_id = hd.hd.fb_ads_account_id AND
                     hd2.source_text = hd.source_tx AND
                     hd2.created_at >= '2019-12-13' AND 
                     hd2.created_at < '2019-12-14'
               );

对于此查询,您需要在FD_CPC_HISTORICAL_DATA(fb_ads_account_id, source_text,created_at) 上建立索引。

【讨论】:

谢谢。它是综合指数还是类似的东西?我只是问,因为我对索引没有太多专业知识:) @SandunPerera 。 . .具有多个键的索引称为复合索引。所以建议的索引是一个复合索引。 非常感谢。我得到了显着的增强。 @sandunPerera 。 . .你接受了另一个答案。这是否意味着答案提供了更好的性能?【参考方案4】:
SELECT  f.*
    FROM  
        ( SELECT  source_text, MAX(created_at) AS mx
            FROM  FD_CPC_HISTORICAL_DATA
            WHERE  fb_ads_account_id=1462257067274960
              AND  created_at >= '2019-12-13'
              AND  created_at  < '2019-12-13' + INTERVAL 1 DAY
            GROUP BY  source_text 
        ) AS x
    JOIN  FD_CPC_HISTORICAL_DATA AS f
       ON  f.account_id = x.account_id
      AND  f.source_text = x.source_text
      AND  f.created_at = x.mx

那么你需要这个复合索引:

INDEX(account_id, source_text, created_at)  -- in this order

如果由于具有相同 created_at 的重复条目而无法正常工作,则可以进行调整。

【讨论】:

以上是关于需要一个高效的sql查询的主要内容,如果未能解决你的问题,请参考以下文章

oracle 高效分页查询SQL

如何使这个 SQL 查询更高效?

高效的asp.net sql查询

案例分析:SQL 窗口函数实现高效分页查询

让 SQL 查询更高效

适用于所有(或大多数)数据库的高效 SQL 测试查询或验证查询