CodeGym /Kursy /SQL SELF /Obsługa błędów podczas ładowania danych ( ON CONFL...

Obsługa błędów podczas ładowania danych ( ON CONFLICT)

SQL SELF
Poziom 23 , Lekcja 3
Dostępny

Witaj w samym sercu dramatycznych scenariuszy masowego ładowania danych! Dzisiaj nauczymy się skutecznie radzić sobie z błędami pojawiającymi się podczas ładowania danych, używając konstrukcji ON CONFLICT. To trochę jak włączenie autopilota w samolocie: nawet jeśli coś pójdzie nie tak, będziesz wiedzieć, co zrobić, żeby nie było katastrofy. Lecimy rozgryźć triki PostgreSQL!

Nikt nie lubi niespodzianek, szczególnie gdy dane nie chcą się załadować! W procesach masowego ładowania możesz natknąć się na kilka typowych problemów:

  • Duplikacja danych. Na przykład, jeśli w tabeli masz ograniczenie UNIQUE, a Twój plik z danymi jest pełen powtórek.
  • Konflikty z ograniczeniami. Na przykład, spróbuj załadować pustą wartość do kolumny z ograniczeniem NOT NULL. Efekt? Błąd. PostgreSQL zawsze jest sztywny w takich sytuacjach.
  • Duplikujące się kluczowe informacje. Tabela może już zawierać dane z tymi samymi identyfikatorami, co Twój plik CSV.

Sprawdźmy, jak uniknąć tych "podwodnych kamieni" z ON CONFLICT.

Użycie ON CONFLICT do obsługi błędów

Składnia konstrukcji ON CONFLICT pozwala określić, co zrobić w przypadku konfliktu z ograniczeniami (np. UNIQUE lub PRIMARY KEY). PostgreSQL daje Ci możliwość albo zaktualizowania istniejących danych, albo zignorowania konfliktującego wiersza.

Tak wygląda podstawowa składnia konstrukcji ON CONFLICT:

INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...)
ON CONFLICT (conflict_target)
DO UPDATE SET column1 = new_value1, column2 = new_value2;

Możesz zamienić DO UPDATE na DO NOTHING, jeśli chcesz po prostu zignorować konflikt.

Przykład: aktualizacja danych przy konflikcie

Załóżmy, że mamy tabelę students:

CREATE TABLE students (
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    age INT
);

Teraz chcemy załadować nowe dane, ale niektóre z nich już są w bazie:

INSERT INTO students (id, name, age)
VALUES 
    (1, 'Peter', 22),  -- Ten student już jest
    (2, 'Anna', 20),  -- Nowy student
    (3, 'Mal', 25) -- Nowy student
ON CONFLICT (id) DO UPDATE SET 
    name = EXCLUDED.name, 
    age = EXCLUDED.age;

W tym przykładzie, jeśli już jest student z ID, który chcemy dodać, jego dane zostaną zaktualizowane:

ON CONFLICT (id) DO UPDATE SET
    name = EXCLUDED.name, 
    age = EXCLUDED.age;

Zwróć uwagę na magiczne słowo EXCLUDED. Oznacza ono "wartości, które próbowałeś wstawić, ale zostały wykluczone z powodu konfliktu".

Efekt:

  • Student z id = 1 zaktualizuje swoje dane (imię i wiek).
  • Studenci z id = 2 i id = 3 zostaną dodani do tabeli.

Przykład: ignorowanie konfliktów

Jeśli nie chcesz aktualizować danych, a tylko zignorować wiersze, które powodują konflikt, użyj DO NOTHING:

INSERT INTO students (id, name, age)
VALUES 
    (1, 'Peter', 22),  -- Ten student już jest
    (2, 'Anna', 20),  -- Nowy student
    (3, 'Mal', 25) -- Nowy student
ON CONFLICT (id) DO NOTHING;

Teraz konfliktujące wiersze po prostu nie zostaną wstawione, a reszta spokojnie zamieszka w Twojej bazie.

