• ベストアンサー

相関副問い合わせが良く分かりません

はじめて投稿させていただきます。 データベースの勉強をしていて、相関副問い合わせのところで躓いてしまいました。 例えば、商品ごとの販売数量の平均を上回った日と商品名を一覧で出すというものがあるとします。テーブルはSales(カラムはQuantity、ProductID、SaleDate)とProducts(カラムはProductID、ProductName)を使用します。 そうすると、 SELECT p.ProductName, s1.SaleDate FROM Sales AS s1 JOIN Products AS p ON s1.ProductID = p.ProductID WHERE s1.Quantity > ( SELECT AVG(Quantity) FROM Sales AS s2 WHERE s1.ProductID = s2.ProductID ) ; となるらしいのですが、いまいちわかりません。 副問い合わせ内では、SalesテーブルのQuantityの平均を出し、それを副問い合わせを呼び出す側のテーブルから1行ずつ取り出してきたレコード内のQuantityの値と比較し、平均より多ければ抽出していくのだと思ったんですが、そうなると後の WHERE s1.ProductID = s2.ProductID が何故必要なんだろう?となってしまい今に至ります。 それと、AVG(Quantity)はAVG(s2.Quantity)とした方がいいのでしょうか?(テーブル名による修飾はRDBMSの内部動作の負担を軽減させると書いてあったので) 分かりにくい文章で申し訳ないのですが、よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • jamshid6
  • ベストアンサー率88% (591/669)
回答No.3

No.1です。 >相関副問い合わせは、副問い合わせを呼び出す側のSELECT文と、副問い合わせ側のSELECT文が結合しながら結果を作るものであると書いてあるのですが これは文字通りの意味だと思います。 今回の例だと、 SELECT * FROM Sales AS s1 INNER JOIN Products AS p ON s1.ProductID = p.ProductID から1件ずつ読みだして(仮にそのデータのProductIDが@ProductIDとします)、 サブクエリ側の SELECT AVG(Quantity) FROM Sales WHERE ProductID=@ProductID を実行し、2つの結果を結合して抽出するか判断するということだからです。 相関サブクエリは外側のクエリから実行し、サブクエリは繰り返し実行される構造なのでコストがかかるのは確かです。私もほとんどのケースではJOINで書いています。が、相関サブクエリで書いた方が分かりやすい場合もあります。実際のコーディングはパフォーマンスがすべてではないことが多いです。

nexus1415
質問者

お礼

何度も質問してすいませんでした。 よく分かりました、ありがとうございます。 分かりやすさも重要なんですね。

その他の回答 (2)

  • jjon-com
  • ベストアンサー率61% (1599/2592)
回答No.2

通常のサブクエリと相関サブクエリとでは実行の方法が違います。 >副問い合わせ内では、SalesテーブルのQuantityの平均を出し…… というのは,通常の副問合せの実行方法であり,相関副問合せのそれではありません。 http://www.jjon.com/oracle/OracleSilver/06/CorrelSubquery.html

nexus1415
質問者

お礼

ありがとうございます。 何故「相関」なのかが良く分かりました。

  • jamshid6
  • ベストアンサー率88% (591/669)
回答No.1

SalesテーブルのQuantityの平均を出して、それと各レコードのQuantityと比較していく、というのは合っているのですが、ここで比較する平均というのは「Salesテーブル全体の平均」ではなくて、「Salesテーブルの商品単位での平均」です。 したがって、今読みだしたレコードの商品に絞って平均を出さなければいけないので、最後の条件が必要になるわけです。 そうすると、1件読み出す毎にその商品の平均を算出して比較する、を繰り返すことになるので、非常にコストがかかるようにみえます。 実際、相関副問い合わせはかなりコストがかかります。 ただ、RDBMSにはオプティマイザというものがついていて、そのクエリがよりコストの低い別のクエリで実現できると判断すれば、クエリを置き換えて実行します。 結果、以下のクエリと同じ方法に組み替えて実行したりします(RDBMSによります)。 SELECT p.ProductName, s1.SaleDate FROM Sales AS s1 INNER JOIN Products AS p ON s1.ProductID = p.ProductID INNER JOIN (SELECT ProductID, AVG(Quantity) AvgQty FROM Sales GROUP BY ProductID) s2 ON s2.ProductID=s1.ProductID WHERE s1.Quantity > s2.AvgQty Quantityをs2.Quantityにした方がいいかという点については、「その方が早いか」という意味ではほとんど効果はないと思います。むしろ、「その方がわかりやすいか」という意味でつけてもいいかなと思います。 (実務ではそもそも全くs2を使わない書き方も一般的です)

