Apakah Anda tahu cara mudah untuk menghasilkan satu catatan untuk setiap jam selama 12 jam terakhir?

12

Saya memiliki laporan yang menunjukkan jumlah acara selama 12 jam terakhir, dikelompokkan berdasarkan jam. Kedengarannya cukup mudah, tetapi yang saya perjuangkan adalah bagaimana memasukkan catatan yang menutupi kesenjangan.

Ini adalah contoh tabel:

Event
(
  EventTime datetime,
  EventType int
)

Data terlihat seperti ini:

  '2012-03-08 08:00:04', 1
  '2012-03-08 09:10:00', 2
  '2012-03-08 09:11:04', 2
  '2012-03-08 09:10:09', 1
  '2012-03-08 10:00:17', 4
  '2012-03-08 11:00:04', 1

Saya perlu membuat set hasil yang memiliki satu catatan untuk setiap jam selama 12 jam terakhir, terlepas dari ada peristiwa selama jam itu atau tidak.

Dengan asumsi waktu saat ini adalah '2012-03-08 11:00:00', laporan akan menunjukkan (kurang-lebih):

Hour  EventCount
----  ----------
23    0
0     0
1     0
2     0
3     0
4     0
5     0
6     0
7     0
8     1
9     3
10    1

Saya datang dengan solusi yang menggunakan tabel yang memiliki satu catatan untuk setiap jam dalam sehari. Saya berhasil mendapatkan hasil yang saya cari menggunakan UNION dan beberapa logika kasus yang berbelit-belit di klausa mana, tetapi saya berharap seseorang memiliki solusi yang lebih elegan.

datagod
sumber

Jawaban:

21

Untuk SQL Server 2005+ Anda dapat menghasilkan 12 catatan tersebut dengan sangat mudah dengan loop dan CTE rekursif. Berikut adalah contoh CTE rekursif:

DECLARE @Date DATETIME
SELECT @Date = '20120308 11:00:00'

;WITH Dates AS
(
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,@Date)) [Hour], 
      DATEADD(HOUR,-1,@Date) [Date], 1 Num
    UNION ALL
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,[Date])), 
      DATEADD(HOUR,-1,[Date]), Num+1
    FROM Dates
    WHERE Num <= 11
)
SELECT [Hour], [Date]
FROM Dates

Kemudian Anda baru saja akan bergabung dengan tabel acara Anda.

Lamak
sumber
2
Saya menemukan ini tepat setelah Anda memposting. answerextended.com/2009/10/21/... Ini menunjukkan bahwa menggunakan CTE untuk tujuan ini kurang efisien daripada tabel yang disimpan. Apakah ini benar? Seperti kata Nick, mungkin tidak masalah untuk kasus ini, tapi ...
Leigh Riffel
4
Saya pikir itu akan membuat perbedaan dengan jumlah baris yang lebih besar, jika Anda membutuhkan 12 catatan, maka tidak akan ada performa yang memukul sama sekali
Lamak
Lamak dan @swasheck. Heh ... Saya sedikit terlambat (kehilangan jejak utas ini) untuk sampai ke sana tetapi tidak ada masalah. Lihat jawaban yang akhirnya saya posting untuk mendukung klaim saya di atas. Dan ingat, semua kode memiliki efek kumulatif. Jika semua orang yang menulis kode "hanya" 16 kali lebih cepat, setengah dari posting di forum seperti ini tidak lagi diperlukan. Dan, tidak butuh waktu lebih lama (terkadang lebih pendek) untuk menulis kode lebih cepat.
Jeff Moden
10

Tabel penghitungan dapat digunakan untuk hal-hal seperti ini. Mereka bisa sangat efisien. Buat tabel penghitungan di bawah ini. Saya membuat tabel penghitungan dengan hanya 24 baris untuk contoh Anda, tetapi Anda dapat membuatnya dengan berapa pun Anda ingin sesuai dengan tujuan lain.

SELECT TOP 24 
        IDENTITY(INT,1,1) AS N
   INTO dbo.Tally
   FROM Master.dbo.SysColumns sc1,
        Master.dbo.SysColumns sc2

