0

しばらくの間、この質問PostgreSQL multiple criteria statementへの回答を投稿しました。

タスクは非常に単純で、別のテーブルに対応する値がない場合は、あるテーブルから値を選択します。以下のようなテーブルがあると仮定します。

CREATE TABLE first  (foo numeric);
CREATE TABLE second (foo numeric);

first.fooに出現しないすべての値を取得したいと考えていますsecond.foo。私は2つの解決策を提案しました:

  • 使用してLEFT JOIN
SELECT first.foo
FROM first
LEFT JOIN  second 
ON first.foo = second.foo
WHERE second.foo IS NULL;
  • サブクエリとIN演算子の組み合わせ:
SELECT first.foo 
FROM first
WHERE first.foo NOT IN (
  SELECT second.foo FROM second
);

何らかの理由で、OPのコンテキストでは最初の行が機能せず(0行が返されました)、それ以来私を悩ませてきました。異なるバージョンの PostgreSQL を使用してその問題を再現しようとしましたが、今のところ成功していません。

最初のソリューションが失敗し、2 番目のソリューションが期待どおりに機能する特定の理由はありますか? 明らかな何かが欠けていますか?

ここに sqlfiddle がありますが、利用可能などのプラットフォームでも動作するようです。

編集

@bma と @MostyMostacho がコメントで指摘したように、結果が返されなかったのはむしろ 2 番目のもの ( sqlfiddle ) である必要があります。

4

2 に答える 2

2

select values from one table if there is no corresponding value in another table.あなたはちょうどあなた自身の質問に答えました:

SELECT o.value
FROM table_one o
WHERE NOT EXISTS (
    SELECT *
    FROM table_two t
    WHERE t.value = o.value
    );

短いデモンストレーション:

CREATE TABLE first  (foo numeric);
CREATE TABLE second (foo numeric);

INSERT INTO first VALUES (1);
INSERT INTO first VALUES (2);
INSERT INTO first VALUES (3);
INSERT INTO first VALUES (4);
INSERT INTO first VALUES (5);
INSERT INTO first VALUES (NULL); -- added this for completeness

INSERT INTO second VALUES (1);
INSERT INTO second VALUES (3);
INSERT INTO second VALUES (NULL);


SELECT f.foo AS ffoo, s.foo AS sfoo
        -- these expressions all yield boolean values
        , (f.foo = s.foo)                                               AS is_equal
        , (f.foo IN (SELECT foo FROM second))                           AS is_in
        , (f.foo NOT IN (SELECT foo FROM second))                       AS is_not_in
        , (EXISTS (SELECT * FROM second x WHERE x.foo = f.foo))         AS does_exist
        , (NOT EXISTS (SELECT * FROM second x WHERE x.foo = f.foo))     AS does_not_exist
        , (EXISTS (SELECT * FROM first x LEFT JOIN second y ON x.foo = y.foo
                WHERE x.foo = f.foo AND y.foo IS NULL))
                                                                        AS left_join_is_null
FROM first f
FULL JOIN second s ON (f.foo = s.foo AND (f.foo IS NOT NULL OR s.foo IS NOT NULL) )
        ;

結果:

CREATE TABLE
CREATE TABLE
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
 ffoo | sfoo | is_equal | is_in | is_not_in | does_exist | does_not_exist | left_join_is_null 
------+------+----------+-------+-----------+------------+----------------+-------------------
    1 |    1 | t        | t     | f         | t          | f              | f
    2 |      |          |       |           | f          | t              | t
    3 |    3 | t        | t     | f         | t          | f              | f
    4 |      |          |       |           | f          | t              | t
    5 |      |          |       |           | f          | t              | t
      |      |          |       |           | f          | t              | f
      |      |          |       |           | f          | t              | f
(7 rows)

ご覧のとおり、boolean はIN()およびequalsの場合は NULL になる可能性があります。この場合、NULL にすることはできませんEXISTS()。なるかどうか。これは、実際にクエリ結果LEFT JOIN ... WHERE s.foo IS NULLに含まれることを除いて、NOT EXISTS ケースと (ほぼ) 同等です (ほとんどの場合、これは必要ありません)。second.*

于 2015-12-20T12:13:48.527 に答える
2

あなたのSQLフィドルによると、2番目のテーブルにNULLがあるため、NOT INクエリは結果を返すことができません。

問題は、NULL は「UNKNOWN」を意味するため、次の式が真であるとは言えません 10 not in (5, null)

その理由は、10 = NULL を比較するとどうなるかです。true ではなく、NULL が返されます。つまり、NOT IN 句の NULL は、行が渡されないことを意味します。

2 番目のものを期待どおりに実行するには、比較的複雑なクエリを使用します。

SELECT first.foo 
FROM first
WHERE (first.foo  IN (
  SELECT second.foo FROM second
) IS NOT TRUE);

これにより、NULL 比較が適切に処理されますが、結合構文はおそらくきれいになります。

于 2013-12-03T03:05:49.640 に答える