MySQL Hatası 1093 - FROM yan tümcesinde güncelleme için hedef tablo belirlenemiyor


593

story_categoryVeritabanımda bozuk girdiler içeren bir tablo var . Sonraki sorgu bozuk girdileri döndürür:

SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);

Onları çalıştırarak silmeye çalıştım:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category 
      INNER JOIN story_category ON category_id=category.id);

Ama bir sonraki hatayı alıyorum:

# 1093 - FROM yan tümcesinde güncelleme için 'story_category' hedef tablosunu belirleyemezsiniz

Bunun üstesinden nasıl gelebilirim?



2
Görünüşe göre MySQL hata izleyicisindeki özellik isteği burada: bir tabloyu güncelleyemiyor ve bir alt sorguda aynı tablodan seçim yapamıyorum
Ben Creasy

Yanıtlar:


713

Güncelleme: Bu cevap genel hata sınıflandırmasını kapsar. OP'nin tam sorgusunu en iyi nasıl ele alacağınız hakkında daha spesifik bir cevap için lütfen bu sorunun diğer cevaplarına bakın

MySQL'de, SELECT bölümünde kullandığınız tabloyu değiştiremezsiniz.
Bu davranış şu adreste belgelenmiştir: http://dev.mysql.com/doc/refman/5.6/en/update.html

Belki sadece masaya katılabilirsin

Mantık sorguyu yeniden şekillendirecek kadar basitse, alt sorguyu kaybedip uygun seçim ölçütlerini kullanarak tabloya kendiniz katılın. Bu, MySQL'in tabloyu iki farklı şey olarak görmesine neden olacak ve yıkıcı değişikliklerin devam etmesine izin verecektir.

UPDATE tbl AS a
INNER JOIN tbl AS b ON ....
SET a.col = b.col

Alternatif olarak, alt sorguyu daha derinden bir yan tümcesine yerleştirmeyi deneyin ...

Alt sorguya kesinlikle ihtiyacınız varsa, bir geçici çözüm vardır, ancak performans da dahil olmak üzere çeşitli nedenlerle çirkin:

UPDATE tbl SET col = (
  SELECT ... FROM (SELECT.... FROM) AS x);

FROM yan tümcesindeki iç içe geçmiş alt sorgu örtük bir geçici tablo oluşturur , bu nedenle güncelleştirdiğiniz tablo ile sayılmaz.

... ancak sorgu optimizatörüne dikkat edin

Bununla birlikte, MySQL 5.7.6 ve sonrasında, optimize edicinin alt sorguyu optimize edebileceğini ve size hala hata verebileceğini unutmayın. Neyse ki, optimizer_switchdeğişken bu davranışı kapatmak için kullanılabilir; Her ne kadar kısa vadeli bir düzeltme ya da küçük bir defalık görevler için bunu tavsiye edemem.

SET optimizer_switch = 'derived_merge=off';

Peter V. Mørch'a yorumlardaki bu tavsiye için teşekkürler .

Örnek teknik, orijinal olarak Nabble'da yayınlanan, burada açıklanmış ve genişletilmiş Baron Schwartz'dan alınmıştır .


1
Öğeleri silmek zorunda kaldım ve başka bir tablodan bilgi alamadım, aynı tablodan alt sorgu yapmak zorunda kaldım çünkü bu cevabı iptal ettiler. Bu hata için googling yaparken üstte ne bu yana bu benim için ve aynı tablodan alt sorgulama sırasında güncelleme çalışırken bir sürü insan için en uygun cevap olacaktır.
HMR

2
@Cheekysoft, Neden değerleri değişkenlere kaydetmiyorsunuz?
Pacerier

19
O, Dikkat üzerinde MySQL 5.7.6 , iyileştirici uzağa alt sorgu optimize edebilir ve hala sürece, size hata vermek SET optimizer_switch = 'derived_merge=off';:-(
Peter V. Morch

1
PeterV.Mørch ardından @ mysqlserverteam.com/derived-tables-in-mysql-5-7 , durumunda belli işlemleri olamaz birleştirme yürütülmektedir. Örneğin, türetilmiş kukla tabloya bir LIMIT (inifity) sağlayın ve hata asla oluşmayacaktır. Bu oldukça acayip ve MySQL'in gelecekteki sürümlerinin sorguları LIMIT ile birleştirmeyi destekleme riski hala var.
user2180613

Lütfen, bu geçici çözüm hakkında tam bir örnek verebilir misiniz? UPDATE tbl SET col = (SEÇ ... SEÇ (SEÇ ... SEÇ) AS x); hala hata alıyorum
JoelBonetR

310

NexusRex Sağlanan çok iyi bir çözüm aynı tablodan katılmak ile silmek için.

Eğer bunu yaparsan:

DELETE FROM story_category
WHERE category_id NOT IN (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
)

bir hata alırsınız.

Ancak koşulu bir daha seçerseniz:

DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
    ) AS c
)

