Postgresql

選擇指定數量的唯一 ID,其中第二列是唯一的

  • July 23, 2018

查看下面的範例,從第一行 ( id=9) 開始,然後繼續向下,選擇我們尚未看到的具有 ‘s的行的**限制。**我們“選擇”是因為我們還沒有. 我們繼續像這樣向下工作,但是當我們到達時,我們跳過它,因為我們已經有了(從帶有 的行開始)。我們以同樣的方式繼續,我們最終停下來,因為我們已經積累了行(我們想要的限制)。4``sec``id=9``sec=1``id=7``sec=5``id=8``id=3``4

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

當然,SQL算法可以(將!)與我描述的不同。

期望的結果:

id
----
 9
 8
 6
 3
(4 rows)

如果我想增加對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);

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

在這個相關答案中的詳細解釋:

對於大桌子和**小LIMIT**桌子,這個和@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;

它沒有我希望的那麼快或優雅,也沒有下面的函式快,但在我的測試中比上面的查詢快。

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;

稱呼:

SELECT * FROM f_first_uniq(4);

**SQL Fiddle**展示了這三個。

可以適用於任何以表名和列名作為參數的表和動態 SQL EXECUTE

何必?

在只有30k行的測試表中,該函式的執行速度比上述查詢快 2000 倍(已經比 a_horse 的版本快 30%)**。**這種差異隨著表的大小而增加。該函式的性能幾乎是恆定的,而查詢的性能逐漸變差,因為它首先嘗試在所有表中找到不同的值。在具有一百萬行的表中嘗試此操作…

引用自:https://dba.stackexchange.com/questions/62598