SQL query mengembalikan data dari beberapa tabel


434

Saya ingin tahu yang berikut:

  • bagaimana cara mendapatkan data dari beberapa tabel di basis data saya?
  • apa jenis metode yang ada untuk melakukan ini?
  • apa yang bergabung dan serikat dan bagaimana mereka berbeda satu sama lain?
  • Kapan saya harus menggunakan masing-masing dibandingkan dengan yang lain?

Saya berencana untuk menggunakan ini dalam aplikasi saya (misalnya - PHP), tetapi tidak ingin menjalankan beberapa kueri terhadap basis data, opsi apa yang harus saya dapatkan data dari beberapa tabel dalam satu permintaan?

Catatan: Saya menulis ini karena saya ingin dapat menautkan ke panduan yang ditulis dengan baik pada banyak pertanyaan yang saya temui dalam antrian PHP, jadi saya dapat menautkan ini untuk detail lebih lanjut ketika saya mengirim jawaban.

Jawabannya mencakup hal-hal berikut:

  1. Bagian 1 - Bergabung dan Serikat Pekerja
  2. Bagian 2 - Subqueries
  3. Bagian 3 - Trik dan Kode Efisien
  4. Bagian 4 - Subqueries dalam Klausa Dari
  5. Bagian 5 - Tas Campuran Trik John

Jawaban:


469

Bagian 1 - Bergabung dan Serikat Pekerja

Jawaban ini mencakup:

  1. Bagian 1
    • Bergabung dengan dua atau lebih tabel menggunakan gabungan dalam (Lihat entri wikipedia untuk info tambahan)
    • Cara menggunakan kueri gabungan
    • Gabungan Luar Kiri dan Kanan ( jawaban stackOverflow ini sangat bagus untuk menggambarkan tipe gabungan)
    • Quers intersectect (dan bagaimana mereproduksi mereka jika database Anda tidak mendukung mereka) - ini adalah fungsi dari SQL-Server ( lihat info ) dan bagian dari alasan saya menulis semua ini di tempat pertama.
  2. Bagian 2
    • Subqueries - apa itu, di mana mereka dapat digunakan dan apa yang harus diperhatikan
    • Cartesian bergabung dengan AKA - Oh, kesengsaraan!

Ada sejumlah cara untuk mengambil data dari beberapa tabel dalam database. Dalam jawaban ini, saya akan menggunakan sintaks join ANSI-92. Ini mungkin berbeda dengan sejumlah tutorial lain di luar sana yang menggunakan sintaks ANSI-89 yang lebih lama (dan jika Anda terbiasa dengan 89, mungkin tampak jauh kurang intuitif - tetapi yang bisa saya katakan adalah mencobanya) karena jauh lebih mudah untuk memahami kapan pertanyaan mulai menjadi lebih kompleks. Kenapa menggunakannya? Apakah ada peningkatan kinerja? The Jawaban singkatnya adalah tidak, tetapi adalah lebih mudah dibaca setelah Anda terbiasa untuk itu. Lebih mudah membaca kueri yang ditulis oleh orang lain menggunakan sintaks ini.

Saya juga akan menggunakan konsep caryard kecil yang memiliki database untuk melacak mobil apa yang telah tersedia. Pemilik telah mempekerjakan Anda sebagai orang IT Computer-nya dan mengharapkan Anda untuk dapat memberikan data yang dia minta pada saat itu.

Saya telah membuat sejumlah tabel pencarian yang akan digunakan oleh tabel final. Ini akan memberi kita model yang masuk akal untuk bekerja. Untuk memulai, saya akan menjalankan kueri terhadap contoh database yang memiliki struktur berikut. Saya akan mencoba memikirkan kesalahan umum yang dibuat ketika memulai dan menjelaskan apa yang salah dengan mereka - dan tentu saja menunjukkan cara memperbaikinya.

Tabel pertama hanyalah daftar warna sehingga kita tahu warna apa yang kita miliki di halaman mobil.

mysql> create table colors(id int(3) not null auto_increment primary key, 
    -> color varchar(15), paint varchar(10));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> insert into colors (color, paint) values ('Red', 'Metallic'), 
    -> ('Green', 'Gloss'), ('Blue', 'Metallic'), 
    -> ('White' 'Gloss'), ('Black' 'Gloss');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from colors;
+----+-------+----------+
| id | color | paint    |
+----+-------+----------+
|  1 | Red   | Metallic |
|  2 | Green | Gloss    |
|  3 | Blue  | Metallic |
|  4 | White | Gloss    |
|  5 | Black | Gloss    |
+----+-------+----------+
5 rows in set (0.00 sec)

Tabel merek mengidentifikasi berbagai merek mobil yang bisa dijual caryard.

