MySQL'de Join ile sil


501

İşte benim tabloları oluşturmak için komut dosyası:

CREATE TABLE clients (
   client_i INT(11),
   PRIMARY KEY (client_id)
);
CREATE TABLE projects (
   project_id INT(11) UNSIGNED,
   client_id INT(11) UNSIGNED,
   PRIMARY KEY (project_id)
);
CREATE TABLE posts (
   post_id INT(11) UNSIGNED,
   project_id INT(11) UNSIGNED,
   PRIMARY KEY (post_id)
);

PHP kodumda, bir istemciyi silerken, tüm proje yayınlarını silmek istiyorum:

DELETE 
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id;

Mesajlar tablosunda client_idyalnızca bir yabancı anahtar yoktur project_id. Başarılı olan projelerdeki yayınları silmek istiyorum client_id.

Hiçbir yayın silinmediği için bu şu an çalışmıyor.


10
Sanırım Yehosef cevabı kabul edilen cevap olmalı, çünkü istediğin gibi Join kullanıyor ve yukondude'un önerdiği gibi bir IN cümlesi kullanmaktan daha iyi performans gösterdiğini düşünüyorum ...
Gerardo Grignoli

2
Tercih edilen desen, bir desen DELETE posts FROM posts JOIN projects ...yerine IN (subquery)a'dır. (Yehosef'in cevabı tercih edilen kalıbın bir örneğini veriyor.)
spencer7593

@GerardoGrignoli, belirli bir motor veya MySQL sürümü için daha iyi performans gösterir mi? AFAIK aynı olduklarından, iki sorgunun farklı bir şekilde işlemesi için hiçbir neden yoktur. Tabii ki, her zaman için bir nikel olsaydı sorgu iyileştirici aptalca bir şey yaptı ....
Paul Draper

aliasTablo adı için de kullanabilir ve kullanabilirsiniz.
biniam

Yanıtlar:


1254

Sadece girişleri poststablodan silmek istediğinizi belirtmeniz gerekir :

DELETE posts
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

EDIT: Daha fazla bilgi için bu alternatif cevabı görebilirsiniz


117
Bu doğru yanıt olduğuna dikkat edilmelidir çünkü birleştirme, silme tablosu artık açık olmadığından, normal mesajlardan "SİLİNDEN SİL" yerine "Mesajları SİLİNMEYİ SİL" seçeneğini kullanmaya zorlar. Teşekkürler!
siannopollo

8
Burada 'as' yöntemini kullanamayacağınızı unutmayın, örn. İç birleştirme projeleri p.project_id üzerinde p olarak ...
zzapper

14
Aslında, birleştirilen tablolar için bir takma ad kullanabilirsiniz, ancak ana tablo (gönderiler) için kullanamazsınız. "Mesajlardan DELETE yayınlardan SİL INNER JOIN projects p ON p.project_id = posts.project_id"
Weboide 29:12

85
"Bir tablo için takma adDELETE d FROM posts AS d JOIN projects AS p ON ...
bildirirseniz

14
bu en iyi yanıttır çünkü her iki tablodan da tek bir işlemle silebilirsinizDELETE posts , projects FROM posts INNER JOIN projects ON projects.project_id = posts.project_id WHERE projects.client_id = :client_id
Developerium

84

Birden çok tablo seçtiğiniz için, Silinecek tablo artık açık değil. Sen gerekir seçmek :

DELETE posts FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

Bu durumda table_name1ve table_name2aynı tablodur, bu yüzden bu işe yarar:

DELETE projects FROM posts INNER JOIN [...]

İsterseniz iki tablodan da silebilirsiniz:

DELETE posts, projects FROM posts INNER JOIN [...]

Bunu unutmayın order byve limit çoklu tablo silme işlemlerinde çalışmaz .

Ayrıca, bir tablo için takma ad bildirirseniz, tabloya atıfta bulunurken takma adı kullanmanız gerektiğini unutmayın:

DELETE p FROM posts as p INNER JOIN [...]

Carpetsmoker vb . Katkılar .


3
Daha iyi bir yanıt alıyorsanız - en azından SQL'i daha okunabilir hale getirebilirsiniz. Bu sorudaki diğer tüm SQL referansları büyük harfli anahtar kelimelere sahiptir (0 oylu olanlar hariç). Düzenlemelerimi neden geri aldığınızdan emin değilim.
Yehosef

3
@Yehosef, CAPS'ı gerçekten göze batan bulan bir grup insan var. Tek kişi olmadığımı düşünüyorum, birkaç kişinin de küçük harf tarzı olduğunu gördüm.
Pacerier

1
yeterince adil - Cevabınızı istediğiniz tarzda yazma hakkınıza saygı duyuyorum;)
Yehosef

7
Büyük / küçük harf tartışmasına eklemek için doğru, istediğiniz stili kullanabilirsiniz, ancak cevabınızda aslında uygun bulduğunuz stilleri karıştırıyorsunuz - ONbüyük harfle yazılıyor. Daha az deneyimli geliştiriciler için, stil açısından dağınık ve tutarsız olmanın uygun olduğunu gösterebilir.
Gölge

