2017-06-15 84 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 
    )