Данные цепочки запросов в MySQL

У меня есть образец таблицы ниже: Таблица_А

столбец 1 столбец 2 881 113 988 899 113 765 765 765 122 881 300 400 765 910 910 345 999 988

Чего я хочу здесь добиться, так это получить последние данные цепочки на основе приведенной выше таблицы со следующим требованием: каждые данные должны найти свои данные цепочки на основе от col2 до col1 до тех пор, пока данные не найдут какую-либо цепочку и игнорировать данные с помощью то же значение, например, в этом случае для col1 = 122 и col2 = 881 должно быть 122>881>113>765>910>365. Для данных, которые не имеют какой-либо цепочки данных или данных с одинаковым значением, можно оставить их пустыми или нулевыми.

Я пытаюсь выполнить самостоятельное присоединение, но оно поддерживает только одну временную цепочку. Я ищу динамическую цепочку, основанную на количестве самих данных.

SELECT A.col1,
       A.col2,
       B.col1
FROM Table_A AS A
LEFT JOIN Table_A AS B

Ожидаемый результат следующий:

столбец 1 столбец 2 последняя_цепочка_данные 881 113 345 988 899 НУЛЕВОЙ 113 765 345 765 765 НУЛЕВОЙ 122 881 345 300 400 НУЛЕВОЙ 765 910 345 910 345 НУЛЕВОЙ 999 988 899

есть ли способ добиться этого в MySQL?

может ли быть более одной записи с одним и тем же столбцом 1 (игнорируя запись с столбцом 1 = столбец 2)? если да, то может быть несколько значений последней цепочки

ysth 05.03.2024 17:35

@ysth на данный момент я просто знаю и сосредоточился на этом примере, но я записываю это и сохраняю как возможную проблему в будущем, скажем так, да, это может быть более одной записи.

Muhtarom Zain 05.03.2024 17:43

Схема не запрещает циклы. Неразрешима в текущем состоянии. Представьте, что есть 3 строки: (1,2), (2,3), (3,1) — какой результат для любой из этих строк вы хотите получить?

Akina 05.03.2024 18:15

Если циклов нет, вы сможете сделать это с помощью рекурсивного CTE.

Barmar 05.03.2024 18:25

Будет надежнее обрабатывать циклы со столбцом уникального идентификатора. По крайней мере, через row_number().

ValNik 05.03.2024 20:14
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
0
5
103
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Используйте рекурсивный CTE:

WITH RECURSIVE
cte1 AS (
  SELECT col1, col2, CAST(col2 AS CHAR(65535)) path, 1 level
  FROM test
  UNION ALL
  SELECT cte1.col1, cte1.col2, CONCAT_WS(',', cte1.path, test.col2), cte1.level + 1
  FROM test
  JOIN cte1 ON FIND_IN_SET(test.col1, cte1.path)
  WHERE NOT FIND_IN_SET(test.col2, cte1.path)
  ),
cte2 AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY LENGTH(path) DESC) rn
  FROM cte1
  )
SELECT col1, col2, SUBSTRING_INDEX(path, ',', -1) last_chain_data
FROM cte2 
WHERE rn = 1
ORDER BY last_chain_data, level DESC;
столбец 1 столбец 2 последняя_цепочка_данные 122 881 345 881 113 345 113 765 345 765 765 345 765 910 345 910 345 345 300 400 400 999 988 899 988 899 899

рабочий пример

не соответствует запрошенному выводу, который имеет значение null, если в цепочке нет второй записи

ysth 05.03.2024 20:19

@ysth Это можно легко исправить, посчитав количество запятых в path.

Akina 05.03.2024 21:56

с тем, как можно присоединиться к ранее начавшейся цепочке, мне не ясно, легко ли это исправить.

ysth 05.03.2024 22:17

@Акина, это тот близкий человек, которого я ищу. Большое спасибо!

Muhtarom Zain 08.03.2024 05:11

@Акина, можно ли как-нибудь изменить find_in_set на то, где находится? Потому что, как мы знаем, find_in_set не использует index. Я тестировал с большим количеством данных, и они стали медленными, есть мысли?

Muhtarom Zain 13.03.2024 10:09