16
CAPS ANAHTAR KELİMELER göze batan değil. QUERIES'I OKUYABİLİYORLAR;)
Sachem

50

Ya da aynı şey, biraz farklı (IMO dostu) sözdizimi ile:

DELETE FROM posts 
USING posts, projects 
WHERE projects.project_id = posts.project_id AND projects.client_id = :client_id;

BTW, mysql ile birleştirme kullanarak neredeyse her zaman alt sorgulardan daha hızlıdır ...


KULLANIM ne demektir?
CMCDragonkai

1
Şunun için iyi bir açıklama USING: stackoverflow.com/questions/11366006/mysql-on-vs-using
bigtex777

10
@ bigtex777: SELECT ifadelerinde USING anahtar kelimesinin bir DELETE ifadesinde aynı anahtar kelimeyle ilgisi olmadığını lütfen unutmayın.
SELECTs öğesinde

42

Ayrıca benim veritabanı üzerinde kullanılan çalışır gibi ALIAS kullanabilirsiniz! t tablo silme ihtiyacı var!

DELETE t FROM posts t
INNER JOIN projects p ON t.project_id = p.project_id
AND t.client_id = p.client_id

1
tablo adlarını yinelemekten kaçınmak için bileşik anahtar birleşimlerinde yararlıdır
Marquez

1
"Aslında birleştirilen tablolar için bir takma ad kullanabilirsiniz, ancak ana tablo (gönderiler) için kullanamazsınız. 'Gönderilerden yayınları SİLİN INNER JOIN projeleri p ON p.project_id = posts.project_id'" - @ Weboide
Jeaf Gilbert

2
Aslında ( dev.mysql.com/doc/refman/5.0/tr/delete.html adresinden alıntılar ) "Bir tablo için takma ad bildirirseniz , tabloya atıfta bulunurken takma adı kullanmanız gerekir: DELETE t1 FROM test AS t1, test2 NEREDE ... "yani takma ad kullanmak iyidir.
Peter Bowers

25

Ben daha bu alt sorgu çözümü için alışkınım, ama MySQL denemedim:

DELETE  FROM posts
WHERE   project_id IN (
            SELECT  project_id
            FROM    projects
            WHERE   client_id = :client_id
        );

85
SQL'de IN anahtar sözcüğünü kullanmaktan gerçekten kaçınmalısınız (yeni başlayanlar için genellikle anlaşılması daha kolay olsa da) ve bunun yerine (mümkün olduğunda) JOIN'i kullanmalısınız, çünkü alt sorgular genellikle işleri daha yavaş hale getirir.
user276648

14
Bu, alt sorgu tarafından döndürülen çok sayıda satır olduğunda DB'yi çökme eğilimindedir. Aynı zamanda son derece yavaştır.
Raj

2
@yukondude Gerçekten de "IN" nin 1'de "JOIN" dan anlaşılması çok daha kolaydır ve bu yüzden SQL'e gerçekten aşina olmayan insanlar her yerde "IN" yazarken, daha iyi performans gösteren "JOIN" kullanabilirler ( veya sorguya bağlı olarak bir whooole lotu). Birkaç yıl önce hatırlıyorum, neredeyse tüm SQL sorgularım, iyi sorguların nasıl yazılacağını bilen biri tarafından yeniden yazılacaktı. Bu yüzden "IN" 'den kaçınmak için yorumu ekledim, böylece insanlar mümkünse kullanmaktan kaçınmaları gerektiğini biliyorlardı.
user276648

10
Bu sayfaya gelmemizin nedeni, IN ifadesiyle yazdığım sorgunun yavaş olması. Kesinlikle burada kabul edilen cevap kaçının.
MikeKulls

4
Bu soru kadar eskiyse, NEDEN IN yerine JOIN kullanmanız gerektiğini bilmek önemlidir. Koşul bir satırda çalıştığında, IN içinde bu sorguyu çalıştırır. Bu, söz konusu WHERE ile karşılaştırılması gereken 100 satır varsa, bu alt sorgunun 100 kez çalıştırılacağı anlamına gelir. Oysa bir JOIN sadece ONCE ile çalışır. Yani, db gittikçe büyüdükçe, bu sorgunun tamamlanması daha uzun sürecektir. @markus bir şeyin kritik olmaması nedeniyle kötü kod yazmanız gerektiği anlamına gelmez. Biraz daha iyi yazmak, gelecekte çok zaman ve baş ağrısından tasarruf etmenizi sağlayacaktır. :)
RisingSun

11

JOIN ile MySQL DELETE kayıtları

Diğer tablolarda karşılık gelen kayıtları olan bir tablodan kayıtları seçmek için genellikle SELECT deyiminde INNER JOIN kullanırsınız. INNER JOIN yan tümcesini DELETE deyimiyle ve diğer tablolardaki karşılık gelen kayıtları silmek için de kullanabiliriz, örneğin, belirli bir koşulu karşılayan T1 ve T2 tablolarındaki kayıtları silmek için aşağıdaki ifadeyi kullanırsınız:

