河端善博 ブログ / SQL Server / PASSJ

SQL Server, .NET, PASSJ

  PASSJブログ :: ホーム :: 連絡をする :: RSS  :: ATOM :: Login
  952 投稿数 :: 112 ストーリー :: 1482 コメント :: 597 トラックバック

ニュース

Windows Live Alerts
My Yahoo!に追加
Add to Google dotnetnuke.jp
NPOPLUS.JP
MVP
PASSJ

記事のカテゴリ

過去の記事

カテゴリ

イメージギャラリ

.TEXT

INETA コミュニティ

MOM2005

MSMVP

PASSJ

SQL コミュニティ

セキュリティ TIPS

セキュリティのキホン

テスト

ブログ翻訳

多言語対応

.TEXT

INETA コミュニティ

MOM2005

MSMVP

PASSJ

SQL コミュニティ

セキュリティ TIPS

セキュリティのキホン

テスト

ブログ翻訳

多言語対応

項目別の TOP 3 を求めるクエリー 」のセットベース版として、渋谷さんにいただいたアイデアです。
キーワードは、「相関サブクエリー」
改めて、クエリーエンジンの奥の深さを学んだ気がします。

■クエリー

SET NOCOUNT ON
 
-- 変数宣言
DECLARE @start datetime
-- 時間計測開始
SET @start = GETDATE() 
 
SELECT T.Name, T.Value, T.ID 
FROM T 
WHERE T.Value <= ( 
    SELECT MAX(T2.Value) 
    FROM ( 
        SELECT TOP 3 T3.Name, T3.Value 
        FROM T T3 
        WHERE T3.Name = T.Name 
        ORDER BY T3.Value 
    ) T2 
    GROUP BY T2.Name 
    HAVING T2.Name = T.Name 
) 
ORDER BY T.Name, T.Value 
 
-- 時間表示
SELECT 'TIME(ms)' = DATEDIFF(ms, @start, GETDATE())
投稿日時 : 2004年5月17日 16:34

コメントを追加

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/17 16:58 CAMUS
コストのかかりが微妙に違うのですが、どう判断すりゃいいのやら未熟者ゆえ判りません。(_o_)
SELECT T.Name, T.Value, T.ID
FROM T
WHERE EXISTS (
SELECT 'T'
FROM (
SELECT TOP 3 T3.Name, T3.Value
FROM T T3
WHERE T3.Name = T.Name
ORDER BY T3.Value
) T2
WHERE T2.Name = T.Name
GROUP BY T2.Name
HAVING
T.Value <= MAX(T2.Value)
)
ORDER BY T.Name, T.Value

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/17 17:25 渋谷 泰宏
> GROUP BY T2.Name
> HAVING T2.Name = T.Name
の部分はやはり不要です。

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/17 17:27 渋谷 泰宏
上記にともない
> SELECT TOP 3 T3.Name, T3.Value
の部分も
SELECT TOP 3 T3.Value だけで良いです。
少しでもコストが下がるかと思います。

# re: 項目別の TOP 3 を求めるクエリー (セットベース)  2004/05/17 19:30 KoujiKana
少し改造してみました。
ORDER BYが1回になるので、少しだけ効率UPのような気がします。
はずしていたらすみません。

SELECT T.Name,T.Value, T.ID
FROM T
WHERE
T.Value=
(SELECT MAX(T2.VALUE)
FROM (SELECT TOP 3 T3.Name,T3.Value FROM T AS T3 WHERE T.NAME =T3.NAME) AS T2
WHERE T.name=T2.NAME AND T.VALUE=T2.VALUE)
ORDER BY T.Name, T.Value

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/17 19:55 KoujiKana
思いっきりはずしてました。すみません無かったことにしてください。

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/18 11:50 渋谷 泰宏
新しいアイデアを盛り込んでみました。
またもや書いた自分でクエリの動きを追いきれていませんので、
まだまだ改善の余地はありそうです。

相関サブクエリの
> 1 つ上のレベルのクエリによって選択される各行に対して一度ずつ、
> サブクエリが繰り返し実行される
という特徴に注目してみました。

SELECT T.Name, T.Value, T.ID
FROM T
INNER JOIN (SELECT DISTINCT Name FROM T) AS TN
ON T.Name = TN.Name
WHERE T.Value <= (
SELECT MAX(T2.Value)
FROM (
SELECT TOP 3 T3.Value
FROM T T3
WHERE T3.Name = TN.Name
ORDER BY T3.Value
) T2
)
ORDER BY T.Name, T.Value

興味深いのは、
> WHERE T3.Name = TN.Name
の部分です。

これが
> WHERE T3.Name = T.Name
だと結果は同じすが、コストは大きく異なります。

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/18 12:28 kojA
こんにちは。
河端さん、お招きいただきありがとうございます。

私のホームページでも同様の事をやっていますが、
実は結構、非効率な部分がありました。
他の方に指摘を受けた事もあり、コンテンツの修正
を予定しています。

指摘を受けた部分も含めて考えたのは、こんな感じです。

SELECT T.Name, T.Value, T.ID
FROM T
WHERE (SELECT COUNT(T2.Value)
FROM T T2
WHERE T2.Name = T.Name
and T2.Value <= T.Value) <= 3
ORDER BY T.Name, T.Value

シンプルではありますが…。
全然遅いです。ダメだなこりゃw
相関副問合せの入れ子の方が「全部持って来い」なので、
ハッシュテーブルを作成するのにコストが掛かっていました。

見せていただいたサンプルのクエリーは、top句が効いて、
ハッシュのコストが抑えられているようです。
このサンプルに、渋谷さんの指摘されている「GROUP BY と
HAVING 外す」を適用した形が、セットベースでは一番、
良さげかと思いましたが、更にブラッシュアップされている
ようですね。

正直、実務では「カーソル+一時テーブル」で解決する
場合が多いですし、今後は、分析関数RANKを使う場合が
多くなっていくんだろうな、とは思いますが。


# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/19 10:44 河端善博
渋谷さんの MAX() を利用した場合、項目につき 3件以上選択されてしまう場合がありますね。
同じ Value が複数ある場合です。
これをどうクリアするかですね。
それ以外は、カーソル版より早い !

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/19 12:02 河端善博
渋谷さんの最新版、項目につき 3件以上になってしまいますが、カーソル版の最新版と比較して、倍ほど早い !

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/19 17:31 渋谷 泰宏
> 渋谷さんの MAX() を利用した場合、項目につき 3件以上選択されてしまう場合がありますね。
> 同じ Value が複数ある場合です。
> これをどうクリアするかですね。
そうなんですよ。
カーソル版の場合、同順のものを含むかどうかを簡単に制御できるので実用的ですよね。
今のところ、セットベースで同順のものを含まないようにするには、
SOME、EXISTS、IN を使うしかないような気がしています。
結合という選択肢もあるんですが、相関サブクエリの結果を結合することは
不可能なのではないかと思っています。
他には、相関サブクエリ内ではなく、その上のレベルで同順を排除する
方法などが考えられますかね。
時間をみつけてもう少し Dive してみます。

# re: 項目別の TOP 3 を求めるクエリー (セットベース) 2004/05/19 17:37 渋谷 泰宏
このレベルになってくると、全体のレコード件数だけでなく
Name の種類の数や 1 Name あたりの平均レコード件数
などによっても得意・不得意なクエリが出てきそうですね。

コメント

タイトル:
名前:
Url:
コメント: