8

以下は、私が 2 年前に使い始めたパターンで、レガシー コードで何度も繰り返されています。

異なる期間を使用して同じデータを効果的にグループ化します。

私がこれに近づくべき標準的な方法はありますか、それともこの長くて複雑な方法は私が得るのと同じくらい良いですか?

この質問を別の言い方をすれば、次の文をより簡潔にするにはどうすればよいでしょうか?
4 つのクエリはすべて同じデータ ソースから生成され、4 つすべてが同じ出力テーブルに入力されます。これら 4 つのクエリを 1 つの短いスクリプトに結合できますか?

DECLARE @myDate DATETIME = CONVERT(DATETIME,CONVERT(VARCHAR(11),GETDATE(),106)); 
DECLARE @myFirstDateLastMth CHAR(8) =CONVERT(CHAR(6),DATEADD(mm,-1,@myDate-1),112) + '01';
DECLARE @myFirstDateCurrentMth CHAR(8) =CONVERT(CHAR(6),DATEADD(mm,0,@myDate-1),112) + '01'; 

DELETE FROM WH.dbo.tb_myTable

--day on day==========
INSERT INTO WH.dbo.tb_myTable
SELECT 
  TimePeriod =
    CASE 
        WHEN x.DateKey = CONVERT(VARCHAR(11),@myDate - 1,112) THEN 'Day'                    
        WHEN x.DateKey = CONVERT(VARCHAR(11),@myDate - 2,112) THEN 'Day-1'  
    END,
  Game              = x.Name,
  Score             = SUM(x.Score),
  Ticks             = SUM(x.Ticks),
  ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHEREx.DateKey >= CONVERT(VARCHAR(11),@myDate - 2,112)
GROUP BY
  CASE 
    WHEN x.DateKey = CONVERT(VARCHAR(11),@myDate - 1,112) THEN 'Day'                    
    WHEN x.DateKey = CONVERT(VARCHAR(11),@myDate - 2,112) THEN 'Day-1'  
  END,
  x.Name;

--wk on wk==========
INSERT INTO WH.dbo.tb_myTable
SELECT 
  TimePeriod =
        CASE 
        WHEN x.DateKey >= CONVERT(VARCHAR(11),@myDate - 7,112) THEN 'Week'                  
        WHEN x.DateKey < CONVERT(VARCHAR(11),@myDate - 7,112) 
                    AND x.DateKey >= CONVERT(VARCHAR(11),@myDate - 14,112)  
                            THEN 'Week-1'
    END,
  Game               = x.Name,
  Score              = SUM(x.Score),
  Ticks              = SUM(x.Ticks),
  ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHERE   x.DateKey >= CONVERT(VARCHAR(11),@myDate - 14,112)
GROUP BY
  CASE 
    WHEN x.DateKey >= CONVERT(VARCHAR(11),@myDate - 7,112) THEN 'Week'                  
    WHEN x.DateKey < CONVERT(VARCHAR(11),@myDate - 7,112) 
                AND x.DateKey >= CONVERT(VARCHAR(11),@myDate - 14,112)  
                        THEN 'Week-1'
    END,
  g.Name;                   


--mth on mth==========
INSERT INTO WH.dbo.tb_myTable
SELECT 
  TimePeriod =
    CASE 
        WHEN x.DateKey >= CONVERT(VARCHAR(11),@myDate - 28,112) THEN 'Month'                    
        WHEN x.DateKey < CONVERT(VARCHAR(11),@myDate - 28,112) 
                        AND x.DateKey >= CONVERT(VARCHAR(11),@myDate - 56,112)  
                                THEN 'Month-1'
    END,
  Game               = x.Name,
  Score              = SUM(x.Score),
  Ticks              = SUM(x.Ticks),
  ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHERE   x.DateKey >=  CONVERT(VARCHAR(11),@myDate - 56,112)
