2014-01-30 64 views
1

均線我有兩列TOTALDATE視圖,後者不包括星期六和星期天,即SQL服務器:5天,上個月

TOTAL  DATE 
0   1-1-2014 
33   2-1-2014 
11   3-1-2014 
55   5-1-2014 
... 
25   15-1-2014 
35   16-1-2014 
17   17-1-2014 
40   20-1-2014 
33   21-1-2014 
... 

,我試圖完成的任務是在整個月份計算5天的平均總計,即在13日至17日,14日和20日(我們跳過週末),15日和21日等直至當前日期。 是的,他們是重疊的範圍。

任何想法如何在SQL中實現它?

輸出的例子(從6開始,使用假電話號碼)

5daysAVG  Start_day 
22    1-01-2014 <-counted between 1st to 6th Jan excl 4 and 5 of Jan 
25    2-01-2014 <- Counted between 2nd to 7th excluding 4 and 5 
27    3-01-2014 <- 3rd to 8th excluding 4/5 
24    6-01-2014 <-6th to 10th 
... 
33    today-5 
+0

爲什麼在13日開始的?月份從1月份開始,對嗎?此外,它看起來像你有超過研磨範圍。 –

+0

當然,這只是一個例子,是的,它們重疊 – Yasskier

+0

因此,讓我們得到這個。你想從你的桌子平均五個組? –

回答

1

SQL Server提供的datepart(wk, ...)函數來獲取一年的一週。不幸的是,它使用一年中的第一天來定義年份。

相反,你可以找到連續值的序列和它們組合在一起:

