Berikut adalah dua solusi berbeda:(Catatan:Saya menyebut bidang enum "package_type")
Solusi pertama (melalui fungsi IF()):
select
i.location,
if(ps.id is not null, ps.id, pg.id) as package_id
from
(select distinct location from Items) i
inner join
(select i.location, p.id
from Items i
inner join Packages p on (i.package_id = p.id and p.package_type = 'general')
) pg on (i.location = pg.location)
left join
(select i.location, p.id
from Items i
inner join Packages p on (i.package_id = p.id and p.package_type = 'special')
) ps on (i.location = ps.location)
Solusi ini pada dasarnya mengambil lokasi dan menggabungkannya ke paket dengan general (yang dianggap ada; karenanya inner join
) dan paket khusus (yang opsional; karenanya left join
). Itu membuat catatan seperti ini:
location | general-package | [special-package]
Kemudian menggunakan MySQL IF
berfungsi untuk mencoba memilih ID paket khusus terlebih dahulu, lalu kembali ke ID paket umum.
Solusi ke-2 (melalui casting enum ke integer):
select i.location, p.id
from
(select i.location, max(cast(package_type as unsigned)) as package_type
from Items i
left join Packages p on (i.package_id = p.id)
group by location
) i
inner join
(select i.location, p.id, p.package_type
from Items i
inner join Packages p on (i.package_id = p.id)
) p on (i.location = p.location and i.package_type = p.package_type)
Solusi ini mengeksploitasi fakta bahwa enum disimpan sebagai bilangan bulat. Itu melemparkan enum ke bilangan bulat. special
dalam hal ini akan mengembalikan 2
dan general
akan mengembalikan 1
. Karena khusus ini dijamin lebih tinggi dari umum dalam hal ini (yaitu 2> 1), kita dapat menggunakan MAX
fungsi agregat. Sekarang kami pada dasarnya memiliki tabel lokasi dan "paket yang direkomendasikan" mereka (yaitu khusus jika ada, umum sebaliknya). Kami cukup menggabungkan ini ke kueri normal bersama dengan jenis paket yang diharapkan, dan itu mengembalikan hasil yang benar.
Penafian:Saya tidak yakin tentang efisiensi salah satu metode ini, jadi Anda mungkin ingin mengujinya sendiri.
Jika Anda ingin mendesain ulang tabel atau mendenormalkannya untuk efisiensi, saya pikir desain ini mungkin lebih cocok:
GeneralPackages table
id, name
1, General Package 1
SpecialPackages table
id, name
1, Special Package 1
2, Special Package 2
Items table
id, general_package_id, special_package_id, location
1, 1, NULL, America
2, 1, 2, Europe
Keuntungannya adalah lebih mudah untuk menerapkan beberapa aturan di tingkat basis data:
- Lokasi harus selalu memiliki paket umum (Items.general_package_id dapat didefinisikan sebagai NOT NULL)
- Lokasi hanya boleh memiliki satu paket umum (menambahkannya di bidang daripada gabungan menjamin bahwa hanya ada satu yang ditentukan)
- Sebuah lokasi mungkin memiliki paling banyak satu paket khusus (menambahkannya di bidang daripada gabungan menjamin bahwa hanya ada satu yang ditentukan)
- Kunci asing pada Items.general_package_id =GeneralPackages.id akan menjamin bahwa kolom tersebut hanya berisi paket valid yang bersifat "umum".
- Hal yang sama dapat dilakukan untuk special_package_id.
Kerugiannya adalah Anda mungkin perlu menggunakan UNION ALL setiap kali Anda menggunakan salah satu kueri lama Anda.