Saya memiliki tabel yang ingin saya dapatkan entri terbaru untuk setiap grup. Ini tabelnya:
DocumentStatusLogs
Meja
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
Tabel akan dikelompokkan berdasarkan DocumentID
dan diurutkan berdasarkan DateCreated
urutan menurun. Untuk masing-masing DocumentID
, saya ingin mendapatkan status terbaru.
Output pilihan saya:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
Apakah ada fungsi agregat untuk mendapatkan yang teratas dari setiap grup? Lihat pseudo-code di
GetOnlyTheTop
bawah ini:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Jika fungsi seperti itu tidak ada, apakah ada cara saya dapat mencapai output yang saya inginkan?
- Atau pertama-tama, bisakah ini disebabkan oleh database yang tidak dinormalisasi? Saya berpikir, karena apa yang saya cari hanya satu baris, haruskah itu
status
juga terletak di tabel induk?
Silakan lihat tabel induk untuk informasi lebih lanjut:
Documents
Tabel Saat Ini
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
Haruskah tabel induk seperti ini sehingga saya dapat dengan mudah mengakses statusnya?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
PEMBARUAN Saya baru belajar cara menggunakan "berlaku" yang membuatnya lebih mudah untuk mengatasi masalah seperti itu.
Jawaban:
Jika Anda mengharapkan 2 entri per hari, maka ini akan secara sewenang-wenang memilih satu entri. Untuk mendapatkan kedua entri selama sehari, gunakan DENSE_RANK sebagai gantinya
Adapun dinormalisasi atau tidak, itu tergantung jika Anda ingin:
Seperti berdiri, Anda menyimpan riwayat status. Jika Anda menginginkan status terbaru di tabel induk juga (yang merupakan denormalisasi), Anda perlu pemicu untuk mempertahankan "status" di induk. atau jatuhkan tabel riwayat status ini.
sumber
Partition By
?With
juga baru bagi saya :( Saya menggunakan mssql 2005.ROW_NUMBER
ada semacam subquery untuk setiap baris?Saya baru belajar cara menggunakannya
cross apply
. Berikut cara menggunakannya dalam skenario ini:sumber
Saya telah melakukan beberapa pengaturan waktu atas berbagai rekomendasi di sini, dan hasilnya benar-benar tergantung pada ukuran tabel yang terlibat, tetapi solusi yang paling konsisten adalah menggunakan CROSS BERLAKU Tes ini dijalankan terhadap SQL Server 2008-R2, menggunakan tabel dengan 6.500 catatan, dan satu lagi (skema identik) dengan 137 juta catatan. Kolom yang dipertanyakan adalah bagian dari kunci utama pada tabel, dan lebar tabel sangat kecil (sekitar 30 byte). Waktu dilaporkan oleh SQL Server dari rencana eksekusi yang sebenarnya.
Saya pikir hal yang sangat menakjubkan adalah seberapa konsisten waktu untuk CROSS BERLAKU terlepas dari jumlah baris yang terlibat.
sumber
Saya tahu ini adalah utas lama tapi
TOP 1 WITH TIES
solusinya cukup bagus dan mungkin bisa membantu untuk membaca beberapa solusi.Lebih lanjut tentang klausa TOP dapat ditemukan di sini .
sumber
Jika Anda khawatir tentang kinerja, Anda juga dapat melakukan ini dengan MAX ():
ROW_NUMBER () membutuhkan semacam semua baris dalam pernyataan SELECT Anda, sedangkan MAX tidak. Harus mempercepat permintaan Anda secara drastis.
sumber
row_number()
bahkan dengan pengindeksan yang tepat. Saya merasa ini sangat berharga dalam skenario self-join. Yang perlu disadari, adalah bahwa metode ini akan sering menghasilkan jumlah pembacaan logis dan pemindaian yang lebih tinggi, meskipun melaporkan biaya subtree yang rendah. Anda harus mempertimbangkan biaya / manfaat dalam kasus khusus Anda untuk menentukan apakah itu sebenarnya lebih baik.Server basis data apa? Kode ini tidak berfungsi pada mereka semua.
Mengenai paruh kedua pertanyaan Anda, tampaknya masuk akal bagi saya untuk memasukkan status sebagai kolom. Anda dapat meninggalkan
DocumentStatusLogs
sebagai log, tetapi masih menyimpan info terbaru di tabel utama.BTW, jika Anda sudah memiliki
DateCreated
kolom di tabel Documents, Anda bisa bergabungDocumentStatusLogs
menggunakan itu (asalkanDateCreated
unikDocumentStatusLogs
).Sunting: MsSQL tidak mendukung USING, jadi ubah ke:
sumber
max(DateCreated)
Ini adalah salah satu pertanyaan yang paling mudah ditemukan pada topik, jadi saya ingin memberikan jawaban modern untuk itu (baik untuk referensi saya dan untuk membantu orang lain). Dengan menggunakan
first_value
danover
Anda dapat membuat karya pendek dari pertanyaan di atas:Ini harus bekerja di Sql Server 2008 dan lebih tinggi.
First_value
dapat dianggap sebagai cara untuk mencapaiSelect Top 1
ketika menggunakanover
klausa.Over
memungkinkan pengelompokan dalam daftar pilih jadi alih-alih menulis subqueries bersarang (seperti banyak jawaban yang ada lakukan), ini melakukannya dengan cara yang lebih mudah dibaca. Semoga ini membantu.sumber
Ini adalah utas yang cukup lama, tetapi saya pikir saya akan melemparkan dua sen saya sama saja dengan jawaban yang diterima tidak bekerja dengan baik bagi saya. Saya mencoba solusi gbn pada dataset besar dan ternyata sangat lambat (> 45 detik pada 5 juta plus catatan dalam SQL Server 2012). Melihat rencana eksekusi, jelas bahwa masalahnya adalah membutuhkan operasi SORT yang memperlambat segalanya secara signifikan.
Berikut adalah alternatif yang saya angkat dari kerangka entitas yang tidak memerlukan operasi SORT dan melakukan pencarian Indeks NON-Clustered. Ini mengurangi waktu eksekusi menjadi <2 detik pada set catatan yang disebutkan di atas.
Sekarang saya mengasumsikan sesuatu yang tidak sepenuhnya ditentukan dalam pertanyaan asli, tetapi jika desain tabel Anda sedemikian rupa sehingga kolom ID Anda adalah ID kenaikan-otomatis, dan DateCreated diatur ke tanggal saat ini dengan setiap sisipan, maka bahkan tanpa berjalan dengan kueri saya di atas, Anda sebenarnya bisa mendapatkan peningkatan kinerja yang cukup besar untuk solusi gbn (sekitar setengah dari waktu eksekusi) hanya dari memesan pada ID daripada memesan pada DateCreated karena ini akan memberikan urutan pengurutan yang identik dan ini merupakan pengurutan yang lebih cepat.
sumber
Kode saya untuk memilih 1 teratas dari setiap grup
sumber
Memverifikasi jawaban Clint yang luar biasa dan benar dari atas:
Kinerja antara dua pertanyaan di bawah ini menarik. 52% menjadi yang teratas. Dan 48% menjadi yang kedua. Peningkatan kinerja 4% menggunakan DISTINCT bukan ORDER BY. Tetapi ORDER BY memiliki keuntungan untuk mengurutkan berdasarkan beberapa kolom.
Pilihan 1:
Pilihan 2:
Studio Manajemen M $: Setelah menyorot dan menjalankan blok pertama, sorot Opsi 1 dan Opsi 2, Klik kanan -> [Tampilkan Perkiraan Rencana Eksekusi]. Kemudian jalankan semuanya untuk melihat hasilnya.
Opsi 1 Hasil:
Opsi 2 Hasil:
catatan:
Saya juga menghindari subqueries EXISTS / IN dalam klausa WHERE atau ON, karena saya telah mengalami hal ini menyebabkan beberapa rencana eksekusi yang mengerikan. Tetapi jarak tempuh bervariasi. Tinjau rencana eksekusi dan kinerja profil di mana dan kapan diperlukan!
sumber
Solusi ini dapat digunakan untuk mendapatkan baris TOP N terbaru untuk setiap partisi (dalam contoh, N adalah 1 dalam pernyataan WHERE dan partisi adalah doc_id):
sumber
Jika Anda ingin mengembalikan hanya pesanan dokumen terbaru oleh DateCreated, itu hanya akan mengembalikan 1 dokumen teratas oleh DocumentID
sumber
CROSS APPLY
adalah metode yang saya gunakan untuk solusi saya, karena itu bekerja untuk saya, dan untuk kebutuhan klien saya. Dan dari apa yang saya baca, harus memberikan kinerja keseluruhan terbaik jika database mereka tumbuh secara substansial.sumber
Berikut adalah 3 pendekatan terpisah untuk masalah yang ada bersama dengan pilihan terbaik pengindeksan untuk masing-masing pertanyaan tersebut (silakan coba sendiri indeksnya dan lihat bacaan logis, waktu yang berlalu, rencana pelaksanaan. Saya telah memberikan saran dari pengalaman saya tentang pertanyaan seperti itu tanpa mengeksekusi untuk masalah khusus ini).
Pendekatan 1 : Menggunakan ROW_NUMBER (). Jika indeks rowstore tidak dapat meningkatkan kinerja, Anda dapat mencoba indeks kolomstore nonclustered / clustered untuk permintaan dengan agregasi dan pengelompokan dan untuk tabel yang dipesan oleh dalam kolom yang berbeda setiap saat, indeks columnstore biasanya merupakan pilihan terbaik.
Pendekatan 2 : Menggunakan FIRST_VALUE. Jika indeks rowstore tidak dapat meningkatkan kinerja, Anda dapat mencoba indeks kolomstore nonclustered / clustered untuk permintaan dengan agregasi dan pengelompokan dan untuk tabel yang dipesan oleh dalam kolom yang berbeda setiap saat, indeks columnstore biasanya merupakan pilihan terbaik.
Pendekatan 3 : Menggunakan CROSS APPLY. Membuat indeks rowstore pada tabel DocumentStatusLogs yang mencakup kolom yang digunakan dalam kueri harus cukup untuk mencakup permintaan tanpa perlu indeks columnstore.
sumber
Saya percaya ini bisa dilakukan seperti ini. Ini mungkin perlu beberapa penyesuaian tetapi Anda dapat memilih maks dari grup.
Jawaban-jawaban ini berlebihan ...
sumber
Dalam skenario di mana Anda ingin menghindari menggunakan row_count (), Anda juga dapat menggunakan gabungan kiri:
Untuk skema contoh, Anda juga bisa menggunakan "tidak dalam subquery", yang umumnya mengkompilasi ke output yang sama dengan gabungan kiri:
Catatan, pola subquery tidak akan berfungsi jika tabel tidak memiliki setidaknya satu kolom kunci unik / kendala / indeks, dalam hal ini kunci primer "Id".
Kedua kueri ini cenderung lebih "mahal" daripada kueri row_count () (seperti yang diukur oleh Query Analyzer). Namun, Anda mungkin menemukan skenario di mana mereka mengembalikan hasil lebih cepat atau mengaktifkan optimasi lainnya.
sumber
sumber
Coba ini:
sumber
Ini adalah TSQL vanilla paling banyak yang bisa saya buat
sumber
Itu diperiksa dalam SQLite bahwa Anda dapat menggunakan permintaan sederhana berikut dengan GROUP BY
Di sini MAX membantu untuk mendapatkan DateCreated maksimum DARI masing-masing kelompok.
Tapi sepertinya MYSQL tidak mengaitkan * -kolom dengan nilai max DateCreated :(
sumber