2017-09-13 45 views
3

每个不同的值我有一个包含队列项它们是周期性地(500毫秒)的表以分批的10获取2行用于

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 

不过,我想平等地对待每一个客户,所以我期望的结果を可以是:

---------------------------- 
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 

请注意,我不关心选择哪个object_key。有两个重要的要求:

  1. 如果有来自不同客户端的物品本中,每个客户端 件必须选择(优选同样)。
  2. 查询必须始终返回10行, (除少于10行外)。

建议的解决方案必须在Mysql和PostgreSQL中工作,并且从表中插入和选择它不会太昂贵。

我已经想加入像sort_idx一列并插入每个客户的实际行数每排的,所以我能做到这一点:

-------------------------------------- 
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 

不过,我不是对于有效地计算每个插入的排序索引过于自信。可能会出现20.000行同时插入(或快速插入)的情况。

我不是在寻找引入一些变量或函数的解决方案,我也不想引入数据库触发器,只是普通的sql。

编辑13.09.2017 17:13:如果在spring的JdbcTemplate的上下文中使用用户定义的变量是可能的,那么我想这是一个可接受的解决方案。

不过我喜欢让事情尽可能的简单数据库层(即不使用特定数据库/独家命令)

编辑2017年9月26日11:18 所以我测试过建议的解决方案针对具有大约40,000条记录的现实生活数据集,并且我不得不发现它表现不佳。事实上,大约一分钟左右之后,我就取消了查询执行。

此查询的解释给出了下面的输出:

explain 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; 



Limit (cost=512.53..512.56 rows=10 width=12) 
    -> Sort (cost=512.53..513.03 rows=200 width=12) 
     Sort Key: c1.prio, ((count(*) - 1)) 
     -> HashAggregate (cost=505.71..508.21 rows=200 width=12) 
       Group Key: c1.client_id, c1.object_key, c1.prio 
       -> Nested Loop Left Join (cost=0.15..484.80 rows=2091 width=12) 
        -> Seq Scan on clients c1 (cost=0.00..29.40 rows=1940 width=12) 
        -> Index Only Scan using clients_idx on clients c2 (cost=0.15..0.22 rows=1 width=12) 
          Index Cond: ((client_id = c1.client_id) AND (object_key <= c1.object_key) AND (prio <= c1.prio)) 

我不是解释说明计划的专家,但只要成本数字是高还是我看到的嵌套循环连接我感觉到危险。

与密集排名函数给出的语句运行速度快很多,不幸的是我需要支持mysql和postgres。

+1

*我不寻找解决方案引入一些变量*为什么不呢? – Strawberry

+0

请原谅我可能是过分限制。我们使用java和org.springframework.jdbc.core.jdbcTemplate来读写我们的数据库。我不熟悉,如果使用弹簧JdbcTemplate可以使用用户定义的变量。如果是这样,我想应该没有理由不使用它们。编辑:我添加了java和jdbctemplate作为关键字 – SebastianRiemer

+0

据推测,java只关心它收到的结果,而不是MySQL用来构造结果的步骤?如果是这样,变量对我来说似乎是个好主意。 – Strawberry

回答

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,欢迎您来测试它

UPDATE: 也做了PostgreSQL的与DENSE_RANK的解决方案 - 以防万一别人不会与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; 

而且fiddle

更新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; 

fiddle

+1

不错!正是我所希望的。我必须用更大的队列在真实世界中测试它。我猜JOIN-condition/GROUP BY-clause中所有列的索引都会被建议获得更好的性能? – SebastianRiemer

+0

@SebastianRiemer,肯定会的。在小提琴中添加了“创建索引”表达式。 –

+0

我已经更新了我的问题 - 很遗憾,提出的解决方案表现不佳。不过,我会坚持接受你的答案,因为这是现在最好的答案。 – SebastianRiemer