0

2 つのデータベースがあります。1 つはリビジョン番号と日付を含むサブバージョン ログで、もう 1 つはリビジョン中に変更されたリビジョン番号とパスを含みます。私のクエリは、月ごとに最もコミットされているディレクトリを見つけます。問題は、実行に数分かかることです。この獣のようなクエリを最適化するのを手伝ってくれる人はいますか? もっと良い方法があると確信しています。

SELECT   [Directory]
    ,[Month]
    ,COUNT([PathMonth]) OVER (PARTITION BY [PathMonth]) AS [Count] INTO ##temp
FROM
    (SELECT  [Path]
            ,[Month]
            ,[Directory]
            ,[Directory] + [Month] AS [PathMonth]
        FROM
            (SELECT [Path]
                    ,SUBSTRING([Path], 0, LEN([Path]) - CHARINDEX('/', REVERSE([Path])) + 1) AS [Directory]
                    ,CONVERT(CHAR(4), [LogDate], 120) + '-' + CONVERT(CHAR(2), [LogDate], 110) AS [Month]
            FROM [SubversionLog] JOIN [PathsLog] ON [SubversionLog].[Revision] = [PathsLog].[Revision]
            WHERE [Path] LIKE '/%/%/%/_%'
            ) one) two
    ORDER BY [Month]
SELECT * INTO ##tempTwo
FROM ##temp
GROUP BY [Directory], [Month], [Count]

SELECT    t1.[Directory]
         ,t1.[Month]
         ,t1.[Count]
    FROM ##tempTwo t1 LEFT JOIN ##tempTwo t2 ON t1.[Month] = t2.[Month] AND t1.[Count] < t2.[Count]
    WHERE t2.[Count] IS NULL
    GROUP BY t1.[Directory], t1.[Month], t1.[Count]
    ORDER BY [Month] DESC


IF EXISTS (SELECT * FROM ##temp)
    DROP TABLE ##temp

IF EXISTS (SELECT * FROM ##tempTwo)
    DROP TABLE ##tempTwo

この作業の半分は、YYYY-MM-DD HH:MM:SS.SSS タイムスタンプを YYYY-MM にフォーマットし、ファイル名パスをディレクトリに変換することです。

4

1 に答える 1

1

これはあなたのバージョン管理であるため、テーブルにはおそらく数十万行しかないと思います。両方のテーブルを実行することは世界の終わりではありませんが、Peter Schott が言うように、それは良いインデックス リビジョン:

CREATE nonclustered index <name> on SubversionLog (revision)
CREATE nonclustered index <name> on PathsLog (revision)

個人的な経験から言えば、いくつかの文字列操作を行うことで深刻な速度低下が生じるとは思いません。元のテーブルのほとんどの行に対して新しい行を作成していて、一時テーブルにインデックスが付けられていないため、複数の一時テーブルを作成すると速度が低下していると思います。したがって、これらの一時テーブルを取り除き、クエリを簡素化することをお勧めします。

;with filteredData as (
    SELECT [path], 
           Substring([path], 0, Len([path]) - Charindex('/', 
                                              Reverse([path])) + 1) 
                   AS 
           [Directory], 
           CONVERT(CHAR(4), [logdate], 120) + '-' 
           + CONVERT(CHAR(2), [logdate], 110) 
                   AS [Month] 
    FROM   [subversionlog] 
           JOIN [pathslog] 
             ON [subversionlog].[revision] = [pathslog].[revision] 
    WHERE  [path] LIKE '/%/%/%/_%'
), countRevisions as (
    SELECT [month], 
           [directory], 
           count(*) as [Count]
    FROM filteredData
    GROUP BY [MONTH], [directory]
), rankDirectories as (
    SELECT *, RANK() over (partition by month order by count desc) as [Rank]
    from countRevisions
)
select [month], [directory], [count]
from rankDirectories
WHERE [rank] = 1

編集:

一部の結果をキャッシュせずにできることはこれ以上ないと思います。最適化が必要なものを確認するには、クエリ プランを確認する必要があります。グループ化、順序付け、および/またはランキング、おそらく結合またはキー検索にあると推測しています。キーの検索と結合のために、カバリング インデックスを作成できます。他のものについては、結果をキャッシュする必要があります。最新の状態に保つための余分なテーブルが必要になるため、実際のテーブルではキャッシュしません。代わりに、マテリアライズド ビューを使用して、SQL Server が更新を維持できるようにします。確かに、これは更新が遅くなることを意味しますが、1 分間に数回の更新 (ソース管理ログの場合) では、これが大きな問題になるとは思いません。

于 2013-06-21T00:07:02.370 に答える