Получить значение для первой даты в месяце - PullRequest
0 голосов
/ 30 ноября 2018

У меня есть еженедельные данные о каждом запасе товара.Я хочу сгруппировать его по годам и месяцам и получить первое значение каждого месяца.Другими словами, я хочу получать начальный запас каждого месяца, независимо от дня месяца.

+------------+---------+
|   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])

Редактировать .Спасибо.Акцент в моем вопросе не о том, как получить первый день месяца.Я знаю, что для этого есть разные методы.

Акцент на том, как получить ПЕРВОЕ значение в месяц (начальный запас).Если есть шанс получить закрытый запас за один выстрел - было бы здорово.Ответы, основанные на ROW_NUMBER, не позволяют получить итоговый запас за один выстрел, потребуют двух объединений.

Редактировать после принятия ответа
Пожалуйста, рассмотрите ответ Джона Каппеллетти как альтернативу принятому: https://stackoverflow.com/a/53559750/1903793

Ответы [ 4 ]

0 голосов
/ 30 ноября 2018

Еще одним вариантом является использование 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
0 голосов
/ 30 ноября 2018

Вам не нужно 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 MyDate DESC.Это даст вам две строки на месяц продукта.

0 голосов
/ 30 ноября 2018

Можно также использовать rowNumber и cte.

ДЕМО

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 |
+----+---------------------+---------+
0 голосов
/ 30 ноября 2018

Вы можете использовать apply & eomonth, чтобы найти последний день месяца и добавить один день:

select distinct dateadd(day, 1, eomonth(t1.mydate, -1)) as FistDayOfMonth, t1.myvalue
from table t cross apply
     ( select top (1) t1.mydate, t1.myvalue
       from table t1
       where t1.product = t.product and
             year(t1.MyDate) = year(t.MyDate) and month(t1.MyDate) = month(t.MyDate)
       order by t1.mydate
     ) t1;
...