• 締切済み

SQL EXISTS演算子について

------------------------------------------------------- SELECT DISTINCT 商社.商社名 FROM 商社 WHERE NOT EXISTS (SELECT * FROM 商品 WHERE NOT EXISTS (SELECT * FROM 納品 WHERE 納品.商品番号 = 商品.商品番号 AND 納品.商社番号 = 商社.商社番号)) 各テーブルレイアウト 納品([商品番号]、[商社番号]、納品数量) 商品([商品番号]、商品名) 商社([商社番号]、商社名) []は主キー 条件:納品表に行が存在することは、その商品を商社が納品することを意味する。 --------------------------------------------------------- 上記SQLは、「全ての商品を納入する商社の商社名を求める」ものだそうですが、なぜそうなるのか理解できません。 具体的にどういう判定でそうなるのか教えていただけないでしょうか。

みんなの回答

  • piroin654
  • ベストアンサー率75% (692/917)
回答No.6

単純にして味わい深くかつ巧妙なSQL文ですが、直積というヒントでこのSQL文のからくりをイメージ 出来たとはすばらしい。もはや上級者の仲間入りです。SQL文と直積の概念を結びつけるのは大変です 。集合を十分わかっていないとイメージがわかないものです。ましてや指定のテーブルを使って直積 の概念を取り入れた題意のSQLを一つだけ開発しなさい、と言われたとんでもない難問です。 何か少し疑問が残ったみたいですが、Ms Accessを持たれていたら以下のSQLを登録して実行してみて ください。ただしMs Accessはこのままで受け付けますが、登録後に若干の変更を行います。これは Ms Accessの方言だと思ってください。実行すると商社.商社番号を問い合わせてきます。そのときに 納品していないものがある商社の商社番号を入れると、納品していない商品を実行結果として表示 します。ここがこのSQLのミソです。 SELECT * FROM 商品 WHERE Not EXISTS (SELECT * FROM 納品 WHERE 納品.商品番号=商品.商品番号 And 納品.商社番号=商社.商社番号); もちろんデータが大量にある場合は商社テーブルのレコードをループしながら商社番号をこのSQLに 送り込むプログラムが必要ですが、数個のレコードでこのからくりは確認出来るのでそこまでは必要 無いと思います。 また直積という概念はSQLを自在に扱う上で重要なカギの一つとなります。ただ、このSQL文に内在 する見えるデータと見えないデータをTrueとFalseというスイッチではなく、Exsitsあるいは Not Existsという言葉の概念で捉えてください。たしかにTrueやFalseを条件の判断に使う データベース(例えばMs Access)がありますが、本来はこのSQL文が標準的な書き方です。直積が わかればTrueとかFalseという補助手段は必要ありません。そのほうが将来のためです。 データを処理するアルゴリズムとこまごまとしたテクニックは分けて考えてください。 必要があればテクニックは後からついてきます。そっか、アルゴリズムを構築するときはTrueとか Falseを使いますよね。話が少々迷路に入りかけているのでこのあたりにします。がんばってください。

  • piroin654
  • ベストアンサー率75% (692/917)
回答No.5

