innodb 的 COUNT 替代方案以防止 table 扫描?
Alternative to COUNT for innodb to prevent table scan?
我已经设法组合了一个适合我需要的查询,尽管它比我希望的要复杂。但是,对于 tables 的大小,查询比应有的速度慢 (0.17s)。根据下面提供的 EXPLAIN
,原因是 meta_relationships
table 上有一个 table 扫描,因为 COUNT
在 WHERE
innodb
引擎上的子句。
查询:
SELECT
posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
RIGHT JOIN meta_relationships ON (posts.post_id = meta_relationships.object_id)
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE meta.meta_name = computers AND meta_relationships.object_id
NOT IN (SELECT meta_relationships.object_id FROM meta_relationships
GROUP BY meta_relationships.object_id HAVING count(*) > 1)
GROUP BY meta_relationships.object_id
这个特定的查询,selects 帖子只有 computers
类别。 count > 1
的目的是排除包含 computers/hardware
、computers/software
等的帖子。selected 的类别越多,计数就越高。
理想情况下,我想让它像这样运行:
WHERE meta.meta_name IN ('computers') AND meta_relationships.meta_order IN (0)
或
WHERE meta.meta_name IN ('computers','software')
AND meta_relationships.meta_order IN (0,1)
等..
但不幸的是,这不起作用,因为它没有考虑到可能存在 meta_relationships.meta_order
= 2。
我试过了...
WHERE meta.meta_name IN ('computers')
GROUP BY meta_relationships.meta_order
HAVING meta_relationships.meta_order IN (0) AND meta_relationships.meta_order NOT IN (1)
但它 return 行数不正确。
解释:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY meta ref PRIMARY,idx_meta_name idx_meta_name 602 const 1 Using where; Using index; Using temporary; Using filesort
1 PRIMARY meta_data ref PRIMARY,idx_meta_id idx_meta_id 8 database.meta.meta_id 1
1 PRIMARY meta_relationships ref idx_meta_data_id idx_meta_data_id 8 database.meta_data.meta_data_id 11 Using where
1 PRIMARY posts eq_ref PRIMARY PRIMARY 4 database.meta_relationships.object_id 1
2 MATERIALIZED meta_relationships index NULL idx_object_id 4 NULL 14679 Using index
Tables/Indexes:
元
此 table 包含类别和标签名称。
索引:
主键 (meta_id
), 键 idx_meta_name
(meta_name
)
meta_data
此 table 包含有关类别和标签的其他数据,例如类型(类别或标签)、描述、父项、计数。
索引:
主键 (meta_data_id
), 键 idx_meta_id
(meta_id
)
meta_relationships
这是一个junction/lookuptable。它包含 posts_id 的外键,meta_data_id 的外键,还包含类别的顺序。
索引:
主键 (relationship_id
), 键 idx_object_id
(object_id
), 键 idx_meta_data_id
(meta_data_id
)
- 计数只允许我 select 具有正确类别级别的帖子。例如,类别计算机有仅包含计算机类别的帖子,但也有包含 computers/hardware 的帖子。该计数过滤掉包含这些额外类别的帖子。我希望这是有道理的。
- 我认为优化查询的关键是完全摆脱
COUNT
。
COUNT
的替代方法可能是使用 meta_relationships.meta_order
或 meta_data.parent
。
meta_relationships
table 将快速增长,并且以当前大小(~15K 行)我希望在第 100 秒而不是第 10 秒内实现执行时间。
- 由于每个 category/tag 的
WHERE
子句中需要有多个条件,因此首选针对动态查询优化的任何答案。
- 我用 sample data 创建了一个 IDE。
如何优化这个查询?
编辑:
我一直没能找到解决这个问题的最佳方案。这实际上是 smcjones 改进索引的建议的组合,我建议对其进行 EXPLAIN
并查看 EXPLAIN Output Format,然后将索引更改为任何可以提供最佳性能的索引。
此外,hpf 建议添加另一列总计数帮助极大。最后,在更改索引后,我结束了这个查询。
SELECT posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
JOIN meta_relationships ON meta_relationships.object_id = posts.post_id
JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE posts.meta_count = 2
GROUP BY posts.post_id
HAVING category = 'category,subcategory'
摆脱 COUNT
后,最大的性能杀手是 GROUP BY
和 ORDER BY
,但索引是您最好的朋友。我了解到做一个GROUP BY
时,WHERE
子句很重要,越具体越好。
看看这是否为您提供了正确的答案,可能更快:
SELECT p.post_id, p.post_name,
GROUP_CONCAT(IF(md.type = 'category', meta.meta_name, null)) AS category,
GROUP_CONCAT(IF(md.type = 'tag', meta.meta_name, null)) AS tag
FROM
( SELECT object_id
FROM meta_relation
GROUP BY object_id
HAVING count(*) = 1
) AS x
JOIN meta_relation AS mr ON mr.object_id = x.object_id
JOIN posts AS p ON p.post_id = mr.object_id
JOIN meta_data AS md ON mr.meta_data_id = md.meta_data_id
JOIN meta ON md.meta_id = meta.meta_id
WHERE meta.meta_name = ?
GROUP BY mr.object_id
由于 HAVING 似乎是问题所在,您能否改为在 posts table 中创建一个标志字段并改用它?如果我对查询的理解正确,那么您正在尝试查找只有一个 meta_relationship link 的 post。如果您在 posts table 中创建了一个字段,它是 post 的 meta_relationships 的计数,或者是否只有一个的布尔标志,并当然将其编入索引,这可能会快得多。如果 post 被编辑,这将涉及更新字段。
所以,考虑一下:
向 post 的 table 添加一个名为 "num_meta_rel" 的新字段。它可以是一个无符号的 tinyint,只要你永远不会有超过 255 个标签到任何一个 post。
像这样更新字段:
UPDATE posts
SET num_meta_rel=(SELECT COUNT(object_id) from meta_relationships WHERE object_id=posts.post_id);
此查询需要一些时间才能完成 运行,但一旦完成,您就可以预先计算所有计数。请注意,这可以通过连接更好地完成,但 SQLite (Ideone) 只允许子查询。
现在,您像这样重写查询:
SELECT
posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
RIGHT JOIN meta_relationships ON (posts.post_id = meta_relationships.object_id)
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE meta.meta_name = computers AND posts.num_meta_rel=1
GROUP BY meta_relationships.object_id
如果我没有做错,运行可用代码在这里:http://ideone.com/ZZiKgx
请注意,此解决方案要求您更新 num_meta_rel(选择一个更好的名称,那个太糟糕了......)如果 post 有一个与之关联的新标签。但这应该比一遍又一遍地扫描整个 table 快得多。
通过结合优化查询 AND 优化您的 table,您将获得快速查询。但是,如果没有经过优化的 table.
,您将无法进行快速查询
我怎么强调都不为过:如果您的 table 结构正确且索引数量正确,则您不应该在查询中遇到任何完整的 table 读取像 GROUP BY...HAVING 除非你有意这样做。
根据您的示例,我创建了 this SQLFiddle。
将其与 SQLFiddle #2 进行比较,其中我添加了索引并针对 meta.meta_naame
添加了一个 UNIQUE
索引。
根据我的测试,Fiddle#2 更快。
优化您的查询
这个查询让我发疯,即使在我提出索引是优化它的最佳方式之后也是如此。尽管我仍然认为 table 是提高性能的最大机会,但似乎必须有更好的方法来 运行 MySQL 中的此查询。在解决这个问题后我得到了启示,并使用了以下查询(见in SQLFiddle #3):
SELECT posts.post_id,posts.post_name,posts.post_title,posts.post_description,posts.date,meta.meta_name
FROM posts
LEFT JOIN meta_relationships ON meta_relationships.object_id = posts.post_id
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE meta.meta_name = 'animals'
GROUP BY meta_relationships.object_id
HAVING sum(meta_relationships.object_id) = min(meta_relationships.object_id);
HAVING sum() = min()
on a GROUP BY
应该检查每种类型是否有多个记录。显然,每次记录出现时,总和都会增加更多。 (编辑:在随后的测试中,这似乎与 count(meta_relationships.object_id) = 1
具有相同的影响。哦,重点是我相信您可以删除子查询并获得相同的结果)。
我想明确一点,如果对我提供给您的查询进行任何优化,您不会注意到太多,除非 WHERE meta.meta_name = 'animals'
部分正在查询索引(最好是唯一索引,因为我怀疑您'您将需要其中的多个,这将防止意外重复数据)。
所以,而不是像这样的 table:
CREATE TABLE meta_data (
meta_data_id BIGINT,
meta_id BIGINT,
type VARCHAR(50),
description VARCHAR(200),
parent BIGINT,
count BIGINT);
您应该确保像这样添加主键和索引:
CREATE TABLE meta_data (
meta_data_id BIGINT,
meta_id BIGINT,
type VARCHAR(50),
description VARCHAR(200),
parent BIGINT,
count BIGINT,
PRIMARY KEY (meta_data_id,meta_id),
INDEX ix_meta_id (meta_id)
);
不要过度,但每个 table 都应该有一个主键,并且任何时候你在聚合或查询特定值时,都应该有索引。
当不使用索引时,MySQL 将遍历 table 的每一行,直到找到您想要的内容。在像您这样的有限示例中,这不会花费太长时间(即使它仍然明显较慢),但是当您添加数千条或更多条记录时,这将变得异常痛苦。
将来,在查看您的查询时,请尝试确定完整 table 扫描发生的位置,并查看该列上是否有索引。一个好的起点是聚合或使用 WHERE
语法的任何地方。
关于 count
列的注释
我没有发现将 count
列放入 table 中有什么帮助。它会导致一些非常严重的完整性问题。如果一个 table 被适当优化,它应该很容易使用 count()
并获得当前计数。如果你想把它放在 table 中,你可以使用 VIEW
,尽管这不是最有效的拉动方式。
将 count
列放入 table 的问题在于您需要使用 TRIGGER
或更糟的应用程序逻辑来更新该计数。随着程序的扩展,逻辑可能会丢失或被埋没。添加该列是对规范化的偏离,当发生这样的事情时,应该有一个 非常 很好的理由。
关于是否有曾经这样做的充分理由存在一些争论,但我认为我最好远离这场争论,因为有很多争论在两侧。相反,我会选择一个小得多的战斗,并说我看到这在这个用例中给你带来的麻烦多于好处,所以它可能值得 A/B 测试。
遗憾的是我无法测试性能,
但请使用您的真实数据尝试我的查询:
http://sqlfiddle.com/#!9/81b29/13
SELECT
posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
INNER JOIN (
SELECT meta_relationships.object_id
FROM meta_relationships
GROUP BY meta_relationships.object_id
HAVING count(*) < 3
) mr ON mr.object_id = posts.post_id
LEFT JOIN meta_relationships ON mr.object_id = meta_relationships.object_id
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
INNER JOIN (
SELECT *
FROM meta
WHERE meta.meta_name = 'health'
) meta ON meta_data.meta_id = meta.meta_id
GROUP BY posts.post_id
使用
sum(1)
而不是
count(*)
我已经设法组合了一个适合我需要的查询,尽管它比我希望的要复杂。但是,对于 tables 的大小,查询比应有的速度慢 (0.17s)。根据下面提供的 EXPLAIN
,原因是 meta_relationships
table 上有一个 table 扫描,因为 COUNT
在 WHERE
innodb
引擎上的子句。
查询:
SELECT
posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
RIGHT JOIN meta_relationships ON (posts.post_id = meta_relationships.object_id)
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE meta.meta_name = computers AND meta_relationships.object_id
NOT IN (SELECT meta_relationships.object_id FROM meta_relationships
GROUP BY meta_relationships.object_id HAVING count(*) > 1)
GROUP BY meta_relationships.object_id
这个特定的查询,selects 帖子只有 computers
类别。 count > 1
的目的是排除包含 computers/hardware
、computers/software
等的帖子。selected 的类别越多,计数就越高。
理想情况下,我想让它像这样运行:
WHERE meta.meta_name IN ('computers') AND meta_relationships.meta_order IN (0)
或
WHERE meta.meta_name IN ('computers','software')
AND meta_relationships.meta_order IN (0,1)
等..
但不幸的是,这不起作用,因为它没有考虑到可能存在 meta_relationships.meta_order
= 2。
我试过了...
WHERE meta.meta_name IN ('computers')
GROUP BY meta_relationships.meta_order
HAVING meta_relationships.meta_order IN (0) AND meta_relationships.meta_order NOT IN (1)
但它 return 行数不正确。
解释:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY meta ref PRIMARY,idx_meta_name idx_meta_name 602 const 1 Using where; Using index; Using temporary; Using filesort
1 PRIMARY meta_data ref PRIMARY,idx_meta_id idx_meta_id 8 database.meta.meta_id 1
1 PRIMARY meta_relationships ref idx_meta_data_id idx_meta_data_id 8 database.meta_data.meta_data_id 11 Using where
1 PRIMARY posts eq_ref PRIMARY PRIMARY 4 database.meta_relationships.object_id 1
2 MATERIALIZED meta_relationships index NULL idx_object_id 4 NULL 14679 Using index
Tables/Indexes:
元
此 table 包含类别和标签名称。
索引:
主键 (meta_id
), 键 idx_meta_name
(meta_name
)
meta_data
此 table 包含有关类别和标签的其他数据,例如类型(类别或标签)、描述、父项、计数。
索引:
主键 (meta_data_id
), 键 idx_meta_id
(meta_id
)
meta_relationships
这是一个junction/lookuptable。它包含 posts_id 的外键,meta_data_id 的外键,还包含类别的顺序。
索引:
主键 (relationship_id
), 键 idx_object_id
(object_id
), 键 idx_meta_data_id
(meta_data_id
)
- 计数只允许我 select 具有正确类别级别的帖子。例如,类别计算机有仅包含计算机类别的帖子,但也有包含 computers/hardware 的帖子。该计数过滤掉包含这些额外类别的帖子。我希望这是有道理的。
- 我认为优化查询的关键是完全摆脱
COUNT
。 COUNT
的替代方法可能是使用meta_relationships.meta_order
或meta_data.parent
。meta_relationships
table 将快速增长,并且以当前大小(~15K 行)我希望在第 100 秒而不是第 10 秒内实现执行时间。- 由于每个 category/tag 的
WHERE
子句中需要有多个条件,因此首选针对动态查询优化的任何答案。 - 我用 sample data 创建了一个 IDE。
如何优化这个查询?
编辑:
我一直没能找到解决这个问题的最佳方案。这实际上是 smcjones 改进索引的建议的组合,我建议对其进行 EXPLAIN
并查看 EXPLAIN Output Format,然后将索引更改为任何可以提供最佳性能的索引。
此外,hpf 建议添加另一列总计数帮助极大。最后,在更改索引后,我结束了这个查询。
SELECT posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
JOIN meta_relationships ON meta_relationships.object_id = posts.post_id
JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE posts.meta_count = 2
GROUP BY posts.post_id
HAVING category = 'category,subcategory'
摆脱 COUNT
后,最大的性能杀手是 GROUP BY
和 ORDER BY
,但索引是您最好的朋友。我了解到做一个GROUP BY
时,WHERE
子句很重要,越具体越好。
看看这是否为您提供了正确的答案,可能更快:
SELECT p.post_id, p.post_name,
GROUP_CONCAT(IF(md.type = 'category', meta.meta_name, null)) AS category,
GROUP_CONCAT(IF(md.type = 'tag', meta.meta_name, null)) AS tag
FROM
( SELECT object_id
FROM meta_relation
GROUP BY object_id
HAVING count(*) = 1
) AS x
JOIN meta_relation AS mr ON mr.object_id = x.object_id
JOIN posts AS p ON p.post_id = mr.object_id
JOIN meta_data AS md ON mr.meta_data_id = md.meta_data_id
JOIN meta ON md.meta_id = meta.meta_id
WHERE meta.meta_name = ?
GROUP BY mr.object_id
由于 HAVING 似乎是问题所在,您能否改为在 posts table 中创建一个标志字段并改用它?如果我对查询的理解正确,那么您正在尝试查找只有一个 meta_relationship link 的 post。如果您在 posts table 中创建了一个字段,它是 post 的 meta_relationships 的计数,或者是否只有一个的布尔标志,并当然将其编入索引,这可能会快得多。如果 post 被编辑,这将涉及更新字段。
所以,考虑一下:
向 post 的 table 添加一个名为 "num_meta_rel" 的新字段。它可以是一个无符号的 tinyint,只要你永远不会有超过 255 个标签到任何一个 post。
像这样更新字段:
UPDATE posts
SET num_meta_rel=(SELECT COUNT(object_id) from meta_relationships WHERE object_id=posts.post_id);
此查询需要一些时间才能完成 运行,但一旦完成,您就可以预先计算所有计数。请注意,这可以通过连接更好地完成,但 SQLite (Ideone) 只允许子查询。
现在,您像这样重写查询:
SELECT
posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
RIGHT JOIN meta_relationships ON (posts.post_id = meta_relationships.object_id)
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE meta.meta_name = computers AND posts.num_meta_rel=1
GROUP BY meta_relationships.object_id
如果我没有做错,运行可用代码在这里:http://ideone.com/ZZiKgx
请注意,此解决方案要求您更新 num_meta_rel(选择一个更好的名称,那个太糟糕了......)如果 post 有一个与之关联的新标签。但这应该比一遍又一遍地扫描整个 table 快得多。
通过结合优化查询 AND 优化您的 table,您将获得快速查询。但是,如果没有经过优化的 table.
,您将无法进行快速查询我怎么强调都不为过:如果您的 table 结构正确且索引数量正确,则您不应该在查询中遇到任何完整的 table 读取像 GROUP BY...HAVING 除非你有意这样做。
根据您的示例,我创建了 this SQLFiddle。
将其与 SQLFiddle #2 进行比较,其中我添加了索引并针对 meta.meta_naame
添加了一个 UNIQUE
索引。
根据我的测试,Fiddle#2 更快。
优化您的查询
这个查询让我发疯,即使在我提出索引是优化它的最佳方式之后也是如此。尽管我仍然认为 table 是提高性能的最大机会,但似乎必须有更好的方法来 运行 MySQL 中的此查询。在解决这个问题后我得到了启示,并使用了以下查询(见in SQLFiddle #3):
SELECT posts.post_id,posts.post_name,posts.post_title,posts.post_description,posts.date,meta.meta_name
FROM posts
LEFT JOIN meta_relationships ON meta_relationships.object_id = posts.post_id
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
LEFT JOIN meta ON meta_data.meta_id = meta.meta_id
WHERE meta.meta_name = 'animals'
GROUP BY meta_relationships.object_id
HAVING sum(meta_relationships.object_id) = min(meta_relationships.object_id);
HAVING sum() = min()
on a GROUP BY
应该检查每种类型是否有多个记录。显然,每次记录出现时,总和都会增加更多。 (编辑:在随后的测试中,这似乎与 count(meta_relationships.object_id) = 1
具有相同的影响。哦,重点是我相信您可以删除子查询并获得相同的结果)。
我想明确一点,如果对我提供给您的查询进行任何优化,您不会注意到太多,除非 WHERE meta.meta_name = 'animals'
部分正在查询索引(最好是唯一索引,因为我怀疑您'您将需要其中的多个,这将防止意外重复数据)。
所以,而不是像这样的 table:
CREATE TABLE meta_data (
meta_data_id BIGINT,
meta_id BIGINT,
type VARCHAR(50),
description VARCHAR(200),
parent BIGINT,
count BIGINT);
您应该确保像这样添加主键和索引:
CREATE TABLE meta_data (
meta_data_id BIGINT,
meta_id BIGINT,
type VARCHAR(50),
description VARCHAR(200),
parent BIGINT,
count BIGINT,
PRIMARY KEY (meta_data_id,meta_id),
INDEX ix_meta_id (meta_id)
);
不要过度,但每个 table 都应该有一个主键,并且任何时候你在聚合或查询特定值时,都应该有索引。
当不使用索引时,MySQL 将遍历 table 的每一行,直到找到您想要的内容。在像您这样的有限示例中,这不会花费太长时间(即使它仍然明显较慢),但是当您添加数千条或更多条记录时,这将变得异常痛苦。
将来,在查看您的查询时,请尝试确定完整 table 扫描发生的位置,并查看该列上是否有索引。一个好的起点是聚合或使用 WHERE
语法的任何地方。
关于 count
列的注释
我没有发现将 count
列放入 table 中有什么帮助。它会导致一些非常严重的完整性问题。如果一个 table 被适当优化,它应该很容易使用 count()
并获得当前计数。如果你想把它放在 table 中,你可以使用 VIEW
,尽管这不是最有效的拉动方式。
将 count
列放入 table 的问题在于您需要使用 TRIGGER
或更糟的应用程序逻辑来更新该计数。随着程序的扩展,逻辑可能会丢失或被埋没。添加该列是对规范化的偏离,当发生这样的事情时,应该有一个 非常 很好的理由。
关于是否有曾经这样做的充分理由存在一些争论,但我认为我最好远离这场争论,因为有很多争论在两侧。相反,我会选择一个小得多的战斗,并说我看到这在这个用例中给你带来的麻烦多于好处,所以它可能值得 A/B 测试。
遗憾的是我无法测试性能,
但请使用您的真实数据尝试我的查询:
http://sqlfiddle.com/#!9/81b29/13
SELECT
posts.post_id,posts.post_name,
GROUP_CONCAT(IF(meta_data.type = 'category', meta.meta_name,null)) AS category,
GROUP_CONCAT(IF(meta_data.type = 'tag', meta.meta_name,null)) AS tag
FROM posts
INNER JOIN (
SELECT meta_relationships.object_id
FROM meta_relationships
GROUP BY meta_relationships.object_id
HAVING count(*) < 3
) mr ON mr.object_id = posts.post_id
LEFT JOIN meta_relationships ON mr.object_id = meta_relationships.object_id
LEFT JOIN meta_data ON meta_relationships.meta_data_id = meta_data.meta_data_id
INNER JOIN (
SELECT *
FROM meta
WHERE meta.meta_name = 'health'
) meta ON meta_data.meta_id = meta.meta_id
GROUP BY posts.post_id
使用
sum(1)
而不是
count(*)