Secara utama, saya punya dua jenis interval waktu:
presence time
dan absence time
absence time
dapat dari jenis yang berbeda (misalnya istirahat, absen, hari khusus dan sebagainya) dan interval waktu mungkin tumpang tindih dan / atau berpotongan.
Hal ini tidak pasti, bahwa hanya kombinasi yang masuk akal dari interval ada di data mentah, misalnya. interval kehadiran yang tumpang tindih tidak masuk akal, tetapi mungkin ada. Saya sudah mencoba mengidentifikasi interval waktu kehadiran yang dihasilkan dalam banyak cara sekarang - bagi saya, yang paling nyaman tampaknya adalah yang mengikuti.
;with "timestamps"
as
(
select
"id" = row_number() over ( order by "empId", "timestamp", "opening", "type" )
, "empId"
, "timestamp"
, "type"
, "opening"
from
(
select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
( select "empId", "starttime", "endtime", 1 as "type" from "worktime" ) as data
unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
union all
select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
( select "empId", "starttime", "endtime", 2 as "type" from "break" ) as data
unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
union all
select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
( select "empId", "starttime", "endtime", 3 as "type" from "absence" ) as data
unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
) as data
)
select
T1."empId"
, "starttime" = T1."timestamp"
, "endtime" = T2."timestamp"
from
"timestamps" as T1
left join "timestamps" as T2
on T2."empId" = T1."empId"
and T2."id" = T1."id" + 1
left join "timestamps" as RS
on RS."empId" = T2."empId"
and RS."id" <= T1."id"
group by
T1."empId", T1."timestamp", T2."timestamp"
having
(sum( power( 2, RS."type" ) * RS."opening" ) = 2)
order by
T1."empId", T1."timestamp";
lihat SQL-Fiddle untuk beberapa data demo.
Data mentah ada di berbagai tabel dalam bentuk "starttime" - "endtime"
atau "starttime" - "duration"
.
Idenya adalah untuk mendapatkan daftar yang terurut dari setiap cap waktu dengan jumlah bergulir "bitmasked" dari interval terbuka pada setiap waktu untuk memperkirakan waktu kehadiran.
Biola bekerja dan memberikan hasil yang diperkirakan, meskipun waktu jeda dari interval yang berbeda sama. Tidak ada indeks yang digunakan dalam contoh ini.
Apakah ini cara yang tepat untuk mencapai tugas yang dipertanyakan atau apakah ada cara yang lebih elegan untuk ini?
Jika relevan untuk menjawab: jumlah data akan mencapai beberapa sepuluh ribu dataset per karyawan per tabel. sql-2012 tidak tersedia untuk menghitung jumlah bergulir para pendahulu inline secara agregat.
edit:
Hanya menjalankan kueri terhadap jumlah testdata yang lebih besar (1000, 10.000, 100.000, 1 juta) dan dapat melihat bahwa runtime meningkat secara eksponensial. Jelas sebuah bendera peringatan, bukan?
Saya mengubah kueri dan menghapus agregasi jumlah bergulir dengan pembaruan yang unik.
Saya telah menambahkan tabel tambahan:
create table timestamps
(
"id" int
, "empId" int
, "timestamp" datetime
, "type" int
, "opening" int
, "rolSum" int
)
create nonclustered index "idx" on "timestamps" ( "rolSum" ) include ( "id", "empId", "timestamp" )
dan saya pindah menghitung rolling sum ke tempat ini:
declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"
Runtime menurun menjadi 3 detik mengenai 1 juta entri dalam tabel "worktime".
Pertanyaan tetap sama : Apa cara paling efektif untuk menyelesaikan ini?
[this]
. Saya hanya suka itu lebih baik daripada tanda kutip ganda, saya kira.Jawaban:
Saya tidak bisa menjawab pertanyaan Anda tentang cara yang terbaik. Tetapi saya dapat menawarkan cara berbeda untuk menyelesaikan masalah, yang mungkin atau mungkin tidak lebih baik. Ini memiliki rencana pelaksanaan yang cukup datar, dan saya pikir itu akan berkinerja baik. (Saya ingin tahu, jadi bagikan hasilnya!)
Saya minta maaf karena menggunakan gaya sintaksis saya sendiri dan bukan gaya Anda - ini membantu permintaan sihir datang kepada saya ketika semuanya berbaris di tempat biasanya.
Kueri tersedia dalam SqlFiddle . Saya melemparkan tumpang tindih untuk EmpID 1 hanya untuk memastikan saya memiliki itu tertutup. Jika pada akhirnya Anda menemukan bahwa tumpang tindih tidak dapat terjadi dalam data keberadaan, maka Anda dapat menghapus kueri akhir dan
Dense_Rank
perhitungan.Catatan: kinerja kueri ini akan ditingkatkan Anda menggabungkan tiga tabel dan menambahkan kolom untuk menunjukkan seperti apa waktu: bekerja, istirahat, atau tidak ada.
Dan mengapa semua CTE, Anda bertanya? Karena masing-masing dipaksa oleh apa yang perlu saya lakukan untuk data. Ada agregat, atau saya harus meletakkan kondisi WHERE pada fungsi windowing atau menggunakannya dalam klausa di mana fungsi windowing tidak diperbolehkan.
Sekarang saya akan pergi dan melihat apakah saya tidak bisa memikirkan strategi lain untuk mencapai ini. :)
Untuk hiburan, saya sertakan di sini "diagram" yang saya buat untuk membantu memecahkan masalah:
Tiga set tanda hubung (dipisahkan oleh spasi) mewakili, secara berurutan: data keberadaan, data absensi, dan hasil yang diinginkan.
sumber