@MuhtaromZain можно ли как-нибудь изменить find_in_set на то, где находится? В MySQL нет типа данных ARRAY. Вы можете использовать массив JSON и MEMBER OF, но я сомневаюсь, что это будет быстрее.

Akina 13.03.2024 11:25

Еще один пример рекурсивного запроса

with RECURSIVE trn as(select *,row_number()over(order by col1,col2) rn from test)
, r as(
  select 0 lvl,t1.rn,t1.col1,t1.col2,t2.col2 next, t2.rn next_rn
    ,concat(':',t1.rn,':',t2.rn) path
  from trn t1 left join trn t2
     on t2.col1=t1.col2 and t1.rn<>t2.rn
  union all
  select lvl+1 lvl,r.rn,r.col1,r.col2,t.col2 next,t.rn 
    ,concat(path,':',t.rn) path
  from r left  join trn t
     on t.col1=r.next and t.rn<>r.rn
  where lvl<7 and locate(t.rn,path)=0
)
select *
  from(
  select * 
     ,row_number()over(partition by rn order by lvl desc)rrn
  from r
  ) x
where rrn=1;

Выход:

уровень р-н столбец 1 столбец 2 следующий путь 2 1 113 765 345 :1:4:5:7 4 2 122 881 345 :2:6:1:4:5:7 0 3 300 400 нулевой нулевой 1 4 765 765 345 :4:5:7 0 5 765 910 345 :5:7 3 6 881 113 345 :6:1:4:5:7 0 7 910 345 нулевой нулевой 0 8 988 899 нулевой нулевой 0 9 999 988 899 :9:8

О (765, 765) --> null - почему результат должен быть таким, должно быть описано далее в вопросе. Я не хочу гадать. В этом случае результат может быть неоднозначным.

рабочий пример

Столбец действительно мог бы выиграть от столбца id. Но можно и без этого:

create table Table_A (col1 int, col2 int);
insert Table_A values
(881,113),
(988,899),
(113,765),
(765,765),
(122,881),
(300,400),
(765,910),
(910,345),
(999,988);

Вот запрос:

with recursive
i as    (select row_number() over() as id,col1,col2
        from table_a), -- as we don't have an id column, we add one here. The table i will be used later on instead of the orginal table
c as    (
        select t1.id, t1.col1,t1.col2
        from i t1 
        left join i t2
        on t1.col2=t2.col1
        where t1.col1<>t1.col2 and t2.col1 is not null
        union 
        select c.id, t.col1,t.col2
        from i t
        right join c
        on c.col2=t.col1
        where t.col1 is not null and t.col1<>t.col2 -- if t.col1<>t.col2 is removed here, the recursive process will have meaningless calculation when t.col1<>t.col2    
) -- table c is to calculate for those rows which have a chained row, also the rows whose col1<>col2
select col1,col2,last_chain_data -- this outter layer is used to make sure each row appears in their original order from the base table, as UNION statement doesn't enforce the feature of ORDER BY clause within it) 
from (select distinct id,first_value(col1)  over w as col1,first_value(col2)  over w as col2,last_value(col2) over w as last_chain_data
     from c  window w as (partition by id) -- the first query in the UNION is to get the required output for those rows which have a chained row, also the rows whose col1<>col2
     UNION
     select distinct t1.id,t1.col1,t1.col2,null
     from i t1 
     left join i t2
     on t1.col2=t2.col1  
     where t1.col1=t1.col2 or t2.col1 is null  -- contrary to the 1st query, the second query in the UNION is for those rows which don't have a chained row, also the rows whose col1=col2
) tb
order by id
;

-- result
+------+------+-----------------+
| col1 | col2 | last_chain_data |
+------+------+-----------------+
|  881 |  113 |             345 |
|  988 |  899 |            NULL |
|  113 |  765 |             345 |
|  765 |  765 |            NULL |
|  122 |  881 |             345 |
|  300 |  400 |            NULL |
|  765 |  910 |             345 |
|  910 |  345 |            NULL |
|  999 |  988 |             899 |
+------+------+-----------------+

См. https://www.db-fiddle.com/f/o1sJwMD7DFRg7iQc2wnJsU/1

Другие вопросы по теме