Определите число строк по идентификатору, которые имеют последовательные значения в другом столбце, причем наибольшее из этих значений равно конкретному значению - PullRequest
0 голосов
/ 03 декабря 2018

https://www.db -fiddle.com / f / 2bzoKxbU2gznwwmQpMmjp5 / 0

(фактическая база данных - Microsoft SQL Server 2014)

Выше приведено описание того, что яя пытаюсь сделать.

CREATE TABLE IF NOT EXISTS table1 (
  id nvarchar(5) NOT NULL,
  year int(4) NOT NULL,
  PRIMARY KEY (id,year)
  );
INSERT INTO table1 (id, year) VALUES
  ('A', '2013'),
  ('A', '2014'),
  ('A', '2017'),
  ('A', '2018'),
  ('B', '2016'),
  ('B', '2017'),
  ('B', '2018'),
  ('C', '2016'),
  ('D', '2014'),
  ('D', '2016'),
  ('D', '2018');

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

Пока что мой мыслительный процесс выглядит так:

select id, count(*)
from table1
group by id;

select main.id,
    case when in_2018.id is not null
        then count(*) 
    else 0
    end
from table1 as main
left join table1 as in_2018
    on in_2018.id = main.id
    and
        in_2018.year = 2018
group by main.id;

/*
    Want a table:
    A | 2
    B | 3
    C | 0
    D | 1

    Count of records that are in a single-step incremental that include 2018     by id
*/

Очевидно, что они не возвращают последовательные строки, а просто подсчитывают, которые соответствуют критериям «2018».

Я попробовал другой подход, который проверяет:

case when count(*) = max(year) - min(year) +1,

В моем примере данных это работает для идентификатора B, потому что все данные B являются последовательными, но это не относится к нарушенной схемедругие идентификаторы.

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Я вижу, что Гордон побил меня этим, и с запросом, который немного короче.Но я зашел так далеко, я все равно отправлю.Я думаю, что общая идея более или менее такая же, но моя не зависит от каких-либо нестандартных функций (я думаю), и я надеюсь, что восполнил дополнительный код, добавив некоторые комментарии, чтобы сделать его еще длиннее.;-)

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

select
  id,
  max(span) as nr_of_years
from
  ( -- This inner query gives all the valid ranges, but they have to be deduplicates
    -- For instance, it can give B 2017-2018 while there is also B 2016-2018, which has precedence.
    -- That's why the outer query uses max, to get the longest range
    select
      s.id,
      s.year,
      s.otheryear,
      s.span,
      s.rows_in_span
    from
      ( -- Find all possible 'spans' of years between two rows with the same id.
        -- also find how much rows are in that span. They should match.
        select 
          a.id, 
          a.year,
          b.year as otheryear,
          a.year - b.year + 1 as span,
          ( select count(*) from table1 c
            where
              c.id = a.id and
              c.year >= b.year and
              c.year <= a.year) as rows_in_span
        from
          table1 a
          join table1 b on b.ID = a.ID and b.year <= a.year -- like a cross join, but per ID
      ) s
    where
      -- if they are not equal, it means one year is missing between the lowest and highest year in the span
      s.span = s.rows_in_span and
      -- If the difference between the year and 2018 is more than this, this is a range, but it's out of scope
      abs(s.year - 2018) < s.span
  ) f
group by
  f.id

В скрипте вы видитеэто работает и для Postgres (Вы можете просто переключаться между базами данных, я просто перебрал оператор create, чтобы разрешить это):

DB Fiddle

0 голосов
/ 03 декабря 2018

В SQL Server вы могли бы решить эту проблему, используя row_number():

select top (1) id, count(*)
from (select t.*, row_number() over (partition by id order by year) as seqnum
      from table1 t
     ) t
group by id, (year - seqnum)
having sum(case when year = 2018 then 1 else 0 end) > 0
order by count(*) desc;

При этом используется наблюдение, что year - seqnum является постоянным, когда годы в последовательности.

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

select id, count(*)
from (select t.*,
             (select count(*)
              from table1 tt
              where tt.id = t.id and tt.year <= t.year
             ) as seqnum
      from table1 t
     ) t
group by id, (year - seqnum)
having sum(case when year = 2018 then 1 else 0 end) > 0
order by count(*) desc
fetch first 1 year only;

Здесь - это db <> fiddle.

...