2017-11-20 9 views
0

私は毎月約500kのレコードを持っています。 2ヶ月分のデータを選択する必要のあるレポートを作成しています。ユーザーは月を選択し、データは12ヶ月前のデータです。2か月間のデータを12カ月に分ける方法はありますか?

私の他の選択基準と、このデータが格納される方法によって、私は完全な結果セットを得るために同じクエリを4回実行する必要があることがわかります。これは私が最適な方法で実行するためのコードを必要とする意味 - 誰も画面がここで「ロード」;-)

を言いながら待って座って好きではない私が実行しているSQLです:

Declare @ReportDate DateTime 
SET @ReportDate = dateadd(month,-1,DATEADD(month, DATEDIFF(month, 0, sysdatetime()), 0)) 


CREATE TABLE #DailyVolumes 
(contract_name VARCHAR(50), 
Volume   INT, 
date_registered DATETIME 
); 

INSERT into #DailyVolumes(contract_name, Volume, date_registered) 
SELECT 
    CONCAT('L1-',a.contract_code) AS contract_name 
    ,count(distinct(CONCAT(rtrim(p.lis_xxxx_id), '-', rtrim(r.lis_req_id)))) AS Volume 
    ,a.date_registered 
-- Create Temp Table to contain daily data 
FROM accession a 
    Left join xxxx P on a.xxxx_id = p.xxxx_id 
    Left join requester R on a.requester_id = r.requester_id 
where 
(([date_registered] >= @ReportDate and [date_registered] < dateadd(month,1,@ReportDate)) 
OR ([date_registered] >= dateadd(month,-12,@ReportDate) and [date_registered] < dateadd(month,-11,@ReportDate))) 
-- Limit results to L1 
    AND a.lis_code = 'L' 
-- Limit results to Visitype NOT I or E 
    AND visit_type NOT IN('I', 'E') 
GROUP BY 
    CONCAT('L1-',a.contract_code) 
    ,a.date_registered 

INSERT into #DailyVolumes(contract_name, Volume, date_registered) 
SELECT 
    CONCAT('L1-',a.contract_code) AS contract_name 
    ,count(distinct rtrim(r.lis_req_id)) AS Volume 
    ,a.date_registered 
-- Create Temp Table to contain daily data 
FROM accession a 
    Left join xxxx P on a.xxxx_id = p.xxxx_id 
    Left join requester R on a.requester_id = r.requester_id 
where 
(([date_registered] >= @ReportDate and [date_registered] < dateadd(month,1,@ReportDate)) 
OR ([date_registered] >= dateadd(month,-12,@ReportDate) and [date_registered] < dateadd(month,-11,@ReportDate))) 
-- Limit results to L1 
    AND a.lis_code = 'L' 
-- Limit results to Visitype = I or E 
    AND visit_type IN('I', 'E') 
GROUP BY 
    CONCAT('L1-',a.contract_code) 
    ,a.date_registered 

INSERT into #DailyVolumes(contract_name, Volume, date_registered) 
SELECT 
    CONCAT('L2-',a.contract_code) AS contract_code 
    ,count(distinct(CONCAT(rtrim(p.lis_xxxx_id), '-', rtrim(r.lis_req_id)))) AS Volume 
    ,a.date_registered 
FROM accession a 
    Left join xxxx P on a.xxxx_id = p.xxxx_id 
    Left join requester R on a.requester_id = r.requester_id 
where 
(([date_registered] >= @ReportDate and [date_registered] < dateadd(month,1,@ReportDate)) 
OR ([date_registered] >= dateadd(month,-12,@ReportDate) and [date_registered] < dateadd(month,-11,@ReportDate))) 
-- Limit results to L2 
    AND a.lis_code = 'S' 
-- Limit results to Visitype NOT I or E 
    AND visit_type NOT IN('I', 'E') 
GROUP BY 
    CONCAT('L2-',a.contract_code) 
    ,a.date_registered 

INSERT into #DailyVolumes(contract_name, Volume, date_registered) 
SELECT 
    CONCAT('L2-',a.contract_code) AS contract_code 
    ,count(distinct rtrim(r.lis_req_id)) AS Volume 
    ,a.date_registered 
FROM accession a 
    Left join xxxx P on a.xxxx_id = p.xxxx_id 
    Left join requester R on a.requester_id = r.requester_id 
where 
(([date_registered] >= @ReportDate and [date_registered] < dateadd(month,1,@ReportDate)) 
OR ([date_registered] >= dateadd(month,-12,@ReportDate) and [date_registered] < dateadd(month,-11,@ReportDate))) 
-- Limit results to L2 
    AND a.lis_code = 'S' 