mysql> create table brands (id int(3) not null auto_increment primary key, 
    -> brand varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from brands;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| brand | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> insert into brands (brand) values ('Ford'), ('Toyota'), 
    -> ('Nissan'), ('Smart'), ('BMW');
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from brands;
+----+--------+
| id | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  3 | Nissan |
|  4 | Smart  |
|  5 | BMW    |
+----+--------+
5 rows in set (0.00 sec)

Tabel model akan mencakup berbagai jenis mobil, akan lebih mudah untuk menggunakan jenis mobil yang berbeda daripada model mobil yang sebenarnya.

mysql> create table models (id int(3) not null auto_increment primary key, 
    -> model varchar(15));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from models;
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| model | varchar(15) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> insert into models (model) values ('Sports'), ('Sedan'), ('4WD'), ('Luxury');
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> select * from models;
+----+--------+
| id | model  |
+----+--------+
|  1 | Sports |
|  2 | Sedan  |
|  3 | 4WD    |
|  4 | Luxury |
+----+--------+
4 rows in set (0.00 sec)

Dan akhirnya, untuk mengikat semua tabel lainnya, tabel yang mengikat semuanya. Bidang ID sebenarnya nomor lot unik yang digunakan untuk mengidentifikasi mobil.

mysql> create table cars (id int(3) not null auto_increment primary key, 
    -> color int(3), brand int(3), model int(3));
Query OK, 0 rows affected (0.01 sec)

mysql> show columns from cars;
+-------+--------+------+-----+---------+----------------+
| Field | Type   | Null | Key | Default | Extra          |
+-------+--------+------+-----+---------+----------------+
| id    | int(3) | NO   | PRI | NULL    | auto_increment |
| color | int(3) | YES  |     | NULL    |                |
| brand | int(3) | YES  |     | NULL    |                |
| model | int(3) | YES  |     | NULL    |                |
+-------+--------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> insert into cars (color, brand, model) values (1,2,1), (3,1,2), (5,3,1), 
    -> (4,4,2), (2,2,3), (3,5,4), (4,1,3), (2,2,1), (5,2,3), (4,5,1);
Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

mysql> select * from cars;
+----+-------+-------+-------+
| id | color | brand | model |
+----+-------+-------+-------+
|  1 |     1 |     2 |     1 |
|  2 |     3 |     1 |     2 |
|  3 |     5 |     3 |     1 |
|  4 |     4 |     4 |     2 |
|  5 |     2 |     2 |     3 |
|  6 |     3 |     5 |     4 |
|  7 |     4 |     1 |     3 |
|  8 |     2 |     2 |     1 |
|  9 |     5 |     2 |     3 |
| 10 |     4 |     5 |     1 |
+----+-------+-------+-------+
10 rows in set (0.00 sec)

Ini akan memberi kita cukup data (saya harap) untuk menutupi contoh-contoh di bawah ini dari berbagai jenis sambungan dan juga memberikan data yang cukup untuk menjadikannya berharga.

Jadi masuk ke grit itu, bos ingin tahu ID semua mobil sport yang dimilikinya .

Ini adalah gabungan dua tabel sederhana. Kami memiliki tabel yang mengidentifikasi model dan tabel dengan stok yang tersedia di dalamnya. Seperti yang Anda lihat, data di modelkolom carstabel terkait dengan modelskolom carstabel yang kita miliki. Sekarang, kita tahu bahwa tabel model memiliki ID 1untuk Sportsjadi mari kita menulis join.

select
    ID,
    model
from
    cars
        join models
            on model=ID

Jadi pertanyaan ini terlihat bagus bukan? Kami telah mengidentifikasi dua tabel dan berisi informasi yang kami butuhkan dan menggunakan gabungan yang mengidentifikasi kolom mana yang akan bergabung.

ERROR 1052 (23000): Column 'ID' in field list is ambiguous

Oh tidak! Kesalahan dalam kueri pertama kami! Ya, dan itu adalah buah prem. Soalnya, kueri memang punya kolom yang tepat, tetapi beberapa di antaranya ada di kedua tabel, sehingga database menjadi bingung tentang apa sebenarnya kolom yang kami maksud dan di mana. Ada dua solusi untuk mengatasi ini. Yang pertama bagus dan sederhana, bisa kita gunakan tableName.columnNameuntuk memberi tahu database apa yang kita maksud, seperti ini:

select
    cars.ID,
    models.model
from
    cars
        join models
            on cars.model=models.ID

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
|  2 | Sedan  |
|  4 | Sedan  |
|  5 | 4WD    |
|  7 | 4WD    |
|  9 | 4WD    |
|  6 | Luxury |
+----+--------+
10 rows in set (0.00 sec)

Yang lain mungkin lebih sering digunakan dan disebut table aliasing. Tabel dalam contoh ini memiliki nama sederhana yang bagus dan pendek, tetapi mengetikkan sesuatu seperti KPI_DAILY_SALES_BY_DEPARTMENTmungkin akan menjadi cepat, jadi cara sederhana adalah dengan memberi nama tabel pada tabel seperti ini:

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID

Sekarang, kembali ke permintaan. Seperti yang Anda lihat, kami memiliki informasi yang kami butuhkan, tetapi kami juga memiliki informasi yang tidak diminta, jadi kami harus menyertakan klausa di mana dalam pernyataan untuk hanya mendapatkan mobil Sport seperti yang diminta. Karena saya lebih suka metode tabel alias daripada menggunakan nama tabel berulang kali, saya akan tetap menggunakannya sejak saat ini dan seterusnya.

Jelas, kita perlu menambahkan klausa tempat ke kueri kami. Kami dapat mengidentifikasi mobil Sport baik dengan ID=1atau model='Sports'. Karena ID diindeks dan kunci utama (dan itu terjadi kurang mengetik), mari kita gunakan dalam kueri kami.

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

Bingo! Bos senang. Tentu saja, sebagai bos dan tidak pernah senang dengan apa yang dia minta, dia melihat informasinya, lalu berkata aku ingin warnanya juga .

Oke, jadi kita sudah memiliki bagian yang baik dari permintaan kita yang sudah ditulis, tetapi kita perlu menggunakan tabel ketiga yang berwarna. Sekarang, tabel informasi utama kami carsmenyimpan ID warna mobil dan tautan ini kembali ke kolom ID warna. Jadi, dengan cara yang mirip dengan aslinya, kita bisa bergabung dengan tabel ketiga:

select
    a.ID,
    b.model
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  3 | Sports |
|  8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)

Sial, meskipun tabel bergabung dengan benar dan kolom terkait telah ditautkan, kami lupa untuk menarik informasi aktual dari tabel baru yang baru saja kami tautkan.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
where
    b.ID=1

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
+----+--------+-------+
4 rows in set (0.00 sec)

