4

鉴于这些表:

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

TABLE Employees (
 employee_id INT,
 store_id INT,
 employee_name VARCHAR,
 currently_employed BOOLEAN,
 etc
);

我想列出每家商店雇佣时间最长的 15 名员工(假设 15 名最低employee_id),或者如果有 15 名员工,则列出商店的所有员工currently_employed='t'。我想用一个连接子句来做。

我发现很多人为 1 行执行此操作的示例,通常是最小或最大(单个最长雇员),但我想基本上在连接中结合 anORDER BY和 a LIMIT。其中一些示例可以在这里找到:

我还找到了逐个商店进行此操作的不错示例(我没有,我有大约 5000 家商店):

我还看到您可以使用andTOP代替,但不能用于 PostgreSQL。ORDER BYLIMIT

我认为两个表之间的连接子句不是唯一(甚至不一定是最好的方法)这样做,如果可以通过store_id员工表的不同内部工作,所以我愿意接受其他方法。以后可以随时加入。

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

4

2 回答 2

9

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;
  • PARTITION BY应该使用store_id,它保证是唯一的(相对于store_name)。

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

  • 要获得 15 行,请使用row_number()not 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

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

CREATE INDEX ON employees (store_id, employee_id) WHERE  currently_employed

细节取决于问题中缺少的细节。相关示例:

两个版本都排除了没有现有员工的商店。如果你需要的话,有办法解决这个问题......

于 2015-06-10T22:42:46.440 回答
2

执行此操作的经典方法是使用窗口函数,例如rank

SELECT employee_name, store_name
FROM   (SELECT employee_name, store_name, 
        RANK() OVER (PARTITION BY store_name ORDER BY employee_id ASC) AS rk
        FROM   employees e
        JOIN   stores s ON e.store_id = s.store_id) t
WHERE  rk <= 15
于 2015-06-10T22:34:36.617 回答