--===== Add a Primary Key to maximize performance
  ALTER TABLE dbo.Tally
    ADD CONSTRAINT PK_Tally_N 
        PRIMARY KEY CLUSTERED (N) WITH FILLFACTOR = 100

Saya berasumsi meja Anda disebut dbo.tblEvents, jalankan kueri di bawah ini. Saya percaya ini yang Anda cari:

SELECT t.n, count(e.EventTime)
FROM dbo.Tally t
LEFT JOIN dbo.tblEvent e  on t.n = datepart(hh, e.EventTime)
GROUP BY t.n
ORDER BY t.n

Saya percaya kredit diberikan ke tautan berikut, saya percaya ini adalah tempat saya pertama kali menemukan ini:

http://www.sqlservercentral.com/articles/T-SQL/62867/

http://www.sqlservercentral.com/articles/T-SQL/74118/

Henry Lee
sumber
+1 tetapi semantik itu adalah tabel angka, bukan tabel penghitungan.
Aaron Bertrand
1
Salah satu definisi "Tally" adalah "Menghitung". "Tally Table" dinamai "Tally Stick", yang merupakan tongkat panjang dan kurus yang digunakan untuk menghitung.
Jeff Moden
7

Pertama, permintaan maaf saya atas keterlambatan respons saya sejak komentar terakhir saya.

Subjek muncul dalam komentar yang menggunakan CTE Rekursif (rCTE dari sini) berjalan cukup cepat karena rendahnya jumlah baris. Meskipun mungkin terlihat seperti itu, tidak ada yang lebih jauh dari kebenaran.

BANGUN TALLY TABLE DAN FUNGSI TALLY

Sebelum kita memulai pengujian, kita perlu membuat Tabel Tally fisik dengan Indeks Clustered yang sesuai dan Fungsi Tally gaya Itzik Ben-Gan. Kami juga akan melakukan semua ini di TempDB sehingga kami tidak sengaja menjatuhkan barang siapa pun.

Berikut kode untuk membuat Tally Table dan versi produksi saya saat ini dari kode hebat Itzik.

--===== Do this in a nice, safe place that everyone has
    USE tempdb
;
--===== Create/Recreate a Physical Tally Table
     IF OBJECT_ID('dbo.Tally','U') IS NOT NULL
        DROP TABLE dbo.Tally
;
     -- Note that the ISNULL makes a NOT NULL column
 SELECT TOP 1000001
        N = ISNULL(ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1,0)
   INTO dbo.Tally
   FROM      sys.all_columns ac1
  CROSS JOIN sys.all_columns ac2
;
  ALTER TABLE dbo.Tally
    ADD CONSTRAINT PK_Tally PRIMARY KEY CLUSTERED (N)
;
--===== Create/Recreate a Tally Function
     IF OBJECT_ID('dbo.fnTally','IF') IS NOT NULL
        DROP FUNCTION dbo.fnTally
;
GO
 CREATE FUNCTION [dbo].[fnTally]
