PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

PostgreSQL - Cách loại bỏ các giá trị lặp lại

Có thể trong một bảng, một số trường có các giá trị lặp lại là cần thiết để giữ nó là duy nhất.
Và làm thế nào để tiếp tục với các giá trị lặp lại mà không loại bỏ tất cả chúng?
Có thể chỉ để lại giá trị mới nhất ?

Cột hệ thống ctid

Mỗi bảng đều có một số cột được xác định ngầm bởi hệ thống, các cột này được đặt trước.
Hiện tại, các cột của hệ thống là:tableoid, xmin, cmin, xmax, cmax và ctid. Mỗi cái đều có siêu dữ liệu từ bảng mà chúng thuộc về.
Cột hệ thống ctid nhằm lưu trữ phiên bản của vị trí thực của hàng. Phiên bản này có thể thay đổi nếu hàng
được cập nhật (CẬP NHẬT) hoặc bảng đi qua ĐẦY ĐỦ VACUUM.
Kiểu dữ liệu của ctid là tid, có nghĩa là mã định danh tuple (hoặc mã định danh hàng), là một cặp (số khối, chỉ số bộ trong khối)
xác định vị trí thực của hàng trong bảng.
Cột này luôn có giá trị duy nhất trong bảng, vì vậy khi có các hàng có giá trị lặp lại nó có thể được sử dụng làm tiêu chí để loại bỏ chúng.

Tạo bảng kiểm tra:

CREATE TABLE tb_test_ctid (
    col1 int,
    col2 text);

Chèn một số dữ liệu:

INSERT INTO tb_test_ctid VALUES 
(1, 'foo'),
(2, 'bar'),
(3, 'baz');

Kiểm tra các hàng hiện tại:

SELECT ctid, * FROM tb_test_ctid;
 ctid  | col1 | col2 
-------+------+------
 (0,1) |    1 | foo
 (0,2) |    2 | bar
 (0,3) |    3 | baz

Cập nhật một hàng:

UPDATE tb_test_ctid SET col2 = 'spam' WHERE col1 = 1;

Kiểm tra lại bảng:

SELECT ctid, * FROM tb_test_ctid;
 ctid  | col1 | col2 
-------+------+------
 (0,2) |    2 | bar
 (0,3) |    3 | baz
 (0,4) |    1 | spam

Chúng tôi có thể nhận thấy rằng hàng được cập nhật cũng đã thay đổi ctid của nó…

Một bài kiểm tra ĐẦY ĐỦ VACUUM đơn giản:

VACUUM FULL tb_test_ctid;

Kiểm tra bảng sau VACUUM:

SELECT ctid, * FROM tb_test_ctid;

ctid   | col1 | col2 
-------+------+------
(0,1)  | 2    | bar
(0,2)  | 3    | baz
(0,3)  | 1    | spam

Cập nhật lại cùng một hàng bằng cách sử dụng mệnh đề RETURNING:

UPDATE tb_test_ctid
    SET col2 = 'eggs'
    WHERE col1 = 1
    RETURNING ctid;

 ctid  
-------
 (0,4)

Kiểm tra lại bảng:

SELECT ctid, * FROM tb_test_ctid;
 ctid  | col1 | col2 
-------+------+------
 (0,2) |    2 | bar
 (0,3) |    3 | baz
 (0,4) |    1 | spam

Loại bỏ các giá trị lặp lại bằng ctid

Hãy tưởng tượng một bảng có các giá trị lặp lại trong một trường và chính trường đó được quyết định làm cho nó trở thành duy nhất sau này.
Hãy nhớ rằng trường CHÍNH CHÍNH cũng là duy nhất.
Được, chúng tôi quyết định rằng các giá trị lặp lại trong trường đó sẽ bị xóa.
Bây giờ cần thiết lập một tiêu chí để quyết định trong số các giá trị lặp lại này sẽ vẫn còn.
Trong trường hợp sau, tiêu chí là dòng mới nhất, tức là dòng có giá trị ctid cao nhất.

