选择指定数量的唯一 ID,其中第二列是唯一的

use*_*833 4 postgresql select greatest-n-per-group

查看下面的示例,从第一行 ( id=9)开始,然后向下工作,选择我们尚未看到的具有's的行数限制。我们“选择”是因为我们还没有. 我们继续像这样往下工作,但是当我们到达时我们跳过它,因为我们已经有了(从行开始)。我们以同样的方式继续,我们最终停下来,因为我们已经积累了行(我们想要的限制)。4secid=9sec=1id=7sec=5id=8id=34

 id | sec
----+-----
  9 |   1  <- 1
  8 |   5  <- 2
  7 |   5  # skip, already have sec=5
  6 |   4  <- 3
  5 |   1  # skip, already have sec=1
  4 |   1  # skip, already have sec=1
  3 |   3  <- 4
  2 |   2
  1 |   1
Run Code Online (Sandbox Code Playgroud)

当然,SQL算法可以(将!)与我描述的不同。

想要的结果:

 id
----
  9
  8
  6
  3
(4 rows)
Run Code Online (Sandbox Code Playgroud)

如果我想增加5行的限制,那么结果id=2 中将包含行。但是,如果我增加了限6行,与行id=1不会被添加,因为sec=1已经看到。

注意:虽然没关系,但我使用的是PostgreSQL 9.3.1


如果您想快速构建表以进行测试:

CREATE TABLE my_table (id serial primary key, sec integer DEFAULT 0 NOT NULL);
INSERT INTO my_table (sec) VALUES
  (1)
, (2)
, (3)
, (1)
, (1)
, (4)
, (5)
, (5)
, (1);
CREATE INDEX index_my_table_on_sec ON my_table (sec);
Run Code Online (Sandbox Code Playgroud)

a_h*_*ame 5

SELECT id,
       sec
FROM (
  SELECT id,
         sec,
         row_number() OVER (PARTITION BY sec ORDER BY id DESC) AS rn
  FROM my_table
) t
WHERE rn = 1
ORDER BY id DESC 
LIMIT 4;
Run Code Online (Sandbox Code Playgroud)

SQLFiddle 示例:http ://sqlfiddle.com/#!15/1ca01/1


Erw*_*ter 5

在 Postgres 中,这更简单DISTINCT ON

SELECT *
FROM (
   SELECT DISTINCT ON (sec)
          id, sec
   FROM   tbl
   ORDER  BY sec, id DESC
   ) sub
ORDER  BY id DESC
LIMIT  4;
Run Code Online (Sandbox Code Playgroud)

关于SO的相关答案中的详细说明:

对于大表LIMIT,无论是 this 还是@a_horse 的解决方案都不是很有效。子查询将遍历整个表,浪费大量时间......

递归 CTE

过去,我曾尝试使用递归 CTE 解决类似问题,但未能解决,于是求助于 PL/pgSQL 的过程式解决方案。例子:

最后,这是一个有效的 rCTE:

WITH RECURSIVE cte AS (
   (  -- parentheses required
   SELECT id, '{}'::int[] AS last_arr, ARRAY[sec] AS arr
   FROM   tbl
   ORDER  BY id DESC
   LIMIT  1
   )
   UNION ALL
   (
   SELECT b.id, c.arr
        , CASE WHEN b.sec = ANY (c.arr) THEN c.arr ELSE b.sec  || c.arr END
   FROM   cte c
   JOIN   tbl b ON b.id < c.id
   WHERE  array_length(c.arr, 1) < 4
   ORDER  BY id DESC
   LIMIT  1
   )
   )
SELECT id, arr[1] AS sec
FROM   cte
WHERE  last_arr <> arr;
Run Code Online (Sandbox Code Playgroud)

它不像我希望的那样快速或优雅,也不像下面的函数那么快,但在我的测试中比上面的查询快。

PL/pgSQL 函数

迄今为止最快的:

CREATE OR REPLACE FUNCTION f_first_uniq(_rows int)
   RETURNS TABLE (id int, sec int) AS
$func$
DECLARE
   _arr int[];
BEGIN
   FOR id, sec IN
      SELECT t.id, t.sec FROM tbl t ORDER BY t.id DESC
   LOOP
      IF sec = ANY (_arr) THEN
         -- do nothing
      ELSE
         RETURN NEXT;
         _arr := _arr || sec;
         EXIT WHEN array_length(_arr, 1) >= _rows;
      END IF;
   END LOOP;
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

称呼:

SELECT * FROM f_first_uniq(4);
Run Code Online (Sandbox Code Playgroud)

SQL Fiddle演示了所有三个。

可以为任何以表名和列名作为参数和动态 SQL 的表工作EXECUTE...

何苦?

在只有30k行的测试表中,该函数的运行速度比上述查询快2000 倍(已经比 a_horse 的版本快了约 30%)。这种差异随着表的大小而增长。函数的性能几乎是恒定的,而查询的性能会逐渐变差,因为它首先尝试在所有表中找到不同的值。在一个有一百万行的表中试试这个......