/**********************************************************************************************************************
 Purpose:
 Return a column of BIGINTs from @ZeroOrOne up to and including @MaxN with a max value of 1 Trillion.

 As a performance note, it takes about 00:02:10 (hh:mm:ss) to generate 1 Billion numbers to a throw-away variable.

 Usage:
--===== Syntax example (Returns BIGINT)
 SELECT t.N
   FROM dbo.fnTally(@ZeroOrOne,@MaxN) t
;

 Notes:
 1. Based on Itzik Ben-Gan's cascading CTE (cCTE) method for creating a "readless" Tally Table source of BIGINTs.
    Refer to the following URLs for how it works and introduction for how it replaces certain loops. 
    http://www.sqlservercentral.com/articles/T-SQL/62867/
    http://sqlmag.com/sql-server/virtual-auxiliary-table-numbers
 2. To start a sequence at 0, @ZeroOrOne must be 0 or NULL. Any other value that's convertable to the BIT data-type
    will cause the sequence to start at 1.
 3. If @ZeroOrOne = 1 and @MaxN = 0, no rows will be returned.
 5. If @MaxN is negative or NULL, a "TOP" error will be returned.
 6. @MaxN must be a positive number from >= the value of @ZeroOrOne up to and including 1 Billion. If a larger
    number is used, the function will silently truncate after 1 Billion. If you actually need a sequence with
    that many values, you should consider using a different tool. ;-)
 7. There will be a substantial reduction in performance if "N" is sorted in descending order.  If a descending 
    sort is required, use code similar to the following. Performance will decrease by about 27% but it's still
    very fast especially compared with just doing a simple descending sort on "N", which is about 20 times slower.
    If @ZeroOrOne is a 0, in this case, remove the "+1" from the code.

    DECLARE @MaxN BIGINT; 
     SELECT @MaxN = 1000;
     SELECT DescendingN = @MaxN-N+1 
       FROM dbo.fnTally(1,@MaxN);

 8. There is no performance penalty for sorting "N" in ascending order because the output is explicity sorted by
    ROW_NUMBER() OVER (ORDER BY (SELECT NULL))

 Revision History:
 Rev 00 - Unknown     - Jeff Moden 
        - Initial creation with error handling for @MaxN.
 Rev 01 - 09 Feb 2013 - Jeff Moden 
        - Modified to start at 0 or 1.
 Rev 02 - 16 May 2013 - Jeff Moden 
        - Removed error handling for @MaxN because of exceptional cases.
 Rev 03 - 22 Apr 2015 - Jeff Moden
        - Modify to handle 1 Trillion rows for experimental purposes.
**********************************************************************************************************************/
        (@ZeroOrOne BIT, @MaxN BIGINT)
RETURNS TABLE WITH SCHEMABINDING AS 
 RETURN WITH
  E1(N) AS (SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
            SELECT 1)                                  --10E1 or 10 rows
, E4(N) AS (SELECT 1 FROM E1 a, E1 b, E1 c, E1 d)      --10E4 or 10 Thousand rows
,E12(N) AS (SELECT 1 FROM E4 a, E4 b, E4 c)            --10E12 or 1 Trillion rows                 
            SELECT N = 0 WHERE ISNULL(@ZeroOrOne,0)= 0 --Conditionally start at 0.
             UNION ALL 
            SELECT TOP(@MaxN) N = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E12 -- Values from 1 to @MaxN
;
GO

Ngomong-ngomong ... perhatikan bahwa membangun jutaan dan satu baris Tabel Tally dan menambahkan Indeks Clustered ke dalam sekitar satu detik atau lebih. Coba ITU dengan rCTE dan lihat berapa lama! ;-)

BANGUN BEBERAPA DATA UJI

Kami juga membutuhkan beberapa data uji. Ya, saya setuju bahwa semua fungsi yang akan kami uji, termasuk rCTE, berjalan dalam milidetik atau kurang untuk hanya 12 baris tetapi itulah jebakan yang banyak orang jatuh ke dalamnya. Kita akan berbicara lebih banyak tentang jebakan itu nanti, tetapi, untuk saat ini, mari kita simulasikan memanggil setiap fungsi 40.000 kali, yaitu berapa kali fungsi-fungsi tertentu di toko saya dipanggil dalam 8 jam sehari. Bayangkan saja berapa kali fungsi seperti itu dapat disebut dalam bisnis ritel online besar.

Jadi, inilah kode untuk membangun 40.000 baris dengan tanggal acak, masing-masing memiliki Nomor Baris hanya untuk tujuan pelacakan. Saya tidak meluangkan waktu untuk membuat waktu penuh karena itu tidak masalah di sini.

--===== Do this in a nice, safe place that everyone has
    USE tempdb
;
--===== Create/Recreate a Test Date table
     IF OBJECT_ID('dbo.TestDate','U') IS NOT NULL
        DROP TABLE dbo.TestDate
;
DECLARE  @StartDate DATETIME
        ,@EndDate   DATETIME
        ,@Rows      INT
;
 SELECT  @StartDate = '2010' --Inclusive
        ,@EndDate   = '2020' --Exclusive
        ,@Rows      = 40000  --Enough to simulate an 8 hour day where I work
;
 SELECT  RowNum       = IDENTITY(INT,1,1)
        ,SomeDateTime = RAND(CHECKSUM(NEWID()))*DATEDIFF(dd,@StartDate,@EndDate)+@StartDate
   INTO dbo.TestDate
   FROM dbo.fnTally(1,@Rows)
