2012-02-28 12 views
1

私は木曜日に行った試験のためにいくつかのSQLを行ってきましたが、EXISTSステートメントを正しく使用しているかどうか疑問があります。これはEXISTSを使用する正しい方法ですか?

だから、ここで私は2つのテーブルそう

Machines    Maintenance 
    ============   ============== 
PK ID_Machine   PK ID_Machine FK 
    Name    PK ID_Task  FK 
    Date_bought    Date 

でDBを持って、彼らは私が書きたいクエリが「2011年に任意のメンテナンスを受けていない最も古いマシンから表示するすべてのデータを」と言います

私はそれをやった方法は以下の通りです:

SELECT M.ID_MACHINE, M.NAME, M.DATE_BOUGHT 
FROM MACHINES M 
WHERE NOT EXISTS (SELECT MA.* 
        FROM MAINTENANCE MA 
        WHERE MA.ID_MACHINE = M.ID_MACHINE 
        AND YEAR(MA.DATE) = 2011)     
AND EXISTS (SELECT MIN(M2.DATE_BOUGHT) 
       FROM MACHINE M2 
       WHERE M2.ID_MACHINE = M.ID_MACHINE) 

これは、このクエリを実行するための正しい方法は何ですか? EXISTS文の中でSELECT MIN()を使うのは意味がありますか?

おかげさまで皆様に感謝します!

+0

クエリを実行しようとすると、まあ....何が起こりますか? –

+0

さて、実際には、私はこのDBをどのDBMSでも作成していないということです。それは教科書の単なる運動であり、私はそれを紙で解決しています。 Unfortunatelly、教科書には答えがないので、私は尋ねていたのです。 – nachoargentina

+0

うまく始めるには、2番目の「どこか」の代わりに 'AND' *が必要です。また、 'EXISTS'の中で' SELECT MIN() 'を使わないでください。意味がありません。 – Bohemian

答えて

2

存在を使用すると、フィルタ(結合および場所)に対してデータが返されることの検証のみが行われます。多くの場合、select 1 from ....というクエリが存在します。これは、実際の戻り値が使用されていないためです。

これは新しい考えであり、私は個人的にテストしなければなりませんでした。しかし、私が上記のように、戻りデータはほとんどの場合無視されます。それは、フィルタ自体がフィルタであるように見えるにもかかわらず、結合とフィルタが一致し、MINについて気にしないことだけを気にします。それは集約の詳細なので、根底にあるデータは残っているようです。最初に存在するものは有効ですが、次の部分は作業が必要です。私は私がやることのためにそれを下に更新しました。

SELECT M.ID_MACHINE, M.NAME, M.DATE_BOUGHT 
FROM MACHINES M 
WHERE NOT EXISTS (SELECT 1 
       FROM MAINTENANCE MA 
       WHERE MA.ID_MACHINE = M.ID_MACHINE 
       AND YEAR(MA.DATE) = 2011)     
    AND M.ID_MACHINE = (SELECT TOP 1 M2.ID_MACHINE 
      FROM MACHINE M2 
      WHERE M2.ID_MACHINE = M.ID_MACHINE 
      ORDER BY M2.DATE_BOUGHT) 
+0

そうですね、私の場合、EXISTS条件では、最も古いマシンを選択しようとしています...そのため、SubQueryのselect文にMIN()が含まれています。私はそれを実行することによって、EXISTSステートメントは他のすべてのマシンを削除しますが、MIN(Date_bought)を持つマシンはすべて削除すると考えました。これは本当ですか?または私はこれに間違ったやり方で取り組んでいますか? – nachoargentina

+0

私の回答を更新しますが、簡単な答えはあなたが本当に間違った方法に取り組んでいるということです。それは新しい考えでした:) –

+0

申し訳ありません、私の最後のコメントを無視して、私は最後まで、すべてのあなたのコメントを読んでいない最初の段落。だから、あなたはそれがこのように働くかもしれないと思いますか?あなたが言ったように、MIN()は理論的にはフィルターとして機能するだろうから、そう思った。私はチャンスがあるとすぐに実際のデータベースでこれをテストしようとします。 – nachoargentina

0

私はこれが良い方法だと思います。

SELECT TOP 1 MIN(A.Date_bought), A.ID_Machine, A.Name 
FROM Machines A 
JOIN Maintenance B on A.ID_Machine = B.ID_Machine 
WHERE DATEPART(year, B.Date) != '2011' 
GROUP BY A.Date_bought, A.ID_Machine, A.Name 
+0

-1:OPはクエリが有効かどうかを確認していましたが、書き換えはしませんでした。 –

