Создать диапазон дат

Я пытаюсь создать диапазон дат на основе определенных условий.

Мои данные имеют поставщика, upc, invdate и цену. Я хочу создать 1 строку, показывающую поставщика, оптовую цену, цену, дату начала, дату окончания, если разрыв между 1 датой поступления и следующей у любого поставщика, акционной ценой, ценовой точкой составляет менее 4 дней.

Мы только недавно перешли на SQL Server 2016, и я не уверен, что это лучший/самый простой способ сделать это.

образец

Заранее спасибо.

Результаты должны быть:

183020   2840000211 1.47  12/23/16 - 12/26/16
183020   2840000211 1.39  12/31/16 - 01/03/17
183020   2840000211 1.39  01/09/17 - 01/16/17

person Joel    schedule 29.11.2018    source источник
comment
показать образцы данных   -  person nikhil sugandh    schedule 29.11.2018
comment
поставь вопрос и выдели все нажми CTRL+K   -  person nikhil sugandh    schedule 29.11.2018
comment
Что делать, если цена изменилась, но датировка меньше 4 дней.   -  person Salman A    schedule 29.11.2018
comment
Не размещайте образцы данных в виде изображений. Предоставьте его в виде текста.   -  person Stanislovas Kalašnikovas    schedule 29.11.2018
comment
если цена изменится ИЛИ будет разрыв более 4 дней, мне нужна новая линия.   -  person Joel    schedule 29.11.2018
comment
Привет, Джоэл, обычно ваш инструмент отчетности, такой как SSRS, заботится об этом запросе (разбивая его на новую строку). Лучшее, что вы можете сделать, это использовать LAG, чтобы определить inv_date предыдущей записи и сравнить предыдущую с текущей inv_date, чтобы определить, больше ли она или равна 4, какие SSRS затем могут использовать и разбить на новую строку, включая хорошие группировки и т. д.   -  person zuckerburg    schedule 29.11.2018


Ответы (2)


Это идеальное пробелы-и -islands, где каждая запись в желаемом выводе представляет остров (где net_cost не изменяется, и у вас есть inv_date моментальных снимков, подтверждающих это менее чем через 4 дня после предыдущего). Пробелы — это значения net_cost / inv_date, которые не представлены (например, мы не знаем, каким был net_cost на inv_date из 2016-12-27 по 2016-12-30).

Следующий запрос делает три вещи:

  1. Определяет пробелы (столбец is_gap_start), проверяя, есть ли нарушения 2 ограничений, изложенных в вопросе (изменение в net_cost или разрыв в 4 дня или более значений inv_date)
  2. Присваивает номер острова (столбец island_nbr), используя промежуточную сумму обнаруженных пропусков.
  3. Определяет значения, отображаемые для каждого острова в конечном результате.

Ответ:

select b.vnd_nbr
, b.upc_nbr
, b.net_cost
, min(b.inv_date) as inv_date_bgn
, max(b.inv_date) as inv_date_end
from (
    select a.vnd_nbr
    , a.upc_nbr
    , a.inv_date
    , a.net_cost
    --determine the island number
    , sum(a.is_gap_start) over (partition by a.vnd_nbr, a.upc_nbr order by a.inv_date asc rows between unbounded preceding and 1 preceding) as island_nbr
    from (
        select t.vnd_nbr
        , t.upc_nbr
        , t.inv_date
        , t.net_cost
        --check if the next row meets either condition to start new date range (4 day gap, change in net_cost)
        , case when datediff(d, t.inv_date, lead(t.inv_date, 1, t.inv_date) over (partition by t.vnd_nbr, t.upc_nbr order by t.inv_date asc)) >= 4 
                    or t.net_cost <> lead(t.net_cost, 1, t.net_cost) over (partition by t.vnd_nbr, t.upc_nbr order by t.inv_date asc)
            then 1 
            else 0 
            end as is_gap_start
        from data_table as t
        ) as a
    ) as b
group by b.vnd_nbr
, b.upc_nbr
, b.net_cost
, isnull(b.island_nbr, 0) --forces each island to be a separate row
order by b.vnd_nbr
, b.upc_nbr
, isnull(b.island_nbr, 0)

