2013-05-11 23 views
5

Ho una tabella di grandi dimensioni con record creati ogni secondo e desidero selezionare solo i record creati all'inizio di ogni ora negli ultimi 2 mesi. Così vorremmo ottenere 24 record selezionati per ogni giorno negli ultimi 60 giorniSQL Server: selezionare tutti i record delle ore migliori

struttura

la tabella è DateAndTime, Valore1, Valore2, ecc

Molte grazie

+4

Mostraci cosa hai provato –

+2

Definisci chiaramente "la parte superiore di ogni ora" - ad es. ''2013-05-11 09: 00: 00.000'' ma non'' 2013-05-11 09: 00: 01.000'' eccetera.Il livello di precisione che intendi aiuterà a determinare quali elementi temporali sono criteri validi. – J0e3gan

risposta

0

Prova:

select * from mytable 
where datepart(mi, dateandtime)=0 and 
     datepart(ss, dateandtime)=0 and 
     datediff(d, dateandtime, getdate()) <=60 
+0

Questo sembra pericoloso poiché presuppone che tutti i record siano, infatti, registrati ogni secondo senza duplicati in un dato secondo. –

+0

@GordonLinoff: In effetti, questa è la mia interpretazione della * * parte dei record creati ogni secondo "* della domanda. –

+0

Grazie ha funzionato bene perché non c'erano duplicati – user2119912

2

Si potrebbe group by da parte data (cast(col1 as date)) . e la parte ora (datepart(hh, col1) Quindi scegliere la data minima per ogni ora, e filtrare che:

select * 
from YourTable yt 
join (
     select min(dateandtime) as dt 
     from YourTable 
     where datediff(day, dateandtime, getdate()) <= 60 
     group by 
       cast(dateandtime as date) 
     ,  datepart(hh, dateandtime) 
     ) filter 
on  filter.dt = yt.dateandtime 

in alternativa, è possibile raggruppare in un formato di data che include solo la data e l'Hou r. Ad esempio, convert(varchar(13), getdate(), 120) restituisce 2013-05-11 18.

 ... 
     group by 
       convert(varchar(13), getdate(), 120) 
     ) filter 
     ... 
1

È possibile utilizzare le funzioni della finestra per questo:

select dateandtime, val1, val2, . . . 
from (select t.*, 
      row_number() over (partition by cast(dateandtime as date), hour(dateandtime) 
           order by dateandtime 
           ) as seqnum 
     from t 
    ) t 
where seqnum = 1 

La funzione row_number() assegna un numero progressivo a ciascun gruppo definito dalla clausola partition - in questo caso ogni ora di ogni giorno. All'interno di questo gruppo, ordina dal valore dateandtime, quindi quello più vicino alla parte superiore dell'ora ottiene il valore 1. La query esterna seleziona solo questo record per ciascun gruppo.

Potrebbe essere necessaria una clausola di filtro aggiuntiva per ottenere record negli ultimi 60 giorni. Utilizzare questo nel subquery:

where dateandtime >= getdate() - 60 
2

Per amor di chiarezza, avrei probabilmente usare un due fasi, l'approccio CTE-based (questo funziona in SQL Server e più recente - non hai chiaramente specificare quale versione di SQL Server che si sta utilizzando, quindi sto solo sperando che non sei su una versione antica come 2000 più):

-- define a "base" CTE to get the hour component of your "DateAndTime" 
-- column and make it accessible under its own name 
;WITH BaseCTE AS 
(
    SELECT 
     ID, DateAndTime, 
     Value1, Value2, 
     HourPart = DATEPART(HOUR, DateAndTime) 
    FROM dbo.YourTable 
    WHERE DateAndTime >= @SomeThresholdDateHere 
), 
-- define a second CTE which "partitions" the data by this "HourPart", 
-- and number all rows for each partition starting at 1. So each "last" 
-- event for each hour is the one with the RN = 1 value 
HourlyCTE AS 
(
    SELECT ID, DateAndTime, Value1, Value2, 
     RN = ROW_NUMBER() OVER(PARTITION BY HourPart ORDER BY DateAndTime DESC) 
    FROM BaseCTE 
) 
SELECT * 
FROM HourlyCTE 
WHERE RN=1 

Inoltre: non ero sicuro di cosa esattamente intendi per "inizio dell'ora" - la riga che è stata creata all'inizio di ogni ora (ad es. a 04:00:00) - o meglio l'ultima riga creata nell'arco di quell'ora? Se si intende il primo per ogni ora - allora avresti bisogno di cambiare il ORDER BY DateAndTime DESC a ORDER BY DateAndTime ASC

2

È possibile utilizzare l'opzione con operatore EXISTS

SELECT * 
FROM dbo.tableName t 
WHERE t.DateAndTime >= @YourDateCondition 
    AND EXISTS (
       SELECT 1 
       FROM dbo.tableName t2 
       WHERE t2.Dateandtime >= DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dateandtime), 0) 
         AND t2.Dateandtime < DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dateandtime)+1, 0) 
       HAVING MAX(t2.Dateandtime) = t.Dateandtime 
      ) 

o l'opzione con CROSS APPLY operatore

SELECT * 
FROM dbo.test83 t CROSS APPLY (
           SELECT 1 
           FROM dbo.test83 t2 
           WHERE t2.Dateandtime >= DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dateandtime), 0) 
            AND t2.Dateandtime < DATEADD(HOUR, DATEDIFF(HOUR, 0, t.Dateandtime)+1, 0) 
           HAVING MAX(t2.Dateandtime) = t.Dateandtime        
           ) o(IsMatch) 
WHERE t.DateAndTime >= @YourDateCondition 

Per migliorare le prestazioni utilizzare questo indice:

CREATE INDEX x ON dbo.test83(DateAndTime) INCLUDE(Value1, Value2) 
Problemi correlati