如何使用SQL获取两个表的所有3个组件的计数?

时间:2014-09-14 03:05:37

标签: mysql sql

假设我想为两个表A和B找到A中但不是B的所有记录的计数,A和B中的所有记录,以及B中但不在A中的所有记录。我不想要实际记录,只需要所有3个组件的计数(想想维恩图)。

当我说,例如,A和B中的记录时,我的意思是对所有具有相同值的记录的计数,例如,四个变量(如ID,年,月,日)。

是否有一个时髦的查询会有效地返回这些计数?

3 个答案:

答案 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输出将显示"使用索引"。)

假设这些列的组合在每个表中都是独一无二的......

要获取ab中没有匹配行的行数,我们可以使用反连接模式:返回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

要获得ba中没有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

要计算bSELECT 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