Как пометить другой диапазон значений в SQL - PullRequest
1 голос
/ 23 января 2020

(решено) Скажите, что у меня есть таблица:

+ ---+-----+------+
| id | Low | High |
+ ---+-----+------+
| 1  |   3 |   10 |
| 1  |   2 |    9 |
| 1  |  11 |   14 |
| 2  |   3 |   10 |
+ ---+-----+------+

Как пометить элементы с одинаковым идентификатором и диапазоном значений перекрытия с помощью int (1,2,3, ...)? В этом случае новая таблица с тегом будет иметь вид:

+ ---+-----+------+-----+
| id | Low | High | tag |
+ ---+-----+------+-----+
| 1  |   3 |   10 | 1   |
| 1  |   2 |    9 | 1   |
| 1  |  11 |   14 | 2   |
| 2  |   3 |   10 | 1   |
+ ---+-----+------+-----+

, поскольку row2 и row1 имеют одинаковый идентификатор, а диапазон значений перекрывается, оба тега равны 1 для row3, поскольку диапазон значений (низкий , high) отличается от row1 и row2, его тег равен 2. Для row4, поскольку он имеет другой идентификатор с более чем 3 строками, его тег будет равен 1. Основы c logi c: внутри одного идентификатора нам просто нужно различить guish всех типов диапазонов значений и присвоить им теги (1,2,3 ...). мы определяем диапазон значений двух элементов должен быть присвоен один и тот же тег, если они перекрываются. Таким образом, диапазон значений (3,9), (3,10), (2,9) - это тег 1, потому что они перекрываются. для (11, 14), поскольку это совершенно другой диапазон значений (без перекрытия с другими), поэтому ему присваивается новый тег. Присвоение тега выполняется отдельно для каждого идентификатора. Я так растерялся, пожалуйста, помогите.

Ответы [ 3 ]

1 голос
/ 31 января 2020

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

WITH RECURSIVE cte AS
(
    SELECT id, Low, High, Low AS MinLow
    FROM YourTable
    UNION ALL
    SELECT cte.id, cte.Low, cte.High, yt.Low
    FROM cte
    INNER JOIN YourTable yt ON yt.id = cte.id AND cte.MinLow > yt.Low AND cte.MinLow <= yt.High
),
g AS
(
    SELECT id, Low, High, MIN(MinLow) AS MinLow
    FROM cte
    GROUP BY id, Low, High
)
SELECT id, Low, High, DENSE_RANK() OVER (PARTITION by id ORDER BY id, MinLow ASC) AS tag
FROM g
1 голос
/ 31 января 2020

Вы хотите объединить свои диапазоны по идентификатору. Допустим, данные для идентификатора 1:

+ ---+-----+------+
| id | Low | High |
+ ---+-----+------+
| 1  |   2 |    7 |
| 1  |   3 |    4 |
| 1  |   7 |    8 |
| 1  |   7 |   10 |
| 1  |  12 |   13 |
| 1  |  13 |   20 |
| 1  |  20 |   24 |
+ ---+-----+------+

. У нас есть два кластера: 2-10, 12-24. Недостаточно взглянуть на еще один ряд, скажем, на предыдущий, чтобы определить, находятся ли они в одном кластере. 3-4 и 7-8 могут показаться не связанными на первый взгляд, но они принадлежат к одному кластеру, поскольку они оба перекрываются с 2-7.

Я вижу итерационный процесс, где мы упорядочиваем строки по ID и Низкий и сохраняем самое высокое значение диапазона при переходе:

+ ---+-----+------+---------+----------------------------------+
| id | Low | High | Highest | Cluster                          |
+ ---+-----+------+---------+----------------------------------+
| 1  |   2 |    7 |       7 | 1 (start with #1)                |
| 1  |   3 |    4 |       7 | 1 (3 <= 7)                       |
| 1  |   7 |    8 |       8 | 1 (7 <= 7)                       |
| 1  |   7 |   10 |      10 | 1 (7 <= 8)                       |
| 1  |  12 |   13 |      13 | 2 (next number, because 12 > 10) |
| 1  |  13 |   20 |      20 | 2 (13 <=  13)                    |
| 1  |  20 |   24 |      24 | 2 (20 <= 20                      |
+ ---+-----+------+---------+----------------------------------+

Для итеративного процесса нам нужен рекурсивный запрос в SQL:

with recursive numbered as
(
  select t.*, row_number() over (partition by id order by low) as rn
  from mytable t
)
, cte (id, rn, low, high, highest, tag) as
(
  select id, rn, low, high, high, 1 from numbered where rn = 1
  union all
  select 
    n.id, n.rn, n.low, n.high, 
    case when n.low <= c.highest then greatest(n.high, c.highest) else n.high end,
    case when n.low <= c.highest then c.tag else c.tag + 1 end
  from cte c
  join numbered n on n.id = c.id and n.rn = c.rn + 1
)
select id, low, high, tag
from cte
order by id, low, high;

Демо: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=3f38a60cdcbb2711e5fb1d7fd7fefa07

0 голосов
/ 23 января 2020

Я не уверен в других примерах данных, но с тем, что мне дано, это будет работать:

select id, low, high,
DENSE_RANK() over (partition by id order by id, res desc) as tag
from
(select id, low, high, (high-low) as res
from test) a

Вот ДЕМО

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...