DELETE T1, T2
FROM T1
INNER JOIN T2 ON T1.key = T2.key
WHERE condition

T1 ve T2 tablo adlarını DELETE ve FROM arasına koyduğunuza dikkat edin. T1 tablosunu atlarsanız, DELETE deyimi yalnızca T2 tablosundaki kayıtları siler ve T2 tablosunu atlarsanız, yalnızca T1 tablosundaki kayıtlar silinir.

Birleştirme koşulu T1.key = T2.key, T2 tablosunda silinmesi gereken karşılık gelen kayıtları belirtir.

WHERE yan tümcesindeki koşul, T1 ve T2'deki silinmesi gereken kayıtları belirtir.


11

Tekli Tablo Silme:

Girişleri poststablodan silmek için :

DELETE ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

Girişleri projectstablodan silmek için :

DELETE pj 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

Girişleri clientstablodan silmek için :

DELETE C
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id;

Birden Fazla Tablo Silme:

Birleştirilen sonuçların dışındaki birden çok tablodaki girişleri silmek için DELETE, virgülle ayrılmış liste olarak sonra tablo adlarını belirtmeniz gerekir :

Eğer her üç tablolardan (girişleri silmek istediğiniz varsayalım posts, projects, clientsbelirli bir müşteri için):

DELETE C,pj,ps 
FROM clients C 
INNER JOIN projects pj ON C.client_id = pj.client_id
INNER JOIN posts ps ON pj.project_id = ps.project_id
WHERE C.client_id = :client_id

7

Aşağıdaki gibi deneyin:

DELETE posts.*,projects.* 
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id;

4

Bir alt kullanarak silme başka yöntem olduğunu kullanmaktan daha iyidir seçmek INolurduWHERE EXISTS

DELETE  FROM posts
WHERE   EXISTS ( SELECT  1 
                 FROM    projects
                 WHERE   projects.client_id = posts.client_id);

Birleştirme yerine bunu kullanmanın bir nedeni DELETE, JOINa'nın kullanılmasını yasaklamasıdır LIMIT. Tam masa kilitleri üretmemek için bloklar halinde silmek isterseniz, LIMITbu DELETE WHERE EXISTSyöntemi kullanabilirsiniz .


1
Bu sorgu "takma adlar" ile yazılabilir mi? Sözdiziminden, postsEXISTS () postsiçindeki satırların nasıl silindiği çok açık değildir . (IMHO neyse)
MattBianco

Seni duyuyorum, ancak tablodaki takma adların silinmesine izin verilmiyor. Alt sorgudaki "gönderiler", tam tablo adı olmalıdır; başka bir deyişle, alt tabloyu Kimden cümlesinde bu tabloyu yeniden kullanmak isterseniz, orada takma ad kullanmanız gerekir.
Jim Clouse

1
Bu çalışıyor:DELETE p FROM posts p WHERE EXISTS ( SELECT 1 FROM projects WHERE projects.client_id = p.client_id);
MattBianco

4
mysql> INSERT INTO tb1 VALUES(1,1),(2,2),(3,3),(6,60),(7,70),(8,80);

mysql> INSERT INTO tb2 VALUES(1,1),(2,2),(3,3),(4,40),(5,50),(9,90);

Kayıtları bir tablodan SİL:

mysql> DELETE tb1 FROM tb1,tb2 WHERE tb1.id= tb2.id;

Her iki tablodan KAYITLARI SİL:

mysql> DELETE tb2,tb1 FROM tb2 JOIN tb1 USING(id);

1

Birleştirme sizin için işe yaramazsa bu çözümü deneyebilirsiniz. Yabancı anahtarlar + özel durumlarda kullanılmadığında t1'den yetim kayıtlarını silmek içindir. Yani boş alan "kodu" olan ve tablo2'de "isim" alanı ile eşleşen kayıtları olmayan tablo1'den kayıtları siler.

delete table1 from table1 t1 
    where  t1.code = '' 
    and 0=(select count(t2.name) from table2 t2 where t2.name=t1.name);

0

Bunu dene,

DELETE posts.*
FROM posts
INNER JOIN projects ON projects.project_id = posts.project_id
WHERE projects.client_id = :client_id

-3

- Silmeniz gereken tablo üzerinde takma ad kullanamayacağınızı unutmayın

DELETE tbl_pagos_activos_usuario
FROM tbl_pagos_activos_usuario, tbl_usuarios b, tbl_facturas c
Where tbl_pagos_activos_usuario.usuario=b.cedula
and tbl_pagos_activos_usuario.cod=c.cod
and tbl_pagos_activos_usuario.rif=c.identificador
and tbl_pagos_activos_usuario.usuario=c.pay_for
and tbl_pagos_activos_usuario.nconfppto=c.nconfppto
and NOT ISNULL(tbl_pagos_activos_usuario.nconfppto)
and c.estatus=50
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.