GROUP BY
  CASE 
    WHEN x.DateKey >= CONVERT(VARCHAR(11),@myDate - 28,112) THEN 'Month'                    
    WHEN x.DateKey < CONVERT(VARCHAR(11),@myDate - 28,112) 
                AND x.DateKey >= CONVERT(VARCHAR(11),@myDate - 56,112)  
                        THEN 'Month-1'
  END,
  g.Name;                   


--MTD and PrevCalMonth==========
INSERT INTO WH.dbo.tb_myTable
SELECT 
  TimePeriod
  = CASE 
        WHEN  x.DateKey >= @myFirstDateCurrentMth   THEN 'MTD'
        WHEN  x.DateKey < @myFirstDateCurrentMth  
                AND  x.DateKey >=@myFirstDateLastMth THEN 'PrevCalMonth'                                      
  END,
  Game              = x.Name,
  Score             = SUM(x.Score),
  Ticks             = SUM(x.Ticks),
  ScorePerTick = SUM(x.Score)/SUM(x.Ticks)
FROM #LimitedBetinfo x
WHERE   x.DateKey >=  CONVERT(CHAR(6),DATEADD(mm,-1,@myDate-1),112) + '01'
GROUP BY
  CASE 
    WHEN  x.DateKey >= @myFirstDateCurrentMth   THEN 'MTD'
    WHEN  x.DateKey < @myFirstDateCurrentMth  
            AND  x.DateKey >=@myFirstDateLastMth THEN 'PrevCalMonth'            
  END,
  g.Name;   
4

5 に答える 5

7

私はそれを単一の挿入ステートメントにします。

個々の日のグループで計算された行が、より大きな期間グループで計算された行よりも少なくならないように制限する方法がわかりません。

したがって、それが起こらないようにするには、common-table-expression (;WITH mycte AS (...subquery...))、一時テーブル、テーブル変数、または期間を含む XML 形式のテキスト オブジェクトを作成します。それぞれに1行/要素。

このスクリプトは、アプリからサーバーへの 1 回の移動だけですべての結果を取得するように定義された期間を増減して実行することもできます。

これは、簡単にテーブル変数にすることもできる一時テーブルの例です。

--Define time periods
CREATE TABLE #TempTimePeriods (
    TimePeriod VARCHAR(20) PRIMARY KEY,
    TPBegin VARCHAR(11) NOT NULL,
    TPEnd VARCHAR(11) NULL
);

DECLARE @myDate DATETIME = '2012-10-10';
DECLARE @myDateMinusOne DATETIME = DATEADD(dd, -1, @myDate);
INSERT INTO #TempTimePeriods ( TimePeriod, TPBegin, TPEnd )
SELECT [TimePeriod], CONVERT(VARCHAR(11), TPBegin, 112) TPBegin, CONVERT(VARCHAR(11), TPEnd, 112) TPEnd
FROM (
    SELECT 'Day'          [TimePeriod], @myDate - 1 TPBegin, @myDate -  1 TPEnd UNION ALL
    SELECT 'Day-1'        [TimePeriod], @myDate - 2 TPBegin, @myDate -  2 TPEnd UNION ALL
    SELECT 'Week'         [TimePeriod], @myDate - 7 TPBegin,                                  NULL TPEnd UNION ALL
    SELECT 'Week-1'       [TimePeriod], @myDate - 14 TPBegin, @myDate -  8 TPEnd UNION ALL
    SELECT 'Month'        [TimePeriod], @myDate - 28 TPBegin,                                  NULL TPEnd UNION ALL
    SELECT 'Month-1'      [TimePeriod], @myDate - 56 TPBegin, @myDate - 29 TPEnd UNION ALL
    SELECT 'MTD'          [TimePeriod], DATEADD(dd, -1 * DAY(@myDateMinusOne) + 1, @myDateMinusOne) TPBegin, NULL TPEnd UNION ALL
    SELECT 'PrevCalMonth' [TimePeriod], DATEADD(mm,-1,DATEADD(dd, -1 * DAY(@myDateMinusOne) + 1, @myDateMinusOne)) TPBegin, DATEADD(dd, -1 * DAY(@myDateMinusOne), @myDateMinusOne) TPEnd
) TT;  

