2017-04-03 4 views
0

Ich möchte alle Datensätze abrufen, die vor einem Monat erstellt wurden, genau zur selben Stunde. (Alter der Aufzeichnungen sollte nur n Monate, 0 Tage, 0 Stunden)Abrufen von Datensätzen mit monatlichem Erstellungsdatum

n = {1, 2, ...}

zum Beispiel, wenn now()2016-11-15 13:15:00 ist, jeder Datensatz mit create_date, wie unten zu erwarten zurück.

2010-11-15 13:15:00 -- different year 
2016-02-15 13:15:00 -- different month 
2016-11-15 13:46:23 -- different minute/seconds 
2010-11-15 13:46:23 -- different year, month, minute and sec 

Die beste einfache Abfrage, die ich denken konnte, war:

SELECT * 
FROM mytable 
WHERE extract(day from age(now(), created_date)) = 0 -- same day any month ago 
    AND extract(hour from age(now(), created_date)) = 0; -- same hour 

Aber das Problem gibt es einige bestimmte Tage sind, die aus der Abfrage wird gelassen werden. Wie wenn in diesem Fall ist das Alter des Datensatzes 28 days statt 1 Monat

created_date=2016-01-31 13:00:00 
now() is 2016-02-28 13:00:00 

; nach der age Funktion in Postgresql. So wird es nie Ende Februar oder 1. März geholt werden.

Jede Idee, wie dieses Problem am einfachsten behandelt werden kann, ist willkommen.


Hier einige Testdaten, die Sie überprüfen können:

create table mytable (created_date timestamp, expected_records int); 
truncate mytable; 
insert into mytable (created_date, expected_records) values 
('2013-01-11 13:22:33', 2), 
('2014-02-11 13:58:22', 2), 
('2015-03-21 13:22:00', 1), 
('2013-02-28 13:11:00', 5), 
('2017-02-28 13:22:00', 5), 
('2016-03-31 13:22:00', 5), 
('2015-04-30 13:22:00', 5), 
('2014-05-31 13:22:00', 5), 
('2014-05-31 12:22:00', 1); 

die letzte SELECT-Abfrage kann auf jeder dieser timstamps ausgeführt werden sollte aber kehrt gleiche Menge an Zeilen wie expected_records Staaten. Sie können auch 'some date'::timestamp anstelle von jetzt() verwenden, um Zeit zu fälschen.

+0

Um zu bestätigen, zählen Sie Kalendermonate, nicht Sätze von 30 Tagen usw.? – toonice

+0

korrekt. @toonice Ich zähle nicht 30 Tage, ich möchte N Monate Aufzeichnungen. – Rahman

+0

Soll '2010-11-15 13:46:23' (anderes Jahr und Minuten/Sekunden) aufgeführt werden? – toonice

Antwort

1

Zuerst versuchen wir, Ihre Anforderungen besser zu verstehen. Sie werden alle Datensätze müssen, für die der Timestamp-Spalte (created_date) & die aktuelle Zeit (now()) ist in der folgenden Beziehung:

  • hat genau die gleiche Stunde
    UND
    • hat exakt die gleichen Tag
      OR
    • created_date einen Tag hat, die größer ist alsist's Tag
      ABER nur wenn now() am letzten Tag des aktuellen Monats ist.

SELECT * 
FROM mytable 
WHERE EXTRACT(hour FROM created_date) = EXTRACT(hour FROM now()) 
AND (EXTRACT(day FROM created_date) = EXTRACT(day FROM now()) 
OR  CASE 
      WHEN EXTRACT(month FROM CURRENT_DATE + 1) <> EXTRACT(month FROM CURRENT_DATE) 
      THEN EXTRACT(day FROM created_date) > EXTRACT(day FROM now()) 
      ELSE FALSE 
     END) 

http://rextester.com/TXGCSI34243

