需要 sql 高效查询
Need a sql efficient query
我在 table 中有大约 600 万行,我正在使用以下查询查询 table。
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 秒才能得到结果?
有没有其他方法可以更有效地创建此查询?
这里是mysqlexplain命令解释
此查询可能无需针对相同 table 的子查询即可执行,即:
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 才能获得所需的结果。
我认为索引正是您所需要的。 EXPLAIN
中让我感到困惑的部分是(猜测?)来自子查询的行数与主查询中的行数如此不同。
老实说,我对MYSQL不是很熟悉,但是在MSSQL中我会尝试先将子查询的结果转储到一个临时的table,把一个它上面的唯一聚集索引,然后 select 从原始 table 连接到 ID 列上的临时 table 的所有内容。 (不要使用 IN,使用 JOIN,因为临时 table 中不能有任何双打)
这可能还会显示所有时间都花在了哪里。
我的猜测是,这主要是一个统计问题,但我真的不知道如何强制更新 MYSQL 中索引的统计信息。
(在https://dzone.com/articles/updating-innodb-table-statistics-manually中有一些关于FLUSH TABLE
的讨论,但它似乎也有一些缺点,谨慎使用)
这是您的查询:
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)
上的索引。
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
的重复条目而无法正常工作,则可以进行调整。
我在 table 中有大约 600 万行,我正在使用以下查询查询 table。
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 秒才能得到结果?
有没有其他方法可以更有效地创建此查询?
这里是mysqlexplain命令解释
此查询可能无需针对相同 table 的子查询即可执行,即:
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 才能获得所需的结果。
我认为索引正是您所需要的。 EXPLAIN
中让我感到困惑的部分是(猜测?)来自子查询的行数与主查询中的行数如此不同。
老实说,我对MYSQL不是很熟悉,但是在MSSQL中我会尝试先将子查询的结果转储到一个临时的table,把一个它上面的唯一聚集索引,然后 select 从原始 table 连接到 ID 列上的临时 table 的所有内容。 (不要使用 IN,使用 JOIN,因为临时 table 中不能有任何双打)
这可能还会显示所有时间都花在了哪里。
我的猜测是,这主要是一个统计问题,但我真的不知道如何强制更新 MYSQL 中索引的统计信息。
(在https://dzone.com/articles/updating-innodb-table-statistics-manually中有一些关于FLUSH TABLE
的讨论,但它似乎也有一些缺点,谨慎使用)
这是您的查询:
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)
上的索引。
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
的重复条目而无法正常工作,则可以进行调整。