そして、ここに主なクエリがあります...

--compute/insert results
INSERT INTO WH.dbo.tb_myTable
SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM #TempTimePeriods TimePeriods
--for periods with no data use left outer join to return 0-value results, otherwise inner join
LEFT OUTER JOIN #LimitedBetInfo x 
ON x.DateKey >= [TimePeriods].TPBegin
AND (
    [TimePeriods].TPEnd IS NULL
    OR x.DateKey <= [TimePeriods].TPEnd
)
GROUP BY TimePeriods.TimePeriod, x.Name

以下の Common-Table-Expression を使用して #TempTimePeriods テーブルを削除することもできます。

DECLARE @myDate DATETIME = '2012-10-10';
DECLARE @myDateMinusOne DATETIME = DATEADD(dd, -1, @myDate);
;WITH TimePeriods AS (
    SELECT [TimePeriod], CONVERT(VARCHAR(11), TPBegin, 112) TPBegin, CONVERT(VARCHAR(11), TPEnd, 112) TPEnd
    FROM (
        SELECT 'Day'          [TimePeriod], @myDate - 1 TPBegin, @myDate -  1 TPEnd UNION ALL
        SELECT 'Day-1'        [TimePeriod], @myDate - 2 TPBegin, @myDate -  2 TPEnd UNION ALL
        SELECT 'Week'         [TimePeriod], @myDate - 7 TPBegin,                                  NULL TPEnd UNION ALL
        SELECT 'Week-1'       [TimePeriod], @myDate - 14 TPBegin, @myDate -  8 TPEnd UNION ALL
        SELECT 'Month'        [TimePeriod], @myDate - 28 TPBegin,                                  NULL TPEnd UNION ALL
        SELECT 'Month-1'      [TimePeriod], @myDate - 56 TPBegin, @myDate - 29 TPEnd UNION ALL
        SELECT 'MTD'          [TimePeriod], DATEADD(dd, -1 * DAY(@myDateMinusOne) + 1, @myDateMinusOne) TPBegin, NULL TPEnd UNION ALL
        SELECT 'PrevCalMonth' [TimePeriod], DATEADD(mm,-1,DATEADD(dd, -1 * DAY(@myDateMinusOne) + 1, @myDateMinusOne)) TPBegin, DATEADD(dd, -1 * DAY(@myDateMinusOne), @myDateMinusOne) TPEnd
    ) TT
)
INSERT INTO WH.dbo.tb_myTable
SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM [TimePeriods]
--for periods with no data use left outer join to return 0-value results, otherwise inner join
LEFT OUTER JOIN #LimitedBetInfo x
ON x.DateKey >= [TimePeriods].TPBegin
AND (
     [TimePeriods].TPEnd IS NULL
     OR x.DateKey <= [TimePeriods].TPEnd
)
GROUP BY [TimePeriods].TimePeriod, x.Name

最後に、必要に応じてストアド プロシージャに渡すのに便利な XML 文字列で期間を定義し、次のように進めることができます。

--example XML string with time period definitions
DECLARE @TimePeriodsXml NVARCHAR(MAX) = '
<TimePeriod name="Day" tpbegin="20121010" tpend="20121010" />
<TimePeriod name="Day-1" tpbegin="20121009" tpend="20121009" />
<TimePeriod name="Week" tpbegin="20121004"/>
<TimePeriod name="Week-1" tpbegin="20120927" tpend="20121004" />
<TimePeriod name="Month" tpbegin="20120913" />
<TimePeriod name="Month-1" tpbegin="20120815" tpend="20120912" />
<TimePeriod name="MTD" tpbegin="20121001" />
<TimePeriod name="PrevCalMonth" tpbegin="20120901" tpend="20120930" />
';

そして、XML を読み取るように変更されたメイン クエリ:

SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM (
    SELECT
    E.TimePeriod.value('./@name', 'VARCHAR(20)') TimePeriod,
    E.TimePeriod.value('./@tpbegin', 'VARCHAR(20)') TPBegin,
    E.TimePeriod.value('./@tpend', 'VARCHAR(20)') TPEnd
    FROM (
        SELECT CAST(@TimePeriodsXml AS XML) tpxml
    ) TT
    CROSS APPLY tpxml.nodes('/TimePeriod') AS E(TimePeriod)
) TimePeriods
--for periods with no data use left outer join to return 0-value results, otherwise inner join
LEFT OUTER JOIN #LimitedBetInfo x
ON x.DateKey >= [TimePeriods].TPBegin
AND (
     [TimePeriods].TPEnd IS NULL
     OR x.DateKey <= [TimePeriods].TPEnd
)
GROUP BY TimePeriods.TimePeriod, x.Name

1 つ以上の期間の 1 つのパラメーターをサポートするために、XML 文字列クエリをプロシージャに変換する方法の例については、次を参照してください。

CREATE PROCEDURE dbo.GetTimePeriodAggregates
@TimePeriodsXmlString NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
SELECT TimePeriods.TimePeriod,
x.Name Game,
SUM(x.Score) Score,
SUM(x.Ticks) Ticks,
CASE WHEN SUM(x.Ticks) != 0 THEN SUM(x.Score)/SUM(x.Ticks) END ScorePerTick
FROM (
    SELECT
    E.TimePeriod.value('./@name', 'VARCHAR(20)') TimePeriod,
    E.TimePeriod.value('./@tpbegin', 'VARCHAR(20)') TPBegin,
    E.TimePeriod.value('./@tpend', 'VARCHAR(20)') TPEnd
    FROM (
        SELECT CAST(@TimePeriodsXml AS XML) tpxml
    ) TT
    CROSS APPLY tpxml.nodes('/TimePeriod') AS E(TimePeriod)
) TimePeriods
LEFT OUTER JOIN #LimitedBetInfo x
ON x.DateKey BETWEEN TimePeriods.TPBegin AND TimePeriods.TPEnd
GROUP BY TimePeriods.TimePeriod, x.Name
END

次のように実行できます。

--This declare is just an example, it could be instead a parameter passed from an application
DECLARE @ThisExecutionsXmlString NVARCHAR(MAX) = N'
<TimePeriod name="Day" tpbegin="20121010" tpend="20121010" />
<TimePeriod name="Day-1" tpbegin="20121009" tpend="20121009" />
<TimePeriod name="Week" tpbegin="20121004"/>
<TimePeriod name="Week-1" tpbegin="20120927" tpend="20121004" />
<TimePeriod name="Month" tpbegin="20120913" />
<TimePeriod name="Month-1" tpbegin="20120815" tpend="20120912" />
<TimePeriod name="MTD" tpbegin="20121001" />
<TimePeriod name="PrevCalMonth" tpbegin="20120901" tpend="20120930" />
';

INSERT INTO WH.dbo.tb_myTable
EXEC dbo.GetTimePeriodAggregates @TimePeriodsXmlString=@ThisExecutionsXmlString
于 2013-01-04T14:20:52.460 に答える
6

このストアド プロシージャを作成できます

CREATE PROCEDURE InsertData
    @minLimit date,
    @maxLimit date,
    @minTerm nvarchar(50),
    @maxTerm nvarchar(50)
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO tb_myTable
    SELECT 
        [TimePeriod]        = CASE WHEN x.DateKey >= @maxLimit THEN @maxTerm ELSE @minTerm END,
        [Game]              = x.Name,
        [Score]             = SUM(x.[Score]),
        [Ticks]             = SUM(x.[Ticks]),
        [ScorePerTick]      = SUM(x.[Score])/SUM(x.[Ticks])

    FROM #LimitedBetinfo x
    WHERE x.DateKey >= @minLimit
    GROUP BY 
        CASE WHEN x.DateKey >= @maxLimit THEN @maxTerm ELSE @minTerm END,
        x.Name

