PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

PostgreSQL – Cara Menghilangkan Nilai Berulang

Mungkin saja dalam sebuah tabel, beberapa bidang yang memiliki nilai berulang diperlukan untuk menjadikannya unik.
Dan bagaimana cara melanjutkan dengan nilai yang berulang tanpa menghilangkan semuanya?
Apakah mungkin hanya menyisakan yang terbaru ?

Kolom Sistem ctid

Setiap tabel memiliki beberapa kolom yang didefinisikan secara implisit oleh sistem, yang namanya dicadangkan.
Saat ini kolom sistem adalah:tableoid, xmin, cmin, xmax, cmax dan ctid. Masing-masing memiliki metadata dari tabel tempat mereka berada.
Kolom sistem ctid dimaksudkan untuk menyimpan versi lokasi fisik baris. Versi ini dapat berubah jika baris
diperbarui (UPDATE) atau tabel melewati VACUUM FULL.
Tipe data ctid adalah tid, artinya pengidentifikasi tuple (atau pengidentifikasi baris), yang merupakan pair (nomor blok, indeks tuple di dalam blok)
yang mengidentifikasi lokasi fisik baris dalam tabel.
Kolom ini selalu memiliki nilai unik dalam tabel, jadi ketika ada baris dengan nilai berulang itu dapat digunakan sebagai kriteria untuk eliminasi mereka.

Pembuatan tabel uji:

CREATE TABLE tb_test_ctid (
    col1 int,
    col2 text);

Masukkan beberapa data:

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

Periksa baris saat ini:

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

Perbarui satu baris:

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

Periksa tabel lagi:

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

Kami dapat melihat bahwa baris yang diperbarui telah mengubah ctidnya juga…

Tes PENUH VAKUM sederhana:

VACUUM FULL tb_test_ctid;

Memeriksa tabel setelah VACUUM:

SELECT ctid, * FROM tb_test_ctid;

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

Perbarui baris yang sama lagi menggunakan klausa RETURNING:

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

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

Periksa tabel lagi:

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

Menghilangkan Nilai Berulang dengan ctid

Bayangkan sebuah tabel yang memiliki nilai berulang dalam sebuah bidang dan bidang yang sama diputuskan untuk membuatnya unik nanti.
Ingat bahwa bidang KUNCI UTAMA juga unik.
OK, diputuskan bahwa nilai yang diulang dalam bidang itu akan dihapus.
Sekarang perlu menetapkan kriteria untuk memutuskan di antara nilai-nilai berulang yang akan tetap ada.
Dalam kasus berikut, kriterianya adalah baris terbaru, yaitu satu dengan nilai ctid tertinggi.

Pembuatan tabel pengujian baru:

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

Sisipkan 10 record:

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

Periksa tabelnya:

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
Masukkan 3 catatan lagi:
INSERT INTO tb_foo (id_, letter) SELECT generate_series(1, 3), 'b';

Periksa nilai berulang:

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

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

Ada nilai berulang di bidang id_ tabel…

Mencoba menjadikan bidang id_ sebagai kunci utama:

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.

Menggunakan fungsi CTE dan jendela, cari tahu nilai berulang mana yang akan disimpan:

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)

Meninggalkan tabel dengan nilai unik untuk bidang id_, menghapus baris yang lebih lama:

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)

Memeriksa nilai tabel tanpa nilai duplikat untuk 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

Anda sekarang dapat mengubah tabel untuk meninggalkan bidang id_ sebagai KUNCI UTAMA:

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. Urutan PostgreSQL berdasarkan kolom lain

  2. PostgreSQL:NOT IN versus KECUALI perbedaan kinerja (diedit #2)

  3. Bagaimana menjaga data tidak mengurutkan?

  4. Interval pencocokan PostgreSQL antara waktu mulai dan waktu berakhir dengan stempel waktu

  5. Ubah representasi tanggal postgres menjadi string ISO 8601