Kuncinya, tentu saja, adalah gabungan antara dua tabel. Saya menunjukkannya secara terpisah terlebih dahulu, daripada kueri lengkap, untuk membantu pemahaman. Untuk setiap item, kami menemukan SEMUA ukuran kotak yang dapat menampung item tersebut.
Dalam semua kasus, kecocokan dimungkinkan jika tinggi produk <=tinggi kotak, dan dua dimensi lainnya cocok, di salah satu permutasi (produk selalu dapat diputar untuk dimasukkan ke dalam kotak, apakah bisa diletakkan atau tidak).
Hanya untuk produk layable, kami diizinkan untuk memutar produk dalam ketiga dimensi agar sesuai dengan kotak. Artinya, hanya untuk produk yang dapat diletakkan, kita dapat membandingkan lebar atau kedalaman produk dengan tinggi kotak, dan membandingkan dua dimensi produk yang tersisa dengan lebar dan kedalaman kotak.
Setelah kita memahami apa yang baru saja saya katakan (seperti yang kita lakukan tanpa komputer, hanya dengan pensil di atas kertas), terjemahan ke dalam kode hampir otomatis:
select p.id, b.box_size
from products p left outer join boxes b
on
p.h <= b.h and least (p.w, p.d) <= least (b.w, b.d)
and greatest(p.w, p.d) <= greatest(b.w, b.d)
or
p.layable = 'y'
and
( p.w <= b.h and least (p.h, p.d) <= least (b.w, b.d)
and greatest(p.h, p.d) <= greatest(b.w, b.d)
or
p.d <= b.h and least (p.w, p.h) <= least (b.w, b.d)
and greatest(p.w, p.h) <= greatest(b.w, b.d)
)
;
Keluaran:
ID BOX_SIZE
--- --------
a S
a M
a L
b M
b L
c L
d S
d M
d L
e L
f L
g S
g M
g L
h M
h L
i L
j
Untuk setiap produk, kami menemukan SEMUA ukuran yang sesuai.
Perhatikan gabungan luar dalam kueri, untuk menyertakan produk yang tidak sesuai dengan ukuran kotak APAPUN; itulah kasus produk j
, yang muncul di akhir output. Perhatikan bahwa saya menggunakan null
sebagai penanda untuk "tidak tersedia " - kata-kata "tidak tersedia" tidak menambahkan informasi berharga selain penggunaan sederhana null
.
Langkah selanjutnya adalah agregasi sederhana - untuk setiap produk, temukan ukuran terkecil yang berfungsi. Alat terbaik untuk ini adalah FIRST
fungsi agregat (seperti yang digunakan di bawah). Kita harus memesan berdasarkan ukuran kotak; karena ukurannya adalah S, M, L (yang dalam urutan abjad terbalik hanya secara tidak sengaja), saya menggunakan decode()
berfungsi untuk menetapkan 1 ke S, 2 ke M, 3 ke L. Kueri agregat menemukan ukuran "pertama" yang berfungsi untuk setiap produk.
Yang penting di sini adalah bahwa kueri dapat digeneralisasi dengan mudah ke sejumlah "ukuran kotak" yang mungkin - bahkan ketika tidak ketiga dimensi dalam urutan yang meningkat. (Anda juga dapat memiliki kotak dengan hanya satu dimensi yang sangat besar sementara yang lain kecil, dll.). Anda dapat memesan berdasarkan volume kotak, atau Anda dapat menyimpan dalam tabel kotak urutan preferensi, setara dengan apa yang saya lakukan dalam kueri dengan decode()
fungsi.
Pada akhirnya, kueri dan output terlihat seperti ini. Perhatikan bahwa saya menggunakan nvl()
di select
klausa untuk menghasilkan 'not available'
untuk item terakhir, jika Anda benar-benar membutuhkannya (yang saya ragu, tapi itu bukan masalah bisnis saya.)
select p.id,
nvl( min(b.box_size) keep (dense_rank first
order by decode(b.box_size, 'S', 1, 'M', 2, 'L', 3))
, 'not available') as box_size
from products p left outer join boxes b
on
p.h <= b.h and least (p.w, p.d) <= least (b.w, b.d)
and greatest(p.w, p.d) <= greatest(b.w, b.d)
or
p.layable = 'y'
and
( p.w <= b.h and least (p.h, p.d) <= least (b.w, b.d)
and greatest(p.h, p.d) <= greatest(b.w, b.d)
or
p.d <= b.h and least (p.w, p.h) <= least (b.w, b.d)
and greatest(p.w, p.h) <= greatest(b.w, b.d)
)
group by p.id
;
ID BOX_SIZE
--- --------
a S
b M
c L
d S
e L
f L
g S
h M
i L
j not available