Logowanie błędów

Czasem ignorowanie lub aktualizacja to za mało. Na przykład, musisz zapisywać konflikty do późniejszej analizy. Możemy stworzyć specjalną tabelę do logowania błędów:

CREATE TABLE conflict_log (
    conflict_time TIMESTAMP DEFAULT NOW(),
    id INT,
    name TEXT,
    age INT,
    conflict_reason TEXT
);

Potem dodajemy obsługę błędów z logowaniem:

INSERT INTO students (id, name, age)
VALUES 
    (1, 'Peter', 22), 
    (2, 'Anna', 20), 
    (3, 'Mal', 25)
ON CONFLICT (id) DO UPDATE SET 
    name = EXCLUDED.name, 
    age = EXCLUDED.age
RETURNING EXCLUDED.id, EXCLUDED.name, EXCLUDED.age
INTO conflict_log;

Ostatni przykład zadziała tylko wewnątrz procedur składowanych. Jak to dokładnie działa, dowiesz się, gdy będziemy przerabiać PL-SQL. Trochę wybiegłem w przyszłość, ale chciałem pokazać jeszcze jeden sposób rozwiązania konfliktu ładowania danych: logowanie wszystkich problematycznych wierszy.

Teraz możesz analizować przyczyny konfliktów. Ta technika jest szczególnie przydatna w złożonych systemach, gdzie ważne jest zachowanie "śladów" podczas masowych ładowań danych.

Praktyczny przykład

Połączmy całą naszą wiedzę w jednym prostym zadaniu. Wyobraź sobie, że masz plik CSV z aktualizacjami studentów, który chcesz załadować do tabeli:

Plik students_update.csv

id name age
1 Otto 23
2 Anna 21
4 Wally 30

Ładowanie danych i obsługa konfliktów

  1. Najpierw tworzymy tymczasową tabelę tmp_students:
CREATE TEMP TABLE tmp_students (
  id   INTEGER,
  name TEXT,
  age  INTEGER
);
  1. Ładujemy dane z pliku używając \COPY:
\COPY tmp_students FROM 'students_update.csv' DELIMITER ',' CSV HEADER
  1. Wstawiamy dane z tymczasowej tabeli do stałej za pomocą INSERT ON CONFLICT:
INSERT INTO students (id, name, age)
SELECT id, name, age FROM tmp_students
ON CONFLICT (id) DO UPDATE
  SET name = EXCLUDED.name,
      age = EXCLUDED.age;

Teraz wszystkie dane, włącznie z aktualizacjami (wiersz z id = 1), zostały pomyślnie załadowane.

Typowe błędy i jak ich unikać

Błędy zdarzają się nawet najbardziej doświadczonym programistom, ale wiedząc, jak ich unikać, możesz zaoszczędzić godziny (a może i dni) nerwów.

  • Konflikt z ograniczeniem UNIQUE. Upewnij się, że wskazałeś poprawne pole w ON CONFLICT. Na przykład, jeśli wskażesz zły klucz (id zamiast email), PostgreSQL po prostu powie "do widzenia" Twojemu zapytaniu.
  • Nieprawidłowe użycie EXCLUDED. Ten alias odnosi się tylko do wartości przekazanych w bieżącym zapytaniu. Nie próbuj używać go w innych kontekstach.
  • Pominięcie kolumn. Upewnij się, że wszystkie kolumny wskazane w SET istnieją w tabeli. Na przykład, jeśli dodasz SET non_existing_column = 'value', dostaniesz błąd.

Użycie ON CONFLICT sprawia, że masowe ładowanie danych w PostgreSQL jest elastyczne i bezpieczne. Możesz nie tylko uniknąć wywalenia zapytań przez konflikty, ale też kontrolować, jak dokładnie obsługujesz swoje dane. Twoi użytkownicy (i serwery!) będą Ci wdzięczni.

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION