SQL: количество строк с момента появления определенного значения: продолжайте считать - PullRequest
0 голосов
/ 25 октября 2018

Этот сценарий похож на SQL: количество строк с момента появления определенного значения

В SQL Server я пытаюсь рассчитать количество дней с той же погоды, что исегодня (предположим, что сегодня 6 августа 2018 года) наблюдалось впервые за последние 5 дней.По городам.

Вот данные:

+---------+---------+--------+--------+--------+
| Date    | Toronto | Cairo  | Zagreb | Ankara |
+---------+---------+--------+--------+--------+
| 1.08.18 | Rain    | Sun    | Clouds | Sun    |
| 2.08.18 | Sun     | Sun    | Clouds | Sun    |
| 3.08.18 | Rain    | Sun    | Clouds | Rain   |
| 4.08.18 | Clouds  | Sun    | Clouds | Clouds |
| 5.08.18 | Rain    | Clouds | Rain   | Rain   |
| 6.08.18 | Rain    | Sun    | Sun    | Sun    |
+---------+---------+--------+--------+--------+

Это должно работать хорошо, но все, что я придумал, - это отдельные запросы для каждого города (и их будут десятки).города, а не только четыре).Это работает, но не масштабируется.

Вот пример для Торонто ...

SELECT 
    DATEDIFF(DAY, MIN([Date]), GETDATE()) + 1 
FROM
    (SELECT TOP 5 * 
     FROM Weather 
     WHERE [Date] <= GETDATE()
     ORDER BY [Date] DESC) a
WHERE 
    Toronto = (SELECT TOP 1 Toronto 
               FROM Weather
               WHERE DataDate = GETDATE())

..., который правильно возвращает 4, так как сегодня идет дождь и первое появление дождяв течение последних 5 дней было 3 августа.

Но я хочу получить следующую таблицу:

+---------+-------+--------+--------+
| Toronto | Cairo | Zagreb | Ankara |
+---------+-------+--------+--------+
| 4       | 5     | 1      | 5      |
+---------+-------+--------+--------+

Слегка изменен из принятого ответа @Used_By_. Уже этот код:

CREATE TABLE mytable(
   Date    date  NOT NULL
  ,Toronto VARCHAR(9) NOT NULL
  ,Cairo   VARCHAR(9) NOT NULL
  ,Zagreb  VARCHAR(9) NOT NULL
  ,Ankara  VARCHAR(9) NOT NULL
);

INSERT INTO mytable(Date,Toronto,Cairo,Zagreb,Ankara) VALUES ('20180801','Rain','Sun','Clouds','Sun');
INSERT INTO mytable(Date,Toronto,Cairo,Zagreb,Ankara) VALUES ('20180802','Sun','Sun','Clouds','Sun');
INSERT INTO mytable(Date,Toronto,Cairo,Zagreb,Ankara) VALUES ('20180803','Rain','Sun','Clouds','Rain');
INSERT INTO mytable(Date,Toronto,Cairo,Zagreb,Ankara) VALUES ('20180804','Clouds','Sun','Clouds','Clouds');
INSERT INTO mytable(Date,Toronto,Cairo,Zagreb,Ankara) VALUES ('20180805','Rain','Clouds','Rain','Rain');
INSERT INTO mytable(Date,Toronto,Cairo,Zagreb,Ankara) VALUES ('20180806','Rain','Sun','Sun','Sun');

with cte as (
        select
              date, city, weather
        FROM (
              SELECT * from mytable
             ) AS cp
        UNPIVOT (
                  Weather FOR City IN (Toronto, Cairo, Zagreb, Ankara)
            ) AS up
    )

select 
        date, city, weather, datediff(day,ca.prior,cte.date)+1 as daysPresent
from cte
cross apply (
    select min(prev.date) as prior
    from cte as prev 
    where prev.city = cte.city
    and prev.date between dateadd(day,-4,cte.date) and dateadd(day,0,cte.date)
    and prev.weather = cte.weather
    ) ca

order by city,date

Вывод:

Output

Однако сейчас я пытаюсь продолжать считать «daysPresent» даже после этих пятипрошедшие дни под вопросом.Это означает, что последняя отмеченная строка в выходной выборке должна показывать 6. Логика заключается в том, чтобы увеличить предыдущее число на количество дней между ними, если между ними меньше 5 дней.Если за последние 5 дней не было такой же погоды, вернитесь к 1.

Я экспериментировал с LEAD и LAG, но не могу заставить его работать.Это даже правильный способ добавить еще один слой к нему, или запрос должен выглядеть совсем иначе?

Я просто озадачен.

1 Ответ

0 голосов
/ 26 октября 2018

У вас серьезная проблема с вашей структурой данных.Значения должны быть в строках , а не столбцах .Итак, начнем с:

select d.dte, v.*from data d cross apply
     (values ('Toronto', Toronto), ('Cairo', Cairo), . . .
     ) v(city, val)
where d.date >= dateadd(day, -5, getdate());

Оттуда мы можем использовать оконную функцию first_value() (или last_value()), чтобы получить самое последнее чтение.Остальное - просто агрегация по городам:

with d as (
      select d.dte, v.*,
             first_value(v.val) over (partition by v.city order by d.dte desc) as last_val
      from data d cross apply
           (values ('Toronto', Toronto), ('Cairo', Cairo), . . .
           ) v(city, val)
      where d.date >= dateadd(day, -5, getdate())
     )
select city, datediff(day, min(dte), getdate()) + 1
from d
where val = last_val
group by city;

Это дает вам необходимую информацию в строках, а не в столбцах.Вы можете повторно развернуть, если вы действительно хотите.Но я советую вам хранить данные с городскими данными в разных строках.

...