我有以下 SQL 语句,该语句在 WordPress 安装的 MySQL 中运行。我正在寻找符合特定条件并在特定日期之后创建的元数据。
但是,WordPress 不存储元数据添加到帖子的日期,因此我创建了元数据本身 (wpcf-post-likes)、添加日期 (wpcf-post-like-date) 和第三个元数据同时创建,其中包含前两个元数据的 ID,并用感叹号分隔 (wpcf-post-like-date-link)。
然后,我使用以下 SQL 语句来获取当前作者在特定日期后撰写的帖子(示例 ID 包含在下面的 SQL 代码中)的点赞数。它可以工作,但运行时间大约需要 7 - 8 秒,这与理想状态相去甚远。同一语句是否有更有效的版本?
SELECT `meta_id` FROM `wp_postmeta`
WHERE `post_id` IN (
SELECT `ID` FROM `wp_posts` WHERE post_author = $user_id
) AND
`meta_value` IN (
SELECT CONCAT(combined_ids_a.`meta_id`, '!', combined_ids_b.`meta_id`) AS `combined_meta_id` FROM (
SELECT `meta_id`, 'like_id' AS `meta_type` FROM `wp_postmeta`
WHERE `meta_key` LIKE 'wpcf-post-likes'
AND `meta_value` NOT LIKE '1837'
AND `meta_id` IN
(SELECT SUBSTRING_INDEX(`meta_value`, '!', 1) FROM `wp_postmeta` WHERE `meta_key` LIKE 'wpcf-post-like-date-link' ORDER BY `meta_id` DESC)
UNION
SELECT `meta_id`, 'like_date' AS `meta_type` FROM `wp_postmeta`
WHERE `meta_key` LIKE 'wpcf-post-like-date'
AND `meta_value` > '01-02-2016 09:20:34'
AND `meta_id` IN
(SELECT SUBSTRING_INDEX(`meta_value`, '!', -1) FROM `wp_postmeta` WHERE `meta_key` LIKE 'wpcf-post-like-date-link' ORDER BY `meta_id` DESC)
) AS combined_ids_a JOIN
(
SELECT `meta_id`, 'like_id' AS `meta_type` FROM `wp_postmeta`
WHERE `meta_key` LIKE 'wpcf-post-likes'
AND `meta_value` NOT LIKE '1837'
AND `meta_id` IN
(SELECT SUBSTRING_INDEX(`meta_value`, '!', 1) FROM `wp_postmeta` WHERE `meta_key` LIKE 'wpcf-post-like-date-link' ORDER BY `meta_id` DESC)
UNION
SELECT `meta_id`, 'like_date' AS `meta_type` FROM `wp_postmeta`
WHERE `meta_key` LIKE 'wpcf-post-like-date'
AND `meta_value` > '01-02-2016 09:20:34'
AND `meta_id` IN
(SELECT SUBSTRING_INDEX(`meta_value`, '!', -1) FROM `wp_postmeta` WHERE `meta_key` LIKE 'wpcf-post-like-date-link' ORDER BY `meta_id` DESC)
) AS combined_ids_b
);
最佳答案
嗯,您正在处理一个键值表,因此性能问题是很自然的。
您交叉自联接一个临时 View (派生表),这是非常特殊的,但对于 DBMS 来说不应该是一个大问题。它应该注意到您的查询中有两次相同的子查询,并且只处理它一次。 (但是,当交叉连接时,您应该显式地使用CROSS JOIN
,以表明这是故意的,并且您不仅仅是忘记了ON
子句,就像您使用简单的JOIN
所建议的那样。)
那么还有什么需要优化的呢?我已经告诉过你,你应该替换 UNION
与 UNION ALL
如果可能的话,删除 ORDER BY
在 IN
条款。此外,您与 LIKE
一起工作和NOT LIKE
尽管你不寻找模式。也许您很幸运,DBMS 事先扫描了您的字符串文字,并注意到根本没有可搜索的模式,但也许事实并非如此。不要给 DBMS 做不必要的工作,并使用 =
和<>
相反。
然后您创建了一个从未使用过的meta_type 列,那为什么还要创建它呢?
无论如何,这个查询应该有两个复合索引:一个在 wp_postmeta(meta_key, meta_id, meta_value) 上,以便快速找到元 ID。另一个关于 wp_postmeta(meta_key, meta_value)。顺便问一下:不能指定最外层查询的meta_key吗?在没有键的情况下查找值看起来很奇怪。
查询变为:
select meta_id
from wp_postmeta
where post_id in (select id from wp_posts where post_author = $user_id)
and meta_value in
(
select concat(a.meta_id, '!', b.meta_id)
from
(
select meta_id
from wp_postmeta
where meta_key = 'wpcf-post-likes'
and meta_value <> '1837'
and meta_id in (select substring_index(meta_value, '!', 1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link')
union all
select meta_id
from wp_postmeta
where meta_key = 'wpcf-post-like-date'
and meta_value > '01-02-2016 09:20:34'
and meta_id in (select substring_index(meta_value, '!', -1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link')
) a
cross join
(
select meta_id
from wp_postmeta
where meta_key = 'wpcf-post-likes'
and meta_value <> '1837'
and meta_id in (select substring_index(meta_value, '!', 1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link')
union all
select meta_id
from wp_postmeta
where meta_key = 'wpcf-post-like-date'
and meta_value > '01-02-2016 09:20:34'
and meta_id in (select substring_index(meta_value, '!', -1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link')
) b
);
或者使用 OR
而不是UNION ALL
:
select meta_id
from wp_postmeta
where post_id in (select id from wp_posts where post_author = $user_id)
and meta_value in
(
select concat(a.meta_id, '!', b.meta_id)
from
(
select meta_id
from wp_postmeta
where
(
meta_key = 'wpcf-post-likes'
and meta_value <> '1837'
and meta_id in (select substring_index(meta_value, '!', 1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link' order by meta_id desc)
)
or
(
meta_key = 'wpcf-post-like-date'
and meta_value > '01-02-2016 09:20:34'
and meta_id in (select substring_index(meta_value, '!', -1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link' order by meta_id desc)
)
) a
cross join
(
select meta_id
from wp_postmeta
where
(
meta_key = 'wpcf-post-likes'
and meta_value <> '1837'
and meta_id in (select substring_index(meta_value, '!', 1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link' order by meta_id desc)
)
or
(
meta_key = 'wpcf-post-like-date'
and meta_value > '01-02-2016 09:20:34'
and meta_id in (select substring_index(meta_value, '!', -1) from wp_postmeta where meta_key like 'wpcf-post-like-date-link' order by meta_id desc)
)
) b
);
还有一件事:meta_value 包含一个字符串。如果键是“wpcf-post-like-date”,您希望它包含日期时间字符串。但是,它仍然是一个字符串,因此“02-01-1980 09:20:34”比“01-02-2016 09:20:34”大。为了能够比较日期时间,您应该存储“yyyy-mm-dd hh:mi:ss”或转换为日期时间或时间戳。
关于mysql - 这个 SQL 语句有更高效的版本吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35131109/