doğru olanı yapardı !!

Açıklama: Sorgu iyileştirici , ilk sorgu için türetilmiş birleştirme optimizasyonu yapar (bu hata ile başarısız olmasına neden olur), ancak ikinci sorgu türetilmiş birleştirme optimizasyonu için uygun değildir . Bu nedenle, optimize edici önce alt sorguyu yürütmek zorunda kalır.


6
Belki bugün bir bağda olduğum için, ama belki de en iyi cevap olmasa bile, bu en kolay cevaptı.
Tyler

4
Bu harika çalıştı, teşekkürler! Peki buradaki mantık nedir? Bir seviye daha iç içe yerleştirilmişse dış kısımdan önce mi çalıştırılır? Ve eğer iç içe değilse, silme işlemi masaya kilitlendikten sonra mySQL çalıştırmayı dener?
Flat Cat

43
Bu hata ve çözüm mantıklı değil ... ama işe yarıyor. Bazen MySQL geliştiricilerinin hangi ilaçlarda olduğunu merak ediyorum ...
Cerin

1
@Cerin ile aynı fikirde .. Bu tamamen saçma, ama işe yarıyor.
FastTrack

@ekonoval Çözüm için teşekkür ederim ama benim için minimum bir anlam ifade etmiyor, MySQL'i kandırıyor ve kabul ediyor gibi görünüyor, lol
deFreitas

106

inner joinSenin alt sorguda gereksizdir. Eğer girişleri silmek istediğiniz gibi görünüyor story_categorynerede category_iddeğil categorymasada.

Bunu yap:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category);

Bunun yerine:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN
         story_category ON category_id=category.id);

5
Bu en iyi cevap olmalı! Belki ilk yerine "yerine" silin.
hoyhoy

1
Bence DISTINCTburada gereksiz - daha iyi bir performans için;).
shA.t

1
Deli olmalıyım. Cevap, orijinalinde belirtilenle aynıdır.
Jeff Lowery

Bu benim de cevabım. Do not where inbirincil tablo alt sorgunun gerek yok bu yüzden, kimlik sütun üzerinde.
Richard

@JeffLowery - ilk kod bloğu burada cevaptır; sorunun ikinci kod bloğu.
ToolmakerSteve

95

Son zamanlarda aşağıdaki gibi yaptım aynı tabloda kayıtları güncellemek zorunda kaldı:

UPDATE skills AS s, (SELECT id  FROM skills WHERE type = 'Programming') AS p
SET s.type = 'Development' 
WHERE s.id = p.id;

11
Bu sadece olarak yazılamaz UPDATE skills SET type='Development' WHERE type='Programming';mı? Bu orijinal soruyu cevaplıyor gibi görünmüyor.
lilbyrdie

1
Overkill gibi görünüyor, @lilbyrdie doğru - sadece olabilir UPDATE skills SET type='Development' WHERE type='Programming';. Neden bu kadar çok insanın yaptıklarını düşünmediğini anlamıyorum ...
shadyyx

1
buradaki en iyi cevap bu, IMHO. Sözdizimini anlamak kolaydır, önceki ifadenizi tekrar kullanabilirsiniz ve bazı süper özel durumlar ile sınırlı değildir.
Steffen Winkler

2
Örnek olay şüpheli olsa bile, prensip gerçek dünya senaryolarında anlaşılması ve uygulanması en iyisidir. Gösterilen sorgu, tablo listesindeki örtük birleştirmenin bir alt sorgu için geçici bir tablo oluşturması nedeniyle çalışır, böylece kararlı sonuçlar sağlar ve aynı tablonun alınması ve değiştirilmesi arasında çakışmadan kaçınır.
AnrDaemon

