我有一个表(T1),其中包含以下列:department,dateofsale,totalsales。 我想要实现的是从开始日起一年内每月销售部门,并向后退一年。 也许以下查询将更好地展示我想要实现的目标。
-- 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
问题是我还想要总数为零的月份。所以结果必须是:
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
我该怎么做?
答案 0 :(得分:2)
您可以创建一个表Months并使用它进行左连接
SELECT *
FROM Months M
LEFT JOIN T1 T ON M.month = T.Month
答案 1 :(得分: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
答案 2 :(得分:1)
您可以创建两个查询并对它们进行UNION,或者为了制作缺失的行,使用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函数从销售日期中提取月份。上面的查询只是为了向您展示如何获得数据中没有的月份。
答案 3 :(得分: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
上述唯一的问题是部门在临时表创建时是硬编码的,可以作为参数传递。
答案 4 :(得分:1)
数字表的一个重要用途:
-- 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]
答案 5 :(得分:0)
为每个月或每个部门插入一个零值。现在您的数据是明确的,您的查询也得到了简化。
假设缺少数据意味着零值不是一个好的数据实践。