nexus1415
質問者

補足

とてもわかりやすい回答ありごとうございます。 集合関数を使用する場合はグループ化という間違った固定概念がありましたので混乱していたようです。また、オプティマイザと呼ばれる機能も始めて知りました。大変勉強になります。 特に速いわけではないんですね。今まで几帳面にすべてつけて来ました(笑)このような実務上の話も大変参考になります。 確かに集合関数を一回一回計算して比較するのはコストが高そうです。では、なるべくSQLを記述する際には相関副問い合わせは使用しない方が良いのでしょうか? もう1つ疑問に思ったことなのですが、相関副問い合わせは、副問い合わせを呼び出す側のSELECT文と、副問い合わせ側のSELECT文が結合しながら結果を作るものであると書いてあるのですが、これはどのような意味なのかわかりますか?副問い合わせ内のWHERE句で結合するとあるのですが、これは指定された行を抽出するために使われているような気がするのですが。 度々質問してすいません。 よろしくお願いします。

関連するQ&A

  • 相関副問い合わせについて

    いつもお世話になっています。 相関副問い合わせについて質問です。テーブルSalesに三つのカラム、ProductID、QuantityとSaleDateがあるとします。 各商品の日ごとの売上数量が商品毎の平均の売上数量より上の日を取り出すSQL文がSQL1のようになるようです。ここでわからないのがSQLの動きです。AVG関数は集合関数だと思うのですが、副問い合わせにGROUP BY句がないけど、相関によりそれが成り立っているというのはそういう動きなんだなと理解しました。 ただ自分のこれまでの経験では、こういったものをやったことがないので、基本的に副問い合わせが先に評価され、その結果と主SQLのWHERE句で比較をして主SQLが評価されるという基本的な動きが普通という考え方がこびりついています。 この相関副問い合わせのSQL1だとテーブルs1がProductID毎に評価され、ProductIDが移り変わると副問い合わせのテーブルs2でそのs1.ProductIDと同じ値毎に平均が計算されていくという動きをしているととらえているのですが、それであっているのでしょうか? よろしくおねがいします。 (SQL1) SELECT s1.SaleDate FROM Sales AS s1 WHERE s1.Quantity > (SELECT AVG(s2.Quantity) FROM Sales AS s2 WHERE s1.ProductID = s2.ProductID )

  • 相関サブクエリの中で、グループ化はできない?

    いつもお世話になっています。 オラクルではないのですが、SQLの質問が多く感じたため、質問させていただきました。 タイトルの通り、相関サブクエリはグループ化した状態で行えるのか教えていただきたいんです。 課題は、売上表(Sales)から、売上数量(Quantity)の平均の10倍よりも売上数量(Quantity)の最大の方が大きい商品IDを見つけ出す。というものです。 考えたSQLは SELECT __S1.ProductID FROM __Sales AS S1 GROUP BY __ProductID HAVING __AVG(Quantity) * 10 < (SELECT    __MAX(Quantity)    FROM    __Sales AS S2    GROUP BY    __ProductID    HAVING    __S1.ProductID = S2.ProductID) です。考え方として 相関サブクエリで、現在の商品ID(HAVING S1.ProductID = S2.ProductIDのところ)の商品数量の最大値を比較として返そうと思いましたが、どうもうまくいきません。 こういった相関サブクエリは作れないのでしょうか?

  • 単純なjoin句の必要性

    失礼致します。 joinについて勉強しています。 下記サイトで手ごろな例題を見つけました。 <http://www.atmarkit.co.jp/fnetwork/rensai/sql04/sql1.html> ここで、 SELECT OrderID, Prd.ProductID, ProductName, Prd.UnitPrice, Quantity, Discount FROM "Order Details" Ord INNER JOIN Products Prd ON Ord.ProductID=Prd.ProductID という記述があるのですが、これはjoinを使わず SELECT Ord.OrderID, Prd.ProductID, Ord.ProductName, Prd.UnitPrice, Ord.Quantity, Ord.Discount FROM "Order Details" Ord, "Products" Prd WHERE Ord.ProductID=Prd.ProductID とするのと何が違うのでしょうか? また、INNER JOINやLEFT JOINなどの例はよく見るのですが、 単純な(頭に何も付かない)JOINは何を意味するのでしょうか? 以上、よろしくお願いします。M(_ _)M

    • ベストアンサー
    • Java
  • select文の結果から新しいテーブル作成の方法

    SQLは初心者で現在勉強をしています。 (SQLServer2000を使用) select文の結果から新しいテーブル作成したいのですが、下記SQLで上手くいきません。 どなたかアドバイス宜しくお願いします。 create table 新テーブル as select OrderID, Prd.ProductID, ProductName, Prd.UnitPrice from "Order Details" Ord inner join Products Prd on Ord.ProductID = Prd.ProductID (エラー内容) キーワード 'as' 付近に正しくない構文があります。 SQLserverでは 『create table 新テーブル as』の構文は使用できないのでしょうか?

  • SQL文(副問合せについて)

    SELECT URIAGE_NO FROM URIAGE AS U1 WHERE URIAGE_SURYO > (SELECT AVG(URIAGE_SURYO) FROM URIAGE AS U2 WHERE U1.HINMOKU_CODE = U2.HINMOKU_CODE); 上記SQLの構文で (SELECT AVG(URIAGE_SURYO) FROM URIAGE AS U2 WHERE U1.HINMOKU_CODE = U2.HINMOKU_CODE この部分の論理がわかりません。 書籍では「HINMOKU_CODEごとのURIAGE_SURYOの 平均を求める」ということですが、何故このように なるのかがわかりません。副問合せではGROUP BY句 が使えないので、非常に困っております。 下記にデータを記載致します。 以上、宜しくお願いします。 ■主問合せ URIAGE.NO HINMOKU_CODE URIAGE_SURYO 001 BP001 10 002 BP002 20 003 BP002 30 004 BP002 10 005 BP001 20 006 BP003 30 007 BP004 10 008 BP004 20 009 BP003 20 ■副問合せ HINMOKU_CODE AVG(URIAGE_SURYO) BP001 10 BP002 20 BP003 25 BP004 15 ■結果 URIAGE.NO HINMOKU_CODE  003 BP002 005 BP001 006 BP003 008 BP004

  • SQLiteによる相関副問い合わせ

    SQLiteによる相関副問い合わせ SQLiteのデータベースについて質問です。 以下のような、商品データベースと、その商品の色に関するデータベースを持っています。 【テーブル head 】 帽子のデータベース name | 値段 | 季節 ------------------------- シルクハットA | 3980 | 春 シルクハットB | 2980 | 夏 キャップA | 1980 | 春 【テーブル color 】 name | color | 配合率 ------------------------- シルクハットA | 赤 | 80 シルクハットA | 白 | 20 シルクハットB | 黄 | 40 シルクハットB | 黒 | 60 キャップA | 青 | 30 キャップA | 黒 | 70 上記のデータベースから黒を含む商品の名前と値段を出力しようと以下の構文を実行しました。 SELECT * FROM head AS H WHERE EXISTS ( SELECT * FROM color WHERE H.name = name AND color = '黒' ) 上記で「Error: near "SELECT": syntax error」になってしまいました。どこが間違っているのでしょうか?

  • 副問い合わせについて

    お世話になります。明日から仕事で使うのですが、下記の構文が よく理解できません。宜しくお願い致します。 SQL Severは経験ありません。 経験はInfomixSql,Access,MySql程度です。 (1)行副問い合わせ(副問い合わせの結果が1行n列で返ってくる) SELECT * FROM t1 WHERE (c4, c2) = (SELECT MAX(c1), 'a' FROM t2); このwhere節の結合条件はどのような感じになるのでしょうか? (2)表副問い合わせ(副問い合わせの結果がm行n列(表)で返ってくる) SELECT * FROM t1 WHERE c4 IN ( SELECT c1 FROM t2 WHERE c4 >= 10 ) ORDER BY c1; 、 SELECT x1.c1, x2.y4 FROM t1 x1, ( SELECT c3, SUM(c4) AS y4 FROM t2 GROUP BY c3) AS x2 WHERE x1.c3 = x2.y4 ORDER BY c1; (1)に同じです。 参考になりそうなサイトでも結構ですので 宜しくお願い致します。

  • 副問い合わせの限界値はどれくらいなのでしょうか? 

    副問い合わせの限界値はどれくらいなのでしょうか?  現在下記のようなSQLを考えています。 SELECT * FROM A_TABLE WHERE a_field in ( SELECT a_field FROM B_TABLE WHERE b_field := ?c ) ; ※(便宜上、外部変数の指定を[:= ?c]としました) B_TABLEの検索に引っかかったデータをinの条件にしてA_TABLEを検索するのですが、 B_TABLEの検索で出力される結果(サブクエリで出力される件数)が 現在の理論値は平均でおおよそ1000件、最大10万件になる予定です。 これに対し、10万件での問い合わせに副問い合わせの実行はmysqlで可能なのでしょうか?  現状のテスト環境では10万件のテストデータを用意できない状況です・・・。 また、どなたか副問い合わせでのサブクエリ出力件数の限界値をご存知の方がいらっしゃいましたらご教授頂ければ幸いです。 宜しくお願い致します。

    • ベストアンサー
    • MySQL
  • SQL の 質問

    ORACLE の SQLの質問です。 現在 名称  銘柄  単価 甘さ ビタミン りんご  , 1 , 50 , 5 , 5 りんご , 1 , 40 , 4 , 4 りんご , 1 , 30 , 3 , 3 りんご , 2 , 100 , 5 , 5 りんご , 2 , 80 , 4 , 4 りんご , 3 , 50 , 2 , 5 りんご , 3 , 40 , 2 , 4 メロン , 1 , 1000 , 10 , 3 というテーブル 果実 があり、りんごの 銘柄ごとの 平均単価、平均甘さ、平均ビタミンを 出したいんですが、 SELECT AVG(単価) AS 平均単価, AVG(甘さ) AS 平均甘さ, AVG(ビタミン) AS 平均ビタミン FROM 果実 WHERE 銘柄 = (SELECT DISTINCT 銘柄 FROM 果実) AND 名称 = ‘りんご’ ORDER BY 銘柄 というSQLを考えたのですが、どうしても、WHERE 句の後の銘柄のSQLで「式がありません。」のエラーが出てしまいます。できるだけ * ワイルドカードは使いたくないのですが、もし、どなたか、教えていただける方いましたら、ご教授ください。お願いいたします。

  • UPDATE文の副問い合わせ

    こんばんわ。 今少し書き方がわからないSQL文があって困っています。 テーブルA(データが入ってるテーブル)に 新規でカラムを追加したので、 今UPDATE文を使いたいと思っています。 膨大なので、1つ1つする訳にはいかないので、 条件指定して、あるカラムにあるとある条件に合致するものは 新規カラムの値が4とか ある条件なら、新規カラムの値が6などと入れたい感じです。 自分なりに調べて UPDATE テーブル名 SET 新規カラム = 値 WHERE (SELECT * FROM 同じテーブル名 WHERE 条件) だと思って色々書いたり試行錯誤したのですが、 全然できませn。 どのように書けばよろしいのでしょうか? またUPDATEをかける際の条件指定に2つの違った 条件(同じカラムの違う条件、または別カラムの条件) を入れることは可能でしょうか? 回答いただけるとありがたいです。 よろしくお願いします。

    • ベストアンサー
    • MySQL