У меня есть образец таблицы ниже: Таблица_А
Чего я хочу здесь добиться, так это получить последние данные цепочки на основе приведенной выше таблицы со следующим требованием: каждые данные должны найти свои данные цепочки на основе от 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
Ожидаемый результат следующий:
есть ли способ добиться этого в MySQL?
@ysth на данный момент я просто знаю и сосредоточился на этом примере, но я записываю это и сохраняю как возможную проблему в будущем, скажем так, да, это может быть более одной записи.
Схема не запрещает циклы. Неразрешима в текущем состоянии. Представьте, что есть 3 строки: (1,2), (2,3), (3,1) — какой результат для любой из этих строк вы хотите получить?
Если циклов нет, вы сможете сделать это с помощью рекурсивного CTE.
Будет надежнее обрабатывать циклы со столбцом уникального идентификатора. По крайней мере, через row_number().






Используйте рекурсивный 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;
не соответствует запрошенному выводу, который имеет значение null, если в цепочке нет второй записи
@ysth Это можно легко исправить, посчитав количество запятых в path.
с тем, как можно присоединиться к ранее начавшейся цепочке, мне не ясно, легко ли это исправить.
@Акина, это тот близкий человек, которого я ищу. Большое спасибо!
@Акина, можно ли как-нибудь изменить find_in_set на то, где находится? Потому что, как мы знаем, find_in_set не использует index. Я тестировал с большим количеством данных, и они стали медленными, есть мысли?
@MuhtaromZain можно ли как-нибудь изменить find_in_set на то, где находится? В MySQL нет типа данных ARRAY. Вы можете использовать массив JSON и MEMBER OF, но я сомневаюсь, что это будет быстрее.
Еще один пример рекурсивного запроса
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;
Выход:
О (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 |
+------+------+-----------------+
может ли быть более одной записи с одним и тем же столбцом 1 (игнорируя запись с столбцом 1 = столбец 2)? если да, то может быть несколько значений последней цепочки