■相関サブクエリ / 自己相関サブクエリ とは?
http://blogs.yahoo.co.jp/dk521123/35657803.htmlで、「サブクエリ」を扱ったが...
* 相関サブクエリ(co-related subquery)とは... => 内部クエリから外部クエリの FROM 句のテーブルを参照するサブクエリ * 自己相関サブクエリとは... => 自己結合と組み合わせた相関サブクエリ
補足:サブクエリとの違い
* 相関サブクエリは、内部クエリと外部クエリで、単独のSQL文としては実行できない 下記【サンプル】の例2より ~~~~~~ ・・・略・・・ WHERE ・・・略・・・ FROM Employees E2 WHERE E1.departmentId = E2.departmentId -- 2つのテーブルにまたがっている ~~~~~~
■相関サブクエリの捉え方
http://gihyo.jp/dev/serial/01/sql_academy2/000902より * 相関サブクエリには2つの側面がある 1) 手続き言語的な観点から見た場合、相関サブクエリの持つ役割は、『ループ隠し』 => サブクエリの外側のテーブル1行ごとに対して、内側のサブクエリが実行される 2) 集合指向言語的な観点から見た場合、『集合のカット』http://language-and-engineering.hatenablog.jp/entry/20101108/p1
より * サブクエリを『関数』と考えてみよう (1) 引数 :外部クエリから内部クエリへのパラメータ (2) 戻り値:内部クエリの出力 (3-1) 非相関サブクエリの場合は、一度実行すればよい (3-2) 相関サブクエリの場合は、毎回実行する必要がある
■使用上の注意
1) 正規形違反の返却結果 2) パフォーマンス問題http://gihyo.jp/dev/serial/01/sql_academy2/000904
より
1) 正規形違反の返却結果
* 更新やスカラサブクエリと組み合わせるときは、結果が第1正規形を満たすか、厳重にチェックしないと危険 => 常に単一行に制限する安全な方法は、「集約した結果のみを返す」か 「サブクエリ内の結合条件で一意キーを指定する」こと
2) パフォーマンス問題
* 単純なサブクエリに比べて、相関サブクエリの実行コストは相当高い => 軽減するための方法として有効なことは、 サブクエリ内の結合キーにインデックスを作成する(あるいは主キーを結合キーとして使う)
サンプル
例1:性別別の最高齢SELECT E1.Id, E1.name, D.name, E1.bossId, E1.sex, E1.birthDate, ((CONVERT(INT,CONVERT(VARCHAR(8),GETDATE(),112)) - CONVERT(INT,CONVERT(VARCHAR(8),E1.birthDate,112))) / 10000) AS age FROM Employees E1 INNER JOIN Departments D ON E1.departmentId = D.id WHERE ((CONVERT(INT,CONVERT(VARCHAR(8),GETDATE(),112)) - CONVERT(INT,CONVERT(VARCHAR(8),E1.birthDate,112))) / 10000) = (SELECT MAX((CONVERT(INT,CONVERT(VARCHAR(8),GETDATE(),112)) - CONVERT(INT,CONVERT(VARCHAR(8),E2.birthDate,112))) / 10000) FROM Employees E2 WHERE E1.sex = E2.sex )例2:部署別の最高齢
SELECT E1.Id, E1.name, D.name, E1.bossId, E1.sex, E1.birthDate, ((CONVERT(INT,CONVERT(VARCHAR(8),GETDATE(),112)) - CONVERT(INT,CONVERT(VARCHAR(8),E1.birthDate,112))) / 10000) AS age FROM Employees E1 INNER JOIN Departments D ON E1.departmentId = D.id WHERE ((CONVERT(INT,CONVERT(VARCHAR(8),GETDATE(),112)) - CONVERT(INT,CONVERT(VARCHAR(8),E1.birthDate,112))) / 10000) = (SELECT MAX((CONVERT(INT,CONVERT(VARCHAR(8),GETDATE(),112)) - CONVERT(INT,CONVERT(VARCHAR(8),E2.birthDate,112))) / 10000) FROM Employees E2 WHERE E1.departmentId = E2.departmentId )
補足:使用テーブル/データ
-- テーブル CREATE TABLE Employees ( id char(4) NOT NULL, name varchar(20) NULL, departmentId char(4) NULL, bossId char(4) NULL, sex char(1) NULL, -- m : male, f : famale birthDate Date NULL ) CREATE TABLE Departments ( id char(4) NOT NULL, name varchar(20) NULL, ) -- データ INSERT INTO Employees VALUES('1000', 'Robin','D000', NULL, 'm', '1973-5-6') INSERT INTO Employees VALUES('1001', 'Mike','D001', NULL, 'm', '1945-11-9') INSERT INTO Employees VALUES('1002', 'Tony','D000', NULL, 'm', '1965-1-12') INSERT INTO Employees VALUES('1003', 'Claire','D001', '1001', 'f', '1987-9-22') INSERT INTO Employees VALUES('1004', 'John','D002', '1002', 'm', '1972-4-2') INSERT INTO Employees VALUES('1005', 'Ken','D002', '1001', 'm', '1982-12-22') INSERT INTO Employees VALUES('1006', 'Hilary','D001', '1003', 'f', '1979-9-9') INSERT INTO Employees VALUES('1007', 'Becky','D000', '1007', 'f', '1980-3-2') INSERT INTO Employees VALUES('1008', 'Anne','D002', '1007', 'f', '1969-1-31') INSERT INTO Employees VALUES('1007', 'Steve','D000', NULL, 'm', '1955-2-24') INSERT INTO Employees VALUES('1008', 'Thomas','D001', '1007', 'm', '1959-9-12') INSERT INTO Departments VALUES('D000', 'IT') INSERT INTO Departments VALUES('D001', 'Sales') INSERT INTO Departments VALUES('D002', 'Accounting') -- 表示する select SELECT E1.Id, E1.name, D.name, E1.bossId, E1.sex, E1.birthDate, ((CONVERT(INT,CONVERT(VARCHAR(8),GETDATE(),112)) - CONVERT(INT,CONVERT(VARCHAR(8),E1.birthDate,112))) / 10000) AS age FROM Employees E1 INNER JOIN Departments D ON E1.departmentId = D.id -- 結果 Id name name bossId sex birthDate age 1000 Robin IT NULL m 1973-05-06 42 1001 Mike Sales NULL m 1945-11-09 70 1002 Tony IT NULL m 1965-01-12 51 1003 Claire Sales 1001 f 1987-09-22 28 1004 John Accounting 1002 m 1972-04-02 43 1005 Ken Accounting 1001 m 1982-12-22 33 1006 Hilary Sales 1003 f 1979-09-09 36 1007 Becky IT 1007 f 1980-03-02 35 1008 Anne Accounting 1007 f 1969-01-31 46 1007 Steve IT NULL m 1955-02-24 60 1008 Thomas Sales 1007 m 1959-09-12 56
参考文献
http://codezine.jp/article/detail/907以下のシリーズが分かりやすい
http://gihyo.jp/dev/serial/01/sql_academy2/000901
http://gihyo.jp/dev/serial/01/sql_academy2/000902
http://gihyo.jp/dev/serial/01/sql_academy2/000903
http://gihyo.jp/dev/serial/01/sql_academy2/000904
関連記事
サブクエリ / 副問合せ ~SELECT文中のSELECT文~
http://blogs.yahoo.co.jp/dk521123/35657803.htmlSQLを書くコツ
* 「ループは GROUP BY句と相関サブクエリで置き換える」http://blogs.yahoo.co.jp/dk521123/35726358.html