我有一个包含Queue-Items的表,这些表定期(500ms)以10批次进行处理。
</h1>
每次从表中选择时,我都会限制我想要处理的金额:
select * from tbl_queue order by prio asc;
----------------------------
client_id |object_key |prio
----------------------------
1 |4711 | 10
1 |2831 | 10
1 |1912 | 10
1 |1913 | 10
1 |1914 | 10
1 |1915 | 10
1 |1922 | 10
1 |1933 | 10
1 |1944 | 10
1 |1955 | 10
1 |1966 | 10
2 |7861 | 10
2 |1234 | 10
3 |5463 | 10
3 |5464 | 10
4 |7341 | 10
4 |7342 | 10
5 |9425 | 10
5 |9426 | 10
5 |9427 | 10
select * from tbl_queue order by prio asc limit 10;
但是,我想平等对待每个客户,因此我希望得到的结果是:
client_id |object_key |prio
----------------------------
1 |4711 | 10
1 |2831 | 10
1 |1912 | 10
1 |1913 | 10
1 |1914 | 10
1 |1915 | 10
1 |1922 | 10
1 |1933 | 10
1 |1944 | 10
1 |1955 | 10
请注意,我不关心选择哪个object_key。有两个重要的要求:
建议的解决方案必须在Mysql和PostgreSQL中运行,并且从表中插入和选择它不能太昂贵。
我已经想过添加一个像sort_idx这样的列并在每行中插入每个客户的实际行数,所以我能够做到这一点:
----------------------------
client_id |object_key |prio
----------------------------
1 |1913 | 10
1 |1966 | 10
2 |7861 | 10
2 |1234 | 10
3 |5463 | 10
3 |5464 | 10
4 |7341 | 10
4 |7342 | 10
5 |9425 | 10
5 |9426 | 10
但是,我对有效计算每个插入的排序索引并不太自信。可能会出现一次(或以快速方式)插入20,000行的情况。
我不是在寻找引入一些变量或函数的解决方案,我既不想引入数据库触发器,也只是简单的sql。
EDIT 13.09.2017 17:13:如果在弹簧的背景下使用用户定义的变量是可能的。 JdbcTemplate我想它是一个可以接受的解决方案。
但是我想在数据库层上保持尽可能简单(即不使用特定于数据库的/独占命令)
编辑26.09.2017 11:18 所以我已经针对具有大约40.000条记录的真实数据集测试了建议的解决方案,我不得不发现它表现不佳。实际上我在大约一分钟后就取消了查询执行。
对此查询的解释提供以下输出:
--------------------------------------
client_id |object_key |prio| sort_idx
--------------------------------------
1 |4711 | 10 | 1
1 |2831 | 10 | 2
1 |1912 | 10 | 3
1 |1913 | 10 | 4
1 |1914 | 10 | 5
1 |1915 | 10 | 6
1 |1922 | 10 | 7
1 |1933 | 10 | 8
1 |1944 | 10 | 9
1 |1955 | 10 | 10
1 |1966 | 10 | 11
2 |7861 | 10 | 1
2 |1234 | 10 | 2
3 |5463 | 10 | 1
3 |5464 | 10 | 2
4 |7341 | 10 | 1
4 |7342 | 10 | 2
5 |9425 | 10 | 1
5 |9426 | 10 | 2
5 |9427 | 10 | 3
select * from tbl_queue order by prio, sort_index asc limit 10;
--------------------------------------
client_id |object_key |prio| sort_idx
--------------------------------------
1 |4711 | 10 | 1
2 |7861 | 10 | 1
3 |5463 | 10 | 1
4 |7341 | 10 | 1
5 |9425 | 10 | 1
1 |2831 | 10 | 2
2 |1234 | 10 | 2
3 |5464 | 10 | 2
4 |7342 | 10 | 2
5 |9426 | 10 | 2
我不是解释解释计划的专家,但每当成本数字很高或者我看到嵌套循环连接时,我都会感觉到危险。
使用密集等级函数给出的语句工作得更快,不幸的是我需要支持mysql和postgres。
答案 0 :(得分:1)
通常(MSSQL,Oracle,PostgreSQL等)这个问题可以在DENSE_RANK函数的帮助下解决。 但由于MySQL没有窗口函数,你可以这样做:
select c1.client_id,
c1.object_key,
c1.prio,
count(*)-1 as sort_idx
from clients as c1
left join clients as c2
on c1.client_id = c2.client_id
and c1.object_key >= c2.object_key
and c1.prio >= c2.prio
group by c1.client_id,
c1.object_key,
c1.prio
order by
c1.prio,
sort_idx
limit 10;
我已经制作了fiddle,欢迎您对其进行测试
<强>更新强> 还使用DENSE_RANK为PostgreSQL提供了一个解决方案 - 以防万一其他人不受MySQL限制的限制:
select c.client_id,
c.object_key,
c.prio,
DENSE_RANK() OVER
(PARTITION BY c.client_id ORDER BY c.object_key) as sort_idx
from clients as c
order by
c.prio,
sort_idx
limit 10;
更新2:
为dense_rank
计算制作了一个特定于MySQL的解决方案,比40000记录上的自联接工作速度快得多。但它依赖于顺序,因此您必须以某种方式使用此查询结果(可能使用临时表)来获取prio ASC, dense_rank ASC
排序的结果。
SELECT
t2.client_id,
t2.object_key,
t2.prio,
t2.dense_rank
FROM
(SELECT
t1.*,
@dense:=IF(@prev_client_id=t1.client_id AND @prev_prio=t1.prio, @dense+1, 1) AS dense_rank,
@prev_client_id:=t1.client_id,
@prev_prio:=t1.prio
FROM (select * from clients c1 order by prio, client_id) t1,
(SELECT @dense:=0, @prev_client_id:=NULL, @prev_prio:=NULL) var1
) t2;