Mam story_category
bazę danych w mojej bazie danych z uszkodzonymi wpisami. Następne zapytanie zwraca uszkodzone wpisy:
SELECT *
FROM story_category
WHERE category_id NOT IN (
SELECT DISTINCT category.id
FROM category INNER JOIN
story_category ON category_id=category.id);
Próbowałem je usunąć, wykonując:
DELETE FROM story_category
WHERE category_id NOT IN (
SELECT DISTINCT category.id
FROM category
INNER JOIN story_category ON category_id=category.id);
Ale pojawia się następny błąd:
# 1093 - Nie można określić tabeli docelowej „story_category” do aktualizacji w klauzuli FROM
Jak mogę to przezwyciężyć?
mysql
subquery
sql-delete
mysql-error-1093
Sergio del Amo
źródło
źródło
Odpowiedzi:
Aktualizacja: Ta odpowiedź obejmuje ogólną klasyfikację błędów. Aby uzyskać bardziej szczegółową odpowiedź na temat tego, jak najlepiej obsłużyć dokładne zapytanie OP, zobacz inne odpowiedzi na to pytanie
W MySQL nie można modyfikować tej samej tabeli, której używasz w części SELECT.
To zachowanie jest udokumentowane na stronie: http://dev.mysql.com/doc/refman/5.6/en/update.html
Może po prostu przyłączysz się do stołu
Jeśli logika jest wystarczająco prosta, aby zmienić kształt zapytania, należy utracić podzapytanie i dołączyć tabelę do siebie, stosując odpowiednie kryteria wyboru. Spowoduje to, że MySQL zobaczy tabelę jako dwie różne rzeczy, co pozwoli na destrukcyjne zmiany.
Alternatywnie spróbuj zagnieżdżić podkwerendę głębiej w klauzuli from ...
Jeśli absolutnie potrzebujesz podzapytania, istnieje obejście, ale jest brzydkie z kilku powodów, w tym wydajności:
Zagnieżdżone podzapytanie w klauzuli FROM tworzy domyślną tabelę tymczasową , więc nie jest liczona jako ta sama tabela, którą aktualizujesz.
... ale uważaj na optymalizator zapytań
Pamiętaj jednak, że od MySQL 5.7.6 i późniejszych optymalizator może zoptymalizować podkwerendę i nadal wyświetla błąd. Na szczęście
optimizer_switch
zmienna może zostać użyta do wyłączenia tego zachowania; chociaż nie mogłem zalecić robienia tego jako czegoś więcej niż krótkoterminowej naprawy lub w przypadku drobnych, jednorazowych zadań.Dziękuję Peterowi V. Mørchowi za tę radę w komentarzach.
Przykładowa technika pochodzi od barona Schwartza, pierwotnie opublikowana w Nabble , sparafrazowana i rozszerzona tutaj.
źródło
SET optimizer_switch = 'derived_merge=off';
:-(NexusRex zapewnił bardzo dobre rozwiązanie do usuwania z łączenia z tej samej tabeli.
Jeśli to zrobisz:
dostaniesz błąd.
Ale jeśli owiniesz warunek jeszcze jednym, wybierz:
zrobiłoby to właściwą rzecz !!
Objaśnienie: Optymalizator kwerendy wykonuje pochodną optymalizację scalania dla pierwszego zapytania (co powoduje błąd z błędem), ale drugie zapytanie nie kwalifikuje się do optymalizacji pochodnej scalania . Dlatego optymalizator jest zmuszony najpierw wykonać podzapytanie.
źródło
W
inner join
pod-zapytaniu nie jest konieczne. Wygląda na to, że chcesz usunąć wpisy,story_category
którychcategory_id
nie ma wcategory
tabeli.Zrób to:
Zamiast tego:
źródło
DISTINCT
jest tu potrzebny - dla lepszej wydajności;).where in
kolumny identyfikatora, więc nie musisz subquery podstawowej tabeli.Ostatnio musiałem zaktualizować rekordy w tej samej tabeli, co zrobiłem jak poniżej:
źródło
UPDATE skills SET type='Development' WHERE type='Programming';
? To nie wydaje się odpowiadać na pierwotne pytanie.UPDATE skills SET type='Development' WHERE type='Programming';
. Nie rozumiem, dlaczego tak wielu ludzi nie myśli o tym, co robią ...źródło
UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col
- działałoby to jako inny alias dla tej samej tabeli. Podobnie w odpowiedzi @ NexusRex pierwszeSELECT
zapytanie działa jak tabela pochodna, w którejstory_category
jest używana po raz drugi. Zatem błąd wymieniony w PO nie powinien mieć miejsca tutaj, prawda?Jeśli nie możesz
ponieważ jest to ten sam stół, możesz oszukać i zrobić:
[zaktualizuj lub usuń lub cokolwiek]
źródło
Tak zrobiłem, aby zaktualizować wartość kolumny Priority o 1, jeśli jest ona> = 1 w tabeli i jej klauzuli WHERE, używając podzapytania w tej samej tabeli, aby upewnić się, że co najmniej jeden wiersz zawiera Priority = 1 (ponieważ to był warunek do sprawdzenia podczas aktualizacji):
Wiem, że to trochę brzydkie, ale działa dobrze.
źródło
Najprostszym sposobem na to jest użycie aliasu tabeli, gdy odwołujesz się do nadrzędnej tabeli zapytań wewnątrz zapytania podrzędnego.
Przykład:
Zmień na:
źródło
Możesz wstawić żądane identyfikatory wierszy do tabeli tymczasowej, a następnie usunąć wszystkie wiersze znajdujące się w tej tabeli.
co może oznaczać @Cheekysoft, robiąc to w dwóch krokach.
źródło
Zgodnie ze składnią Mysql UPDATE połączoną przez @CheekySoft, napisano na samym dole.
Wydaje mi się, że usuwasz z store_category, wciąż wybierając z niej w unii.
źródło
W przypadku konkretnego zapytania, które OP próbuje osiągnąć, idealnym i najbardziej wydajnym sposobem na to NIE jest wcale korzystanie z podzapytania.
Oto
LEFT JOIN
wersje dwóch zapytań PO:Uwaga:
DELETE s
ogranicza operacje usuwania dostory_category
tabeli.Dokumentacja
źródło
UPDATE
instrukcjami i połączonymi pod-zapytaniami. Umożliwiając działanieLEFT JOIN ( SELECT ... )
w przeciwieństwie doWHERE IN( SELECT ... )
, dzięki czemu implementacja jest przydatna w wielu przypadkach użycia.Jeśli coś nie działa, przechodząc przez drzwi frontowe, weź tylne drzwi:
To jest szybkie. Im większe dane, tym lepiej.
źródło
Spróbuj zapisać wynik instrukcji Select w osobnej zmiennej, a następnie użyj go do usunięcia zapytania.
źródło
Spróbuj tego
źródło
co powiesz na to zapytanie? Mam nadzieję, że to pomoże
źródło
DELETE story_category FROM ...
Jednak dołączył sub-query nie jest konieczne w tym kontekście i może być wykonywane za pomocąLEFT JOIN category AS cat ON cat.id = story_category.category_id WHERE cat.id IS NULL
urządzenia, należy przestrzegać dołączyć kryteria w odpowiedzi błędnego odwołaniastory_category.id = cat.id
Jeśli chodzi o sprawy, chcesz usunąć wiersze
story_category
, które nie istnieją wcategory
.Oto twoje oryginalne zapytanie, aby zidentyfikować wiersze do usunięcia:
Łączenie
NOT IN
z podzapytaniem, któreJOIN
jest oryginalną tabelą, wydaje się niepotrzebnie zawiłe. Można to wyrazić w prostszy sposób za pomocąnot exists
i skorelowanego podkwerendy:Teraz łatwo jest zmienić to w
delete
stwierdzenie:To pytanie działałoby na dowolnej wersji MySQL, a także w większości innych baz danych, które znam.
Demo na DB Fiddle :
źródło