假设我想为两个表A和B找到A中但不是B的所有记录的计数,A和B中的所有记录,以及B中但不在A中的所有记录。我不想要实际记录,只需要所有3个组件的计数(想想维恩图)。
当我说,例如,A和B中的记录时,我的意思是对所有具有相同值的记录的计数,例如,四个变量(如ID,年,月,日)。
是否有一个时髦的查询会有效地返回这些计数?
答案 0 :(得分:2)
对于A和B中的所有人来说,这是一个简单的JOIN:
SELECT COUNT(*)
FROM A
JOIN B ON A.ID = B.ID AND A.Year = B.Year AND A.Month = B.Month AND A.Day = B.Day
请注意,这假设组合(ID, Year, Month, Day)
在每个表中都是唯一的;如果有重复项,它将计算等效项之间的所有交叉产品。如果ID
是表中的唯一键,那应该不是问题。
对于不在B中的所有A,请使用LEFT JOIN:
SELECT COUNT(*)
FROM A
LEFT JOIN B ON A.ID = B.ID AND A.Year = B.Year AND A.Month = B.Month AND A.Day = B.Day
WHERE B.ID IS NULL
对于不在A中的所有B,做同样的事情,但是反转A和B的角色:
SELECT COUNT(*)
FROM B
LEFT JOIN A ON A.ID = B.ID AND A.Year = B.Year AND A.Month = B.Month AND A.Day = B.Day
WHERE A.ID IS NULL
您可以将前两个组合成一个查询:
SELECT SUM(B.ID IS NOT NULL) AS A_and_B_count, SUM(B.ID IS NULL) AS A_not_B_count
FROM A
LEFT JOIN B ON A.ID = B.ID AND A.Year = B.Year AND A.Month = B.Month AND A.Day = B.Day
但我认为不可能在此包含第三个查询。这需要FULL OUTER JOIN
,MySQL没有。
对于所有这些查询,请确保您要比较的至少一个列具有索引,否则这将非常慢;越多越好。虽然它们中的任何一个是唯一的(例如ID)字段,但这应该足够了。
答案 1 :(得分:1)
如果两个表上都有合适的索引(最好),包含要比较的列作为前导列,那么获取这些计数的查询将是最有效的,例如
ON `table_A` (`id`, `year`, `month`, `day`)
ON `table_B` (`id`, `year`, `month`, `day`)
有了这些索引,MySQL可以完全从索引中满足一些查询(EXPLAIN输出将显示"使用索引"。)
假设这些列的组合在每个表中都是独一无二的......
要获取a
中b
中没有匹配行的行数,我们可以使用反连接模式:返回a中的所有行,以及任何行匹配来自b
的行,然后排除找到匹配项的所有行,因此我们留下来自a
但没有匹配的行。请注意,这是"外部" join,WHERE子句中的谓词,用于测试NULL值
SELECT COUNT(1) AS cnt
FROM Table_A a
LEFT
JOIN Table_B b
ON b.id = a.id
AND b.year = a.year
AND b.month = a.month
AND b.day = a.day
WHERE b.id IS NULL
要获得b
中a
中没有SELECT COUNT(1) AS cnt
FROM Table_B b
LEFT
JOIN Table_A a
ON a.id = b.id
AND a.year = b.year
AND a.month = b.month
AND a.day = b.day
WHERE a.id IS NULL
匹配行的行数,它是相同的查询,但是相反。
a
要计算b
和SELECT COUNT(1) AS cnt
FROM Table_A a
INNER
JOIN Table_B b
ON b.id = a.id
AND b.year = a.year
AND b.month = a.month
AND b.day = a.day
中的行数,我们可以使用内部联接
UNION ALL
可以使用SELECT c.in_a_and_b
, c.in_a_not_b
, d.in_b_not_a
FROM ( SELECT IFNULL(SUM(b.id IS NOT NULL),0) AS `in_a_and_b`
, IFNULL(SUM(b.id IS NULL),0) AS `in_a_not_b`
FROM Table_A a
LEFT
JOIN Table_B b
ON b.id = a.id
AND b.year = a.year
AND b.month = a.month
AND b.day = a.day
) c
CROSS
JOIN ( SELECT COUNT(1) AS `in_b_not_a`
FROM Table_B b
LEFT
JOIN Table_A a
ON a.id = b.id
AND a.year = b.year
AND a.month = b.month
AND a.day = b.day
WHERE a.id IS NULL
) d
集合运算符将这些查询合并为单个查询;我们想在每个查询中包含一个鉴别器列,让我们知道哪个查询返回了哪一行。
或者,它们可以作为SELECT列表中的子查询运行,也可以作为内联视图运行。
为了提高性能,我们可以将两个查询组合起来,"在a和b"计数和"在一个非b"在一个查询中。
我可能会将这些内容合并在一个查询中获取所有三个计数,我会使用两个内联视图,如下所示:
{{1}}
答案 2 :(得分:1)
您可以使用union
(自动删除重复项)来获取所有唯一行的主表,并将该表连接到表a和b以获取您的计数。
这假设表a和b不包含表中的重复项(否则左连接将产生膨胀计数)。
select
count(all_rows.id) total_unique_count,
sum(a.id is not null and b.id is not null) in_both_count,
sum(a.id is not null and b.id is null) only_in_a_count,
sum(a.id is null and b.id not null) only_in_b_count
from (
select id, year, month, day from tablea
union
select id, year, month, day from tableb
) all_rows
left join tablea a
on a.id = all_rows.id
and a.year = all_rows.year
and a.month = all_rows.month
and a.day = all_rows.day
left join tableb b
on b.id = all_rows.id
and b.year = all_rows.year
and b.month = all_rows.month
and b.day = all_rows.day