-- Limit results to Visitype = I or E 
    AND visit_type IN('I', 'E') 
GROUP BY 
    CONCAT('L2-',a.contract_code) 
    ,a.date_registered 



-- SUM Daily Data into Monthly Slices 
Select 
    sum(Volume) AS Volume 
    ,contract_name 
    ,DATEADD(MONTH, DATEDIFF(MONTH, 0, date_registered), 0) AS MonthRegistered 
FROM #DailyVolumes 
group by 
    contract_name 
    ,DATEADD(MONTH, DATEDIFF(MONTH, 0, date_registered), 0) 
ORDER BY MonthRegistered DESC 
-- Clear Temp Table 
DROP TABLE #DailyVolumes 

このResultSetが取りますSSMSで約25秒戻ります。

私は4回ではなく8回のクエリを実行しようとしました。上記の日付句ごとに1回、結果セットを約6秒に戻しました。これはかなりOKですが直感的です... EDIT: - サーバーが結果をキャッシュしている必要があります。私はそれを1時間放置した後、私が期待していたような結果を得ました。実行には約48秒かかりました。 OR節を分割して、4つのクエリのそれぞれを2回目に実行すると、正味の利得はありません。

結果セットをより迅速に返す、より効率的なSQLがあるかどうかを知りたいと思います。私はこのようなORサテライトにサーバーに大きなオーバーヘッドをもたらすと信じていますが、これを単一の選択で実行するためのより良い方法は不明です。

EDIT:date_registeredは

+1

はdate_registered上のインデックスがありますか? – scsimon

+2

完全なクエリとテーブル構造を投稿できますか?インデックスを忘れないでください。また、実行計画を見ることは非常に役立ちます。 https://www.brentozar.com/pastetheplan/ –

+0

残念ながら私はSHOWPLANの権限が拒否されています... – kiltannen

答えて

0

をインデックスされるおそらく、私の目は、テストが必要ですが、あなたは4つのクエリを行う理由は、私は実際には表示されません。 a.list_code = 'L'は、あなたがテキストをしたい場合は、「L1-」、a.list_code = 'S'は、あなたが「テキストをしたいそうでない場合には表示され

DECLARE @ReportDate datetime 
SET @ReportDate = DATEADD(MONTH, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, SYSDATETIME()), 0)) 

CREATE TABLE #DailyVolumes (
     contract_name varchar(50) 
    , Volume int 
    , date_registered datetime 
); 

WITH myCTE as (
     SELECT visit_type, a.lis_code, a.contract_code, a.date_registered, p.lis_xxxx_id, r.lis_req_id 
     FROM accession a 
     LEFT JOIN xxxx p ON a.xxxx_id = p.xxxx_id 
     LEFT JOIN requester r ON a.requester_id = r.requester_id 
     WHERE (([date_registered] >= @ReportDate 
     AND [date_registered] < DATEADD(MONTH, 1, @ReportDate)) 
     OR ([date_registered] >= DATEADD(MONTH, -12, @ReportDate) 
     AND [date_registered] < DATEADD(MONTH, -11, @ReportDate))) 
    ) 

INSERT INTO #DailyVolumes (contract_name, Volume, date_registered) 
     SELECT 
      CONCAT('L1-', a.contract_code)           AS contract_name 
      , COUNT(DISTINCT (CONCAT(RTRIM(p.lis_xxxx_id), '-', RTRIM(r.lis_req_id)))) AS volume 
      , a.date_registered 
     -- Create Temp Table to contain daily data 
     FROM myCTE a 
     -- Limit results to L1 
     WHERE a.lis_code = 'L' 
     -- Limit results to Visitype NOT I or E 
     AND visit_type NOT IN ('I', 'E') 
     GROUP BY 
      CONCAT('L1-', a.contract_code) 
      , a.date_registered 

INSERT INTO #DailyVolumes (contract_name, Volume, date_registered) 
     SELECT 
      CONCAT('L1-', a.contract_code)  AS contract_name 
      , COUNT(DISTINCT RTRIM(r.lis_req_id)) AS volume 
      , a.date_registered 
     -- Create Temp Table to contain daily data 
     FROM myCTE a 
     WHERE a.lis_code = 'L' 
     -- Limit results to Visitype = I or E 
     AND visit_type IN ('I', 'E') 
     GROUP BY 
      CONCAT('L1-', a.contract_code) 
      , a.date_registered 