Benar, itu bos di belakang kita sejenak. Sekarang, untuk menjelaskan beberapa hal ini dengan sedikit lebih detail. Seperti yang Anda lihat, fromklausa dalam pernyataan kami menautkan tabel utama kami (saya sering menggunakan tabel yang berisi informasi daripada tabel pencarian atau dimensi. Kueri akan bekerja sama baiknya dengan semua tabel yang diputar, tetapi kurang masuk akal saat kami kembali ke kueri ini untuk membacanya dalam waktu beberapa bulan, jadi sering kali paling baik untuk mencoba menulis kueri yang bagus dan mudah dipahami - lay out secara intuitif, gunakan indentasi yang bagus sehingga semuanya sejelas seperti Jika Anda terus mengajar orang lain, cobalah untuk menanamkan karakteristik ini dalam permintaan mereka - terutama jika Anda akan memecahkan masalah mereka.

Sangat mungkin untuk terus menautkan semakin banyak tabel dengan cara ini.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

Meskipun saya lupa menyertakan tabel di mana kami mungkin ingin bergabung lebih dari satu kolom dalam joinpernyataan, berikut adalah contohnya. Jika modelstabel memiliki model khusus merek dan oleh karena itu juga memiliki kolom yang disebut brandyang terhubung kembali ke brandstabel di IDlapangan, bisa dilakukan sebagai berikut:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
            and b.brand=d.ID
where
    b.ID=1

Anda dapat melihat, kueri di atas tidak hanya menautkan tabel yang bergabung ke tabel utama cars, tetapi juga menentukan gabungan antara tabel yang sudah bergabung. Jika ini tidak dilakukan, hasilnya disebut bergabung kartesian - yang dba berbicara buruk. Penggabungan kartesian adalah tempat baris dikembalikan karena informasi tidak memberi tahu database cara membatasi hasil, sehingga kueri mengembalikan semua baris yang sesuai dengan kriteria.

Jadi, untuk memberikan contoh gabungan kartesian, mari jalankan query berikut:

select
    a.ID,
    b.model
from
    cars a
        join models b

+----+--------+
| ID | model  |
+----+--------+
|  1 | Sports |
|  1 | Sedan  |
|  1 | 4WD    |
|  1 | Luxury |
|  2 | Sports |
|  2 | Sedan  |
|  2 | 4WD    |
|  2 | Luxury |
|  3 | Sports |
|  3 | Sedan  |
|  3 | 4WD    |
|  3 | Luxury |
|  4 | Sports |
|  4 | Sedan  |
|  4 | 4WD    |
|  4 | Luxury |
|  5 | Sports |
|  5 | Sedan  |
|  5 | 4WD    |
|  5 | Luxury |
|  6 | Sports |
|  6 | Sedan  |
|  6 | 4WD    |
|  6 | Luxury |
|  7 | Sports |
|  7 | Sedan  |
|  7 | 4WD    |
|  7 | Luxury |
|  8 | Sports |
|  8 | Sedan  |
|  8 | 4WD    |
|  8 | Luxury |
|  9 | Sports |
|  9 | Sedan  |
|  9 | 4WD    |
|  9 | Luxury |
| 10 | Sports |
| 10 | Sedan  |
| 10 | 4WD    |
| 10 | Luxury |
+----+--------+
40 rows in set (0.00 sec)

Ya Tuhan, itu jelek. Namun, sejauh menyangkut database, itu persis apa yang diminta. Dalam kueri, kami meminta IDdari carsdan modeldari models. Namun, karena kami tidak menentukan cara bergabung dengan tabel, database telah mencocokkan setiap baris dari tabel pertama dengan setiap baris dari tabel kedua.

Oke, jadi bos sudah kembali, dan dia ingin lebih banyak informasi lagi. Saya ingin daftar yang sama, tetapi juga menyertakan 4WD di dalamnya .

Namun ini, memberi kita alasan besar untuk melihat dua cara berbeda untuk mencapai ini. Kita dapat menambahkan kondisi lain ke klausa di mana seperti ini:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
    or b.ID=3

Meskipun hal di atas akan bekerja dengan baik, mari kita lihat secara berbeda, ini adalah alasan yang bagus untuk menunjukkan bagaimana sebuah unionkueri akan bekerja.

Kita tahu bahwa yang berikut akan mengembalikan semua mobil Sport:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1

Dan yang berikut ini akan mengembalikan semua 4WD:

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

Jadi dengan menambahkan union allklausa di antara mereka, hasil permintaan kedua akan ditambahkan ke hasil permintaan pertama.

select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=1
union all
select
    a.ID,
    b.model,
    c.color
from
    cars a
        join models b
            on a.model=b.ID
        join colors c
            on a.color=c.ID
        join brands d
            on a.brand=d.ID
where
    b.ID=3

+----+--------+-------+
| ID | model  | color |
+----+--------+-------+
|  1 | Sports | Red   |
|  8 | Sports | Green |
| 10 | Sports | White |
|  3 | Sports | Black |
|  5 | 4WD    | Green |
|  7 | 4WD    | White |
|  9 | 4WD    | Black |
+----+--------+-------+
7 rows in set (0.00 sec)

Seperti yang Anda lihat, hasil dari query pertama dikembalikan terlebih dahulu, diikuti oleh hasil dari query kedua.

Dalam contoh ini, tentu saja akan jauh lebih mudah untuk hanya menggunakan kueri pertama, tetapi unionkueri bisa bagus untuk kasus-kasus tertentu. Mereka adalah cara yang bagus untuk mengembalikan hasil spesifik dari tabel dari tabel yang tidak mudah bergabung bersama - atau dalam hal ini tabel yang sama sekali tidak terkait. Namun ada beberapa aturan yang harus diikuti.

  • Jenis kolom dari kueri pertama harus cocok dengan jenis kolom dari setiap kueri lainnya di bawah ini.
  • Nama-nama kolom dari kueri pertama akan digunakan untuk mengidentifikasi seluruh rangkaian hasil.
  • Jumlah kolom di setiap kueri harus sama.

Sekarang, Anda mungkin bertanya-tanya apa perbedaan antara menggunakan uniondan union all. Sebuah unionpermintaan akan menghapus duplikat, sementara union alltidak akan. Ini berarti bahwa ada hit kinerja kecil ketika menggunakan unionlebih dari union alltetapi hasilnya mungkin sepadan - saya tidak akan berspekulasi pada hal semacam itu dalam hal ini sekalipun.

Pada catatan ini, mungkin perlu dicatat beberapa catatan tambahan di sini.

  • Jika kami ingin memesan hasilnya, kami dapat menggunakan order bytetapi Anda tidak dapat menggunakan alias lagi. Dalam kueri di atas, menambahkan suatu order by a.IDakan menghasilkan kesalahan - sejauh menyangkut hasil, kolom disebut IDbukan a.ID- meskipun alias yang sama telah digunakan di kedua kueri.
  • Kami hanya dapat memiliki satu order bypernyataan, dan itu harus sebagai pernyataan terakhir.

Untuk contoh berikutnya, saya menambahkan beberapa baris tambahan ke tabel kami.

Saya telah menambahkan Holdenke tabel merek. Saya juga menambahkan baris ke dalam carsyang memiliki colornilai 12- yang tidak memiliki referensi dalam tabel warna.

Oke, bos kembali lagi, menggonggong permintaan - * Saya ingin hitungan masing-masing merek yang kami bawa dan jumlah mobil di dalamnya! .

Rightyo, jadi hal pertama yang perlu kita lakukan adalah mendapatkan daftar lengkap merek yang mungkin.

select
    a.brand
from
    brands a

+--------+
| brand  |
+--------+
| Ford   |
| Toyota |
| Nissan |
| Smart  |
| BMW    |
| Holden |
+--------+
6 rows in set (0.00 sec)

Sekarang, ketika kita bergabung ini ke meja mobil kita, kita mendapatkan hasil berikut:

select
    a.brand
from
    brands a
        join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Nissan |
| Smart  |
| Toyota |
+--------+
5 rows in set (0.00 sec)

Yang tentu saja merupakan masalah - kami tidak melihat penyebutan Holdenmerek indah yang saya tambahkan.

Ini karena gabungan mencari baris yang cocok di kedua tabel. Karena tidak ada data di mobil yang bertipe Holdentidak dikembalikan. Di sinilah kita bisa menggunakan outergabungan. Ini akan mengembalikan semua hasil dari satu tabel apakah mereka cocok di tabel lain atau tidak:

select
    a.brand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+
| brand  |
+--------+
| BMW    |
| Ford   |
| Holden |
| Nissan |
| Smart  |
| Toyota |
+--------+
6 rows in set (0.00 sec)

Sekarang kita memiliki itu, kita dapat menambahkan fungsi agregat yang indah untuk mendapatkan hitungan dan membuat bos mundur sejenak.

select
    a.brand,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
group by
    a.brand

+--------+--------------+
| brand  | countOfBrand |
+--------+--------------+
| BMW    |            2 |
| Ford   |            2 |
| Holden |            0 |
| Nissan |            1 |
| Smart  |            1 |
| Toyota |            5 |
+--------+--------------+
6 rows in set (0.00 sec)

Dan dengan itu, singkirkan bos yang susah.

Sekarang, untuk menjelaskan ini secara lebih rinci, sambungan luar bisa dari tipe leftatau right. Kiri atau Kanan menentukan tabel mana yang sepenuhnya disertakan. A left outer joinakan memasukkan semua baris dari tabel di sebelah kiri, sementara (Anda dapat menebaknya) a right outer joinmembawa semua hasil dari tabel di sebelah kanan ke dalam hasil.

Beberapa database akan memungkinkan full outer joinyang akan mengembalikan hasil (apakah cocok atau tidak) dari kedua tabel, tetapi ini tidak didukung di semua database.

Sekarang, saya mungkin memikirkan saat ini, Anda bertanya-tanya apakah Anda dapat menggabungkan jenis bergabung dalam kueri - dan jawabannya adalah ya, Anda benar-benar bisa.

select
    b.brand,
    c.color,
    count(a.id) as countOfBrand
from
    cars a
        right outer join brands b
            on b.ID=a.brand
        join colors c
            on a.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
+--------+-------+--------------+
9 rows in set (0.00 sec)

Jadi, mengapa itu bukan hasil yang diharapkan? Itu karena meskipun kami telah memilih gabung luar dari mobil ke merek, itu tidak ditentukan dalam gabung ke warna - sehingga gabung tertentu hanya akan membawa kembali hasil yang cocok di kedua tabel.

Berikut adalah kueri yang akan berfungsi untuk mendapatkan hasil yang kami harapkan:

select
    a.brand,
    c.color,
    count(b.id) as countOfBrand
from
    brands a
        left outer join cars b
            on a.ID=b.brand
        left outer join colors c
            on b.color=c.ID
group by
    a.brand,
    c.color

+--------+-------+--------------+
| brand  | color | countOfBrand |
+--------+-------+--------------+
| BMW    | Blue  |            1 |
| BMW    | White |            1 |
| Ford   | Blue  |            1 |
| Ford   | White |            1 |
| Holden | NULL  |            0 |
| Nissan | Black |            1 |
| Smart  | White |            1 |
| Toyota | NULL  |            1 |
| Toyota | Black |            1 |
| Toyota | Green |            2 |
| Toyota | Red   |            1 |
+--------+-------+--------------+
11 rows in set (0.00 sec)

Seperti yang dapat kita lihat, kita memiliki dua gabungan luar dalam kueri dan hasilnya akan seperti yang diharapkan.

Sekarang, bagaimana dengan jenis-jenis sambungan yang Anda tanyakan? Bagaimana dengan titik-temu?

Yah, tidak semua database mendukung, intersectiontetapi hampir semua database akan memungkinkan Anda untuk membuat persimpangan melalui gabungan (atau pernyataan yang terstruktur dengan baik setidaknya).

Persimpangan adalah jenis gabungan yang agak mirip dengan yang uniondijelaskan di atas - tetapi perbedaannya adalah bahwa ia hanya mengembalikan baris data yang identik (dan maksud saya identik) antara berbagai kueri individual yang digabungkan oleh serikat. Hanya baris yang identik dalam setiap hal yang akan dikembalikan.

Contoh sederhana akan seperti itu:

select
    *
from
    colors
where
    ID>2
intersect
select
    *
from
    colors
where
    id<4

Sementara unionkueri normal akan mengembalikan semua baris tabel (kueri pertama mengembalikan apa pun ID>2dan yang kedua memiliki ID<4), yang akan menghasilkan set lengkap, kueri intersect hanya akan mengembalikan pencocokan baris id=3karena memenuhi kedua kriteria.

Sekarang, jika basis data Anda tidak mendukung intersectkueri, hal di atas dapat dengan mudah dilakukan dengan kueri berikut:

select
    a.ID,
    a.color,
    a.paint
from
    colors a
        join colors b
            on a.ID=b.ID
where
    a.ID>2
    and b.ID<4

+----+-------+----------+
| ID | color | paint    |
+----+-------+----------+
|  3 | Blue  | Metallic |
+----+-------+----------+
1 row in set (0.00 sec)

Jika Anda ingin melakukan persimpangan di dua tabel yang berbeda menggunakan database yang secara inheren tidak mendukung kueri persimpangan, Anda perlu membuat gabungan di setiap kolom tabel.


2
@ Fluen jawaban yang bagus. Saya punya saran: Jika Anda ingin menjadikannya Tutorial SQL yang mematikan, Anda hanya perlu menambahkan diagram Venn; Saya langsung mengerti kiri dan kanan bergabung berkat mereka. Permintaan pribadi: Apakah Anda memiliki tutorial tentang kesalahan umum / penyempurnaan kinerja?
StrayChild01

25
Astaga. Roda gulir saya rusak. Pertanyaan dan jawaban yang bagus. Saya berharap saya bisa mengungguli ini 10 kali.
Amal Murali

3
Hehe, terima kasih atas umpan balik positif. Terus bergulir, ini hanya jawaban pertama. JADI berkata bahwa jawaban saya terlalu panjang untuk dimasukkan ke dalam satu "jawaban" jadi saya harus menggunakan beberapa :)
Fluffeh

