6

時間がかかるクエリを最適化しようとしています。クエリの目標は、最も類似した F2 を取得することです。(特に類似度測定) これは私が持っているものの例です:

 CREATE TABLE Test
(
   F1 varchar(124),
   F2 varchar(124),
   F3 varchar(124)
)
INSERT INTO TEST ( F1, F2, F3 ) VALUES ( 'A', 'B', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'D', 'B', 'E' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'F', 'I', 'G' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'F', 'I', 'G' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'D', 'B', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'F', 'B', 'G' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'D', 'I', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'A', 'B', 'C' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'A', 'B', 'K' )
INSERT INTO TEST (  F1, F2, F3 ) VALUES ( 'A', 'K', 'K' )

このクエリを実行すると、次のようになります。

SELECT B.f2,COUNT(*) AS CNT  
FROM 
(
select F1,F3 from Test
where F2='B'
 )AS A
    INNER JOIN  Test AS B
   ON A.F1 = B.F1 AND  A.F3 = B.F3
GROUP BY B.F2 
ORDER BY CNT DESC 

テーブルには 1m 以上の行があります。これを行うためのより良い方法は何でしょうか?

4

5 に答える 5

3

このフォームでもクエリを記述できます。選択が 1 つであるため、取得時間が短縮されます。

SELECT  Test_1.F2, COUNT(Test_1.F1) AS Cnt 
FROM    Test 
INNER JOIN Test AS Test_1 ON Test.F1 = Test_1.F1 AND Test.F3 = Test_1.F3 
WHERE   (Test.F2 = 'B') 
GROUP BY Test_1.F2
于 2012-09-16T04:58:40.227 に答える
3

クエリを記述する別の方法を次に示します。MS SQLで実行可能なguidoの回答に近い。

WITH Filtered AS (SELECT DISTINCT F1,F3 FROM Test WHERE F2='B')
SELECT B.f2,COUNT(*) AS CNT
  FROM Test B
       INNER JOIN Filtered
           ON B.F1 = Filtered.F1 AND B.F3 = Filtered.F3
 GROUP BY B.F2
 ORDER BY CNT DESC

フレッドが述べたように、元のクエリにバグがある可能性があると思います。あなたの例では、 F2="B" の数は 8 ではなく 6 である必要があります。8 の場合は、 を取り出しDISTINCTます。

もう 1 つの方法は、TEST テーブルのクラスター化インデックスを (F2、F1、F3) にして、(F1、F3) に別の非クラスター化インデックスを作成することです。

サンプル コードはSqlFiddleでも入手できます。

于 2012-09-16T08:25:13.373 に答える
2

WHERE F2 = 'B'最初の列または唯一の列として F2 を含むインデックスを作成しない限り、すべての行のフィルター検索ではフル テーブル スキャンが発生します。さらに下の結合条件には列 F1 と F3 が含まれていますが、これらは既に F1 で始まるインデックスの一部であると述べています。

また、クエリの最初の部分は、T2 = 'B' である (T1, T3) のセットの重複を排除していないことにも気付きました。これは、同じテーブルの別のサブセットに対してそのセットを交差させたときに予想されることです。これを行う理由があるかもしれませんが、実装しようとしている類似度測定アルゴリズムに関する詳細を提供するまで、私たちは確実に知ることができません.

ORDER BYは、最終的な結果セットで潜在的に大きな内部ソートを発生させることにより、クエリの実行時間にも影響を与えています。

于 2012-09-16T06:34:44.073 に答える
1

これはすでに回答されていることを認識していますが、特に F1 と F3 に多くの重複値がある場合は、このアプローチの方がはるかに高速になると思います。

SELECT B.f2, sum(A.cnt) AS CNT  
FROM (select F1, F3, count(*) as cnt
      from Test
      where F2='B'
      group by f1, f3
     ) A INNER JOIN
     Test B
     ON A.F1 = B.F1 AND A.F3 = B.F3
GROUP BY B.F2 
ORDER BY CNT DESC

F1 と F3 の組み合わせがそれほど多くない場合、最初のサブクエリは数百または数千行に減らす必要があります。(サンプル データには大文字が 1 つ含まれているため、すべての文字を使用すると、組み合わせの数は 576 になります。) SQL Server はおそらく、結果に対してマージまたはハッシュ結合を実行しますが、これはうまく機能するはずです。

Windows 関数を使用して、join と group by を使用せずにこれを行うこともできます。

select t.f2, sum(nummatches) as cnt
from (select t.*,
             sum(isB) over (partition by f1, f3) as nummatches
      from (select t.*,
                   (case when F2 = 'B' then 1 else 0 end) as IsB
            from test
           ) t
     ) t
group by t.f2
order by 2 desc

ウィンドウ関数は、データの小さなチャンクで機能するため、パフォーマンスが向上することがよくあります。

于 2012-09-16T17:46:32.757 に答える
1

Test テーブルに 100 万以上の行がある場合、グループ化する結合された一時テーブルには数億行が簡単に含まれます。

これはmysqlでは機能しますが、sql-server afaikでは機能しません:

SELECT F2,COUNT(*)
FROM Test AS B 
WHERE (B.F1,B.F3) IN (
  SELECT F1,F3 FROM Test
  WHERE F2='B') 
GROUP BY F2
于 2012-09-16T06:01:25.087 に答える