Conosci un modo semplice per generare un record per ogni ora delle ultime 12 ore?


12

Ho un rapporto che mostra il conteggio degli eventi nelle ultime 12 ore, raggruppate per ora. Sembra abbastanza facile, ma quello che sto lottando è come includere i record che coprono le lacune.

Ecco una tabella di esempio:

Event
(
  EventTime datetime,
  EventType int
)

I dati si presentano così:

  '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

Devo creare un set di risultati con un record per ogni ora delle ultime 12 ore, indipendentemente dal fatto che ci siano o meno eventi durante quell'ora.

Supponendo che l'ora corrente sia "2012-03-08 11:00:00", il rapporto mostrerebbe (approssimativamente):

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

Ho trovato una soluzione che utilizza una tabella con un record per ogni ora del giorno. Sono riuscito a ottenere i risultati che stavo cercando usando UNION e qualche logica del caso contorta nella clausola where, ma speravo che qualcuno avesse una soluzione più elegante.

Risposte:


21

Per SQL Server 2005+ è possibile generare quei 12 record molto facilmente con un ciclo o un CTE ricorsivo. Ecco un esempio di CTE ricorsivo:

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

Quindi devi solo unirti a esso con la tabella degli eventi.


2
L'ho trovato subito dopo aver pubblicato. spieginextended.com/2009/10/21/… Indica che l'utilizzo di un CTE a questo scopo è meno efficiente di una tabella memorizzata. È vero? Come ha detto Nick, probabilmente non ha importanza per questo caso, ma ...
Leigh Riffel

4
Penso che farebbe la differenza con un numero maggiore di righe, se avessi bisogno di 12 dischi, allora non ci sarà alcun colpo di performance
Lamak

Lamak e @swasheck. Heh ... Sono un po 'in ritardo (ho perso la traccia di questo thread) per arrivarci, ma nessun problema. Vedi la risposta che ho finalmente pubblicato per supportare le mie affermazioni sopra. E ricorda, tutto il codice ha un effetto cumulativo. Se tutte le persone che scrivono codice fossero "solo" 16 volte più veloci, la metà dei post su forum come questo non sarebbe più necessaria. E non ci vuole più tempo (a volte più breve) per scrivere codice più veloce.
Jeff Moden,

10

Le tabelle Tally possono essere utilizzate per cose come questa. Possono essere molto efficienti. Crea la seguente tabella di conteggio. Ho creato la tabella di conteggio con solo 24 righe per il tuo esempio, ma puoi crearla con qualsiasi numero desideri per altri scopi.

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

Ho pensato che il tuo tavolo si chiamasse dbo.tblEvents, esegui la query qui sotto. Credo che questo sia quello che stai cercando:

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

Credo che il merito vada ai seguenti link, credo che sia qui che mi sono imbattuto per la prima volta in questo:

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

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


+1 ma semanticamente è una tabella di numeri, non una tabella di conteggi.
Aaron Bertrand

1
Una delle definizioni di "Tally" è "Conteggio". Il "Tavolo da Tally" prende il nome dal "Tally Stick", che è un bastone lungo e magro usato per contare.
Jeff Moden,

7

In primo luogo, mi scuso per il ritardo nella mia risposta dai miei ultimi commenti.

L'argomento è emerso nei commenti secondo cui l'utilizzo di un CTE ricorsivo (da qui in poi rCTE) funziona abbastanza velocemente a causa del basso numero di righe. Sebbene possa apparire in questo modo, nulla potrebbe essere più lontano dalla verità.

COSTRUISCI TALLY TABLE E TALLY FUNCTION

Prima di iniziare i test, dobbiamo creare una tabella di conteggio fisica con l'indice cluster appropriato e una funzione di conteggio in stile Itzik Ben-Gan. Faremo anche tutto questo in TempDB in modo da non eliminare accidentalmente le chicche di nessuno.

Ecco il codice per costruire la Tally Table e la mia attuale versione di produzione del meraviglioso codice di 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