Hinweis: Wenn Sie timestamp s für Ihre Lösung (nicht eigentlich mit den now() & current_date Funktionen) binden wollen, dann WHEN EXTRACT(month FROM ? + INTERVAL '1 day') > EXTRACT(month FROM ?) verwenden, um zu testen, ob Der gebundene Zeitstempel ist der letzte Tag des Monats oder nicht.

+0

Bitte beachten Sie, dass der obige Code unwirksam ist, während der Code von rextester ist. Ich rufe Ihre Aufmerksamkeit auf Zeile 6 beider Aussagen. – toonice

+0

@toonice was meinst du mit unwirksam? Sie sind gleich. – pozs

+0

In Zeile 6 von oben testen Sie auf 'current_date' um zu sehen, ob es am Ende des Monats ist, während Sie' NOW() 'testen sollten (wie Sie im Restester-Code sind). – toonice

1

Sie, indem sie den ersten Monat Tag umgehen können - (1 Tag):

Beispiel:

t=# create table so42(t timestamp); 
CREATE TABLE 
t=# insert into so42 select '2016-05-31 13:00:00'; 
INSERT 0 1 
t=# insert into so42 select '2016-10-31 13:00:00'; 
INSERT 0 1 
t=# insert into so42 select '2016-02-29 13:00:00'; 
INSERT 0 1 
t=# insert into so42 select '2016-01-21 13:00:00'; 
INSERT 0 1 

beitreten Beispiel:

t=# with ts as (
     select 
       generate_series('2016-01-01 13:00:00'::timestamp,'2016-12-01'::date,'1 month'::interval) 
       - '1 day'::interval 
     lastday 
) 
select t 
from so42 
join ts on ts.lastday = t 
; 
      t 
--------------------- 
2016-02-29 13:00:00 
2016-05-31 13:00:00 
2016-10-31 13:00:00 
(3 rows) 

auch können Sie wrap es in Funktion mit Argument, etw:

t=# create or replace function so45(_ts timestamp) returns table (lastday timestamp) as 
$$ 
declare 
_start timestamp; 
_stop timestamp; 
begin 
select date_trunc('year', _ts) + (_ts)::time +concat(extract(day from _ts),' days')::interval into _start; 
select date_trunc('year', _ts) + (_ts)::time +concat(extract(day from _ts),' days')::interval + '12 month'::interval into _stop; 
return query with ts as (
     select 
       generate_series(_start,_stop,'1 month'::interval) 
       - '1 day'::interval 
     lastday 
) 
select * from ts order by 1; 
end; 
$$ language plpgsql 
; 
CREATE FUNCTION 

Beispiel beitreten:

t=# with ts as (select so45('2016-03-31 13:00:00')) 
select t 
from so42 
join ts on ts.so45 = t 
; 
      t 
--------------------- 
2016-02-29 13:00:00 
2016-05-31 13:00:00 
2016-10-31 13:00:00 
(3 rows) 

t=# with ts as (select so45('2016-03-21 13:00:00')) 
select t 
from so42 
join ts on ts.so45 = t 
; 
      t 
--------------------- 
2016-01-21 13:00:00 
(1 row) 
+0

Es tatsächlich nicht ganz richtig, denn am Ende möchte ich diese Abfrage jede Stunde ausführen und ich erwarte andere Ergebnisse als jeden Monat. Plus, sogar kann es auf diese Weise einstellbar sein, es scheint auch einige Tage ausgelassen haben.zum Beispiel, wenn Sie diese Abfrage ausführen: 'SELECT generate_series ('2016-01-31 13:00:00' :: Zeitstempel, '2018-01-31' :: DATE, '1 Monat' :: Intervall) - '1 Tag ':: interval lastday; 'Sie werden sehen, was passiert nach' 2017-02-27 ' – Rahman

+0

