PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

PostgreSQL – jak wyeliminować powtarzające się wartości

Możliwe, że w tabeli jakieś pole, które ma powtarzające się wartości, jest konieczne, aby pozostawić je jako unikalne.
A jak postępować z powtarzającymi się wartościami bez ich eliminowania?
Czy byłoby możliwe pozostawienie tylko najbardziej aktualnych ?

ctid Kolumna systemowa

Każda tabela ma kilka kolumn domyślnie zdefiniowanych przez system, których nazwy są zastrzeżone.
Obecnie kolumny systemowe to:tableoid, xmin, cmin, xmax, cmax i ctid. Każda z nich ma metadane z tabeli, do której należą.
Kolumna systemowa ctid jest przeznaczona do przechowywania wersji fizycznej lokalizacji wiersza. Ta wersja może się zmienić, jeśli wiersz
zostanie zaktualizowany (UPDATE) lub tabela przejdzie przez PRÓŻNIĘ PEŁNĄ.
Typ danych ctid to tid, co oznacza identyfikator krotki (lub identyfikator wiersza), który jest para (numer bloku, indeks krotki w bloku)
która identyfikuje fizyczne położenie wiersza w tabeli.
Ta kolumna zawsze ma swoją unikalną wartość w tabeli, więc jeśli istnieją wiersze z powtarzającymi się wartościami może służyć jako kryterium ich eliminacji.

Test tworzenia tabeli:

CREATE TABLE tb_test_ctid (
    col1 int,
    col2 text);

Wstaw jakieś dane:

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

Sprawdź bieżące wiersze:

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

Zaktualizuj wiersz:

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

Sprawdź ponownie tabelę:

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

Możemy zauważyć, że zaktualizowany wiersz miał zmieniony identyfikator ctid…

Prosty test PRÓŻNIOWY PEŁNY:

VACUUM FULL tb_test_ctid;

Sprawdzenie tabeli po PRÓŻNIU:

SELECT ctid, * FROM tb_test_ctid;

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

Zaktualizuj ponownie ten sam wiersz, używając klauzuli RETURNING:

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

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

Sprawdź ponownie tabelę:

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

Eliminowanie powtarzających się wartości za pomocą ctid

Wyobraź sobie tabelę, która zawiera powtarzające się wartości w polu, a to samo pole zostało później uznane za unikatowe.
Pamiętaj, że pole PRIMARY KEY również jest unikatowe.
OK, zdecydowano, że powtarzające się wartości w polu to pole zostanie usunięte.
Teraz konieczne jest ustalenie kryterium, aby wybrać spośród tych powtarzających się wartości, które pozostaną.
W następującym przypadku kryterium jest najbardziej aktualny wiersz, to znaczy ten z najwyższa wartość ctid.

Tworzenie nowej tabeli testowej:

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

Wstaw 10 rekordów:

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

Sprawdź tabelę:

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

Sprawdź powtarzające się wartości:

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

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

W polu id_ tabeli występują powtarzające się wartości…

Spróbuj ustawić pole id_ jako klucz podstawowy:

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.

Korzystając z funkcji CTE i okna, sprawdź, które powtarzające się wartości zostaną zachowane:

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)

Opuszczenie tabeli z unikalnymi wartościami pola id_, usunięcie starszych wierszy:

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)

Sprawdzanie wartości tabeli bez zduplikowanych wartości dla 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

Możesz teraz zmienić tabelę, aby pozostawić pole id_ jako KLUCZ PODSTAWOWY:

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. BŁĄD:odmowa uprawnień dla relacji nazwa tabeli w Postgresie podczas próby SELECT jako użytkownik tylko do odczytu

  2. python pip zainstalować błąd instalacji psycopg2

  3. Createuser:nie można połączyć się z postgresem bazy danych:FATAL:rola tom nie istnieje

  4. Przełączanie projektu Django z backendu sqlite3 na postgresql kończy się niepowodzeniem podczas ładowania zrzutu danych

  5. Jak pobrać kolumnę Postgres bytea jako plik?