7
Jujur, saya pikir jawaban ini perlu dipersingkat.
einpoklum

Artikel yang luar biasa. Database Bergabung dengan 101.
maqs

101

Ok, saya menemukan posting ini sangat menarik dan saya ingin berbagi pengetahuan saya tentang membuat kueri. Terima kasih untuk Fluffeh ini . Orang lain yang mungkin membaca ini dan mungkin merasa bahwa saya salah 101% bebas untuk mengedit dan mengkritik jawaban saya. ( Jujur, saya merasa sangat berterima kasih karena memperbaiki kesalahan saya. )

Saya akan memposting beberapa pertanyaan umum dalam MySQLtag.


Trik No. 1 ( baris yang cocok dengan berbagai kondisi )

Diberikan skema ini

CREATE TABLE MovieList
(
    ID INT,
    MovieName VARCHAR(25),
    CONSTRAINT ml_pk PRIMARY KEY (ID),
    CONSTRAINT ml_uq UNIQUE (MovieName)
);

INSERT INTO MovieList VALUES (1, 'American Pie');
INSERT INTO MovieList VALUES (2, 'The Notebook');
INSERT INTO MovieList VALUES (3, 'Discovery Channel: Africa');
INSERT INTO MovieList VALUES (4, 'Mr. Bean');
INSERT INTO MovieList VALUES (5, 'Expendables 2');

CREATE TABLE CategoryList
(
    MovieID INT,
    CategoryName VARCHAR(25),
    CONSTRAINT cl_uq UNIQUE(MovieID, CategoryName),
    CONSTRAINT cl_fk FOREIGN KEY (MovieID) REFERENCES MovieList(ID)
);

INSERT INTO CategoryList VALUES (1, 'Comedy');
INSERT INTO CategoryList VALUES (1, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Drama');
INSERT INTO CategoryList VALUES (3, 'Documentary');
INSERT INTO CategoryList VALUES (4, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Action');

PERTANYAAN

Temukan semua film yang termasuk setidaknya dalam kategori Comedydan Romancekategori.

Larutan

Pertanyaan ini terkadang sangat rumit. Tampaknya pertanyaan seperti ini akan menjadi jawabannya: -

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName = 'Comedy' AND
        b.CategoryName = 'Romance'

Demo SQLFiddle

yang pasti sangat salah karena menghasilkan hasil . Penjelasan dari ini adalah bahwa hanya ada satu nilai yang valid CategoryNamepada setiap baris . Misalnya, kondisi pertama mengembalikan true , kondisi kedua selalu salah. Jadi, dengan menggunakan ANDoperator, kedua kondisi tersebut harus benar; jika tidak, itu akan salah. Kueri lain seperti ini,

SELECT  DISTINCT a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')

Demo SQLFiddle

dan hasilnya masih salah karena cocok dengan catatan yang sudah setidaknya satu kecocokan pada categoryName. The solusi nyata akan dengan menghitung jumlah kasus record per film . Jumlah instance harus sesuai dengan jumlah total nilai yang disediakan dalam kondisi.

SELECT  a.MovieName
FROM    MovieList a
        INNER JOIN CategoryList b
            ON a.ID = b.MovieID
WHERE   b.CategoryName IN ('Comedy','Romance')
GROUP BY a.MovieName
HAVING COUNT(*) = 2

SQLFiddle Demo (jawabannya)


Trick No. 2 ( catatan maksimum untuk setiap entri )

Skema yang diberikan,

CREATE TABLE Software
(
    ID INT,
    SoftwareName VARCHAR(25),
    Descriptions VARCHAR(150),
    CONSTRAINT sw_pk PRIMARY KEY (ID),
    CONSTRAINT sw_uq UNIQUE (SoftwareName)  
);

INSERT INTO Software VALUES (1,'PaintMe','used for photo editing');
INSERT INTO Software VALUES (2,'World Map','contains map of different places of the world');
INSERT INTO Software VALUES (3,'Dictionary','contains description, synonym, antonym of the words');

CREATE TABLE VersionList
(
    SoftwareID INT,
    VersionNo INT,
    DateReleased DATE,
    CONSTRAINT sw_uq UNIQUE (SoftwareID, VersionNo),
    CONSTRAINT sw_fk FOREIGN KEY (SOftwareID) REFERENCES Software(ID)
);

INSERT INTO VersionList VALUES (3, 2, '2009-12-01');
INSERT INTO VersionList VALUES (3, 1, '2009-11-01');
INSERT INTO VersionList VALUES (3, 3, '2010-01-01');
INSERT INTO VersionList VALUES (2, 2, '2010-12-01');
INSERT INTO VersionList VALUES (2, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 3, '2011-12-01');
INSERT INTO VersionList VALUES (1, 2, '2010-12-01');
INSERT INTO VersionList VALUES (1, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 4, '2012-12-01');

PERTANYAAN

Temukan versi terbaru pada setiap perangkat lunak. Menampilkan kolom berikut: SoftwareName, Descriptions, LatestVersion( dari kolom VersionNo ),DateReleased

Larutan

Beberapa pengembang SQL keliru menggunakan MAX()fungsi agregat. Mereka cenderung membuat seperti ini,

SELECT  a.SoftwareName, a.Descriptions,
        MAX(b.VersionNo) AS LatestVersion, b.DateReleased
FROM    Software a
        INNER JOIN VersionList b
            ON a.ID = b.SoftwareID
GROUP BY a.ID
ORDER BY a.ID

Demo SQLFiddle

( kebanyakan RDBMS menghasilkan kesalahan sintaksis karena ini tidak menentukan beberapa kolom non-agregat pada group byklausa ) hasilnya menghasilkan yang benar LatestVersionpada setiap perangkat lunak tetapi jelas DateReleasedtidak benar. MySQLtidak mendukung Window Functionsdan Common Table Expressionbelum sebagaimana beberapa RDBMS sudah lakukan. Solusi untuk masalah ini adalah membuat subqueryyang mendapat maksimum individu versionNopada setiap perangkat lunak dan kemudian bergabung di tabel lainnya.

SELECT  a.SoftwareName, a.Descriptions,
        b.LatestVersion, c.DateReleased
FROM    Software a
        INNER JOIN
        (
            SELECT  SoftwareID, MAX(VersionNO) LatestVersion
            FROM    VersionList
            GROUP BY SoftwareID
        ) b ON a.ID = b.SoftwareID
        INNER JOIN VersionList c
            ON  c.SoftwareID = b.SoftwareID AND
                c.VersionNO = b.LatestVersion
GROUP BY a.ID
ORDER BY a.ID

SQLFiddle Demo (jawabannya)


Jadi begitulah. Saya akan posting segera lain seperti yang saya ingat lain FAQ diMySQL tag. Terima kasih telah membaca artikel kecil ini. Saya harap Anda setidaknya mendapatkan sedikit pengetahuan dari ini.

PEMBARUAN 1


Trik No. 3 ( Menemukan catatan terbaru antara dua ID )

Skema yang diberikan

CREATE TABLE userList
(
    ID INT,
    NAME VARCHAR(20),
    CONSTRAINT us_pk PRIMARY KEY (ID),
    CONSTRAINT us_uq UNIQUE (NAME)  
);

INSERT INTO userList VALUES (1, 'Fluffeh');
INSERT INTO userList VALUES (2, 'John Woo');
INSERT INTO userList VALUES (3, 'hims056');

CREATE TABLE CONVERSATION
(
    ID INT,
    FROM_ID INT,
    TO_ID INT,
    MESSAGE VARCHAR(250),
    DeliveryDate DATE
);

INSERT INTO CONVERSATION VALUES (1, 1, 2, 'hi john', '2012-01-01');
INSERT INTO CONVERSATION VALUES (2, 2, 1, 'hello fluff', '2012-01-02');
INSERT INTO CONVERSATION VALUES (3, 1, 3, 'hey hims', '2012-01-03');
INSERT INTO CONVERSATION VALUES (4, 1, 3, 'please reply', '2012-01-04');
INSERT INTO CONVERSATION VALUES (5, 3, 1, 'how are you?', '2012-01-05');
INSERT INTO CONVERSATION VALUES (6, 3, 2, 'sample message!', '2012-01-05');

PERTANYAAN

Temukan percakapan terbaru antara dua pengguna.

Larutan

SELECT    b.Name SenderName,
          c.Name RecipientName,
          a.Message,
          a.DeliveryDate
FROM      Conversation a
          INNER JOIN userList b
            ON a.From_ID = b.ID
          INNER JOIN userList c
            ON a.To_ID = c.ID
WHERE     (LEAST(a.FROM_ID, a.TO_ID), GREATEST(a.FROM_ID, a.TO_ID), DeliveryDate)
IN
(
    SELECT  LEAST(FROM_ID, TO_ID) minFROM,
            GREATEST(FROM_ID, TO_ID) maxTo,
            MAX(DeliveryDate) maxDate
    FROM    Conversation
    GROUP BY minFROM, maxTo
)

Demo SQLFiddle


Luar biasa! Peringatan John, solusi pertama Anda hanya berfungsi karena ada kendala unik di kedua bidang. Anda bisa menggunakan solusi yang lebih umum untuk membantu masalah umum. Menurut pendapat saya satu-satunya solusi adalah melakukan seleksi individu untuk comedydan romance. Havingtidak sesuai dengan itu ..
nawfal

@nawfal tidak benar-benar, jika kendala unik tidak ditambahkan, Anda kemudian perlu menambahkan distinctpada klausa yang ada Demo SQLFiddle : D
John Woo

63

Bagian 2 - Subqueries

Oke, sekarang bos sudah masuk lagi - saya ingin daftar semua mobil kami dengan merek dan total berapa banyak merek yang kita miliki!

Ini adalah kesempatan bagus untuk menggunakan trik berikutnya dalam tas kita tentang barang-barang SQL - subquery. Jika Anda tidak terbiasa dengan istilah tersebut, subquery adalah kueri yang berjalan di dalam kueri lain. Ada banyak cara untuk menggunakannya.

Untuk permintaan kami, pertama-tama mari kita satukan pertanyaan sederhana yang akan mencantumkan setiap mobil dan merek:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID

Sekarang, jika kami hanya ingin menghitung jumlah mobil yang disortir berdasarkan merek, tentu saja kami dapat menulis ini:

select
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    b.brand

+--------+-----------+
| brand  | countCars |
+--------+-----------+
| BMW    |         2 |
| Ford   |         2 |
| Nissan |         1 |
| Smart  |         1 |
| Toyota |         5 |
+--------+-----------+

Jadi, kita harus bisa dengan mudah menambahkan fungsi hitungan ke permintaan asli kita, kan?

select
    a.ID,
    b.brand,
    count(a.ID) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID
group by
    a.ID,
    b.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         1 |
|  2 | Ford   |         1 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         1 |
|  6 | BMW    |         1 |
|  7 | Ford   |         1 |
|  8 | Toyota |         1 |
|  9 | Toyota |         1 |
| 10 | BMW    |         1 |
| 11 | Toyota |         1 |
+----+--------+-----------+
11 rows in set (0.00 sec)

Sayangnya, tidak, kita tidak bisa melakukan itu. Alasannya adalah bahwa ketika kita menambahkan ID mobil (kolom a.ID) kita harus menambahkannya ke dalam grup - jadi sekarang, ketika fungsi penghitungan bekerja, hanya ada satu ID yang cocok dengan setiap ID.

Di sinilah kita bisa menggunakan subquery - sebenarnya kita bisa melakukan dua tipe subquery yang sama sekali berbeda yang akan mengembalikan hasil yang sama yang kita butuhkan untuk ini. Yang pertama adalah cukup menempatkan subquery dalam selectklausa. Ini berarti setiap kali kita mendapatkan satu baris data, subquery akan lari, mendapatkan kolom data dan kemudian memasukkannya ke dalam baris data kita.

select
    a.ID,
    b.brand,
    (
    select
        count(c.ID)
    from
        cars c
    where
        a.brand=c.brand
    ) as countCars
from
    cars a
        join brands b
            on a.brand=b.ID

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  2 | Ford   |         2 |
|  7 | Ford   |         2 |
|  1 | Toyota |         5 |
|  5 | Toyota |         5 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 11 | Toyota |         5 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  6 | BMW    |         2 |
| 10 | BMW    |         2 |
+----+--------+-----------+
11 rows in set (0.00 sec)

Dan Bam !, ini akan membantu kita. Jika Anda perhatikan, sub kueri ini harus dijalankan untuk setiap baris data yang kami kembalikan. Bahkan dalam contoh kecil ini, kami hanya memiliki lima Merek mobil yang berbeda, tetapi subquery tersebut berjalan sebelas kali karena kami memiliki sebelas baris data yang akan kami kembalikan. Jadi, dalam hal ini, sepertinya bukan cara paling efisien untuk menulis kode.

Untuk pendekatan yang berbeda, mari jalankan subquery dan anggap itu adalah tabel:

select
    a.ID,
    b.brand,
    d.countCars
from
    cars a
        join brands b
            on a.brand=b.ID
        join
            (
            select
                c.brand,
                count(c.ID) as countCars
            from
                cars c
            group by
                c.brand
            ) d
            on a.brand=d.brand

+----+--------+-----------+
| ID | brand  | countCars |
+----+--------+-----------+
|  1 | Toyota |         5 |
|  2 | Ford   |         2 |
|  3 | Nissan |         1 |
|  4 | Smart  |         1 |
|  5 | Toyota |         5 |
|  6 | BMW    |         2 |
|  7 | Ford   |         2 |
|  8 | Toyota |         5 |
|  9 | Toyota |         5 |
| 10 | BMW    |         2 |
| 11 | Toyota |         5 |
+----+--------+-----------+
11 rows in set (0.00 sec)

Oke, jadi kami memiliki hasil yang sama (dipesan sedikit berbeda - sepertinya database ingin mengembalikan hasil yang dipesan oleh kolom pertama yang kami pilih kali ini) - tetapi angka yang tepat sama.

Jadi, apa perbedaan antara keduanya - dan kapan kita harus menggunakan masing-masing jenis subquery? Pertama, mari pastikan kita memahami cara kerja kueri kedua itu. Kami memilih dua tabel dalam fromklausa kueri kami, dan kemudian menulis kueri dan memberi tahu database bahwa itu sebenarnya sebuah tabel - yang mana database tersebut sangat cocok digunakan. Ada bisa ada beberapa manfaat untuk menggunakan metode ini (serta beberapa keterbatasan). Yang terpenting adalah bahwa subquery ini berjalan sekali . Jika basis data kami berisi sejumlah besar data, mungkin ada peningkatan besar-besaran atas metode pertama. Namun, karena kami menggunakan ini sebagai tabel, kami harus membawa baris data tambahan - sehingga mereka dapat benar-benar digabungkan kembali ke baris data kami. Kami juga harus memastikan bahwa ada cukupbaris data jika kita akan menggunakan gabungan sederhana seperti pada kueri di atas. Jika Anda ingat, gabungan hanya akan menarik kembali baris yang memiliki data yang cocok di kedua sisi gabungan. Jika kami tidak berhati-hati, ini dapat mengakibatkan data yang valid tidak dikembalikan dari tabel mobil kami jika tidak ada baris yang cocok dalam subquery ini.

Sekarang, melihat kembali pada subquery pertama, ada beberapa batasan juga. karena kita menarik data kembali ke satu baris, kita HANYA dapat menarik kembali satu baris data. Subqueries yang digunakan dalam selectklausa query sangat sering hanya menggunakan fungsi agregat seperti sum, count, maxatau fungsi lain agregat serupa. Mereka tidak harus melakukannya, tetapi sering kali itulah cara mereka ditulis.

Jadi, sebelum kita melanjutkan, mari kita lihat di mana lagi kita bisa menggunakan subquery. Kita dapat menggunakannya dalam whereklausa - sekarang, contoh ini sedikit dibuat-buat seperti dalam database kami, ada cara yang lebih baik untuk mendapatkan data berikut, tetapi melihat hanya sebagai contoh, mari kita lihat:

select
    ID,
    brand
from
    brands
where
    brand like '%o%'

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Ford   |
|  2 | Toyota |
|  6 | Holden |
+----+--------+
3 rows in set (0.00 sec)

Ini mengembalikan daftar ID merek dan nama Merek (kolom kedua hanya ditambahkan untuk menunjukkan kepada kami merek) yang berisi huruf odalam nama tersebut.

Sekarang, kita bisa menggunakan hasil dari kueri ini di mana klausa ini:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in
        (
        select
            ID
        from
            brands
        where
            brand like '%o%'
        )

+----+--------+
| ID | brand  |
+----+--------+
|  2 | Ford   |
|  7 | Ford   |
|  1 | Toyota |
|  5 | Toyota |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

Seperti yang Anda lihat, meskipun subquery mengembalikan tiga ID merek, meja mobil kami hanya memiliki entri untuk dua dari mereka.

Dalam hal ini, untuk perincian lebih lanjut, subquery berfungsi seolah-olah kita menulis kode berikut:

select
    a.ID,
    b.brand
from
    cars a
        join brands b
            on a.brand=b.ID
where
    a.brand in (1,2,6)

+----+--------+
| ID | brand  |
+----+--------+
|  1 | Toyota |
|  2 | Ford   |
|  5 | Toyota |
|  7 | Ford   |
|  8 | Toyota |
|  9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)

Sekali lagi, Anda dapat melihat bagaimana input subquery vs manual telah mengubah urutan baris ketika kembali dari database.

Sementara kita membahas subquery, mari kita lihat apa lagi yang bisa kita lakukan dengan subquery:

  • Anda dapat menempatkan subquery di dalam subquery lain, dan seterusnya dan seterusnya. Ada batas yang tergantung pada basis data Anda, tetapi kekurangan fungsi rekursif dari beberapa programmer gila dan gila, kebanyakan orang tidak akan pernah mencapai batas itu.
  • Anda dapat menempatkan sejumlah subqueries ke dalam satu permintaan, beberapa di dalam selectklausa, beberapa di dalam fromklausa dan beberapa lagi di dalam whereklausa - ingatlah bahwa masing-masing yang Anda masukkan membuat kueri Anda lebih kompleks dan cenderung membutuhkan waktu lebih lama untuk menjalankan.

Jika Anda perlu menulis beberapa kode yang efisien, akan bermanfaat untuk menulis kueri beberapa cara dan melihat (baik dengan menentukan waktu atau dengan menggunakan menjelaskan rencana) yang merupakan kueri optimal untuk mendapatkan hasil Anda. Cara pertama yang berhasil mungkin tidak selalu menjadi cara terbaik untuk melakukannya.


Sangat penting untuk pengembang baru: subkueri mungkin dijalankan sekali untuk setiap hasil kecuali Anda dapat menggunakan sub-kueri sebagai gabungan (ditampilkan di atas).
Xeoncross

59

Bagian 3 - Trik dan Kode Efisien

MySQL dalam () efisiensi

Saya pikir saya akan menambahkan beberapa bit tambahan, untuk tips dan trik yang telah muncul.

Satu pertanyaan yang saya lihat muncul sedikit adil, adalah Bagaimana cara mendapatkan baris yang tidak cocok dari dua tabel dan saya melihat jawaban yang paling umum diterima sebagai sesuatu seperti berikut (berdasarkan tabel mobil dan merek kami - yang Holden terdaftar sebagai merek, tetapi tidak muncul di tabel mobil):

select
    a.ID,
    a.brand
from
    brands a
where
    a.ID not in(select brand from cars)

Dan ya itu akan berhasil.

+----+--------+
| ID | brand  |
+----+--------+
|  6 | Holden |
+----+--------+
1 row in set (0.00 sec)

Namun itu tidak efisien dalam beberapa database. Berikut ini tautan ke pertanyaan Stack Overflow yang menanyakannya, dan di sini ada artikel mendalam yang sangat bagus jika Anda ingin masuk ke seluk-beluk seluk beluk.

Jawaban singkatnya adalah, jika optimizer tidak menanganinya secara efisien, mungkin lebih baik menggunakan kueri seperti berikut untuk mendapatkan baris yang tidak cocok:

select
    a.brand
from
    brands a
        left join cars b
            on a.id=b.brand
where
    b.brand is null

+--------+
| brand  |
+--------+
| Holden |
+--------+
1 row in set (0.00 sec)

Perbarui Tabel dengan tabel yang sama di subquery

Ahhh, oldie lain tapi goodie - yang lama Anda tidak dapat menentukan 'target' tabel target untuk pembaruan dalam klausa FROM .

MySQL tidak akan mengizinkan Anda untuk menjalankan update...kueri dengan sub-pilihan pada tabel yang sama. Sekarang, Anda mungkin berpikir, mengapa tidak menampar klausa itu? Tetapi bagaimana jika Anda ingin memperbarui hanya baris dengan max()tanggal di antara sekelompok baris lainnya? Anda tidak bisa melakukan itu di klausa mana.

update 
    brands 
set 
    brand='Holden' 
where 
    id=
        (select 
            id 
        from 
            brands 
        where 
            id=6);
ERROR 1093 (HY000): You can't specify target table 'brands' 
for update in FROM clause

Jadi, kita tidak bisa melakukan itu, eh? Ya tidak. Ada solusi tersembunyi yang tidak diketahui oleh sejumlah besar pengguna - meskipun ini termasuk beberapa peretasan yang perlu Anda perhatikan.

Anda bisa menempelkan subquery di dalam subquery lain, yang menempatkan cukup banyak celah di antara dua kueri sehingga akan berfungsi. Namun, perhatikan bahwa mungkin paling aman untuk tetap menggunakan kueri dalam transaksi - ini akan mencegah perubahan lain yang dibuat ke tabel saat kueri berjalan.

update 
    brands 
set 
    brand='Holden' 
where id=
    (select 
        id 
    from 
        (select 
            id 
        from 
            brands 
        where 
            id=6
        ) 
    as updateTable);

Query OK, 0 rows affected (0.02 sec)
Rows matched: 1  Changed: 0  Warnings: 0

3
Hanya ingin mencatat bahwa konstruksi WHERE NOT EXISTS () hampir sama dari 'sudut pandang efisiensi', tetapi menurut saya lebih mudah dibaca / dipahami. Kemudian lagi, pengetahuan saya terbatas pada MSSQL dan saya tidak bisa bersumpah jika hal yang sama berlaku pada platform lain.
deroby

Saya baru saja mencoba jenis perbandingan ini tempo hari, di mana NOT IN () memiliki daftar sekitar beberapa ratus ID dan tidak ada perbedaan antara itu dan versi gabungan dari kueri. Mungkin itu membuat perbedaan ketika Anda mencapai ribuan atau miliaran.
Buttle Butkus

18

Anda dapat menggunakan konsep beberapa kueri dalam kata kunci FROM. Mari saya tunjukkan satu contoh:

SELECT DISTINCT e.id,e.name,d.name,lap.lappy LAPTOP_MAKE,c_loc.cnty COUNTY    
FROM  (
          SELECT c.id cnty,l.name
          FROM   county c, location l
          WHERE  c.id=l.county_id AND l.end_Date IS NOT NULL
      ) c_loc, emp e 
      INNER JOIN dept d ON e.deptno =d.id
      LEFT JOIN 
      ( 
         SELECT l.id lappy, c.name cmpy
         FROM   laptop l, company c
         WHERE l.make = c.name
      ) lap ON e.cmpy_id=lap.cmpy

Anda bisa menggunakan tabel sebanyak yang Anda mau. Gunakan gabungan luar dan penyatuan di mana pun itu diperlukan, bahkan di dalam subqueries tabel.

Itu metode yang sangat mudah untuk melibatkan sebanyak tabel dan bidang.


8

Berharap ini membuatnya menemukan tabel saat Anda membaca hal:

jsfiddle

mysql> show columns from colors;                                                         
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+           
| id    | int(3)      | NO   | PRI | NULL    | auto_increment |
| color | varchar(15) | YES  |     | NULL    |                |
| paint | varchar(10) | YES  |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.