获取每月第一天的价值

时间:2018-11-30 14:00:39

标签: sql sql-server tsql date greatest-n-per-group

我有每种产品库存的每周数据。我想按年-月对其进行分组,并获得每个月的第一个值。换句话说,我想获取每个月的期初存货,而不管该月的哪一天。

+------------+---------+
|   MyDate   | MyValue |
+------------+---------+
| 2018-01-06 |       2 |*
| 2018-01-13 |       7 |
| 2018-01-20 |       5 |
| 2018-01-27 |       2 |
| 2018-02-03 |       3 |*
| 2018-02-10 |      10 |
| 2018-02-17 |       6 |
| 2018-02-24 |       4 |
| 2018-03-03 |       7 |*
| 2018-03-10 |       5 |
| 2018-03-17 |       3 |
| 2018-03-24 |       4 |
| 2018-03-31 |       6 |
+------------+---------+

所需结果:

+----------------+---------+
| FirstDayOfMonth| MyValue |
+----------------+---------+
| 2018-01-01     |       2 |
| 2018-02-01     |       3 |
| 2018-03-01     |       7 |
+----------------+---------+

我认为这可能有效,但无效。

select 
    [product],
    datefromparts(year([MyDate]), month([MyDate]), 1),
    FIRST_VALUE(MyValue) OVER (PARTITION BY [Product], YEAR([MyDate]), MONTH([MyDate]) ORDER BY [MyDate] ASC) AS MyValue
from 
    MyTable 
group by 
    [Product],
    YEAR([MyDate]), MONTH([MyDate])

修改。谢谢。我问题中的重点不是如何获得每月的第一天。我知道有不同的技术。

重音是如何获得当月的FIRST值(期初存货)。如果有机会一口气拿下期末存货,那就太好了。基于ROW_NUMBER的答案不允许一口气收尾,需要两次加入。

接受答案后进行编辑
请考虑约翰·卡佩莱蒂(John Cappelletti)的回答,以代替被接受的回答:https://stackoverflow.com/a/53559750/1903793

4 个答案:

答案 0 :(得分:3)

您可以使用offsetTopapply查找月份的最后一天并添加一天:

eomonth

答案 1 :(得分:3)

如果您选择了窗口函数路径,则实际上并不需要GROUP BY

SELECT Product, DATEADD(DAY, 1, EOMONTH(MyDate, -1)) AS Month, MyValue
FROM (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY Product, DATEADD(DAY, 1, EOMONTH(MyDate, -1)) ORDER BY MyDate) AS rn
    FROM t
) AS x
WHERE rn = 1

更新

要获取该月的最后一行,只需执行UNION ALL <above query>,但将order by子句更改为ORDER BY MyDate DESC。这将使您每个产品每月两行。

答案 2 :(得分:1)

还可以使用rowNumber和cte。

DEMO

WITH CTE as (
SELECT '2018-01-06' myDate,        2 Myvalue UNION ALL
SELECT '2018-01-13',        7 UNION ALL
SELECT '2018-01-20',        5 UNION ALL
SELECT '2018-01-27',        2 UNION ALL
SELECT '2018-02-03',        3 UNION ALL
SELECT '2018-02-10',       10 UNION ALL
SELECT '2018-02-17',        6 UNION ALL
SELECT '2018-02-24',        4 UNION ALL
SELECT '2018-03-03',        7 UNION ALL
SELECT '2018-03-10',        5 UNION ALL
SELECT '2018-03-17',        3 UNION ALL
SELECT '2018-03-24',        4 UNION ALL
SELECT '2018-03-31',        6),

CTE2 as (SELECT *
              , Row_Number() over (partition by DATEADD(month, DATEDIFF(month, 0, MyDate), 0) order by myDate) RN   
         FROM CTE)

SELECT DATEADD(month, DATEDIFF(month, 0, MyDate), 0), MyValue 
FROM cte2 
WHERE RN = 1

给我们:

+----+---------------------+---------+
|    |  (No column name)   | MyValue |
+----+---------------------+---------+
|  1 | 01.01.2018 00:00:00 |       2 |
|  2 | 01.02.2018 00:00:00 |       3 |
|  3 | 01.03.2018 00:00:00 |       7 |
+----+---------------------+---------+

答案 3 :(得分:1)

另一个选择是使用WITH TIES,然后在日期上作些欺骗

示例

Select top 1 with ties 
       MyDate = convert(varchar(7),MyDate,120)+'-01'
      ,MyValue
 from  YourTable
 Order By Row_Number() over (Partition By convert(varchar(7),MyDate,120) Order By MyDate)

返回

MyDate      MyValue
2018-01-01  2
2018-02-01  3
2018-03-01  7