END
GO

そして、このように使用します

TRUNCATE TABLE tb_myTable

DECLARE @today date = cast(getdate() as date)
DECLARE @yesterday date = dateadd(day, -1, @today)
EXECUTE dbo.InsertData @yesterday, @today, N'Day-1', N'Day' 

DECLARE @thisweek date = DATEADD(ww, DATEDIFF(ww,0,GETDATE()), 0)
DECLARE @lastweek date = DATEADD(ww, -1, @thisweek)
EXECUTE dbo.InsertData @lastweek, @thisweek, N'Week-1', N'Week' 

DECLARE @prev28 date = dateadd(day, -28, @today)
DECLARE @prev56 date = dateadd(day, -56, @today)
EXECUTE dbo.InsertData @prev56, @prev28, N'Month-1', N'Month' 

DECLARE @thismonth date = DATEADD(mm, DATEDIFF(mm,0,GETDATE()), 0)
DECLARE @lastmonth date = DATEADD(mm, -1, @thismonth)
EXECUTE dbo.InsertData @lastmonth, @thismonth, N'PrevCalMonth', N'MTD' 
于 2012-12-30T22:40:34.447 に答える
3

パラメーターを使用 - VALUES をテーブル ソースとして使用し、派生テーブルを使用して CROSS APPLY のパラメーターとして適用します。

DECLARE @myDate datetime = CAST(GETDATE() AS date);
IF OBJECT_ID('WH.dbo.tb_myTable') IS NOT NULL DROP TABLE WH.dbo.tb_myTable
SELECT TimePeriod, Game, Score, Ticks, ScorePerTicks
INTO WH.dbo.tb_myTable
FROM (VALUES('Day', DATEADD(day, -1, @myDate), @myDate),
            ('Day-1', DATEADD(day, -2, @myDate), DATEADD(day, -2, @myDate)),
            ('Week', DATEADD(day, -7, @myDate), @myDate),
            ('Week-1', DATEADD(day, -14, @myDate), DATEADD(day, -8, @myDate)),
            ('Month', DATEADD(day, -28, @myDate), @myDate),
            ('Month-1', DATEADD(day, -56, @myDate), DATEADD(day, -29, @myDate)),
            ('MTD', DATEADD(DAY, 1 - DAY(@myDate), @myDate), @myDate), 
            ('PrevCalMonth', DATEADD(DAY, 1 - DAY(@myDate), DATEADD(MONTH, -1, @myDate)), DATEADD(DAY,  - DAY(@myDate), @myDate)))
RParameters(TimePeriod, BDate, EDate)
  CROSS APPLY (SELECT x.Name AS Game,
                      SUM(x.Score) AS Score,
                      SUM(x.Ticks) AS Ticks,
                      SUM(x.Score) / SUM(x.Ticks) AS ScorePerTicks
               FROM #LimitedBetinfo x
               WHERE DateKey BETWEEN RParameters.BDate AND RParameters.EDate
               GROUP BY Name) AS o

SQLFiddle のデモ

于 2013-01-05T12:32:00.600 に答える
2

これはCUBE グルーピングの仕事のようです。

申し訳ありませんが、あなたの問題に対する正確な解決策を提供することはできませんが、選択のMOCKUP形式は次のようになります。

select * from
(
    select *,count(*) amount from
    (
        select datepart(HOUR, login_time) as hour, 
               datepart(MINUTE, login_time) as minute, 
               cmd as name
        from sys.sysprocesses
    ) tmp
    group by cube(tmp.hour, tmp.minute, tmp.name)
) tmp2
where tmp2.name is not null and
      (
      (tmp2.hour is not null and tmp2.minute is null) or
      (tmp2.hour is null and tmp2.minute is not null)
      )

1 つのマイナス -cubeここでの問題に対して生成されるデータが多すぎます。そのため、フィルタリングする必要があります。大きなプラスは、一時テーブルへの選択が1つだけ必要になることです。

于 2013-01-04T13:25:27.053 に答える