限制每组加入的行数(不是1行)

CMo*_*ney 3 sql postgresql join greatest-n-per-group sql-limit

鉴于这些表格:

TABLE Stores (
 store_id INT,
 store_name VARCHAR,
 etc
);

TABLE Employees (
 employee_id INT,
 store_id INT,
 employee_name VARCHAR,
 currently_employed BOOLEAN,
 etc
);
Run Code Online (Sandbox Code Playgroud)

我想为每家商店列出15名雇员最长的员工(假设15 employee_id名员工最低),或者如果有15名员工,则列出所有员工currently_employed='t'.我想用join子句来做.

我发现很多人这样做的例子只有 1行,通常是最小或最大(单个最长雇用的员工),但我想基本上结合一个ORDER BY和一个LIMIT内部的联接.其中一些例子可以在这里找到:

我也找到了很好的例子来做这个商店(我没有,我有大约5000家商店):

我也看到你可以使用TOP而不是ORDER BYLIMIT,而不是PostgreSQL.

我认为这两个表之间的连接子句不是唯一(或者甚至是最好的方法),如果它可以只store_id在employees表的内部工作,那么我会对其他方法持开放态度.之后可以随时加入.

由于我对SQL很陌生,我想要任何理论背景或其他解释可以帮助我理解工作原理.

Erw*_*ter 6

row_number()

获取每组前n行的一般解决方案是使用窗口函数row_number():

SELECT *
FROM  (
   SELECT *, row_number() OVER (PARTITION BY store_id ORDER BY employee_id) AS rn
   FROM   employees
   WHERE  currently_employed
   ) e
JOIN   stores s USING (store_id)
WHERE  rn <= 15
ORDER  BY store_id, e.rn;
Run Code Online (Sandbox Code Playgroud)
  • PARTITION BY应该使用store_id,保证是唯一的(相对于store_name).

  • 首先识别行中的行employees,然后加入stores,这样便宜.

  • 要获得15行row_number()不使用rank()(将是错误的工具).只要employee_id是独一无二的,差异就不会显示出来.

LATERAL

Postgres 9.3+的替代方案,通常与匹配索引结合使用效果更好,尤其是在从大表中检索小选项时.

SELECT s.store_name, e.*
FROM   stores s
, LATERAL (
   SELECT *  -- or just needed columns
   FROM   employees
   WHERE  store_id = s.store_id
   AND    currently_employed
   ORDER  BY employee_id
   LIMIT  15
   ) e
-- WHERE ... possibly select only a few stores
ORDER  BY s.store_name, e.store_id, e.employee_id
Run Code Online (Sandbox Code Playgroud)

完美索引将是这样的部分多列索引:

CREATE INDEX ON employees (store_id, employee_id) WHERE  currently_employed
Run Code Online (Sandbox Code Playgroud)

详细信息取决于问题中缺少的详细信息.相关示例:

两个版本都不包括没有当前员工的商 如果你需要它,有办法解决这个问题......