pilih semua baris dengan nilai minimum

9

Di Sqlite 3 saya mencoba mencari cara memilih baris berdasarkan nilai minimum. Saya pikir saya dibatasi oleh tidak cukup tahu tentang terminologi terkait untuk mencari google secara efektif.

Tabelnya terlihat seperti:

num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           a           3         
1           b           4         

Saya ingin mendapatkan baris mana num2adalah 1, 2, dan 4. Saya ingin melakukan seleksi berdasarkan nilai minimum num untuk setiap nilai unik kolom teks.

Jadi, untuk text = 'a', nilai minimumnya numadalah 0, jadi saya ingin baris 1 dan 2. Untuk text = 'b', nilai minimumnya numadalah 1, jadi saya ingin baris 4.

Dengan menggunakan berbagai kombinasi grup, saya dapat memperoleh baris 1dan 2atau baris 1dan 4. Saya merasa seperti kehilangan komponen SQL yang akan melakukan apa yang saya inginkan, tetapi saya belum dapat menemukan apa yang bisa dilakukan.

Apa cara yang tepat untuk melakukan jenis permintaan ini?

Kemungkinan Solusi

Saya telah menemukan sebuah cara untuk melakukan ini. Saya tidak cukup memiliki reputasi untuk menjawab pertanyaan saya sendiri, jadi saya melakukan pembaruan di sini. Saya tidak yakin apakah selalu benar atau seperti apa efisiensi itu. Setiap komentar dipersilakan.

Saya menggunakan pernyataan pemilihan majemuk, di mana satu kueri menemukan nilai minimum num untuk setiap nilai teks yang unik:

sqlite> select num, text from t group by text having num = min( num );
num         text      
----------  ----------
0           a         
1           b         

Kemudian saya bergabung dengan tabel lengkap ini untuk mendapatkan semua baris yang cocok dengan dua kolom ini.

sqlite> with u as
      ( select num, text from t group by text having num = min( num ) )
        select t.* from t join u on t.num = u.num and t.text = u.text;
num         text        num2      
----------  ----------  ----------
0           a           1         
0           a           2         
1           b           4         
pengguna35292
sumber

Jawaban:

10

Seperti yang Anda lihat, GROUP BY yang sederhana tidak akan berfungsi karena hanya akan mengembalikan satu record per grup.

Gabung Anda berfungsi dengan baik. Untuk tabel besar, akan efisien hanya jika ada indeks pada kolom gabungan ( numdan text).

Atau, Anda bisa menggunakan subquery yang dikorelasikan:

SELECT *
FROM t
WHERE num = (SELECT MIN(num)
             FROM t AS t2
             WHERE t2.text = t.text);

SQLFiddle

Ketika dieksekusi, kueri ini tidak memerlukan tabel sementara (kueri Anda tidak untuk hasil dari u), tetapi akan mengeksekusi subquery untuk setiap catatan t, jadi textharus diindeks. (Atau gunakan indeks pada keduanya textdan numuntuk mendapatkan indeks penutup .)

CL.
sumber
dia tidak memiliki tabel sementara dalam permintaannya, hanya CTE, yang sangat berbeda.
ypercubeᵀᴹ
Ketika dieksekusi, hasil ukueri disimpan dalam tabel sementara, terlepas dari apakah itu ditulis sebagai CTE, tampilan, atau inline sebagai subquery.
CL.
Terima kasih, versi ini jauh lebih mudah untuk menulis daripada yang saya temukan. Mengetahui terminologi yang tepat juga membantu saya untuk melihat lebih dalam.
user35292
@ CL Apakah itu cara SQLite mengeksekusi query dengan CTE? Apakah Anda punya referensi untuk itu? Karena DBMS lain tidak perlu menggunakan temp tables untuk ctes.
ypercubeᵀᴹ
@ypercube CTE, tampilan, dan subkueri diratakan atau diimplementasikan sebagai coroutine, jika memungkinkan. Tetapi GROUP BY pada kolom yang tidak diindeks harus dapat mengumpulkan data untuk semua grup secara paralel, sehingga memerlukan beberapa bentuk tabel sementara (di semua database).
CL.
1

Saya cenderung melakukan hal ini dengan bergabung dengan diri luar:

SELECT
    M1.Num,
    M1.Text,
    M1.Num2
FROM
    MyDb M1
LEFT OUTER JOIN
    MyDB M2
ON
    M1.text = M2.text
AND
    M1.num > m2.num
WHERE
    M2.num is null

Ini pada dasarnya mengatakan; beri saya semua catatan yang tidak memiliki nilai lebih tinggi yaitu nol.

CIUMAN
sumber
1

Jadi, bagaimana Anda dapat menemukan jawaban untuk pertanyaan Anda sendiri di waktu berikutnya? Menurut pendapat saya, itu adalah dengan menguraikan dan mengikuti logika. Dan Anda benar:

Saya ingin melakukan seleksi berdasarkan nilai minimum num untuk setiap nilai unik kolom teks

Ini diterjemahkan menjadi:

select text, min(num) from t group by text;

(Ini harus setara dengan havingpermintaan Anda . Mungkin menarik untuk melihat baris yang numsama dengan NULL. Lebih tepat: Lihat apa efek yang dimiliki baris dengan nol, yang mungkin ingin Anda saring terlebih dahulu dengan a where num is not null)

Dari sini Anda dapat mencapai hasil yang diinginkan dengan:

select * from t where (num, text) in ( *insert query above* )

Atau menggunakan gabungan:

select t1.* from t t1,
    (select text, min(num) as n from t group by text) t2
where t1.num = t2.n and t1.text = t2.text.

Dan ketika kinerja tidak cukup untuk tabel Anda, mulailah melihat pernyataan yang lebih kompleks.

Grimaldi
sumber
-2

Bukankah seharusnya ini permintaan yang persis Anda butuhkan?

select min(num), text, num2 group by text, num2
Jens W.
sumber
Ini akan mengembalikan keempat catatan, karena num2nilainya unik.
CL.