Результаты:

+---------+------------+----------+--------------+--------------+
| vnd_nbr |  upc_nbr   | net_cost | inv_date_bgn | inv_date_end |
+---------+------------+----------+--------------+--------------+
|  183020 | 2840000211 | 1.47     | 2016-12-23   | 2016-12-26   |
|  183020 | 2840000211 | 1.39     | 2016-12-31   | 2017-01-03   |
|  183020 | 2840000211 | 1.39     | 2017-01-09   | 2017-01-16   |
+---------+------------+----------+--------------+--------------+
person tarheel    schedule 30.11.2018
comment
Вау, спасибо! это на самом деле очень полезно узнать, мой ответ сейчас кажется таким неправильным! ржунимагу - person zuckerburg; 30.11.2018

Это то, что я придумал, и НЕ РЕШАЕТ ВАШУ ПРОБЛЕМУ ТОЧНО, но я чувствую, что могу опубликовать этот код, по крайней мере, чтобы вы поработали над остальными и, возможно, получили ответ, с которым вы можете работать (имея в виду мой комментарий)

DECLARE @TableVar TABLE 
  ( 
     vnd_nbr  INT, 
     upc_nbr  VARCHAR(10), 
     inv_date DATE, 
     net_cost DECIMAL(16, 2) 
  ) 

INSERT INTO @TableVar 
            (vnd_nbr, 
             upc_nbr, 
             inv_date, 
             net_cost) 
VALUES      (183020, 
             '2840000211', 
             '23-Dec-2016', 
             1.47), 
            (183020, 
             '2840000211', 
             '26-Dec-2016', 
             1.47), 
            (183020, 
             '2840000211', 
             '31-Dec-2016', 
             1.39), 
            (183020, 
             '2840000211', 
             '2-Jan-2017', 
             1.39), 
            (183020, 
             '2840000211', 
             '3-Jan-2017', 
             1.39), 
            (183020, 
             '2840000211', 
             '09-Jan-2017', 
             1.39), 
            (183020, 
             '2840000211', 
             '12-Jan-2017', 
             1.39), 
            (183020, 
             '2840000211', 
             '13-Jan-2017', 
             1.39), 
            (183020, 
             '2840000211', 
             '14-Jan-2017', 
             1.39), 
            (183020, 
             '2840000211', 
             '16-Jan-2017', 
             1.39) 

SELECT vnd_nbr, 
       upc_nbr, 
       net_cost, 
       Min(Isnull(previous_date, inv_date)) AS StartDate, 
       Max(inv_date)                        AS EndDate 
FROM   (SELECT vnd_nbr, 
               upc_nbr, 
               inv_date, 
               net_cost, 
               previous_date, 
               CASE 
                 WHEN Datediff(day, Isnull(previous_date, inv_date), inv_date) < 
                      4 THEN 
                 0 
                 ELSE 1 
               END GreaterThanFourDays 
        FROM   (SELECT vnd_nbr, 
                       upc_nbr, 
                       inv_date, 
                       net_cost, 
                       Lag(inv_date, 1, NULL) 
                         OVER ( 
                           partition BY vnd_nbr, upc_nbr, net_cost 
                           ORDER BY inv_date) AS previous_date 
                FROM   @TableVar) r) rr 
GROUP  BY greaterthanfourdays, 
          vnd_nbr, 
          upc_nbr, 
          net_cost 
ORDER  BY Min(inv_date) 

Вышеприведенное выводит это:

vnd_nbr upc_nbr net_cost    StartDate   EndDate
183020  2840000211  1.47    2016-12-23  2016-12-26
183020  2840000211  1.39    2016-12-31  2017-01-16
183020  2840000211  1.39    2017-01-03  2017-01-09

Дата начала/окончания не совпадает, но разбивает ее, если она превышает 4 дня/учитывает net_cost.

Как я уже сказал, это не решает вашу проблему в точности, но дает вам представление о том, как вы можете делать то, о чем просите.

person zuckerburg    schedule 29.11.2018
comment
Спасибо. Я посмотрю, смогу ли я сделать эту работу для того, что мне нужно. - person Joel; 29.11.2018