Tạo bảng thử nghiệm mới:

CREATE TABLE tb_foo(
    id_ int,  --This field will be the primary key in the future!
    letter char(1)
);

Chèn 10 bản ghi:

INSERT INTO tb_foo (id_, letter) SELECT generate_series(1, 10), 'a';

Kiểm tra bảng:

SELECT id_, letter FROM tb_foo;

 id_ | letter 
-----+--------
   1 | a
   2 | a
   3 | a
   4 | a
   5 | a
   6 | a
   7 | a
   8 | a
   9 | a
  10 | a
Chèn thêm 3 bản ghi:
INSERT INTO tb_foo (id_, letter) SELECT generate_series(1, 3), 'b';

Kiểm tra các giá trị lặp lại:

SELECT id_, letter FROM tb_foo WHERE id_ <= 3;

 id_ | letter  
-----+--------
   1 | a
   2 | a
   3 | a
   1 | b
   2 | b
   3 | b

Có các giá trị lặp lại trong trường id_ của bảng…

Cố gắng đặt trường id_ làm khóa chính:

ALTER TABLE tb_foo ADD CONSTRAINT tb_foo_pkey PRIMARY KEY (id_);

ERROR:  could not create unique index "tb_foo_pkey"
DETAIL:  Key (id_)=(3) is duplicated.

Sử dụng CTE và các hàm cửa sổ, tìm hiểu những giá trị lặp lại nào sẽ được giữ lại:

WITH t AS (
SELECT
    id_,
    count(id_) OVER (PARTITION BY id_) AS count_id,  -- Count
    ctid,
    max(ctid) OVER (PARTITION BY id_) AS max_ctid  -- Most current ctid
    
    FROM tb_foo
)

SELECT
    t.id_,
    t.max_ctid
    FROM t
    WHERE t.count_id > 1  -- Filters which values repeat
    GROUP by id_, max_ctid;

 id_ | max_ctid 
-----+----------
   3 | (0,13)
   1 | (0,11)
   2 | (0,12)

Để lại bảng có các giá trị duy nhất cho trường id_, xóa các hàng cũ hơn:

WITH

t1 AS (
SELECT
    id_,
    count(id_) OVER (PARTITION BY id_) AS count_id,
    ctid,
    max(ctid) OVER (PARTITION BY id_) AS max_ctid
    
    FROM tb_foo
),

t2 AS (  -- Virtual table that filters repeated values that will remain
SELECT t1.id_, t1.max_ctid
    FROM t1
    WHERE t1.count_id > 1
    GROUP by t1.id_, t1.max_ctid)

DELETE  -- DELETE with JOIN 
    FROM tb_foo AS f
    USING t2
    WHERE 
        f.id_ = t2.id_ AND  -- tb_foo has id_ equal to t2 (repeated values)
        f.ctid < t2.max_ctid;  -- ctid is less than the maximum (most current)

Kiểm tra giá trị bảng không có giá trị trùng lặp cho id_:

SELECT id_, letter FROM tb_foo;

 id_ | letter 
-----+--------
   4 | a
   5 | a
   6 | a
   7 | a
   8 | a
   9 | a
  10 | a
   1 | b
   2 | b
   3 | b

Bây giờ, bạn có thể thay đổi bảng để để trường id_ là TỪ KHÓA CHÍNH:

ALTER TABLE tb_foo ADD CONSTRAINT tb_foo_pkey PRIMARY KEY (id_);

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. ::Làm gì trong PostgreSQL?

  2. Cách không tạo tiện ích mở rộng PostgreSQL 9.0 trên nền tảng RPM

  3. Heroku psql:FATAL:các khe kết nối còn lại được dành riêng cho các kết nối siêu người dùng không sao chép

  4. Postgresql enum những ưu điểm và nhược điểm là gì?

  5. PostgreSQL 12:Triển khai K-Nearest Neighbor Space Partitioned Generated Tree Indexes