Tabel Riwayat Pengguna berikut berisi satu catatan untuk setiap hari pengguna tertentu telah mengakses situs web (dalam periode 24 jam UTC). Ini memiliki ribuan catatan, tetapi hanya satu catatan per hari per pengguna. Jika pengguna belum mengakses situs web untuk hari itu, tidak ada catatan yang akan dibuat.
Id UserId CreationDate ------ ------ ------------ 750997 12 2009-07-07 18:42: 20.723 750998 15 2009-07-07 18:42: 20.927 751000 19 2009-07-07 18:42: 22.283
Yang saya cari adalah kueri SQL pada tabel ini dengan kinerja yang baik , yang memberi tahu saya pengguna mana yang telah mengakses situs web selama (n) hari terus menerus tanpa melewatkan satu hari pun.
Dengan kata lain, berapa banyak pengguna yang memiliki (n) catatan dalam tabel ini dengan tanggal berurutan (hari sebelum, atau setelah hari) ? Jika ada hari yang hilang dari urutan tersebut, urutan tersebut rusak dan harus dimulai ulang lagi pada 1; kami mencari pengguna yang telah mencapai jumlah hari yang terus menerus di sini tanpa celah.
Kemiripan apa pun antara kueri ini dan lencana Stack Overflow tertentu benar-benar kebetulan, tentu saja .. :)
sumber
Jawaban:
Jawabannya jelas:
EDIT:
Oke, inilah jawaban serius saya:
EDIT:
[Jeff Atwood] Ini adalah solusi cepat yang hebat dan pantas untuk diterima, tetapi solusi Rob Farley juga sangat baik dan bisa dibilang bahkan lebih cepat (!). Silakan periksa juga!
sumber
ON uh2.CreationDate >= uh1.CreationDate AND uh2.CreationDate < DATEADD(dd, DATEDIFF(dd, 0, uh1.CreationDate) + @days, 0)
berarti "Belum pada hari ke-31 nanti". Juga berarti Anda dapat melewati kalkulasi @seconds.Bagaimana dengan (dan pastikan pernyataan sebelumnya diakhiri dengan titik koma):
Idenya adalah bahwa jika kita memiliki daftar hari (sebagai angka), dan row_number, maka hari yang terlewat membuat offset antara kedua daftar ini sedikit lebih besar. Jadi kami mencari rentang yang memiliki offset yang konsisten.
Anda bisa menggunakan "ORDER BY NumConsecutiveDays DESC" di akhir ini, atau ucapkan "HAVING count (*)> 14" untuk ambang ...
Saya belum mengujinya - hanya menuliskannya di atas kepala saya. Semoga bekerja di SQL2005 dan seterusnya.
... dan akan sangat terbantu oleh indeks pada tablename (UserID, CreationDate)
Diedit: Ternyata Offset adalah kata yang dicadangkan, jadi saya menggunakan TheOffset sebagai gantinya.
Diedit: Saran untuk menggunakan JUMLAH (*) sangat valid - Saya seharusnya melakukannya sejak awal tetapi tidak benar-benar berpikir. Sebelumnya itu menggunakan tanggaliff (hari, min (CreationDate), max (CreationDate)) sebagai gantinya.
rampok
sumber
Jika Anda dapat mengubah skema tabel, saya sarankan untuk menambahkan kolom
LongestStreak
ke tabel yang akan Anda setel ke jumlah hari berurutan yang diakhiri denganCreationDate
. Sangat mudah untuk memperbarui tabel pada saat masuk (mirip dengan yang Anda lakukan, jika tidak ada baris hari ini, Anda akan memeriksa apakah ada baris untuk hari sebelumnya. Jika benar, Anda akan menambahLongestStreak
dalam baris baru, jika tidak, Anda akan menyetelnya ke 1.)Kueri akan terlihat jelas setelah menambahkan kolom ini:
sumber
Beberapa SQL ekspresif yang bagus di sepanjang baris:
Dengan asumsi Anda memiliki fungsi agregat yang ditentukan pengguna, sesuatu di sepanjang baris (waspadalah ini buggy):
sumber
Sepertinya Anda bisa memanfaatkan fakta bahwa untuk terus menerus selama n hari akan membutuhkan n baris.
Jadi sesuatu seperti:
sumber
Melakukan ini dengan satu kueri SQL tampaknya terlalu rumit bagi saya. Izinkan saya memecah jawaban ini menjadi dua bagian.
Jalankan tugas cron harian yang memeriksa setiap pengguna apakah dia telah masuk hari ini dan kemudian menambah penghitung jika dia memiliki atau menyetelnya ke 0 jika belum.
- Ekspor tabel ini ke server yang tidak menjalankan situs web Anda dan tidak akan diperlukan untuk sementara waktu. ;)
- Sortir menurut pengguna, lalu tanggal.
- lakukan secara berurutan, pertahankan ...
sumber
Jika ini sangat penting bagi Anda, sumber acara ini dan dorong tabel untuk memberi Anda info ini. Tidak perlu mematikan mesin dengan semua pertanyaan gila itu.
sumber
Anda dapat menggunakan CTE rekursif (SQL Server 2005+):
sumber
Joe Celko memiliki bab lengkap tentang ini dalam SQL untuk Smarties (menyebutnya Runs and Sequences). Saya tidak punya buku itu di rumah, jadi ketika saya mulai bekerja ... Saya akan menjawab ini. (dengan asumsi tabel riwayat disebut dbo.UserHistory dan jumlah hari adalah @Days)
Prospek lainnya adalah dari blog Tim SQL yang sedang berjalan
Ide lain yang saya miliki, tetapi tidak memiliki server SQL yang berguna untuk dikerjakan di sini adalah menggunakan CTE dengan ROW_NUMBER yang dipartisi seperti ini:
Hal di atas mungkin JAUH LEBIH KERAS daripada yang seharusnya, tetapi dibiarkan sebagai penggelitik otak ketika Anda memiliki definisi lain tentang "lari" daripada sekadar kencan.
sumber
Beberapa opsi SQL Server 2012 (dengan asumsi N = 100 di bawah).
Meskipun dengan data sampel saya, berikut ini bekerja lebih efisien
Keduanya mengandalkan batasan yang dinyatakan dalam pertanyaan bahwa ada paling banyak satu record per hari per pengguna.
sumber
Sesuatu seperti ini?
sumber
Saya menggunakan properti matematika sederhana untuk mengidentifikasi siapa yang secara berurutan mengakses situs. Properti ini adalah Anda harus memiliki perbedaan hari antara akses pertama kali dan terakhir kali sama dengan jumlah catatan di log tabel akses Anda.
Berikut adalah skrip SQL yang saya uji di Oracle DB (seharusnya berfungsi di DB lain juga):
Skrip persiapan tabel:
sumber
Pernyataan tersebut
cast(convert(char(11), @startdate, 113) as datetime)
menghapus bagian waktu dari tanggal jadi kami mulai tengah malam.Saya juga akan berasumsi bahwa
creationdate
danuserid
kolom diindeks.Saya baru menyadari bahwa ini tidak akan memberi tahu Anda semua pengguna dan total hari mereka yang berurutan. Tetapi akan memberi tahu Anda pengguna mana yang akan mengunjungi sejumlah hari yang ditentukan sejak tanggal yang Anda pilih.
Solusi yang direvisi:
Saya telah memeriksa ini dan ini akan menanyakan semua pengguna dan semua tanggal. Ini didasarkan pada solusi pertama Spencer (lelucon?) , Tapi solusi saya berhasil.
Pembaruan: meningkatkan penanganan tanggal dalam solusi kedua.
sumber
Ini harus melakukan apa yang Anda inginkan tetapi saya tidak memiliki cukup data untuk menguji efisiensi. Hal-hal CONVERT / FLOOR yang berbelit-belit adalah untuk menghapus bagian waktu dari bidang datetime. Jika Anda menggunakan SQL Server 2008 maka Anda dapat menggunakan CAST (x.CreationDate AS DATE).
Skrip pembuatan
sumber
Spencer hampir melakukannya, tetapi ini seharusnya menjadi kode yang berfungsi:
sumber
Di luar kepalaku, MySQLish:
Belum teruji, dan hampir pasti membutuhkan beberapa konversi untuk MSSQL, tapi saya pikir itu memberi beberapa ide.
sumber
Bagaimana kalau seseorang menggunakan tabel Tally? Ini mengikuti pendekatan yang lebih algoritmik, dan rencana eksekusi sangat mudah. Isi tallyTable dengan angka dari 1 hingga 'MaxDaysBehind' yang ingin Anda pindai tabelnya (mis. 90 akan mencari 3 bulan di belakang, dll).
sumber
Sesuaikan sedikit pertanyaan Bill. Anda mungkin harus memotong tanggal sebelum mengelompokkan untuk menghitung hanya satu login per hari ...
DIEDIT untuk menggunakan DATEADD (hh, DATEDIFF (dd, 0, CreationDate), 0) alih-alih mengonversi (char (10), CreationDate, 101).
@IDisposable Saya mencari untuk menggunakan datepart sebelumnya tetapi saya terlalu malas untuk mencari sintaks jadi saya pikir id menggunakan konversi sebagai gantinya. Saya tidak tahu itu berdampak signifikan Terima kasih! sekarang saya tahu.
sumber
dengan asumsi skema yang berjalan seperti:
ini akan mengekstrak rentang yang berdekatan dari urutan tanggal dengan celah.
sumber