7

我有一个包含以下列的表(T1):部门、销售日期、总销售额。我想要实现的是从开始日期开始的一年内每月获得部门的销售额并向后退一年。也许以下查询会更好地显示我想要实现的目标。

-- 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

我怎样才能做到这一点?

4

6 回答 6

2

你可以创建一个表 Months 并用它做一个左连接

SELECT *
FROM Months M
LEFT JOIN T1 T ON M.month = T.Month
于 2013-02-22T13:38:32.203 回答
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
于 2013-02-22T14:14:42.007 回答
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 函数从您的销售日期中提取月份。上面的查询只是为了向您展示如何获取数据中没有的月份。

于 2013-02-22T13:35:33.770 回答
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]
于 2013-02-22T15:17:15.040 回答
1

当我遇到这个问题时,我设法解决这个问题的方法是创建一个临时表,创建所有必需的日期,然后UNION在临时表和 select 语句中的数据查询之间执行:

-- 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

上面唯一的问题是部门在临时表创建中是硬编码的,但可以作为参数传递。

于 2013-02-22T14:09:23.857 回答
0

为每个月或日期和每个部门插入一个零值。现在您的数据是明确的,您的查询也得到了简化。

假设没有数据意味着零值不是一个好的数据实践。

于 2013-02-22T15:08:53.443 回答