Вот начало, которое может дать некоторые идеи о подходе. Рекурсивный запрос начинается с a_sno
каждой записи, а затем пытается следовать по пути b_sno
, пока не достигнет конца или не сформирует цикл. Путь представлен массивом из sno
целых чисел.
Функция unnest
разбивает массив на строки, поэтому значение sno
сопоставляется с массивом путей, например:
4, {6, 5, 4}
будет преобразован в строку для каждого значения в массиве:
4, 6
4, 5
4, 4
Затем array_agg
меняет операцию, объединяя значения обратно в путь, но избавляясь от дубликатов и упорядочения.
Теперь каждый a_sno
связан с путем, и путь образует группу. dense_rank
можно использовать для сопоставления группировки (кластера) с числовым значением.
SELECT array_agg(DISTINCT map ORDER BY map) AS cluster
,sno
FROM ( WITH RECURSIVE x(sno, path, cycle) AS (
SELECT a_sno, ARRAY[a_sno], false FROM data
UNION ALL
SELECT b_sno, path || b_sno, b_sno = ANY(path)
FROM data, x
WHERE a_sno = x.sno
AND NOT cycle
)
SELECT sno, unnest(path) AS map FROM x ORDER BY 1
) y
GROUP BY sno
ORDER BY 1, 2
Выход:
cluster | sno
--------------+-----
{4,5,6,7} | 4
{4,5,6,7} | 5
{4,5,6,7} | 6
{4,5,6,7} | 7
{9,10,13,14} | 9
{9,10,13,14} | 10
{9,10,13,14} | 13
{9,10,13,14} | 14
{11,15} | 11
{11,15} | 15
(10 rows)
Оберните это еще раз для рейтинга:
SELECT dense_rank() OVER(order by cluster) AS rank
,sno
FROM (
SELECT array_agg(DISTINCT map ORDER BY map) AS cluster
,sno
FROM ( WITH RECURSIVE x(sno, path, cycle) AS (
SELECT a_sno, ARRAY[a_sno], false FROM data
UNION ALL
SELECT b_sno, path || b_sno, b_sno = ANY(path)
FROM data, x
WHERE a_sno = x.sno
AND NOT cycle
)
SELECT sno, unnest(path) AS map FROM x ORDER BY 1
) y
GROUP BY sno
ORDER BY 1, 2
) z
Выход:
rank | sno
------+-----
1 | 4
1 | 5
1 | 6
1 | 7
2 | 9
2 | 10
2 | 13
2 | 14
3 | 11
3 | 15
(10 rows)
person
Glenn
schedule
19.04.2015