2013-02-22 99 views
6

我有一個包含以下列的表(T1):department,dateofsale,totalsales。 我想實現的目標是從開始日期開始每年在部門中實現每月部門的銷售額,並向後推遲1年。 也許下面的查詢會更好地顯示我想要實現的內容。在查詢結果上添加假行

-- Create the table T1 
    CREATE TABLE [dbo].[T1](
    [department] [nvarchar](50) NULL, 
    [dateofsale] [datetime] NULL, 
    [totalsales] [decimal](18, 5) NULL 
    ) ON [PRIMARY] 

-- Add some data 
    INSERT [dbo].[T1] ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29B00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
    INSERT [dbo].[T1] ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A27D00000000 AS DateTime), CAST(300.00000 AS Decimal(18, 5))) 
    INSERT [dbo].[T1] ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29C00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 

-- The query 
    declare @dataBegin datetime 
    declare @dataEnd datetime 
    set @dataEnd = '21/12/2013' 
    set @dataBegin = DATEADD(month,-11, @dataEnd) - (DAY(@dataEnd)-1) 
    set @dataEnd = DATEADD(month,1, @dataEnd) - (DAY(@dataEnd)) 
    SELECT department,SUM(totalsales) AS totsales, MONTH(dateofsale) as month, YEAR(dateofsale) as year 
    FROM T1 
    WHERE dateofsale >= @dataBegin AND dateofsale< @dataEnd 
    GROUP BY department,MONTH(dateofsale), YEAR(dateofsale) 
    ORDER BY department,MONTH(dateofsale), YEAR(dateofsale) 

與查詢的結果之前所添加的以下數據:

department /totsales/ month /year 
    0001/ 300.00000 /11 /2013 
    0001/ 400.00000 /12 /2013 

的問題是,我想也有作爲totalsales零值的月份。因此,其結果必然是:

department /totsales/ month /year 
0001/ 0 /1 /2013 
0001/ 0 /2 /2013 
0001/ 0 /3 /2013 
0001/ 0 /4 /2013 
0001/ 0 /5 /2013 
0001/ 0 /6 /2013 
0001/ 0 /7 /2013 
0001/ 0 /8 /2013 
0001/ 0 /9 /2013 
0001/ 0 /10 /2013 
0001/ 300.00000 /11 /2013 
0001/ 400.00000 /12 /2013 

我怎麼能這樣做?

回答

1

您可以創建兩個查詢並將它們聯合起來,或者編造缺失的行,使用CTE。我理解你的意思是你在11月之前沒有數據。

WITH months 
AS 
( 
    SELECT 2013 as yr, 1 as mnth  
    UNION ALL 
    SELECT 2013 as yr, mnth+1 as mnth 
    FROM months 
    WHERE mnth < 12  
) select months.yr, months.mnth, department, isnull(totsales,0.00) as totsales 
from months 
left join sales on sales.yr = months.yr and sales.month = months.mnth 

只需使用datepart函數從銷售日期中提取月份即可。上面的查詢只是爲了告訴你如何獲得數據中沒有的月份。

2

你可以創建一個表月份與做左連接與它

SELECT * 
FROM Months M 
LEFT JOIN T1 T ON M.month = T.Month 
1

我已經成功地克服這個問題的方法時,我遇到的是創建一個臨時表創建所有規定日期之後進行的臨時表和SELECT語句中的數據查詢之間的UNION

-- Create the table T1 
CREATE TABLE #T1(
[department] [nvarchar](50) NULL, 
[dateofsale] [datetime] NULL, 
[totalsales] [decimal](18, 5) NULL 
) ON [PRIMARY] 
-- Add some data 
INSERT #T1 ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29B00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
INSERT #T1 ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A27D00000000 AS DateTime), CAST(300.00000 AS Decimal(18, 5))) 
INSERT #T1 ([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29C00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 

--Solution Start 
DECLARE @dataBegin datetime 
DECLARE @dataEnd datetime 
DECLARE @CurrentDate DATETIME 
SET @dataEnd = '2013-12-23' 
SET @dataBegin = DATEADD(month,-11, @dataEnd) - (DAY(@dataEnd)-1) 
SET @dataEnd = DATEADD(month,1, @dataEnd) - (DAY(@dataEnd)) 

SET @CurrentDate = @dataBegin 

-- Create Temporary Table 
CREATE TABLE #calDate (calDate DATETIME) 

-- Populate Table 
INSERT INTO #calDate (calDate) 
    SELECT @CurrentDate 

WHILE DATEADD(MONTH, 1, @CurrentDate) <= @dataEnd 
BEGIN 
    INSERT INTO #calDate (calDate) 
     SELECT DATEADD(MONTH, 1, @CurrentDate) 
    SET @CurrentDate = DATEADD(MONTH, 1, @CurrentDate) 
END 

-- Query Data 
SELECT 
    department 
    , sum(totsales) 
    , month 
    , year 
FROM(
    SELECT '0001' as 'department',0 AS totsales, MONTH(calDate) as month, YEAR(calDate) as year FROM #calDate 
    UNION 
    SELECT department,SUM(totalsales) AS totsales, MONTH(dateofsale) as month, YEAR(dateofsale) as year 
    FROM #T1 
    WHERE dateofsale >= @dataBegin AND dateofsale< @dataEnd 
    GROUP BY department,MONTH(dateofsale), YEAR(dateofsale) 
)a 
GROUP BY department,month, year 
ORDER BY department,month, year 
DROP table #calDate 
DROP table #T1 

唯一的問題與上面的是,部門臨時表是硬編碼的創建,可以作爲參數雖然傳遞。