+1

「MIN」が集計で失敗していると思います。あなたはGROUP BYを持っていません。 – Yuval

+0

ありがとう、GROUP BY :)を逃しました。また、ジャックにも、それは正しい方法ですそれだと思うので、私は別の解決策を提案しました。私がここに8人しかいないときに投票する必要はありません。 Jeez。笑 – stringpoet

1

SQL-92 standardを引用:

 Function 

    Specify a test for a non-empty set. 

    Format 

    <exists predicate> ::= EXISTS <table subquery> 


    Syntax Rules 

     None. 

    Access Rules 


     None. 

    General Rules 

    1) Let T be the result of the <table subquery>. 

    2) If the cardinality of T is greater than 0, then the result of 
     the <exists predicate> is true; otherwise, the result of the 
     <exists predicate> is false. 

    Leveling Rules 

    1) The following restrictions apply for Intermediate SQL: 

      None. 

    2) The following restrictions apply for Entry SQL in addition to 
     any Intermediate SQL restrictions: 

      None. 

8.8

だから、いや、サブクエリ(それが有効だだけという)の構文上の特別なルールはありません。 existsステートメントは、単に行を返すかどうかを気にするだけです。

+0

この場合、EXISTSステートメントはM2テーブルの特定の1つの行に対して真を返します。そのテーブルは私のクエリのメインテーブルへの外部参照を持っているので、そのことによってMIN(Date_bought)値を持つものを除いてメインテーブルのすべての行が除外されます。これは基本的に私が目標としているのは – nachoargentina

+0

なので、テーブルから行を削除することはありません。これは 'delete'ステートメントでも' truncate'ステートメントでもありません。 –

1

あなたのクエリ句にタックすることができますブール条件としてEXISTSNOT EXISTSと考えてください。それらは、あなたが見ているデータに関して他のデータ条件が真であるか偽であるかをチェックするために使用されます。

SELECT M.ID_MACHINE, M.NAME, M.DATE_BOUGHT 
FROM MACHINES M 

-- DO NOT want a machine with a maintenance year of 2011 
WHERE NOT EXISTS (SELECT 1 
        FROM MAINTENANCE MA 
        WHERE MA.ID_MACHINE = M.ID_MACHINE 
        AND YEAR(MA.DATE) = 2011)  

-- DO want there to be a matching ID in the Machine table    
WHERE EXISTS (SELECT 1 
       FROM MACHINE M2 
       WHERE M2.ID_MACHINE = M.ID_MACHINE) 

ジャスティンが述べたように、サブクエリの戻り値が使用され、そのSELECT 1EXISTS/NOT EXISTSための規約であるされていません。

+0

しかし、自分の投稿を編集したばかりなので、MINのような行為はフィルタ自体のようなものです... MINを持つかどうかは不思議です。それを試みたことは一度もなく、理論的にはうまくいくかもしれません。私はこの質問に私の応答を更新しました。私は見るためにいくつかのダミーデータをロードするかもしれません:) –

+0

それをテストし、それは失敗します。私は私の答えを更新しています。 –

+0

@JustinPihony Ok、txは私のためにテストしています!それが正しいとは知りませんでした。私はそれを男に訴える。 – nachoargentina

1

初めてのEXISTSの使用は正しいようですが、2番目はオフになっているようです。マシンが最も古くなっているかどうかをチェックしたいが、マシンが同じMACHINE_IDMINの使用は、EXISTS関数の結果に影響しない)が存在するかどうかをチェックしている。

私はDB管理者ではありませんが、一部の実装ではサブクエリのコストが高くなる可能性があります。また、EXISTS関数に配置すると最適化される可能性があります。だから実際にコードを実行する必要があるときにはstringpoetのコードを考慮する必要があります...私はあなたがGROUP BY他のすべてのフィールドが必要になるとコメントしました。

はまた、あなたが二回キーワードWHEREを使用していますが、AND/ORであなたの条件に参加するべきではないことに注意してください。

ここに私の修正はあなたとstringpoetのコードには:

SELECT M.ID_MACHINE, M.NAME, MIN(M.DATE_BOUGHT) 
FROM MACHINES M 
WHERE NOT EXISTS (SELECT MA.* 
        FROM MAINTENANCE MA 
        WHERE MA.ID_MACHINE = M.ID_MACHINE 
        AND YEAR(MA.DATE) = 2011)     
GROUP BY M.ID_MACHINE, M.NAME 
+0

大丈夫です。 tx Yuval、私は自分の答えをEXIST付きのものではなくあなたのコードを使用します – nachoargentina

関連する問題