A proposito ... notate che ha creato un Tally Table da un milione e una riga e aggiunto un indice cluster ad esso in circa un secondo circa. Provalo con un rCTE e vedi quanto tempo impiega! ;-)

COSTRUISCI ALCUNI DATI DI PROVA

Abbiamo anche bisogno di alcuni dati di prova. Sì, sono d'accordo sul fatto che tutte le funzioni che testeremo, incluso l'rCTE, verranno eseguite in un millisecondo o meno per solo 12 righe, ma questa è la trappola in cui cadono molte persone. Parleremo di più di quella trappola più tardi ma, per ora, permettiamo di simulare la chiamata di ciascuna funzione 40.000 volte, ovvero quante volte alcune funzioni del mio negozio vengono chiamate in un giorno di 8 ore. Immagina quante volte tali funzioni potrebbero essere chiamate in una grande impresa di vendita al dettaglio online.

Quindi, ecco il codice per costruire 40.000 righe con date casuali, ognuna con un numero di riga solo a scopo di tracciamento. Non mi sono preso il tempo di fare ore intere perché non importa qui.

--===== 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)
;

COSTRUISCI ALCUNE FUNZIONI PER FARE LA COSA DELLE 12 ORE DI RIGA

Successivamente, ho convertito il codice rCTE in una funzione e ho creato altre 3 funzioni. Sono stati tutti creati come iTVF ad alte prestazioni (Inline Table Valued Functions). Puoi sempre dirlo perché iTVF non ha mai un INIZIO in loro come fanno Scalar o mTVF (funzioni con valori di tabella multiistruzione).

Ecco il codice per costruire quelle 4 funzioni ... Le ho chiamate in base al metodo che usano e non a quello che fanno solo per rendere più facile identificarle.

--=====  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

COSTRUISCI IL CABLAGGIO DI PROVA PER TESTARE LE FUNZIONI

Ultimo ma non meno importante, abbiamo bisogno di un'imbracatura di prova. Eseguo un controllo di base e quindi collaudo ogni funzione in modo identico.

Ecco il codice per il cablaggio di prova ...

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

Una cosa da notare nel cablaggio di prova sopra è che shunt tutto l'output in variabili "usa e getta". Questo per cercare di mantenere le misurazioni delle prestazioni il più pure possibile senza alcun output sul disco o risultati di inclinazione dello schermo.

UNA PAROLA DI ATTENZIONE SULLE STATISTICHE IMPOSTATE

Inoltre, un avvertimento per gli aspiranti tester ... NON DEVI utilizzare SET STATISTICS durante il test delle funzioni scalare o mTVF. Può essere utilizzato in modo sicuro solo su funzioni iTVF come quelle di questo test. SET STATISTICS ha dimostrato di far funzionare le funzioni SCALAR centinaia di volte più lentamente di quanto ne facciano effettivamente senza. Sì, sto cercando di inclinare un altro mulino a vento, ma sarebbe un post più lungo di un articolo e non ho tempo per farlo. Ho un articolo su SQLServerCentral.com che parla di questo, ma non ha senso pubblicare qui il link perché qualcuno si deformerà.

I RISULTATI DELLA PROVA

Quindi, ecco i risultati del test quando eseguo il cablaggio di prova sul mio piccolo laptop i5 con 6 GB di RAM.

--========== 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.

Il "BASELINE SELECT", che seleziona solo i dati (ogni riga creata 12 volte per simulare lo stesso volume di ritorno), è arrivato giusto circa 1/5 di secondo. Tutto il resto arrivò a circa un quarto di secondo. Bene, tutto tranne quella sanguinosa funzione rCTE. Ci sono voluti 4 e 1/4 secondi o 16 volte più a lungo (1.600% più lento).

E guarda le letture logiche (memoria IO) ... L'rCTE ha consumato ben 2.960.000 (quasi 3 MILIONI di letture) mentre le altre funzioni hanno consumato solo circa 82.100. Ciò significa che l'rCTE ha consumato più di 34,3 volte più memoria IO rispetto a qualsiasi altra funzione.

PENSIERI DI CHIUSURA

