日期范围内的平均每月计数

时间:2014-06-09 20:03:07

标签: sql-server date

考虑下表。 Start_Date是将水果收入库存的时间,End_Date是处理它们的时间。请注意,这些日期范围可以重叠。例如,从2014-01-016到2014-02-01,库存中有20个苹果。

+------------------------------------------+
|           [HISTORY_TABLE]                |
+------------+------------+----------+-----+
| Start_Date | End_Date   | Type     | QTY |
+------------+------------+----------+-----+
| 2013-12-16 | 2014-02-01 | Apple    | 12  |
| 2014-01-16 | 2014-06-01 | Apple    |  8  |
| 2014-01-16 | 2014-04-11 | Banana   |  5  |
| 2014-03-16 | 2014-04-16 | Banana   |  7  |
| 2014-02-16 | 2014-03-01 | Orange   | 24  |
| 2013-02-24 | 2014-05-01 | Orange   |  2  |
+------------+------------+----------+-----+

我感兴趣的是每种水果类型的平均每月计数。对于1月份的苹果,每日累计苹果总数为:

((12 apples * 15 days) + (20 apples * 16 days) = 
(180 apple days + 320 apple days) = 500 apple days

1月份有31天,平均值为:

500 / 31 = 16.13

因此,1月份平均可用的苹果是16.13。

可能存在具有相同日期,类型和数量的水果类型,但假设每个记录是水果的唯一计数。我正在寻找的最终结果将类似于下面(除了所有的水果。)结果中的数字是准确的(至少我认为它们是......我手工计算):

+-----------------------------------------+
|  [RESULTS]                              |
+-------+------+------------+-----+-------+
| Month | Year | Fruit Type | QTY |  Avg  |
+-------+------+------------+-----+-------+
|  12   | 2013 | Apple      | 192 |  6.19 |
|  01   | 2014 | Apple      | 500 | 16.13 |
|  02   | 2014 | Apple      | 236 |  8.43 |
|  03   | 2014 | Apple      | 248 |  8.00 |
|  04   | 2014 | Apple      | 240 |  8.00 |
|  05   | 2014 | Apple      | 248 |  8.00 |
|  06   | 2014 | Apple      |   8 |  0.27 |
+-------+------+------------+-----+-------+

设置初始数据的一些代码:

IF OBJECT_ID('tempdb..#LocalTempFruitTable', 'U') IS NOT NULL
    DROP TABLE #LocalTempFruitTable

CREATE TABLE #LocalTempFruitTable(
    Start_Date date,
    End_Date date,
    Type varchar(50),
    QTY int)

INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2013-12-16','2014-02-01','Apple','12')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-01-16','2014-06-01','Apple','8')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-01-16','2014-04-11','Banana','5')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-03-16','2014-04-16','Banana','7')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-02-16','2014-03-01','Orange','24')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2013-02-24','2014-05-01','Orange','2')

SELECT * FROM #LocalTempFruitTable

3 个答案:

答案 0 :(得分:2)

当您使用日期时,您可以利用这些事实并非如此。单独跟踪它们是完全可行的。创建并填充涵盖所需范围的日期表,例如:

CREATE TABLE date_list ([date] date PRIMARY KEY NOT NULL);

INSERT date_list([date])  
SELECT TOP 1000  --This is a quick-and-dirty example
  DATEADD(day,ROW_NUMBER() OVER(ORDER BY(SELECT NULL))-1,'2013-01-01')
FROM master.dbo.spt_values;

然后使用它来协助主查询:

WITH daily_tally AS (
  SELECT
    [date],
    [Type],
    SUM(Qty) AS [daily_Qty]
  FROM date_list
  INNER JOIN Results ON [date] BETWEEN [Start_Date] AND [End_Date]
  GROUP BY [date],[Type]
)
SELECT
  MONTH([date]) AS [month],
  YEAR([date]) AS [year],
  [Type],
  AVG([daily_Qty]) AS [avg_Qty]
FROM daily_tally
GROUP BY MONTH([date]),YEAR([date]),[Type]

答案 1 :(得分:0)

这就是我最终得到的......我们已经创建了一个在两个日期范围之间每天返回的函数:

/****** Object:  UserDefinedFunction [dbo].[udf_SpanOfDates]    Script Date: 06/09/2014 16:35:52 ******/
SET ANSI_NULLS OFF
GO

SET QUOTED_IDENTIFIER ON
GO


CREATE FUNCTION [dbo].[udf_SpanOfDates]
(
    @StartDate Datetime,
    @EndDate DateTime
)

RETURNS
    @Dates TABLE
