Как получить последовательный номер лота в зависимости от диапазона? - PullRequest
0 голосов
/ 20 октября 2019

У меня есть таблица, подобная этой:

LotNumber
1065
1026
95092
95090
95089
95088
85087
95086
95085
95084
95083
95082
95081

Как должен выглядеть мой запрос SQL, чтобы получить последовательный номер лота на основе диапазона (первое совпадение).

Например, вернуть 3 в диапазоне, (95090, 95089, 95088) возвращают 5 в диапазоне (95086, 95085, 95084, 95083, 95082)

Ответы [ 3 ]

1 голос
/ 21 октября 2019

Это сложный вариант проблемы пробелов и островов, поскольку:

  • вам нужны все элементы в группе (не только начало и конец)
  • вам нужны только элементы первой группы, удовлетворяющие условию

Следующий запрос выполняет то, что вы ожидаете:

select lotNumber
from (
    select 
        lotNumber,
        cnt,
        dense_rank() over(order by grp) rn
    from (
        select 
            lotNumber,
            grp,
            count(*) over(partition by grp) cnt
        from (
            select 
                lotNumber,
                sum(case when lotNumber = lagLotNumber - 1 then 0 else 1 end) 
                    over(order by id) grp
            from (
                select
                    id,
                    lotNumber, 
                    lag(lotNumber) over(order by id) lagLotNumber
                from mytable
            ) t
        ) t
    ) t
    where cnt >= 3
) t
where rn = 1

Предложение where cnt >= 3 можно использовать для управления цельюдлина последовательных чисел.

Демонстрация на DB Fiddle

| lotnumber |
| --------- |
| 95090     |
| 95089     |
| 95088     |

Пошаговые пояснения

Для начала, это может быть решено только , если существует столбец, который можно использовать для упорядочения записей (в таблицах sql записи по умолчанию неупорядочены). Я предполагаю, что такой столбец существует и называется id. Вот набор данных:

| lotnumber | id  |
| --------- | --- |
| 1065      | 1   |
| 1026      | 2   |
| 95092     | 3   |
| 95090     | 4   |
| 95089     | 5   |
| 95088     | 6   |
| 85087     | 7   |
| 95086     | 8   |
| 95085     | 9   |
| 95084     | 10  |
| 95083     | 11  |
| 95082     | 12  |
| 95081     | 13  |

1) Первый шаг состоит в восстановлении предыдущих lotNumber каждой записи. Для этого мы используем lag().

select
    id,
    lotNumber, 
    lag(lotNumber) over(order by id) lagLotNumber
from mytable

| id  | lotnumber | laglotnumber |
| --- | --------- | ------------ |
| 1   | 1065      |              |
| 2   | 1026      | 1065         |
| 3   | 95092     | 1026         |
| 4   | 95090     | 95092        |
...

2) Затем мы используем накопленную сумму для помещения записей в группы, где номера лотов являются последовательными. Когда две записи не являются последовательными, начинается новая группа:

select 
    lotNumber,
    sum(case when lotNumber = lagLotNumber - 1 then 0 else 1 end) 
        over(order by id) grp
from (
    ... above query ...
) t

| lotnumber | grp |
| --------- | --- |
| 1065      | 1   |
| 1026      | 2   |
| 95092     | 3   |
| 95090     | 4   |
| 95089     | 4   |
| 95088     | 4   |
| 85087     | 5   |
| 95086     | 6   |
| 95085     | 6   |
| 95084     | 6   |
| 95083     | 6   |
| 95082     | 6   |
| 95081     | 6   |

3) Следующий шаг состоит в подсчете количества записей в каждой группе с количеством окон

select 
    lotNumber,
    grp,
    count(*) over(partition by grp) cnt
from (
    ... above query ...
) t;

| lotnumber | grp | cnt |
| --------- | --- | --- |
| 1065      | 1   | 1   |
| 1026      | 2   | 1   |
| 95092     | 3   | 1   |
| 95090     | 4   | 3   |
| 95089     | 4   | 3   |
| 95088     | 4   | 3   |
| 85087     | 5   | 1   |
| 95086     | 6   | 6   |
| 95085     | 6   | 6   |
| 95084     | 6   | 6   |
| 95083     | 6   | 6   |
| 95082     | 6   | 6   |
| 95081     | 6   | 6   |

4) Имея эту информацию под рукой, мы можем теперь фильтровать группы, которые имеют как минимум целевое количество последовательных записей. В то же время мы ранжируем группы по возрастанию номера лота. Условие фильтрации where cnt >= 3 может быть изменено по мере необходимости для управления целевым числом последовательных записей.

Здесь у нас есть две группы по крайней мере с 3 последовательными числами:

select 
    lotNumber,
    cnt,
    dense_rank() over(order by grp) rn
from (
    ... above query ...
) t
where cnt >= 3;

| lotnumber | cnt | rn  |
| --------- | --- | --- |
| 95090     | 3   | 1   |
| 95089     | 3   | 1   |
| 95088     | 3   | 1   |
| 95086     | 6   | 2   |
| 95085     | 6   | 2   |
| 95084     | 6   | 2   |
| 95083     | 6   | 2   |
| 95082     | 6   | 2   |
| 95081     | 6   | 2   |

5)Последний шаг заключается в фильтрации первой записи в каждой группе.

select lotNumber
from (
    ... above query ...
) t
where rn = 1

| lotnumber |
| --------- |
| 95090     |
| 95089     |
| 95088     |
1 голос
/ 21 октября 2019

Если у вас есть значение n, вы можете использовать lead() для этого. Чтобы получить первое в серии:

select t.*
from (select t.*, 
             lead(lotnumber, <n> - 1) over (order by lotnumber) as n_after
      from t
     ) t
where n_after - lotnumber = <n> - 1;

Postgres позволяет смещению lag() / lead() быть нулевым, так что это работает даже при n = 1.

Здесь - это дБ <> скрипка.

0 голосов
/ 21 октября 2019

Используйте row_number для заказа по

   Select 
          LotNumber, 
          row_number() over 
      (partition by 

 Substr(3,length(LotNumber),lotnumber) 
        order by 
        LotNumber ) rn
       from table  order by rn;
...