すみません。部分的な訂正よりすべてを一緒にします。SQLは個々のコマンドの働きの把握も大切 ですが、集合論と同じように考えると理解が早いと思います。サブクエリ、集合、直積などが キーワードです。以下訂正文です。 集合論的にとらえないと大変です。 一番内側のサブクエリ、 (SELECT * FROM 納品 WHERE 納品.商品番号=商品.商品番号 And 納品.商社番号=商社.商社番号) は、納品テーブルのすべてのレコードを取り出しますが(商品を取り出しているのではありません)、 集合論的にはこのほかに納品テーブルには存在しない商品と商社の組み合わせが存在します。 このサブクエリのNot Existsはこの存在しない組み合わせ持つレコードと考えておきます。 つまり空の集合です。(もしかするとこれがキーポイントかもしれません) ただし、空集合という意味ではありません。商社と商品のすべての組み合わせの中で、 納品テーブルに存在しない商社と商品の組み合わせをもつレコードという意味です。実際に サブクエリを稼動させて出てくるのはこのレコードです。 外のサブクエリ、 SELECT * FROM 商品 WHERE Not EXISTS(SELECT----) は、この内側のサブクエリの集合の中から各要素、すなわち各レコードのフィールドの 商品番号と商品テーブルのデータと合致するレコードを取り出します。 しかし、対象となる内側のサブクエリのデータは納品テーブルに存在しないので外のサブクエリの データは納品テーブルに存在しない商品があるレコードです。つまりこの時点では納品していない 商品があるレコードを取り出します。 そして、この外のサブクエリのNot Existsは納品していない商品のないレコードを取り出します。 最後に、 SELECT DISTINCT 商社.商社名 FROM 商社 によって、商社テーブルの商社番号と取り出したレコードの商社番号が一致する商社名を取り出すと 考えることが出来ます。 実際にテーブルにデータを入れてこれらのサブクエリを稼動させるとよくわかります。

jolyyne
質問者

お礼

piroin654さん 大変丁寧にありがとうございます。 >サブクエリ、集合、直積などがキーワードです。 →直積で考えることでようやく理解できました。内側のNOT EXISTSで、納品テーブルに存在するレコードがFALSEになり、存在しないレコードがTRUEになると考えると整理できました。 >実際にテーブルにデータを入れてこれらのサブクエリを稼動させるとよくわかります。 →まずそれを考えたのですが、今回のクエリはサブクエリ毎に参照テーブルが独立していないので、切り離して実行できないことから頭で考えるしかないのかと・・・。 実際にデータを入れ、「全ての商品を納入する商社の商社名を求める」という結果を得られることの確認だけは出来てたのですが。 「直積」で、全ての謎が解けました。 ありがとうございました。

  • piroin654
  • ベストアンサー率75% (692/917)
回答No.4

回答を少し訂正します。 つまり空の集合です。(もしかするとこれがキーポイントかもしれません) の部分ですが、空集合という意味ではありません。商社と商品のすべての組み合わせの中で、 納品テーブルに存在しない商社と商品の組み合わせをもつレコードという意味です。実際に サブクエリを稼動させて出てくるのはこのレコードです。

  • piroin654
  • ベストアンサー率75% (692/917)
回答No.3

集合論的にとらえないと大変です。 一番内側のサブクエリ、 (SELECT * FROM 納品 WHERE 納品.商品番号=商品.商品番号 And 納品.商社番号=商社.商社番号) は、納品テーブルのすべてのレコードを取り出しますが(商品を取り出しているのではありません)、 集合論的にはこのほかに納品テーブルには存在しない商品と商社の組み合わせが存在します。 このサブクエリのNot Existsはこの存在しない組み合わせ持つレコードと考えておきます。 つまり空の集合です。(もしかするとこれがキーポイントかもしれません) 外のサブクエリ、 SELECT * FROM 商品 WHERE Not EXISTS(SELECT----) は、この内側のサブクエリの集合の中から各要素、すなわち各レコードのフィールドの 商品番号と商品テーブルのデータと合致するレコードを取り出します。 しかし、対象となる内側のサブクエリのデータは空なので外のサブクエリのデータは空です。 つまりこの時点では納品していない商品があるレコードを取り出します。 そして、この外のサブクエリのNot Existsは納品していない商品のないレコードを取り出します。 最後に、 SELECT DISTINCT 商社.商社名 FROM 商社 によって、商社テーブルの商社番号と取り出したレコードの商社番号が一致する商社名を取り出すと 考えることが出来ます。 実際にテーブルにデータを入れてこれらのサブクエリを稼動させるとよくわかります。

回答No.2

