【问题标题】:SQL: join with OR in conditionSQL:在条件下加入 OR
【发布时间】:2016-08-29 12:58:24
【问题描述】:

我有 2 张桌子:

Devices (id (PK))
Links (id (PK), device_id_1 (FK), device_id_2 (FK))

代表通过链接连接的设备。

我需要选择与给定设备连接的所有设备(可以是 device_id_1 或 device_id_2)。我尝试使用以下查询来做到这一点:

select d2.*
from Devices as d1
left outer join Links as l on d1.id in (l.device_id_1, l.device_id_2)
left outer join Devices as d2 on d2.id in (l.device_id_1, l.device_id_2)
where d1.id = 398 and d2.id <> 398;

但只要我添加第二个JOIN,查询就会返回零行。我做错了什么?

【问题讨论】:

  • 将 d2.id 398 作为 AND 条件移动到连接条件。您正在使用当前方法否定左连接;本质上使它成为一个内部连接。一个不太优雅的方法是在 d2.id 上检查 null
  • 似乎您可以仅使用链接表来执行此操作。 Devices 表中是否还有除了 id 之外的其他数据?

标签: sql postgresql join


【解决方案1】:

where 子句实际上使您的最后一个左连接成为内连接。

更正将左连接过滤条件移动到连接条件

select d2.*
from Devices as d1
left outer join Links as l on d1.id in (l.device_id_1, l.device_id_2)
left outer join Devices as d2 on d2.id in (l.device_id_1, l.device_id_2)
and d2.id <> 398
where d1.id = 398;

一种不太优雅但被普遍接受的方法是......

select d2.*
from Devices as d1
left outer join Links as l on d1.id in (l.device_id_1, l.device_id_2)
left outer join Devices as d2 on d2.id in (l.device_id_1, l.device_id_2)
where d1.id = 398 
  and (d2.id <> 398 OR D2.ID is null)

我一般都是这样想的..

使用外连接时,我通常希望在连接发生之前排除行,这样引擎就不必生成如此大的笛卡尔坐标。除了外连接,我想返回空记录。但是,如果我在 where 子句中应用限制,则从外部连接生成的所有空记录都将被删除,除非我也考虑 NULLS。

在这种情况下,由于您使用的是 ... 无法与 null 进行比较,因此它将排除所需的记录,因为您无法对 null 值使用相等检查。

1 = NULL 返回 NULL 并且 1 NULL 返回 NULL;因此不正确

【讨论】:

  • 如果您有 398 是位置 1 与设备,然后位置 2 与同一设备,则会有重复
【解决方案2】:

在我看来,您正在尝试进行存在性测试。

有两种实现方式,具体取决于您的负载。

如果您有大量数据,第二个实现会更好,第一个是针对真正低基数的数据库,性能不是真正的问题:

SELECT d.* (put the list of the column and not *)
FROM device d
where exists (select 1 from Links l where l.device_id_1 = d.id and l.device_id_2 = 398 OR l.device_id_1 = 398 and l.device_id_2 = d.id)



SELECT d.*
FROM Device d
WHERE EXISTS (SELECT 1 FROM Links l where l.device_id_1 = d.id and l.device_id_2 = 398)
UNION ALL
SELECT d.*
FROM Device d
WHERE EXISTS (SELECT 1 FROM Links l where l.device_id_2 = d.id and l.device_id_1 = 398)

您可能希望将 UNION ALL 转换为 UNION,具体取决于您的数据

【讨论】:

    【解决方案3】:

    这个有效且不包含重复项。只需运行两个查询并将它们合并在一起:

    SELECT D.id
    FROM Devices D
    INNER JOIN Links L
    ON D.id = L.device_id_1
    WHERE D.id <> 398
    AND L.device_id_2 = 398
    UNION
    SELECT D.id
    FROM Devices D
    INNER JOIN Links L
    ON D.id = L.device_id_2
    WHERE D.id <> 398
    AND L.device_id_1 = 398
    

    在这里测试:http://sqlfiddle.com/#!9/e1269/6

    【讨论】:

      【解决方案4】:

      我们可以从@kbball 的解决方案中删除设备表

      SELECT L.device_id_1
      FROM Links L
      WHERE L.device_id_2 = 398
         -- AND L.device_id_1!=398 if self-links are possible
      UNION
      SELECT L.device_id_2
      FROM Links L
      WHERE L.device_id_1 = 398
         -- AND L.device_id_2!=398 if self-links are possible
      ;
      

      【讨论】:

        猜你喜欢
        • 2020-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多