DELETE usuwa wiersze i nic więcej
DELETE wyjmuje wiersze z tabeli. Nie usuwa tabeli, nie zmienia jej schematu i nie dotyka innych tabel (chyba że skonfigurowano kaskady). Składnia jest krótka:
DELETE FROM users WHERE id = 2; znajduje wiersze spełniające warunek i je usuwa. Pozostałe dwa wiersze są nietknięte. Sama tabela wciąż istnieje i możesz dalej do niej wstawiać.
Model myślowy: DELETE to SELECT, który zamiast zwracać pasujące wiersze, wyrzuca je.
Całą pracę wykonuje klauzula WHERE
Każde poważne DELETE stoi i upada na klauzuli WHERE. Zapiszesz ją dobrze, a usuniesz to, co zamierzasz. Zapiszesz ją źle, a usuniesz więcej, czasem całą tabelę.
Oba nieopublikowane szkice bez wyświetleń zniknęły. Opublikowane wiersze przetrwały, bo warunek do nich nie pasował. Możesz użyć każdego wyrażenia, które przyjmuje WHERE: IN, LIKE, BETWEEN, podzapytań i kombinacji AND/OR.
Nawyk, który warto wyrobić: zanim uruchomisz DELETE, najpierw uruchom tę samą klauzulę WHERE jako SELECT.
-- Podgląd tego, co zniknie:
SELECT * FROM posts WHERE published = 0 AND views = 0;
-- Wiersze się zgadzają? Teraz je usuń:
DELETE FROM posts WHERE published = 0 AND views = 0;
Ten dwuetapowy taniec uratował więcej baz niż wszystkie narzędzia do kopii zapasowych razem wzięte.
DELETE bez WHERE opróżnia tabelę
Pomiń WHERE, a DELETE usunie każdy wiersz:
Tabela jest pusta, ale wciąż istnieje. SQLite nie ma instrukcji TRUNCATE: DELETE FROM table; jest jej odpowiednikiem, a SQLite stosuje wewnętrzną "truncate optimization", która zwalnia wszystkie strony naraz, zamiast usuwać wiersze pojedynczo. To szybkie, ale wciąż jest to operacja transakcyjna, którą można wycofać.
Jeśli klucz główny używa AUTOINCREMENT, licznik sam się nie zresetuje. Aby id znów zaczynały się od 1, wyczyść też wiersz sekwencji:
DELETE FROM log;
DELETE FROM sqlite_sequence WHERE name = 'log';
Przy zwykłym INTEGER PRIMARY KEY (bez AUTOINCREMENT) SQLite i tak swobodnie używa id ponownie, więc nie jest to potrzebne.
Usuwanie kilku konkretnych wierszy
IN to najczystszy sposób na usunięcie znanego zestawu wierszy:
Usuwanie możesz też sterować podzapytaniem, co przydaje się, gdy wiersze do usunięcia wyznacza złączenie albo inna tabela:
SQLite nie obsługuje składni DELETE ... JOIN tak jak MySQL, ale podzapytanie w WHERE wykonuje to samo zadanie.
RETURNING: zobacz, co usunięto
Dodaj RETURNING, aby dostać usunięte wiersze jako zbiór wyników, tak jak z SELECT:
Dostajesz id i email każdego usuniętego wiersza. To bezcenne przy:
- Logowaniu dokładnie tego, co zostało usunięte.
- Budowaniu funkcji cofania (zwrócone wiersze odkładasz gdzieś na bok).
- Potwierdzaniu w jednej rundzie, że usunięcie objęło oczekiwane wiersze.
RETURNING działa z INSERT, UPDATE i DELETE. Szczegółowo omawia go osobna strona.
ON DELETE CASCADE dla powiązanych wierszy
Gdy tabela nadrzędna i podrzędna są połączone kluczem obcym, usunięcie wiersza nadrzędnego zostawia osierocone wiersze podrzędne, chyba że każesz SQLite działać kaskadowo:
Usunięcie autora usuwa też jego książki. Bez ON DELETE CASCADE to samo DELETE albo by się udało i zostawiło osierocone książki (gdy klucze obce są wyłączone), albo zakończyło się błędem ograniczenia (gdy są włączone).
Największa pułapka: w SQLite klucze obce są domyślnie wyłączone. Musisz uruchamiać PRAGMA foreign_keys = ON; w każdym połączeniu. Jeśli pragma nie jest ustawiona, ON DELETE CASCADE jest po cichu ignorowane i książki zostają. Większość sterowników aplikacyjnych ustawia to za ciebie albo udostępnia opcję: sprawdź swój.
Inne opcje kaskadowe, które warto znać: ON DELETE SET NULL (czyści klucz obcy), ON DELETE RESTRICT (odmawia usunięcia, jeśli istnieją wiersze podrzędne), ON DELETE NO ACTION (domyślna, w większości przypadków działa jak RESTRICT).
DELETE z LIMIT (opcja kompilacji)
Niektóre kompilacje SQLite obsługują DELETE ... LIMIT, przydatne do stopniowego usuwania danych z ogromnych tabel partiami:
DELETE FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000;
Wymaga to SQLite skompilowanego z SQLITE_ENABLE_UPDATE_DELETE_LIMIT. Oficjalne pliki binarne i większość bibliotek dla języków (sqlite3 w Pythonie, better-sqlite3 w Node) mają to włączone. Jeśli twoja nie ma, dostaniesz błąd składni. Wtedy użyj podzapytania:
DELETE FROM logs
WHERE id IN (
SELECT id FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000
);
Usuwanie partiami utrzymuje małe transakcje, co ma znaczenie, gdy inne połączenia czytają bazę.
Duże usunięcia opakuj w transakcję
DELETE jest domyślnie transakcyjne: albo znika każdy pasujący wiersz, albo żaden. Ale gdy zamierzasz usunąć dużo, jawna transakcja pozwala zrobić ROLLBACK, jeśli coś wygląda podejrzanie:
ROLLBACK całkowicie cofa usunięcie. W prawdziwej sesji zrobisz COMMIT, gdy liczba wierszy będzie się zgadzać. Transakcje są też dużo szybsze, gdy usuwasz wiele wierszy po jednej instrukcji naraz: opakowanie pętli w BEGIN/COMMIT pozwala uniknąć jednego fsync na każde usunięcie.
Czego DELETE nie usuwa
Kilka częstych nieporozumień, na które warto zwrócić uwagę:
DELETE FROM table;opróżnia tabelę, ale jej nie usuwa. Aby usunąć samą tabelę, użyjDROP TABLE table;.DELETEnie zmniejsza pliku bazy. Strony są oznaczane jako wolne do ponownego użycia. Aby odzyskać miejsce na dysku, uruchomVACUUM;(omówione w rozdziale o wydajności).- Usunięcie wiersza nie usuwa wierszy podrzędnych w innych tabelach, chyba że ustawiono
ON DELETE CASCADEi włączono klucze obce. DELETE, które nie pasuje do żadnego wiersza, nie jest błędem. To udana instrukcja zchanges() = 0. Jeśli musisz to wiedzieć, sprawdź liczbę wierszy.
Dalej: UPSERT
Często tak naprawdę nie chcesz usuwać, tylko wstawić wiersz, jeśli jest nowy, albo zaktualizować go, jeśli już istnieje. SQLite nazywa to UPSERT, a klauzula ON CONFLICT zamienia to w jedną instrukcję zamiast trzech. O tym na następnej stronie.
Najczęściej zadawane pytania
Jak usunąć wiersz w SQLite?
Użyj DELETE FROM table_name WHERE condition;. Klauzula WHERE wybiera, które wiersze znikną. Na przykład DELETE FROM users WHERE id = 7; usuwa jednego użytkownika o id 7. Bez WHERE usuwany jest każdy wiersz tabeli.
Jak usunąć wszystkie wiersze z tabeli SQLite?
Uruchom DELETE FROM table_name; bez klauzuli WHERE. SQLite nie ma instrukcji TRUNCATE: DELETE bez filtra jest jej odpowiednikiem, a SQLite optymalizuje go wewnętrznie (tak zwana 'truncate optimization'). Aby zresetować też liczniki AUTOINCREMENT, usuń potem odpowiedni wpis z sqlite_sequence.
Czy SQLite potrafi kaskadowo usuwać wiersze w powiązanych tabelach?
Tak, jeśli zadeklarujesz ON DELETE CASCADE na kluczu obcym i włączysz klucze obce przez PRAGMA foreign_keys = ON;. W SQLite klucze obce są domyślnie wyłączone, więc ta pragma ma znaczenie: bez niej kaskady są po cichu ignorowane.
Jak zobaczyć, które wiersze zostały usunięte?
Dodaj klauzulę RETURNING: DELETE FROM users WHERE active = 0 RETURNING id, email; zwraca usunięte wiersze tak jak SELECT. Przydaje się to do logowania, funkcji cofania albo potwierdzenia, że usunięto dokładnie to, co miało zniknąć.