Select

有沒有辦法在單個 SQL 語句中確保列表中的所有項目都出現在某個列中?

  • June 12, 2013

我正在尋找的是一種驗證所有項目列表(比如說“FOO”、“BAR”和“BAZ”)是否在給定的 table.column 中的方法。這就像 SELECT something FROM sometable WHERE somevalue IN (‘THIS’, ‘THAT’, ‘OTHER THING’) … 除了它有點相反:

理想情況下,該語句將為我提供 (‘FOO’, ‘BAR’, ‘BAZ’) NOT IN sometable.somecolumn 中的所有內容,然後我可以將其用作我的檢查(斷言我的 SQL 查詢返回一個空集或獲取結果集和抱怨每個都是“無法辨識”的密鑰)。(蠻力方法是將所有術語/標籤/項目插入到一個臨時的單列表中,並在我的實際表的列上為不在其中的行集執行 JOIN。

我確定我錯過了一些語法技巧,一旦我看到一個例子就會很明顯。

在這種特殊情況下,我的表結構類似於:

CREATE TABLE items (id     INTEGER PRIMARY KEY, item TEXT UNIQUE NOT NULL);

CREATE TABLE tags  (tag_id INTEGER PRIMARY KEY, tag  TEXT UNIQUE NOT NULL);

CREATE TABLE item_tag (item_id integer, tag_id integer,
                              FOREIGN KEY(item_id) REFERENCES items(id),
                              FOREIGN KEY(tag_id) REFERENCES tags(tag_id),
                              PRIMARY KEY (item_id, tag_id));

…但我只是想提出一個模板來獲取任意數量的“標籤”並確保它們都是“tags.tag”列的有效條目。(應用程序必須支持對這些標籤子集的任意複雜查詢以返回項目的 DISTINCT 子集;但我想在建構 JOIN 的 WHERE 表達式之前對任何不存在的標籤提出錯誤)。

顯然我可以遍歷標籤做一個單獨的 SELECT tag_id FROM tags where tag=? … 需要多次往返數據庫。但是當我可以在一條語句中將它們全部發送到某種查詢時,這似乎很愚蠢。

如果我了解您要實現的目標,那麼我認為您想要的是一個 CTE,其中包含您要比較的所有可能標籤的 UNION 列表,然後將標籤表與 CTE 右連接。

例子 -

WITH ListOfTags (Tag) AS (SELECT 'Foo' UNION SELECT 'bar' UNION SELECT 'other' UNION SELECT 'thing')
SELECT  *
FROM    item_tag    i
INNER JOIN  tags    t
   ON i.tag_id = t.tag_id
RIGHT JOIN ListOfTags   l
   ON  l.Tag like t.tag
WHERE   i.item_id is null

這可能不是最優雅的方式,但它可能對你有用

我不會以任何方式呼叫這種語法糖,但在 Oracle 中,您可以在這樣的查詢中將標籤作為分隔列表傳遞:

SELECT a.Data FROM
(
  SELECT SUBSTR(main_string, position_from + 1, position_to - position_from - 1) Data
  FROM
     (
        SELECT main_string, DECODE(rownum - 1, 0, 0, instr(main_string, ',', 1, rownum - 1))
           position_from, instr(main_string, ',', 1, rownum) position_to
        FROM (SELECT 'FOO,BAR,BAZ,' main_string FROM dual)
           CONNECT BY LEVEL <= LENGTH(main_string)
     )
  WHERE position_to > 0`
) a
WHERE NOT EXISTS (SELECT 1 FROM Tags WHERE Tag = a.Data)
;

小提琴

引用自:https://dba.stackexchange.com/questions/44280