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 | aChè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_);