sql >> Database >  >> RDS >> PostgreSQL

Hoe kan ik meer dan 1 record per dag selecteren?

Ik wil maximaal 3 records selecteren per dag vanaf een specifiek datumbereik.

SELECT date_time, other_column
FROM  (
   SELECT *, row_number() OVER (PARTITION BY date_time::date) AS rn
   FROM   tbl
   WHERE  date_time >= '2012-11-01 0:0'
   AND    date_time <  '2012-12-01 0:0'
   ) x
WHERE  rn < 4;

Belangrijkste punten

  • Gebruik de vensterfunctie row_number() . rank() of dense_rank() zou volgens de vraag verkeerd zijn - er kunnen meer dan 3 records worden geselecteerd met dubbele tijdstempels.

  • Aangezien u niet definieert welke rijen die u per dag wilt, is het juiste antwoord geen ORDER BY clausule in de vensterfunctie. Geeft je een willekeurige selectie, die overeenkomt met de vraag.

  • Ik heb je WHERE gewijzigd clausule van

    WHERE  date_time >= '20121101 00:00:00'  
    AND    date_time <= '20121130 23:59:59'
    

    naar

    WHERE  date_time >=  '2012-11-01 0:0'  
    AND    date_time <   '2012-12-01 0:0'
    

    Uw syntaxis zou mislukken voor hoekgevallen zoals '20121130 23:59:59.123' .

    Wat @Craig voorstelde:

    date_time::date BETWEEN '2012-11-02' AND '2012-11-05'
    

    .. zou correct werken, maar is een anti-patroon met betrekking tot prestaties. Als u een cast of een functie toepast op uw databasekolom in de expressie, kunnen gewone indexen niet worden gebruikt.

Oplossing voor PostgreSQL 8.3

Beste oplossing :Upgrade naar een recentere versie, bij voorkeur naar de huidige versie 9.2.

Andere oplossingen :

Slechts enkele dagen je zou UNION ALL kunnen gebruiken :

SELECT date_time, other_column
FROM   tbl t1
WHERE  date_time >= '2012-11-01 0:0'
AND    date_time <  '2012-11-02 0:0'
LIMIT  3
)
UNION ALL 
(
SELECT date_time, other_column
FROM   tbl t1
WHERE  date_time >= '2012-11-02 0:0'
AND    date_time <  '2012-11-03 0:0'
LIMIT  3
)
...

Haakjes zijn hier niet optioneel.

Voor meer dagen er zijn oplossingen met generate_series() - zoiets als ik heb hier gepost (inclusief een link naar meer).

Ik heb het misschien opgelost met een plpgsql-functie vroeger voordat we vensterfuncties hadden:

CREATE OR REPLACE FUNCTION x.f_foo (date, date, integer
                         , OUT date_time timestamp, OUT other_column text)
  RETURNS SETOF record AS
$BODY$
DECLARE
    _last_day date;          -- remember last day
    _ct       integer := 1;  -- count
BEGIN

FOR date_time, other_column IN
   SELECT t.date_time, t.other_column
   FROM   tbl t
   WHERE  t.date_time >= $1::timestamp
   AND    t.date_time <  ($2 + 1)::timestamp
   ORDER  BY t.date_time::date
LOOP
   IF date_time::date = _last_day THEN
      _ct := _ct + 1;
   ELSE
      _ct := 1;
   END IF;

   IF _ct <= $3 THEN
      RETURN NEXT;
   END IF;

   _last_day := date_time::date;
END LOOP;

END;
$BODY$ LANGUAGE plpgsql STABLE STRICT;

COMMENT ON FUNCTION f_foo(date3, date, integer) IS 'Return n rows per day
$1 .. date_from (incl.)
$2 .. date_to  (incl.)
$3 .. maximim rows per day';

Bel:

SELECT * FROM f_foo('2012-11-01', '2012-11-05', 3);


  1. Biedt SQL Server iets zoals MySQL's OP DUPLICATE KEY UPDATE?

  2. hoe selecteer je alleen een rij met een maximale reeks zonder een subquery te gebruiken?

  3. Queryresultaten retourneren als een door komma's gescheiden lijst in Oracle

  4. PostgreSQL verwijderen met inner join