Riassumiamo. Il metodo rCTE per fare questa "piccola" cosa a 12 righe ha usato 16 TIMES (1.600%) più CPU (e durata) e 34.3 TIMES (3.430%) più memoria IO rispetto a qualsiasi altra funzione.

Heh ... So cosa stai pensando. "Un grosso problema! È solo una funzione."

Sì, d'accordo, ma quante altre funzioni hai? Quanti altri posti al di fuori delle funzioni hai? E hai qualcuno di quelli che funzionano con più di solo 12 righe per ogni corsa? E, c'è qualche possibilità che qualcuno in difficoltà per un metodo possa copiare quel codice rCTE per qualcosa di molto più grande?

Ok, è tempo di essere schietti. Non ha assolutamente senso per le persone giustificare il codice con problemi di prestazioni solo a causa del numero limitato di righe o dell'utilizzo. Tranne quando acquisti una scatola MPP per forse milioni di dollari (per non parlare delle spese di riscrittura del codice per farlo funzionare su una macchina del genere), non puoi acquistare una macchina che esegue il tuo codice 16 volte più velocemente (SSD ha vinto non farlo neanche ... tutte queste cose erano nella memoria ad alta velocità quando le abbiamo testate). Le prestazioni sono nel codice. Le buone prestazioni sono in un buon codice.

Riesci a immaginare se tutto il tuo codice fosse "solo" 16 volte più veloce?

Non giustificare mai codice difettoso o con prestazioni sfavorevoli su conteggi di riga bassi o utilizzo basso. Se lo fai, potresti dover prendere in prestito uno dei mulini a vento di cui sono stato accusato di inclinarmi per mantenere le tue CPU e i tuoi dischi abbastanza freschi. ;-)

UNA PAROLA SULLA PAROLA "TALLY"

Sì sono d'accordo. Semanticamente parlando, la Tally Table contiene numeri, non "conteggi". Nel mio articolo originale sull'argomento (non era l'articolo originale sulla tecnica ma era il mio primo su di esso), l'ho chiamato "Tally" non per ciò che contiene, ma per quello che fa ... è abituato a "contare" invece di eseguire il looping e a "Tally" qualcosa significa "contare" qualcosa. ;-) Chiamalo come vuoi ... Tabella dei numeri, Tabella dei riscontri, Tabella delle sequenze, qualunque cosa. Non mi interessa. Per me, "Tally" è più pieno e, essendo un buon DBA pigro, contiene solo 5 lettere (2 sono identiche) anziché 7 ed è più facile da dire per la maggior parte delle persone. È anche "singolare", che segue la mia convenzione di denominazione per i tavoli. ;-) E ' s anche come lo chiamava l'articolo che conteneva una pagina di un libro degli anni '60. Mi riferirò sempre ad esso come una "Tabella dei riscontri" e saprai ancora cosa intendo io o qualcun altro. Evito anche la notazione ungherese come la peste, ma ho chiamato la funzione "fnTally" in modo da poter dire "Beh, se avessi usato la funzione Tally eff-en che ti ho mostrato, non avresti un problema di prestazioni" senza che in realtà fosse un Violazione delle risorse umane. ;-) senza che si tratti effettivamente di una violazione delle risorse umane. ;-) senza che si tratti effettivamente di una violazione delle risorse umane. ;-)

Quello che mi preoccupa di più è che le persone imparano a usarlo correttamente invece di ricorrere a cose come rCTE con prestazioni limitate e altre forme di RBAR nascosto.


2

Avrai bisogno dei RIGHT JOINtuoi dati con una query che restituisca un record per ogni ora di cui hai bisogno.

Vedi questo per un paio di modi per ottenere un numero di riga che potresti sottrarre come ore dall'ora corrente.

In Oracle una query gerarchica su dual genererà righe:

SELECT to_char(sysdate-level/24,'HH24') FROM dual CONNECT BY Level <=24;

È la "query che restituisce un record per ogni ora" con cui sto riscontrando problemi. Sto solo cercando di trovare un modo per generare 12 (o 24) record con ogni ora delle ultime 12 (o 24) ore.
datagod
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.