隐式JOIN返回意外结果

时间:2017-07-26 03:22:16

标签: postgresql

我有两张桌子:

fccuser=# select count(*) from public.fine_collection where user_id = 5000;
 count
-------
  2500
(1 row)

fccuser=# select count(*) from public.police_notice where user_id = 5000;
 count
-------
  1011
(1 row)

当我发出

fccuser=# select count(*) 
from public.fine_collection, public.police_notice 
where fine_collection.user_id = 5000 
  and fine_collection.user_id = police_notice.user_id;

我期待2500但我得到了

计数

2527500 (1排)

,即两者的笛卡尔积。分析是:

fccuser=# explain analyze verbose select count(*) from public.fine_collection, public.police_notice where fine_collection.user_id = 5000 and fine_collection.user_id = police_notice.user_id;
                                                                           QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=47657.20..47657.21 rows=1 width=0) (actual time=1991.552..1991.552 rows=1 loops=1)
   Output: count(*)
   ->  Nested Loop  (cost=0.86..39760.60 rows=3158640 width=0) (actual time=0.448..1462.155 rows=2527500 loops=1)
         ->  Index Only Scan using idx_user_id on public.fine_collection  (cost=0.43..265.98 rows=8774 width=8) (actual time=0.213..2.448 rows=2500 loops=1)
               Output: fine_collection.user_id
               Index Cond: (fine_collection.user_id = 5000)
               Heap Fetches: 1771
         ->  Materialize  (cost=0.42..12.52 rows=360 width=2) (actual time=0.000..0.205 rows=1011 loops=2500)
               Output: police_notice.user_id
               ->  Index Only Scan using idx_pn_userid on public.police_notice  (cost=0.42..10.72 rows=360 width=2) (actual time=0.217..1.101 rows=1011 loops=1)
                     Output: police_notice.user_id
                     Index Cond: (police_notice.user_id = 5000)
                     Heap Fetches: 751
 Planning time: 2.126 ms
 Execution time: 1991.697 ms
(15 rows)

并且postgres文档说明当在非主要列上执行连接时,它首先创建笛卡尔积(交叉连接),然后应用过滤器。但我认为笛卡尔产品在我的情况下会包含所有具有相同user_id的行,因此不确定如何应用过滤器

左连接,内连接等也是如此,只有子查询似乎给出了2500的正确结果。

我有理由相信它在MySQL中不会以这种方式工作。有什么想法吗?

谢谢

2 个答案:

答案 0 :(得分:0)

您的加入结果是正确的。您使用user_id 5000加入每个集合,并在每个警察通知中使用相同的user_id。您有2500行和1011行连接在一起,这会产生2527500个新行。

答案 1 :(得分:0)

您正在使用旧版连接语法,因此这里的查询重新定义为使用可读的ANSI连接。

SELECT
  count(*) 
FROM public.fine_collection
  INNER JOIN public.police_notice 
    ON (fine_collection.user_id = police_notice.user_id)
WHERE fine_collection.user_id = 5000;

所以,你正在做count(*)。这会计算两个表的交叉积中与连接条件和where子句匹配的所有行。

换句话说,结果是每个表中user_id = 5000的行数,乘以

您的查询与

完全相同
SELECT
  (SELECT count(*) FROM public.fine_collection WHERE user_id = 5000)
  *
  (SELECT count(*) FROM public.police_notice.user_id WHERE user_id = 5000);

是的,2500 * 1011 = 2527500,这样才完全正确。

如果您希望2500,则需要在fine_collection中加入或分组键。