Quantcast
Channel: プログラム の個人的なメモ
Viewing all articles
Browse latest Browse all 860

【SQL】相関サブクエリ / 自己相関サブクエリ

$
0
0

■はじめに

http://gihyo.jp/dev/serial/01/sql_academy2/000901
などで、 「SQLでループ→ 相関サブクエリ」とあるので、
相関サブクエリについて、学ぶ。

■相関サブクエリ / 自己相関サブクエリ とは?

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


関連記事

サブクエリ / 副問合せ ~SELECT文中のSELECT文~

http://blogs.yahoo.co.jp/dk521123/35657803.html

SQLを書くコツ

* 「ループは GROUP BY句と相関サブクエリで置き換える」
http://blogs.yahoo.co.jp/dk521123/35726358.html

Viewing all articles
Browse latest Browse all 860

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>