“WHERE(子查询)IN(子查询)”可能吗?

时间:2017-06-15 20:10:14

标签: sql postgresql subquery relational-division

我们需要选择仅使用连接到Machine的标签的测试。

  • 标签对于测试来说是多对多的。 (TagTest关联表)

  • 标签对于机器而言是多对多的。 (TagMachine关联表)

示例:

  • 如果测试有[A,B,C]标签且机器有[A,B,D],则不应选择测试,因为其标签不是机器标签的子集。

    < / LI>
  • 如果测试有[A,B]标签且机器有[A,B,D],则应包括测试。

  • 如果测试没有标签,则应始终包含标签。

像这个结构的东西应该有效:

SELECT *
FROM Test te
WHERE 
    (SELECT tt.tagId
    FROM TagTest tt 
    WHERE tt.testId = te.Id)
 IN
    (SELECT tm.tagId
    FROM TagMachine tm
    WHERE tm.machineId = 123)

但这种查询是否可行?如果没有,怎么可能达到预期的效果?

2 个答案:

答案 0 :(得分:4)

IN()无法自行完成此操作。你可以制作两个CTE并将它们加在一起,但它仍然有点棘手。

相反,让我们解决问题。我们可以查找缺少任何一个必需标记的记录,而不是查找与所有好标记匹配的记录。从问题的第一个示例([A,B,C] vs [A,B,D])开始,我们正在寻找带有TestTag标记的C记录。获得此信息后,我们可以在子查询中使用它来排除这些结果中出现Test的所有Id条记录。

首先要做的是使用排除联接来查找缺少相应TestTag记录的TagMachine结果:

SELECT tt.testId, tt.tagId
FROM TestTag tt 
LEFT JOIN TagMachine tm ON tm.machineId = 123 AND tm.tagId = tt.tagId
WHERE tm.tagId IS NULL

上述查询结果中存在任何testId会使Test Id 不合格 ...但我们确实需要所有其他 Test条记录。所以现在只需将其限制为DISTINCT testId并将其用作任何排除连接,NOT IN()或NOT EXISTS()中的子查询。请选择:

SELECT * 
FROM Tests
WHERE Id NOT IN (
     --identify tests hat are missing at least one tag
     SELECT DISTINCT tt.testId 
     FROM TestTag tt 
     LEFT JOIN TagMachine tm ON tm.machineId = 123 AND tm.tagId = tt.tagId
     WHERE tm.tagId IS NULL)

答案 1 :(得分:3)

如果第一个返回标量值(即单行),则可以进行此查询。因此,仅使用IN是不可能的。一种Postres处理它的方法是使用数组

WHERE (SELECT ARRAY_AGG(tt.tagId)
       FROM TagTest tt 
       WHERE tt.testId = te.Id
      ) <@
      (SELECT ARRAY_AGG(tm.tagId)
       FROM TagMachine tm
       WHERE tm.machineId = 123
      )