why*_*heq 8 sql sql-server design-patterns sql-server-2008-r2
以下是我两年前开始使用的模式,并在我的遗留代码中反复重复.
它使用不同的时间段有效地分组相同的数据.
有没有一种标准的方法我应该接近这个,或者这种长篇大论的方法和我能得到的一样好?
提出这个问题的另一种方法是如何使以下更简洁?
所有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;
Run Code Online (Sandbox Code Playgroud)
我会把它作为一个插入语句.
现在更不希望通过对集合,多维数据集或汇总进行分组来使用该组,因为我看不出如何限制在单个日期组中计算的行数小于在较大时间段组中计算的行数.
因此,为了防止这种情况发生,您可以创建一个common-table-expression(; WITH mycte AS(...子查询...)),临时表,表变量或包含时间段的XML格式文本对象,每行一行/元素.
此脚本也可以在定义的更多或更少时间段内运行,以便从应用程序到服务器只进行一次旅行即可获得所有结果.
这是一个带临时表的例子,也可以很容易地变成表变量:
--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;
Run Code Online (Sandbox Code Playgroud)
这是主要的查询......
--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
Run Code Online (Sandbox Code Playgroud)
您还可以使用下面的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
Run Code Online (Sandbox Code Playgroud)
最后,您可以在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" />
';
Run Code Online (Sandbox Code Playgroud)
并修改主要查询以读取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
Run Code Online (Sandbox Code Playgroud)
有关如何将XML stringed查询转换为过程的示例,以支持1个或多个时间段的单个参数:
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
Run Code Online (Sandbox Code Playgroud)
哪个可以运行:
--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
Run Code Online (Sandbox Code Playgroud)
小智 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
Run Code Online (Sandbox Code Playgroud)
并使用这样的
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'
Run Code Online (Sandbox Code Playgroud)
使用参数 - 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
Run Code Online (Sandbox Code Playgroud)
SQLFiddle上的演示
| 归档时间: |
|
| 查看次数: |
635 次 |
| 最近记录: |