INSERT INTO #DailyVolumes (contract_name, Volume, date_registered) 
     SELECT 
      CONCAT('L2-', a.contract_code)           AS contract_code 
      , COUNT(DISTINCT (CONCAT(RTRIM(p.lis_xxxx_id), '-', RTRIM(r.lis_req_id)))) AS volume 
      , a.date_registered 
     FROM myCTE a 
     WHERE a.lis_code = 'S' 
     -- Limit results to Visitype NOT I or E 
     AND visit_type NOT IN ('I', 'E') 
     GROUP BY 
      CONCAT('L2-', a.contract_code) 
      , a.date_registered 

INSERT INTO #DailyVolumes (contract_name, Volume, date_registered) 
     SELECT 
      CONCAT('L2-', a.contract_code)  AS contract_code 
      , COUNT(DISTINCT RTRIM(r.lis_req_id)) AS volume 
      , a.date_registered 
     FROM myCTE a 
     WHERE a.lis_code = 'S' 
     -- Limit results to Visitype = I or E 
     AND visit_type IN ('I', 'E') 
     GROUP BY 
      CONCAT('L2-', a.contract_code) 
      , a.date_registered 


-- SUM Daily Data into Monthly Slices 
SELECT 
     SUM(Volume)           AS volume 
    , contract_name 
    , DATEADD(MONTH, DATEDIFF(MONTH, 0, date_registered), 0) AS monthregistered 
FROM #DailyVolumes 
GROUP BY 
     contract_name 
    , DATEADD(MONTH, DATEDIFF(MONTH, 0, date_registered), 0) 
ORDER BY 
     monthregistered DESC 
-- Clear Temp Table 
DROP TABLE #DailyVolumes 
+0

4回実行するための要件は次のとおりです。 – kiltannen

+0

- フィールドa.lis_code – kiltannen

+0

で識別される2つのロケーションがあります。各ロケーションには、フィールドa.contract_codeの他のロケーションと同じコードを持つ複数の契約があります。 – kiltannen

0

L2-:しかし私の目が私を欺く行う前提に、あなたはこのようにcommon table expressionを使用することができます'これを行うために私はケースステートメントを使用しました。

where visit_type IN ('I', 'E')where visit_type not in ('I', 'E')が2つの場合はa.list_code IN ('L', 'S')になります。両方とも同じテンポラリテーブルに入るので、フィルタ('I', 'E')の複数のクエリを削除しました。フィルタWHERE a.lis_code IN ('L', 'S')のクエリが1つだけになりました。私はそれが正しいことを願って...これのためのテストデータはありませんので、私は確認できません。

ltrimsとrtrimsは常にクエリを遅くします...それらを削除できるかどうかを確認してください。ここで

は私の試みです:

Declare @ReportDate DateTime 
SET @ReportDate = dateadd(month,-1,DATEADD(month, DATEDIFF(month, 0, sysdatetime()), 0))  

CREATE TABLE #DailyVolumes 
(contract_name VARCHAR(50), 
Volume   INT, 
date_registered DATETIME 
); 

INSERT into #DailyVolumes(contract_name, Volume, date_registered) 
SELECT 
    CASE WHEN a.lis_code = 'L' 
     THEN 'L1-' 
     ELSE 'L2-' 
    END + a.contract_code AS contract_name 
    ,count(distinct(CONCAT(rtrim(p.lis_xxxx_id), '-', rtrim(r.lis_req_id)))) AS Volume 
    ,a.date_registered 
-- Create Temp Table to contain daily data 
FROM accession a 
    Left join xxxx P on a.xxxx_id = p.xxxx_id 
    Left join requester R on a.requester_id = r.requester_id 
where 
(([date_registered] >= @ReportDate and [date_registered] < dateadd(month,1,@ReportDate)) 
OR ([date_registered] >= dateadd(month,-12,@ReportDate) and [date_registered] < dateadd(month,-11,@ReportDate)) 
) 
AND a.lis_code IN ('L', 'S') 

GROUP BY 
    CASE WHEN a.lis_code = 'L' 
     THEN 'L1-' 
     ELSE 'L2-' 
    END + a.contract_code 
    ,a.date_registered  


-- SUM Daily Data into Monthly Slices 
Select 
    sum(Volume) AS Volume 
    ,contract_name 
    ,DATEADD(MONTH, DATEDIFF(MONTH, 0, date_registered), 0) AS MonthRegistered 
FROM #DailyVolumes 
group by 
    contract_name 
    ,DATEADD(MONTH, DATEDIFF(MONTH, 0, date_registered), 0) 
ORDER BY MonthRegistered DESC 
-- Clear Temp Table 
DROP TABLE #DailyVolumes