;

MEMBANGUN BEBERAPA FUNGSI UNTUK MELAKUKAN HAL 12 JAM

Selanjutnya, saya mengonversi kode rCTE ke fungsi dan membuat 3 fungsi lainnya. Semuanya telah dibuat sebagai iTVF kinerja tinggi (Inline Table Valued Functions). Anda selalu bisa tahu karena iTVF tidak pernah memiliki BEGIN di dalamnya seperti Scalar atau mTVF (Multi-statement Table Valued Functions).

Inilah kode untuk membangun 4 fungsi itu ... Saya menamainya setelah metode yang mereka gunakan dan bukan apa yang mereka lakukan hanya untuk membuatnya lebih mudah untuk mengidentifikasi mereka.

--=====  CREATE THE iTVFs
--===== Do this in a nice, safe place that everyone has
    USE tempdb
;
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.OriginalrCTE','IF') IS NOT NULL
        DROP FUNCTION dbo.OriginalrCTE
;
GO
 CREATE FUNCTION dbo.OriginalrCTE
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
WITH Dates AS
(
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,@Date)) [Hour], 
      DATEADD(HOUR,-1,@Date) [Date], 1 Num
    UNION ALL
    SELECT DATEPART(HOUR,DATEADD(HOUR,-1,[Date])), 
      DATEADD(HOUR,-1,[Date]), Num+1
    FROM Dates
    WHERE Num <= 11
)
SELECT [Hour], [Date]
FROM Dates
GO
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.MicroTally','IF') IS NOT NULL
        DROP FUNCTION dbo.MicroTally
;
GO
 CREATE FUNCTION dbo.MicroTally
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
 SELECT  [Hour] = DATEPART(HOUR,DATEADD(HOUR,t.N,@Date))
        ,[DATE] = DATEADD(HOUR,t.N,@Date)
   FROM (VALUES (-1),(-2),(-3),(-4),(-5),(-6),(-7),(-8),(-9),(-10),(-11),(-12))t(N)
;
GO
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.PhysicalTally','IF') IS NOT NULL
        DROP FUNCTION dbo.PhysicalTally
;
GO
 CREATE FUNCTION dbo.PhysicalTally
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
 SELECT  [Hour] = DATEPART(HOUR,DATEADD(HOUR,-t.N,@Date))
        ,[DATE] = DATEADD(HOUR,-t.N,@Date)
   FROM dbo.Tally t
  WHERE N BETWEEN 1 AND 12
;
GO
-----------------------------------------------------------------------------------------
     IF OBJECT_ID('dbo.TallyFunction','IF') IS NOT NULL
        DROP FUNCTION dbo.TallyFunction
;
GO
 CREATE FUNCTION dbo.TallyFunction
        (@Date DATETIME)
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
 SELECT  [Hour] = DATEPART(HOUR,DATEADD(HOUR,-t.N,@Date))
        ,[DATE] = DATEADD(HOUR,-t.N,@Date)
   FROM dbo.fnTally(1,12) t
;
GO

MEMBANGUN UJI HARNESS UNTUK MENGUJI FUNGSI

Terakhir namun tidak kalah pentingnya, kami membutuhkan test harness. Saya melakukan pemeriksaan awal dan kemudian menguji setiap fungsi dengan cara yang identik.

Berikut kode untuk alat uji ...

PRINT '--========== Baseline Select =================================';
DECLARE @Hour INT, @Date DATETIME
;
    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = RowNum
        ,@Date = SomeDateTime
   FROM dbo.TestDate
  CROSS APPLY dbo.fnTally(1,12);
    SET STATISTICS TIME,IO OFF;