(
    Date DateTime
    , Month_MM integer
    , Quarter_QQ integer
    , Year_CCYY integer
)
AS
BEGIN
While @StartDate <= @EndDate
    begin
        insert @Dates 
        (   Date
            , Month_MM
            , Quarter_QQ
            , Year_CCYY
        ) 
        Values 
        (   @StartDate
            , DATEPART(M,@StartDate)
            , DATEPART(Q,@StartDate)
            , DATEPART(YYYY,@StartDate)
        )
        Set @StartDate = @StartDate + 1
    end

RETURN

END
GO

通过该功能,我能够做到这一点:

IF OBJECT_ID('tempdb..#LocalTempFruitTable', 'U') IS NOT NULL
    DROP TABLE #LocalTempFruitTable

CREATE TABLE #LocalTempFruitTable(
    Start_Date date,
    End_Date date,
    Type varchar(50),
    QTY int)

INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2013-12-16','2014-02-01','Apple','12')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-01-16','2014-06-01','Apple','8')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-01-16','2014-04-11','Banana','5')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-03-16','2014-04-16','Banana','7')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2014-02-16','2014-03-01','Orange','24')
INSERT INTO #LocalTempFruitTable (Start_Date,End_Date,Type,QTY) VALUES ('2013-02-24','2014-05-01','Orange','2')

SELECT Month_MM,YEAR_CCYY,Type,SUM(QTY)
FROM    dbo.udf_SpanOfDates('2013-12-01', '2014-04-01') sD
INNER JOIN #LocalTempFruitTable fruit
    ON (fruit.Start_Date <= sD.Date
            AND
            (fruit.END_DATE >= sD.Date
                OR fruit.End_Date IS NULL)
        )
GROUP BY MONTH_MM,YEAR_CCYY,Type
ORDER BY YEAR_CCYY,Month_MM,Type

结果:

MM  YEAR    Type    FruitDays
12  2013    Apple   192
12  2013    Orange  62
1   2014    Apple   500
1   2014    Banana  80
1   2014    Orange  62
2   2014    Apple   236
2   2014    Banana  140
2   2014    Orange  368
3   2014    Apple   248
3   2014    Banana  267
3   2014    Orange  86
4   2014    Apple   8
4   2014    Banana  12
4   2014    Orange  2

此时,计算平均值是学术性的,因为我已经有了月份和“结果日”。

答案 2 :(得分:0)

DECLARE @MyTable TABLE
(
 Start_Date DATETIME,
 End_Date DATETIME,
 Type VARCHAR(20),
 Qty DECIMAL(19,6)
)

INSERT INTO @MyTable
( Start_Date, End_Date, Type, Qty )
VALUES
( '12/16/2013', '02/01/2014', 'Apple', 12),
( '01/16/2014', '06/01/2014', 'Apple', 8),
( '01/16/2014', '04/11/2014', 'Banana', 5),
( '03/16/2014', '04/16/2014', 'Banana', 7),
( '02/16/2014', '03/01/2014', 'Orange', 24),
( '02/24/2013', '05/01/2014', 'Orange', 2);

DECLARE @MinDate DATETIME
SELECT @MinDate = MIN(Start_Date) FROM @MyTable
DECLARE @MaxDate DATETIME
SELECT @MaxDate = MAX(End_Date) FROM @MyTable


DECLARE @number_of_numbers INT = 100000;

;WITH
a AS (SELECT 1 AS i UNION ALL SELECT 1),
b AS (SELECT 1 AS i FROM a AS x, a AS y),
c AS (SELECT 1 AS i FROM b AS x, b AS y),
d AS (SELECT 1 AS i FROM c AS x, c AS y),
e AS (SELECT 1 AS i FROM d AS x, d AS y),
f AS (SELECT 1 AS i FROM e AS x, e AS y),
numbers AS 
(
    SELECT TOP(@number_of_numbers)
    ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS number
    FROM f
)


SELECT d.Type, MONTH(d.CheckDate), YEAR(d.CheckDate), AVG(D.Qty) FROM
(
SELECT c.CheckDate, m.Type, SUM(m.Qty) Qty FROM
(
    SELECT DATEADD(DAY, n.number, '1/1/2000') AS CheckDate FROM numbers n
) C
LEFT JOIN @MyTable m
    ON c.CheckDate >= m.Start_Date and c.CheckDate <= m.End_Date
WHERE c.CheckDate >= @MinDate AND c.CheckDate <= @MaxDate
GROUP BY c.CheckDate, m.Type
) AS d
GROUP BY d.Type, MONTH(d.CheckDate), YEAR(d.CheckDate)