我在SQL中声明了一个任务优先级列表,如下所示:
CREATE TABLE TaskList(
id int not null primary key,
execute_before int null,
SomeData nvarchar(50) NOT NULL)
我现在想要从这个表中选择行,这样行将按id排序,但是任何行都是" execute_before"给定的行将放在这些行之前,再按id进行排序。
因此,如果表包含这样的数据:
id execute_before
1 null
2 null
3 2
4 3
5 2
6 null
结果应如下:
id execute_before
1 null
4 3
3 2
5 2
2 null
6 null
我的目标SQL服务器是PostgreSQL,但是我通过Django使用它,所以这些解决方案都可以。
我目前在代码中使用相当低效(粗略的低估)排序来解决问题:
def annotate_exec_sort(queryset):
from functools import cmp_to_key
from django.db.models import Case, When
def cmp(item1, item2):
rb = item1.run_before
while rb:
if rb.id == item2.id:
return -1
rb = rb.run_before
rb = item2.run_before
while rb:
if rb.id == item1.id:
return 1
rb = rb.run_before
if item1.id < item2.id:
return -1
elif item1.id > item2.id:
return 1
return 0
recs = sorted(queryset.all(), key=cmp_to_key(cmp))
return queryset.annotate(srt=Case(*[When(id=r[1], then=r[0]) for r in enumerate([r.id for r in recs])],
default=0,
output_field=models.IntegerField()))
答案 0 :(得分:1)
这是一个使用PostgreSQL WITH RECURSIVE查询来遍历的解决方案 依赖图。设置如下:
begin;
drop table if exists tasklist;
create table tasklist
(
id int not null primary key,
execute_before int null
);
\copy tasklist(id, execute_before) from stdin with csv delimiter E'\t' null as 'null'
1 null
2 null
3 2
4 3
5 2
6 null
\.
commit;
然后解决方案如下所示:
with recursive s as
(
select id, execute_before, 0 as level, '{}'::integer[] as before
from tasklist t1
where execute_before is null
union
select tasklist.id,
tasklist.execute_before,
level + 1 as level,
case when tasklist.execute_before is not null
then before || tasklist.id
else before
end as before
from tasklist
join s
on tasklist.execute_before = s.id
where not tasklist.id = any(before)
)
select id, execute_before, level, before
from s
order by level desc, id;
给出以下输出:
id │ execute_before │ level │ before
════╪════════════════╪═══════╪════════
4 │ 3 │ 2 │ {3,4}
3 │ 2 │ 1 │ {3}
5 │ 2 │ 1 │ {5}
1 │ ¤ │ 0 │ {}
2 │ ¤ │ 0 │ {}
6 │ ¤ │ 0 │ {}
(6 rows)