1
herhangi birinin bu aşırıya kaçma hakkında ne söyleyebileceğinden bağımsız olarak, yine de soru başlığı altında cevaplıyor. İç içe geçmiş bir sorguda aynı tablo kullanılarak bir güncelleme denendiğinde belirtilen OP de karşılaşılan hata
smac89

35
DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category INNER JOIN story_category ON category_id=category.id
    ) AS c
)

3
Bunun neden işe yaradığını açıklayabilir misiniz, neden sadece bir seviye daha işe yarıyor? Bu soru zaten @ EkoNoval'ın sorusuna bir yorum olarak sorulmuştur, ancak kimse cevap vermedi. Belki yardım edebilirsin.
Akshay Arora

@AkshayArora, @ Cheekysoft'un cevabında 'Belki de masaya kendi başınıza katılabilirsiniz' başlığı altındaki bölümü gözden geçirin. Dedi UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col- bu aynı tablo için burada kullanılan farklı bir takma ad olarak çalışır. Benzer şekilde @ NexusRex'in cevabında, ilk SELECTsorgu story_categoryikinci kez kullanılan türetilmiş bir tablo gibi davranır . Bu yüzden OP'de bahsedilen hata burada olmamalı, değil mi?
Istiaque Ahmed

31

Eğer yapamazsan

UPDATE table SET a=value WHERE x IN
    (SELECT x FROM table WHERE condition);

aynı tablo olduğu için kandırıp şunları yapabilirsiniz:

UPDATE table SET a=value WHERE x IN
    (SELECT * FROM (SELECT x FROM table WHERE condition) as t)

[güncelleme veya silme ya da her neyse]


Yukarıdaki tüm cevapların en anlaşılır ve mantıklı uygulaması. Basit ve noktaya.
Clain Dsilva

Bu benim iş akışımın bir parçası haline geldi. Bu hatayı kaçırmayın, bu yanıta gidin ve sorguyu düzeltin. Teşekkürler.
Vaibhav

13

En az bir satırın Öncelik = 1 içerdiğinden emin olmak için bir tabloda ve WHERE yan tümcesinde aynı tabloda bir alt sorgu kullanarak bir Öncelik sütun değerini 1 ile güncellemek için yaptığım şey budur (çünkü güncelleme yapılırken kontrol edilecek koşul):


UPDATE My_Table
SET Priority=Priority + 1
WHERE Priority >= 1
AND (SELECT TRUE FROM (SELECT * FROM My_Table WHERE Priority=1 LIMIT 1) as t);

Biraz çirkin olduğunu biliyorum ama iyi çalışıyor.


1
@anonymous_reviewer: Birinin yorumuna [-1], hatta [+1] vermesi durumunda, lütfen bunu neden verdiğinizi de belirtin. Teşekkürler!!!
sactiw

1
-1 çünkü bu yanlış. SELECT deyiminde kullandığınız aynı tabloyu değiştiremezsiniz.
Chris

1
@Chris MySQL üzerinde doğruladım ve benim için iyi çalışıyor, bu yüzden lütfen sonunda doğrulamanızı ve daha sonra doğru veya yanlış olduğunu iddia etmenizi rica ediyorum. Teşekkürler!!!
sactiw

1
Bu sayfanın altında 'Şu anda bir tabloyu güncelleyemez ve alt sorgudaki aynı tablodan seçim yapamazsınız' yazıyor. - ve bunu birçok kez doğru olarak deneyimledim. dev.mysql.com/doc/refman/5.0/en/update.html
Chris

19
@Chris biliyorum, ama bunun için bir geçici çözüm var ve tam da 'UPDATE' sorgumla göstermeye çalıştığım ve bana inanıyorum ki gayet iyi çalışıyor. Sorgumu gerçekten doğrulamaya çalıştığınızı sanmıyorum.
sactiw

6

Bunu yapmanın en basit yolu, alt sorgu içindeki üst sorgu tablosuna başvururken tablo diğer adı kullanmaktır.

Misal :

insert into xxx_tab (trans_id) values ((select max(trans_id)+1 from xxx_tab));

Bunu şu şekilde değiştirin:

insert into xxx_tab (trans_id) values ((select max(P.trans_id)+1 from xxx_tab P));

5

İstediğiniz satırların kimliklerini geçici tabloya ekleyebilir ve ardından bu tabloda bulunan tüm satırları silebilirsiniz.

