为列中的每个不同值获取2行

时间:2017-09-13 12:57:42

标签: java mysql postgresql jdbctemplate

我有一个包含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。有两个重要的要求:

  1. 如果有来自不同客户的物品,则各自的物品 必须选择客户(最好是同等)。
  2. 查询必须始终返回10行, 除非行数少于10行。
  3. 建议的解决方案必须在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。

1 个答案:

答案 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;

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