そのSQLって正しく動作しないような気がするんですよね。 ”商品と納品”のサブクエリの結果の有無で、商社の全問合せか0件になりませんか? (商社と無関係なサブクエリ結果を条件として、商社を処理してる)

  • dda167
  • ベストアンサー率76% (55/72)
回答No.1

SELECT * FROM 納品 WHERE 納品.商品番号 = 商品.商品番号 AND 納品.商社番号 = 商社.商社番号 条件がEXISTSの場合、 「納品テーブルに、その商社が納入する商品が存在する」です。 NOT EXISTSだから…… 「納品テーブルに、その商社が納入する商品は存在しない」です。 SELECT * FROM 商品 WHERE NOT EXISTS (...) 条件がEXISTSの場合、 「商品テーブルに、その商社が納入しない商品が存在する」です。 NOT EXISTSだから…… 「商品テーブルに、その商社が納入しない商品は存在しない」です。 「すべての商品を納入する」を 「納入しない商品は存在しない」と言い換えているわけです。 ※DISTINCTはいらないような気がする……

関連するQ&A

  • 【SQL】existsでの商演算

    閲覧ありがとうございます. 現在SQLでデータベースの勉強をしているのですが,existsを用いて商演算を行おうとしてわからない箇所が出てきたので,その点に関しての質問をさせて頂きます. 例として,A,Bと名付けた2つのテーブルを最初に用意します. [テーブル1(名前:机1)] | 番号 |氏名|年齢| 好物 | --------------------------- | 12 | あ | 1 | りんご | | 23 | い | 1 | すいか | | 23 | い | 1 | りんご | | 45 | え | 3 | すいか | | 45 | え | 3 | りんご | [テーブル2(名前:机2)] | 好物 | ---------- | りんご | | すいか | この机1を机2で割ることで,この要素を含む氏名を取り出したいと考えて,以下のようなSQL文を実行しました. select 氏名 from 机1 A where exists( select * from 机2 B where A.好物 = B.好物); [求めている結果表示] | 番号 |氏名| ------------- | 23 | い | | 45 | え | [実際に表示された結果] | 番号 |氏名| ------------- | 12 | あ | | 23 | い | | 23 | い | | 45 | え | | 45 | え | 実行すると (1)氏名が重複で表示されてしまう (2)「りんご」のみ好物の「あ」も表示されてしまう という問題が発生してしまい,色々と調べて考えたものの,結局分からなかったので,今に至ります. 長文・駄文すみませんでした. まだSQLを学び初めて少ししか経っていないので,全く違うことをしていたらすみません.よろしければ,問題解決へのアドバイスをお願いします.

  • SQL文の副問い合わせについて

    SQLの勉強をし始めて間もありませんが、以下のSQLについて教えてください。 実はこの問題は昭晃堂発行の北川博之先生が書いた「データベースシステム」という本に乗っている問題です。以下のようなデータベースがあります。 部門(_部門番号,部門名) 部品(_部品番号,部品名) 業者(_業者番号,業者名,住所,電話番号) 従業員(_従業員番号,従業員名,氏名,住所,年齢) 供給(_部門番号,_部品番号,_業者番号,単価,数量) _がついているのは主キー この中で、登録されているすべての部品の供給を受けている部門の部門番号を表示するSQLを記せという問題があります。 つまり、部品表の供給テーブルの中の部門番号ごとに、供給テーブルのなかに含まれる部品番号と部品テーブルの部品番号がすべて一致するかを調べるSQLを書かなければならないということです。 まったく歯が立たないので、いろいろと調べた結果、 SELECT DISTINCT 部門番号 FROM 供給 AS 供給1 WHERE NOT EXISTS( SELECT * FROM 部品 WHERE NOT EXISTS( SELECT * FROM 供給 AS 供給2 WHERE 部品.部品番号=供給2.部品番号 AND 供給1.部門番号=供給2.部門番号 )); が正解であるということでした。 しかし、NOT EXISTSが2回も出てくるばかりか、副問い合わせの連続で頭が混乱してよく理解できません。 このSQL文について、具体的にはどのようなことをやっているのでしょうか?教えてください。お願いします。

  • SQL文について

    商品表と注文表から、注文のある商品名を知るためのSQL文で、適切なものはどれか。 商品表(商品番号,商品名,単価) 注文表(注文番号,商品番号,注文数) 答えはこれ↓なんですが、なぜなのか分かりません。 SELECT 商品番号,商品名 FROM 商品表        WHERE EXISTS (SELECT * FROM 注文表 WHERE 商品番号 = 商品表.商品番号)

  • distinct をexistsに変換する

    distinctをexistsに変換した方がパフォーマンスが良いようで、 例えば以下の例があるとします ---------------------------------- (前)SELECT DISTINCT a.ID1, a.NAME1 FROM TABLE1 a, TABLE2 b WHERE a.ID1 = b.ID2 (後)SELECT a.ID1, a.NAME1 FROM TABLE1 a    WHERE EXISTS ( SELECT 'X' FROM TABLE2 b WHERE a.ID1 = b.ID2) ---------------------------------- もっと複雑なSQLの場合、EXISTSに変換できるのでしょうか?。複雑なSQLとは、 「複数のテーブルからカラムを取得」「テーブル結合が2つ以上」「外部結合」 などのSQLで、以下に例を示します。 (例)SELECT DISTINCT a.ID1,a.NAME1,b.ID2,b.NAME2,c.ID3,c.NAME3    FROM TABLE1 a,TABLE2 b,TABLE3 c    WHERE a.ID1 = b.ID1(+)      AND a.ID1 = c.ID1(+)

  • レコードが追加できない

    SQL-Server2010を使用しています。 レコードを追加する際にキー項目が存在する場合は追加しないようなSQL文を作成しました。 追加するテーブルにレコードが1件以上あれば思ったとおりの処理となりますが、 1件も存在しない場合は追加できません。 下記がそのSQL文です。 原因と対策方法を教えて頂けませんでしょうか? INSERT INTO foo(商品コード, 商品名) SELECT DISTINCT 'ABC123' , 'りんご' FROM foo WHERE NOT EXISTS( SELECT * FROM foo WHERE 商品コード = 'ABC123' ) 以上です。 宜しくお願い致します。

  • SQL : たしかに DELETE したの?

    ありがちな処理かと思いまして質問させていただきます。 SQLで DELETE を行う際、WHERE で絞り込んだ対象行がなくても、  「エラーは返ってこない」 ということですが、NOT FOUND判定はするのでしょうか。 ごく単純に、削除しようとしたデータが実際にあったのかを確認したいのです。 やろうとしている処理は、次のようなものです。 DELETE FROM a_tbl WHERE NOT EXISTS (SELECT row1 FROM b_tbl WHERE row1 = 'input_data' ) AND NOT EXISTS (SELECT row1 FROM c_tbl WHERE row1 = 'input_data' ); ようするに、他のテーブルに、すでにない行であることが前提で、 a_tbl から DELETE したことを確認したいのです。 Pro*C内で実行するのですが、一般に同じだと思います。 キホンのキかもしれませんが、よろしくお教えください。

  • SQL文について質問させて頂きます。

    いつもお世話になっております。いろいろSQL文の本を読んだのですがわからないので今回もよろしく お願い致します。 SELECT DISTINCT * FROM bukken WHERE (id IN (SELECT fid FROM fudou_kodawari WHERE kid IN (1,4))) というSQL文があるのですが、これだと1,4が1つでも含まれるリストが出てくるのですが1,4が完全に一致しないと出てこないようにするにはどのようにSQL文を変更すれば良いのでしょうか? 何卒よろしくお願い致します。

  • NOT INをNOT EXISTSで書き直したい

    こんにちは。SQLでNOT INが実行速度が遅いため、EXISTSで書き直したいのですが、思っている結果がでません。 SQLのどこが間違っているかお教え頂けませんでしょうか? ■ ・テーブル名:R_TABLE ・フィールド:id データ: id(ユニークではない) 001 002 003 003 003 007 008 009 ■ ・テーブル名:P_Master ・フィールド:id データ: id(ユニーク) 001 002 003 ■やりたい事 R_TABLEのデータを条件によって削除したい。 条件はP_MASTERに無いR_TABLEの行を削除したいです。 ■欲しい結果 R_TABLE 001 002 003 003 003 ■書いたSQL DELETE FROM R_TABLE WHERE R_TABLE.id NOT IN (SELECT R_TABLE.id FROM R_TABLE INNER JOIN P_MASTER ON R_TABLE.id = P_MASTER.id) これは正しい結果がでました。 書きなおしたSQL DELETE FROM R_TABLE WHERE R_TABLE.id WHERE NOT EXISTS (SELECT R_TABLE.id FROM R_TABLE INNER JOIN P_MASTER ON R_TABLE.id = P_MASTER.id) 結果はR_TABLEから何も消されていませんでした。 どこを修正すれば正しい結果が得られるでしょうか? 副問い合わせで、R_TABLEとP_MASTERのINNER JOINの結果が得られ、R_TABLEからINNER JOINに含まれないデータ削除しろという命令だと思っていたのですが違うみたいです。 どこを修正すればよろしいでしょうか? よろしくお願いします。

  • SQL文に関するとある問題について

    SQL文に関するとある問題について (3) 次の出庫記録の表に対して、以下のSQL文を実行したときに得られる値が 最も大きな数が得られるものは何ですか? 該当する記号を書いてください。 --------------------------------------------------------------- 商品番号 数量 日付 PRT145 4 20090610 DP255 2 20090610 DP266 3 20090611 PRT255 2 20090611 a SELECT MAX(数量) FROM 出庫記録 b SELECT AVG(数量) FROM 出庫記録 WHERE 日付='20090610' c SELECT COUNT(*) FROM 出庫記録 d SELECT SUM(数量) FROM 出庫記録 WHERE 日付='20090611' ------------------------------------------------------------ この場合、それぞれの選択肢が出す数字は a=1 b=3 c=4 d=5 なので、正解はdでいいんでしょうか? aのMAXって、最大なのはPRT145の数量4ですが、a=に4をいれるべきか、それともMAXなのはPRT145一つだけなので1にすべきか、まずそこで躓きました。 bのAVGは4+2=6なので、それの半分の3かな? cはそのまんま4にして dはSUMなんで、3+2=5.にしちゃいましたが、間違ってるでしょうか?   宜しくお願い致します。

  • SQL文のEXISTSについて

    DBはmysql5.0を使っています。 以下のSQL文を、EXISTSを境に2つに分解することはできないでしょうか?? SELECT ROUND(SUM(IFNULL(T.DAIKIN,0) - IFNULL(T.TESURYO,0))/10000,1) INTO v_PRICE2 FROM TORIHIKI AS T WHERE T.TORIHIKI_KBN = '1' AND EXISTS (SELECT * FROM TORIHIKI AS T2 WHERE T2.TORIHIKI_DATE >= F_DATE AND T2.TORIHIKI_DATE <= T_DATE AND T2.TORIHIKI_KBN = '2' AND T2.KEHAI_CD = LPAD(v_SOBA_CD,5,'0') AND T2.KAIIN_SBT = v_KAIIN AND T.SEIRI_NO = T2.SEIRI_NO AND ROUND((IFNULL(T2.DAIKIN,0) + IFNULL(T2.TESURYO,0))/10000,1) >= p_MIN AND ROUND((IFNULL(T2.DAIKIN,0) + IFNULL(T2.TESURYO,0))/10000,1) < p_MAX );