2017-06-15 55 views
0

我们需要选择仅使用连接到机器的标签的测试。是“WHERE(subquery)IN(subquery)”可能吗?

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

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

实例:

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

  • 如果测试包含标签[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) 

不过是这种类型的查询的可能吗?如果不是,如何才能达到预期的结果?

+0

检查['ALL'(https://www.postgresql.org/docs/current/static/functions-subquery.html#FUNCTIONS-SUBQUERY-ALL)运算符。 – zerkms

+0

@zerkms ALL运算符是否也需要左值为标量? – Noozen

+0

你是对的,这是无关紧要的,我只是醒了,还没有准备好有效思考。抱歉。 – zerkms

回答

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) 
+0

真的很好的解决方案!不使用任何sql语言特定的构造。 – edi

3

如果第一个返回标量值(即单行),则此查询是可能的。所以,你想要做的只是使用IN。一个Postres'y的方式来处理这个使用数组

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 
    )