@Cheekysoft'un iki adımda bunu kastettiği anlamına gelebilir.


3

Göre MySQL GÜNCELLEME Syntax @CheekySoft ile bağlantılı, doğru alt kısmında diyor.

Şu anda, bir tabloyu güncelleyemez ve bir alt sorguda aynı tablodan seçim yapamazsınız.

Sanırım sendikada hala seçerken store_category'den siliyorsunuz.


3

OP'nin elde etmeye çalıştığı belirli sorgu için, bunu yapmanın ideal ve en etkili yolu bir alt sorgu kullanmak DEĞİLDİR.

İşte LEFT JOINOP'nin iki sorgusunun sürümleri:

SELECT s.* 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

Not: tablodaki DELETE ssilme işlemlerini kısıtlar story_category.
belgeleme

DELETE s 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

1
Bunun daha fazla oyu yok. Ayrıca, çok tablolu sözdiziminin UPDATEifadelerle ve birleştirilmiş alt sorgularla da çalıştığına dikkat edilmelidir . Bunun LEFT JOIN ( SELECT ... )aksine WHERE IN( SELECT ... )çalışmanıza izin vererek , birçok kullanım durumunda uygulamayı kullanışlı hale getirir.
fyrye

2

Bir şey işe yaramazsa, ön kapıdan gelirken arka kapağı alın:

drop table if exists apples;
create table if not exists apples(variety char(10) primary key, price int);

insert into apples values('fuji', 5), ('gala', 6);

drop table if exists apples_new;
create table if not exists apples_new like apples;
insert into apples_new select * from apples;

update apples_new
    set price = (select price from apples where variety = 'gala')
    where variety = 'fuji';
rename table apples to apples_orig;
rename table apples_new to apples;
drop table apples_orig;

Hızlı. Veri ne kadar büyük olursa, o kadar iyidir.


11
Ve tüm yabancı anahtarlarınızı kaybettiniz ve belki de bazı basamaklı silmeleriniz var.
Walf

2

Select deyiminin sonucunu ayrı bir değişkene kaydetmeye çalışın ve ardından sorguyu silmek için bunu kullanın.


2

bunu dene

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc;

1

bu sorguya ne dersin yardımcı olur

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL

Sonuç şunu gösterir: '# 1064 - SQL sözdiziminizde bir hata var; 'LEFT JOIN (SELECT category.id FROM kategorileri) cat ON story_category.id = cat' yakınında kullanılacak doğru sözdizimi için MariaDB sunucu sürümünüze karşılık gelen kılavuzu kontrol edin. 1 '
Istiaque Ahmed

Çok masalı silme özelliğini kullanırken, etkilenen tabloları belirtmeniz gerekir. DELETE story_category FROM ...ancak, bu bağlamda birleştirilmiş alt sorgu gerekli değildir ve kullanılarak gerçekleştirilebilir LEFT JOIN category AS cat ON cat.id = story_category.category_id WHERE cat.id IS NULL, Cevabın birleşme ölçütlerine yanlış başvurulmasına dikkat edinstory_category.id = cat.id
fyrye

0

Endişelere gelince, içinde story_categoryolmayan satırları silmek istiyorsunuz category.

Silinecek satırları belirlemek için orijinal sorgunuz:

SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id
);

Orijinal tablo NOT INolan bir alt sorgu ile birleştirilmesi JOINgereksiz yere kıvrık görünüyor. Bu, not existsilişkili bir alt sorgu ile daha basit bir şekilde ifade edilebilir :

select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id);

Şimdi bunu bir deleteifadeye dönüştürmek kolaydır :

delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);    

Bu sorgu herhangi bir MySQL sürümünde ve bildiğim diğer birçok veritabanında çalışır.

DB Fiddle hakkında demo :

-- set-up
create table story_category(category_id int);
create table category (id int);
insert into story_category values (1), (2), (3), (4), (5);
insert into category values (4), (5), (6), (7);

-- your original query to identify offending rows
SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);
| category_id |
| ----------: |
| 1 |
| 2 |
| 3 |
-- a functionally-equivalent, simpler query for this
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id)
| category_id |
| ----------: |
| 1 |
| 2 |
| 3 |
-- the delete query
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);

-- outcome
select * from story_category;
| category_id |
| ----------: |
| 4 |
| 5 |
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.