Bitte überprüfen Sie die Funktion in meiner Antwort. Ich habe es basierend auf Ihrer Aussage erstellt, dass Sie einen genauen Tag und eine genaue Stunde haben. Sie können kein Datum als Argument für 'generate_series' verwenden, damit die Logik funktioniert (deshalb habe ich beschlossen, Logik in Funktion zu überführen) - versuchen Sie die Funktion mit dem Datum, das Sie verwirrt, und sagen Sie bitte, ob es funktioniert –

+0

Vielen Dank für Ihre großartige Antwort aber ich habe meine Frage aktualisiert, indem ich einige Beispiele zum Testen und Verifizieren bereitgestellt habe. – Rahman

1

Bitte versuchen Sie das folgende Code-Segment, das getestet wurde und als wirksam bestätigt ...

SELECT * 
FROM mytable 
WHERE EXTRACT(HOUR FROM NOW()) = EXTRACT(HOUR FROM created_date) 
    AND ((EXTRACT(DAY FROM NOW()) = EXTRACT(DAY FROM created_date)) OR 

     (EXTRACT(DAY FROM DATE_TRUNC('MONTH', NOW()) + INTERVAL '1 MONTH - 1 DAY') = EXTRACT(DAY FROM NOW()) AND 
      EXTRACT(DAY FROM created_date) >= EXTRACT(DAY FROM NOW()))); 

Die folgenden Code überprüft, beide Daten haben die gleichen Tagezahlen, unabhängig davon, ob einer von ihnen das Ende seines Monats ist. In dieser Situation keine weitere Prüfung der Daten erforderlich ist ...

EXTRACT(DAY FROM NOW()) = EXTRACT(DAY FROM created_date) 

Der folgende Code prüft, ob NOW() am Ende des Monats ...

EXTRACT(DAY FROM DATE_TRUNC('MONTH', NOW()) + INTERVAL '1 MONTH - 1 DAY') = EXTRACT(DAY FROM NOW()) 

der folgende Code prüft, ob created_date einen Tag Wert hat, der größer ist als NOW() ‚s ...

EXTRACT(DAY FROM created_date) >= EXTRACT(DAY FROM NOW()) 

Wenn also das Ende der NOW() nicht größer Wert als den Tag hat Wert von created_date dann können sie als gleichwertig betrachtet werden (daher der folgende Code) z NOW() 's date = 2017.02.28 gegen created_date' s 2017.01.31 oder NOW() 's date = 2017.04.30 gegen created_date' s 2017.12.31 usw.

EXTRACT(DAY FROM DATE_TRUNC('MONTH', NOW()) + INTERVAL '1 MONTH - 1 DAY') = EXTRACT(DAY FROM NOW()) AND 
EXTRACT(DAY FROM created_date) >= EXTRACT(DAY FROM NOW()) 

Wenn Sie Fragen oder Kommentare haben, dann schreiben Sie bitte einen Kommentar entsprechend.

+0

Vielen Dank für Ihre großartige Antwort, aber ich habe meine Frage aktualisiert und einige Beispiele zum Testen und Verifizieren bereitgestellt. – Rahman

+0

Ich habe meinen Code mit den von Ihnen bereitgestellten Beispieltabellen und Daten sowie einigen zusätzlichen Zeilen getestet, die auf der Grundlage meines aktuellen Datums und der aktuellen Uhrzeit Ergebnisse liefern sollen oder nicht. Es scheint wie angegeben zu funktionieren. Wenn dies nicht der Fall ist, geben Sie bitte einen beschreibenden Fehlerbericht an. – toonice

+0

'SELECT * VON mytable WO EXTRAKT (STUNDE VON '2017-02-28 13:22:00' :: timestamp) = EXTRACT (STUNDE VON created_date) UND EXTRAKT (TAG VON '2017-02-28 13: 22:00 ':: timestamp) = EXTRACT (DAY FROM created_date); 'sollte 5 Zeilen zurückgeben, während es 2 zurückgibt. – Rahman

Verwandte Themen