GO
PRINT '--========== Orginal Recursive CTE ===========================';
DECLARE @Hour INT, @Date DATETIME
;

    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.OriginalrCTE(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO
PRINT '--========== Dedicated Micro-Tally Table =====================';
DECLARE @Hour INT, @Date DATETIME
;

    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.MicroTally(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO
PRINT'--========== Physical Tally Table =============================';
DECLARE @Hour INT, @Date DATETIME
;
    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.PhysicalTally(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO
PRINT'--========== Tally Function ===================================';
DECLARE @Hour INT, @Date DATETIME
;
    SET STATISTICS TIME,IO ON;
 SELECT  @Hour = fn.[Hour]
        ,@Date = fn.[Date]
   FROM dbo.TestDate td
  CROSS APPLY dbo.TallyFunction(td.SomeDateTime) fn;
    SET STATISTICS TIME,IO OFF;
GO

Satu hal yang perlu diperhatikan dalam test harness di atas adalah bahwa saya shunt semua output menjadi variabel "throwaway". Itu untuk mencoba menjaga pengukuran kinerja semurni mungkin tanpa output ke disk atau hasil skewing layar.

KATA PERHATIAN PADA SET STATISTIK

Juga, kata hati-hati untuk calon penguji ... Anda TIDAK HARUS menggunakan STATISTIK SET ketika menguji fungsi Scalar atau mTVF. Itu hanya dapat digunakan dengan aman pada fungsi iTVF seperti yang ada dalam tes ini. SET STATISTIK telah terbukti membuat fungsi SCALAR berjalan ratusan kali lebih lambat dari yang sebenarnya mereka lakukan tanpanya. Ya, saya mencoba memiringkan kincir angin lain, tetapi itu akan menjadi posting panjang artikel yang lebih gila dan saya tidak punya waktu untuk itu. Saya memiliki artikel tentang SQLServerCentral.com yang membicarakan semua itu tetapi tidak ada gunanya memposting tautan di sini karena seseorang akan merasa tidak nyaman dengan hal itu.

HASIL UJI

Jadi, inilah hasil tes ketika saya menjalankan test harness pada laptop i5 kecil saya dengan RAM 6GB.

--========== Baseline Select =================================
Table 'Worktable'. Scan count 1, logical reads 82309, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 203 ms,  elapsed time = 206 ms.
--========== Orginal Recursive CTE ===========================
Table 'Worktable'. Scan count 40001, logical reads 2960000, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 4258 ms,  elapsed time = 4415 ms.
--========== Dedicated Micro-Tally Table =====================
Table 'Worktable'. Scan count 1, logical reads 81989, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 234 ms,  elapsed time = 235 ms.
--========== Physical Tally Table =============================
Table 'Worktable'. Scan count 1, logical reads 81989, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Tally'. Scan count 1, logical reads 3, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 250 ms,  elapsed time = 252 ms.
--========== Tally Function ===================================
Table 'Worktable'. Scan count 1, logical reads 81989, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'TestDate'. Scan count 1, logical reads 105, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 250 ms,  elapsed time = 253 ms.

"BASELINE SELECT", yang hanya memilih data (setiap baris dibuat 12 kali untuk mensimulasikan volume pengembalian yang sama), muncul tepat sekitar 1/5 detik. Yang lainnya masuk sekitar seperempat detik. Ya, semuanya kecuali fungsi RCTE yang berdarah. Butuh 4 dan 1/4 detik atau 16 kali lebih lama (1.600% lebih lambat).

Dan lihat bacaan logis (memori IO) ... RCTE menghabiskan 2.960.000 (hampir 3 JUTI berbunyi) sedangkan fungsi lainnya hanya mengkonsumsi sekitar 82.100. Itu berarti rCTE mengkonsumsi IO memori lebih dari 34,3 kali lebih banyak daripada fungsi lainnya.

PIKIRAN PENUTUP

Mari kita simpulkan. Metode rCTE untuk melakukan hal "kecil" 12 baris ini menggunakan 16 KALI (1.600%) lebih banyak CPU (dan durasi) dan 34.3 KALI (3.430%) lebih banyak memori IO daripada fungsi lainnya.

Heh ... Saya tahu apa yang Anda pikirkan. "Kesepakatan Besar! Hanya satu fungsi."

Ya, setuju, tetapi berapa banyak fungsi lain yang Anda miliki? Berapa banyak tempat lain di luar fungsi yang Anda miliki? Dan apakah Anda memiliki salah satu dari mereka yang bekerja dengan lebih dari 12 baris setiap kali dijalankan? Dan, apakah ada kemungkinan bahwa seseorang yang sedang dalam kesulitan untuk metode dapat menyalin kode rCTE untuk sesuatu yang jauh lebih besar?

Ok, waktunya untuk berterus terang. Sangat tidak masuk akal bagi orang untuk membenarkan kode yang ditantang kinerja hanya karena dugaan jumlah baris atau penggunaan yang terbatas. Kecuali ketika Anda membeli kotak MPP dengan harga jutaan dolar (belum lagi biaya penulisan ulang kode untuk membuatnya bekerja pada mesin seperti itu), Anda tidak dapat membeli mesin yang menjalankan kode Anda 16 kali lebih cepat (SSD menang lakukan juga ... semua ini ada dalam memori berkecepatan tinggi ketika kami mengujinya). Kinerja ada dalam kode. Performa bagus dalam kode yang baik.

Bisakah Anda bayangkan jika semua kode Anda berjalan "hanya" 16 kali lebih cepat?

Jangan sekali-kali menjustifikasi kode yang bermasalah atau kinerja bermasalah pada jumlah baris rendah atau bahkan penggunaan rendah. Jika Anda melakukannya, Anda mungkin harus meminjam salah satu kincir angin yang saya tuduh miringkan agar CPU dan disk Anda cukup dingin. ;-)

KATA TENTANG KATA "BENAR-BENAR"

Ya saya setuju. Berbicara secara semantik, Tally Table berisi angka, bukan "penghitungan". Dalam artikel asli saya pada subjek (itu bukan artikel asli tentang teknik tetapi itu adalah pertama saya di atasnya), saya menyebutnya "Tally" bukan karena apa yang dikandungnya, tetapi karena apa yang dilakukannya ... itu digunakan untuk "menghitung" alih-alih perulangan dan "Tally" sesuatu berarti "Menghitung" sesuatu. ;-) Sebut saja apa yang Anda akan ... Tabel Angka, Tabel Penghitungan, Tabel Urutan, apa pun. Saya tidak peduli. Bagi saya, "Tally" lebih berarti penuh dan, karena menjadi DBA malas yang baik, hanya berisi 5 huruf (2 identik) daripada 7 dan lebih mudah untuk mengatakan bagi kebanyakan orang. Ini juga "tunggal", yang mengikuti konvensi penamaan saya untuk tabel. ;-) Itu' S juga apa artikel yang berisi halaman dari buku dari 60-an menyebutnya. Saya akan selalu menyebutnya sebagai "Tally Table" dan Anda akan tetap tahu apa yang saya atau orang lain maksudkan. Saya juga menghindari Notasi Hongaria seperti wabah tetapi menyebut fungsi "fnTally" sehingga saya bisa mengatakan "Yah, jika Anda menggunakan fungsi Tally ef-en yang saya tunjukkan, Anda tidak akan memiliki masalah kinerja" tanpa benar-benar menjadi Pelanggaran SDM. ;-) tanpa itu sebenarnya menjadi pelanggaran SDM. ;-) tanpa itu sebenarnya menjadi pelanggaran SDM. ;-)

Yang lebih saya pedulikan adalah orang-orang yang belajar menggunakannya dengan benar alih-alih menggunakan hal-hal seperti rCTE yang menantang kinerja dan bentuk-bentuk lain dari RBAR Tersembunyi.

Jeff Moden
sumber
2

Anda perlu RIGHT JOINdata Anda dengan kueri yang mengembalikan satu catatan untuk setiap jam yang Anda butuhkan.

Lihat ini untuk beberapa cara untuk mendapatkan nomor baris yang kemudian dapat Anda kurangi sebagai jam dari waktu saat ini.

Di Oracle, kueri hierarki pada dual akan menghasilkan baris:

SELECT to_char(sysdate-level/24,'HH24') FROM dual CONNECT BY Level <=24;
Leigh Riffel
sumber
Ini adalah "kueri yang mengembalikan satu catatan untuk setiap jam" yang saya punya masalah dengannya. Hanya mencoba mencari cara untuk menghasilkan 12 (atau 24) catatan dengan setiap jam dari 12 (atau 24) jam terakhir.
datagod