select min(date), max(date, avg(total*1.0) 
from (select v.*, row_number() over (order by date) as seqnum 
     from view 
    ) v 
group by dateadd(day, -seqnum, date); 

的想法是,從連續幾天的序列中減去的數字序列產生一個常數。

你也可以做到這一點通過使用規範的日期和除以7:

select min(date), max(date, avg(total*1.0) 
from view v 
group by datediff(day, '2000-01-03', date)/7; 

日期'2000-01-03'是任意週一。

編輯:

你似乎想上5日均線。因爲缺少週末數據,avg()應該只是工作:

select v1.date, avg(v2.value) 
from view v1 join 
    view v2 
    on v2.date >= v1.date and v2.date < dateadd(day, 7, v1.date) 
group by v1.date; 
+0

它工作的很棒!只是我無法理解dateadd(day,7,v1.date)。爲什麼7不是5? – Yasskier

+1

@KrisWojcik。 。 。也許我誤解了正在發生的事情,但似乎你想要接下來的五天數據 - 並且7天內會給你(因爲週末的兩個人都失蹤了)。 –

+0

哦對。忘記與dateadd,我們不能簡單地認爲它會錯過週末,ü... – Yasskier

2

好吧,我通常會設置一些測試數據一起玩。

以下是在tempdb中創建[work]表的一些代碼。我正在跳週末。總數是0到40之間的一個隨機數。

-- Just playing 
use tempdb; 
go 

-- drop existing 
if object_id ('work') > 0 
drop table work 
go 

-- create new 
create table work 
(
    my_date date, 
    my_total int 
); 
go 

-- clear data 
truncate table work; 
go 

-- Monday = 1 
SET DATEFIRST 1; 
GO 

-- insert data 
declare @dt date = '20131231'; 
declare @hr int; 
while (@dt < '20140201') 
begin 
    set @hr = floor(rand(checksum(newid())) * 40); 
    set @dt = dateadd(d, 1, @dt); 
    if (datepart(dw, @dt) < 6) 
    insert into work values (@dt, @hr); 
end 
go 

在新的LEAD()窗口函數的SQL SERVER 2012中,這變得很容易。

-- show data 
with cte_summary as 
(
select 
    row_number() over (order by my_date) as my_num, 
    my_date, 
    my_total, 
    LEAD(my_total, 0, 0) OVER (ORDER BY my_date) + 
    LEAD(my_total, 1, 0) OVER (ORDER BY my_date) + 
    LEAD(my_total, 2, 0) OVER (ORDER BY my_date) + 
    LEAD(my_total, 3, 0) OVER (ORDER BY my_date) + 
    LEAD(my_total, 4, 0) OVER (ORDER BY my_date) as my_sum, 
    (select count(*) from work) as my_cnt 
from work 
) 
select * from cte_summary 
where my_num <= my_cnt - 4 

基本上,我們給每行的行號,計算第0行(當前)到第4行(總共4行)的總數和總數。

由於這是一個五個週期的運行總數,其餘日期都缺少數據。因此,我們拋棄它們。 my_row < = my_cnt -4

我希望這可以解決您的問題!

如果您只關心當月的一個號碼,請將選擇更改爲以下內容。我留下了其他行,讓你瞭解發生了什麼。

select avg(my_sum/5) as my_stat 
from cte_summary 
where my_num <= my_cnt - 4 

爲SQL Server 2012 < &> = 2005

就像在這個世界上任何事情,總會有辦法做到這一點。我使用了一個小型計數表來循環數據並收集平均數據集的5個數據點。

-- show data 
with 
cte_tally as 
(
select 
    row_number() over (order by (select 1)) as n 
from 
    sys.all_columns x 
), 
cte_data as 
(
select 
    row_number() over (order by my_date) as my_num, 
    my_date, 
    my_total 
from 
    work 
) 
select 
    (select my_date from cte_data where my_num = n) as the_date, 
    (
    select sum(my_total)/5 
    from cte_data 
    where my_num >= n and my_num < n+5 
) as the_average 
from cte_tally 
where n <= (select count(*)-4 from work) 

下面是對通用表表達式(CTE)的解釋。

cte_data = order data by date and give row numbers 
cte_tally = a set based counting algorithm 

對於五個羣組計算平均值並顯示日期。

enter image description here

該解決方案不依賴於節假日或週末。如果數據在那裏,它就按日期按五個順序分組。

如果您需要過濾假期和週末,請創建假期表。將一個where子句添加到cte_data中,以檢查NOT IN(SELECT DATE FROM HOLIDAY TABLE)。

祝你好運!

+0

它會,但正如你所說LEAD是在SQL服務器2012年,我工作在2008 R2 :( – Yasskier

0

這是一個在SQL 2008中工作的解決方案;

這裏的概念是首先使用表變量來規範化數據;其餘的計算和平均日子很簡單。

通過規範化數據,我的意思是,擺脫週末的日子,並在可用於識別行的臨時表變量中分配ID;

檢查出來:(SqlFiddle also here

-- This represents your original source table 
Declare @YourSourceTable Table 
(
    Total Int, 
    CreatedDate DateTime 
) 

-- This represents some test data in your table with 2 weekends 
Insert Into @YourSourceTable Values (0, '1-1-2014') 
Insert Into @YourSourceTable Values (33, '1-2-2014') 
Insert Into @YourSourceTable Values (11, '1-3-2014') 
Insert Into @YourSourceTable Values (55, '1-4-2014') 
Insert Into @YourSourceTable Values (25, '1-5-2014') 
Insert Into @YourSourceTable Values (35, '1-6-2014') 
Insert Into @YourSourceTable Values (17, '1-7-2014') 
Insert Into @YourSourceTable Values (40, '1-8-2014') 
Insert Into @YourSourceTable Values (33, '1-9-2014') 
Insert Into @YourSourceTable Values (43, '1-10-2014') 
Insert Into @YourSourceTable Values (21, '1-11-2014') 
Insert Into @YourSourceTable Values (5, '1-12-2014') 
Insert Into @YourSourceTable Values (12, '1-13-2014') 
Insert Into @YourSourceTable Values (16, '1-14-2014') 

-- Just a quick test to see the source data 
Select * From @YourSourceTable 

/* Now we need to normalize the data; 
    Let's just remove the weekends and get some consistent ID's to use in a separate table variable 
    We will use DateName SQL Function to exclude weekend days while also giving 
    sequential ID's to the remaining data in our temporary table variable, 
    which are easier to query later 
*/ 

Declare @WorkingTable Table 
(
    TempID Int Identity, 
    Total Int, 
    CreatedDate DateTime 
) 

-- Let's get the data normalized: 
Insert Into 
    @WorkingTable 
Select 
    Total, 
    CreatedDate 
From @YourSourceTable 
Where DateName(Weekday, CreatedDate) != 'Saturday' 
    And DateName(Weekday, CreatedDate) != 'Sunday' 

-- Let's run a 2nd quick sanity check to see our normalized data 
Select * From @WorkingTable 

/* Now that data is normalized, we can just use the ID's to get each 5 day range and 
    perform simple average function on the columns; I chose to use a CTE here just to 
    be able to query it and drop the NULL ranges (where there wasn't 5 days of data) 
    without having to recalculate each average 
*/ 

; With rangeCte (StartDate, TotalAverage) 
As 
(
    Select 
     wt.createddate As StartDate, 
     (
      wt.Total + 
      (Select Total From @WorkingTable Where TempID = wt.TempID + 1) + 
      (Select Total From @WorkingTable Where TempID = wt.TempID + 2) + 
      (Select Total From @WorkingTable Where TempID = wt.TempID + 3) + 
      (Select Total From @WorkingTable Where TempID = wt.TempID + 4) 
     )/5 
     As TotalAverage 
From 
    @WorkingTable wt  
) 
Select 
    StartDate, 
    TotalAverage 
From rangeCte 
Where TotalAverage 
    Is Not Null 
+0

這適用於平均滾動五天;但是,你有多個TSQL語句,它也不平均的平均值。你會計算過去4天裏沒有完整的5天滾動的信息嗎?我把它們扔掉了! –

+0

@CRAFTYDBA:多條語句沒有錯,你的解決方案也使用了多條TSQL語句;不確定你爲什麼提出這個語句。 '我指的是額外的SELECT,他們只是爲了插圖/可視化而被評論,不知道你的平均值是什麼意思,我沒有看到這是一個需求 - 我的代碼平均每個滾動5天忽略週末。最後4天也在我的代碼中拋出(如果您閱讀我的sql中的註釋,您會看到發生了這種情況,提示,它在查詢中選擇來自TotalEverage不爲空的CTE。 –