SQL左连接表现奇怪

时间:2017-10-09 14:02:48

标签: sql postgresql postgresql-9.5

有人可以指出我在这里做错了吗?

我有一张时间戳数据表:

CREATE TABLE data_record (
    id UUID,
    t  TIMESTAMP,
    d  INTEGER,
    PRIMARY KEY(id, t)
);

我从该表中的某些项目获得了一些数据,现在我正在尝试创建一个批处理来维护一个项目,其中数据值(d)是其他项目。首先,我尝试为其他项目有行的所有时间戳插入摘要项目的行:

WITH source_ids AS (
    SELECT UNNEST(ARRAY['1e77b896-9e1b-11e7-a0db-f23c91e2b423'::uuid, '7dd37dd0-9e1a-11e7-a0db-f23c91e2b423'::uuid])
)
INSERT INTO data_record (id, t) (
     SELECT DISTINCT 'ab3b516e-acd7-11e7-a0db-f23c91e2b423'::uuid, d1.t
       FROM data_record d1
  LEFT JOIN data_record d2
         ON d1.t = d2.t
        AND d1.id IN (SELECT * FROM source_ids)
        AND d2.id = 'ab3b516e-acd7-11e7-a0db-f23c91e2b423'::uuid
      WHERE d2.t IS NULL;

据我所知,这应为ab3b516e-acd7-11e7-a0db-f23c91e2b423中的任何一个ID为行中的每个时间戳创建一个标识为source_ids的行。但在执行该查询后,我会这样做:

WITH source_ids AS (
    SELECT UNNEST(ARRAY['1e77b896-9e1b-11e7-a0db-f23c91e2b423'::uuid, '7dd37dd0-9e1a-11e7-a0db-f23c91e2b423'::uuid])
)
   SELECT COUNT(d1.t)
     FROM data_record d1
LEFT JOIN data_record d2
       ON d1.t = d2.t
      AND d1.id IN (SELECT * FROM source_ids)
      AND d2.id = 'ab3b516e-acd7-11e7-a0db-f23c91e2b423'
    WHERE d2.t IS NULL;

INSERT查询影响了28,237行; SELECT查询返回55561,我认为它应该返回零。

我觉得AND d1.id IN (SELECT * FROM source_ids)的某些内容并不像它看起来那样有效,但又是什么?

1 个答案:

答案 0 :(得分:2)

使用LEFT JOIN时,第一个表上的过滤器应位于WHERE子句中。在ON子句中的 second 上进行过滤。通常,错误是在第二个表上的过滤器上进行的。你的是第一张桌子上的过滤器。

所以:

WITH source_ids AS (
    SELECT UNNEST(ARRAY['1e77b896-9e1b-11e7-a0db-f23c91e2b423'::uuid, '7dd37dd0-9e1a-11e7-a0db-f23c91e2b423'::uuid])
)
INSERT INTO data_record (id, t) (
     SELECT DISTINCT 'ab3b516e-acd7-11e7-a0db-f23c91e2b423'::uuid, d1.t
     FROM data_record d1 LEFT JOIN data_record
          d2
          ON d1.t = d2.t AND
             d2.id = 'ab3b516e-acd7-11e7-a0db-f23c91e2b423'::uuid
      WHERE d2.t IS NULL AND d1.id IN (SELECT * FROM source_ids);

这对我来说似乎过于复杂。我可能会建议:

WITH . . . 
SELECT DISTINCT 'ab3b516e-acd7-11e7-a0db-f23c91e2b423'::uuid, dr.t
FROM (SELECT dr.*,
             COUNT(*) FILTER (WHERE dr.id = 'ab3b516e-acd7-11e7-a0db-f23c91e2b423'::uuid) OVER (PARTITION BY t) as cnt
      FROM data_record dr
     ) dr
WHERE cnt = 0;

根据数据和索引的设置方式,原始版本可能会更快。