2

您不需要模擬缺失的行,只需爲其獲取正確的值即可。

注意:數據不僅需要按年份 - 月份輪換,還需要按部門輪換。否則,你會得到NULL值

-- Create the table T1 
    DECLARE @T1 TABLE(
    [department] [nvarchar](50) NULL, 
    [dateofsale] [datetime] NULL, 
    [totalsales] [decimal](18, 5) NULL 
    ) 

-- Add some data 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29B00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A27D00000000 AS DateTime), CAST(300.00000 AS Decimal(18, 5))) 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0001', CAST(0x0000A29C00000000 AS DateTime), CAST(200.00000 AS Decimal(18, 5))) 
    INSERT @T1([department], [dateofsale], [totalsales]) VALUES (N'0003', CAST(0x0000A29C00000000 AS DateTime), CAST(100.00000 AS Decimal(18, 5))) 
-- The query 
DECLARE @dataBegin DATETIME 
DECLARE @dataEnd DATETIME 

SET @dataEnd = '20140101' 
SET @dataBegin = DATEADD(month, - 11, @dataEnd) - (DAY(@dataEnd) - 1) 
SET @dataEnd = DATEADD(month, 1, @dataEnd) - (DAY(@dataEnd)); 

WITH Months (
    MonthNr 
    ,Year 
    ,Department 
    ) 
AS (
    SELECT MonthNr 
     ,Y.Year 
     ,D.department 
    FROM (
     VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12) 
     ) M(MonthNr) 
    CROSS JOIN (
     SELECT DISTINCT T.department 
     FROM @T1 T 
     ) D 
    CROSS JOIN (
     SELECT year 
     FROM (
      VALUES (2013) --insert as many years as you need 
      ) T(year) 
     ) Y 
    ) 
SELECT M.department 
    ,ISNULL(T.totsales, 0) totalSales 
    ,M.MonthNr month 
    ,M.year 
FROM Months M 
LEFT JOIN (
    SELECT department 
     ,SUM(totalsales) AS totsales 
     ,MONTH(dateofsale) AS month 
     ,YEAR(dateofsale) AS year 
    FROM @T1 
    WHERE dateofsale >= @dataBegin 
     AND dateofsale < @dataEnd 
    GROUP BY department 
     ,MONTH(dateofsale) 
     ,YEAR(dateofsale) 
    ) T ON T.month = M.MonthNr and T.department = M.Department 
ORDER BY department 
    ,M.MonthNr 
    ,M.Year 

結果:

department  totalSales   month  year 
--------------- --------------------- ----------- ----------- 
0001   0.00000    1   2013 
0001   0.00000    2   2013 
0001   0.00000    3   2013 
0001   0.00000    4   2013 
0001   0.00000    5   2013 
0001   0.00000    6   2013 
0001   0.00000    7   2013 
0001   0.00000    8   2013 
0001   0.00000    9   2013 
0001   0.00000    10   2013 
0001   300.00000    11   2013 
0001   400.00000    12   2013 
0003   0.00000    1   2013 
0003   0.00000    2   2013 
0003   0.00000    3   2013 
0003   0.00000    4   2013 
0003   0.00000    5   2013 
0003   0.00000    6   2013 
0003   0.00000    7   2013 
0003   0.00000    8   2013 
0003   0.00000    9   2013 
0003   0.00000    10   2013 
0003   0.00000    11   2013 
0003   100.00000    12   2013 
0

插入每月或日期和各部門的零值。現在你的數據是明確的,你的查詢也被簡化了。

假設沒有數據意味着零值不是一個好的數據練習。

1

一個用於Numbers表格的大用途:

-- Populate numbers table; keep this around, you'll find uses for it! 
;WITH 
    Pass0 as (select 1 as C union all select 1), --2 rows 
    Pass1 as (select 1 as C from Pass0 as A, Pass0 as B),--4 rows 
    Pass2 as (select 1 as C from Pass1 as A, Pass1 as B),--16 rows 
    Pass3 as (select 1 as C from Pass2 as A, Pass2 as B),--256 rows 
    Pass4 as (select 1 as C from Pass3 as A, Pass3 as B),--65536 rows 
    Pass5 as (select 1 as C from Pass4 as A, Pass4 as B),--4,294,967,296 rows 
    Tally as (select row_number() over(order by C) as Number from Pass5) 
select Number into dbo.Numbers from Tally where Number <= 1000000 


-- The query 
declare @dataBegin datetime 
declare @dataEnd datetime 
set @dataEnd = '2013-12-21' 
set @dataBegin = DATEADD(month,-11, @dataEnd) - (DAY(@dataEnd)-1) 
set @dataEnd = DATEADD(month,1, @dataEnd) - (DAY(@dataEnd)); 
with sales as (
    SELECT department,SUM(totalsales) AS totsales, MONTH(dateofsale) as month, YEAR(dateofsale) as year 
    FROM T1 
    WHERE dateofsale >= @dataBegin AND dateofsale< @dataEnd 
    GROUP BY department,MONTH(dateofsale), YEAR(dateofsale) 
), 
all_months as (
    select distinct department, Number as [month], 2013 as [year] 
    from T1 as t 
    cross join dbo.Numbers as n 
    where n.Number <= 12 
) 
select m.department, coalesce(s.totsales, 0), m.[month], m.[year] 
from all_months as m 
left join sales as s 
    on m.department = s.department 
    and m.[year] = s.[year] 
    and m.[month] = s.[month] 
ORDER BY m.department, m.[month], m.[year]