PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jak wybrać więcej niż 1 rekord dziennie?

Chcę wybrać maksymalnie 3 rekordy dziennie z określonego zakresu dat.

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;

Główne punkty

  • Użyj funkcji okna row_number() . rank() lub dense_rank() byłoby błędne zgodnie z pytaniem - więcej niż 3 rekordy mogą zostać wybrane z duplikatami znaczników czasu.

  • Ponieważ nie definiujesz którego wierszy, które chcesz dziennie, poprawną odpowiedzią jest nieuwzględnianie ORDER BY klauzula w funkcji okna. Daje ci arbitralny wybór, który pasuje do pytania.

  • Zmieniłem Twoje WHERE klauzula z

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

    do

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

    Twoja składnia zawiedzie w przypadku narożnych przypadków, takich jak '20121130 23:59:59.123' .

    Co zasugerował @Craig:

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

    .. działałby poprawnie, ale jest antywzorcem dotyczącym wydajności. Jeśli zastosujesz rzutowanie lub funkcję do kolumny bazy danych w wyrażeniu, nie można użyć zwykłych indeksów.

Rozwiązanie dla PostgreSQL 8.3

Najlepsze rozwiązanie :Uaktualnij do nowszej wersji, najlepiej do aktualnej wersji 9.2.

Inne rozwiązania :

Tylko przez kilka dni możesz użyć UNION ALL :

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

Nawiasy nie są tutaj opcjonalne.

Przez więcej dni istnieją obejścia z generate_series() - coś takiego, jak zamieściłem tutaj (w tym link do innych).

Mogłem to rozwiązać za pomocą funkcji plpgsql w dawnych czasach, zanim mieliśmy funkcje okien:

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';

Zadzwoń:

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. MigrationSchemaMissing (Nie można utworzyć tabeli django_migrations (%s) % exc)

  2. Łącz/scal wartości tablicy podczas grupowania/agregacji

  3. Flask-SQLAlchemy Small Case Index - pomijanie funkcji, nieobsługiwane przez odbicie SQLAlchemy

  4. Klauzule Care To Know:Wszystko o SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY i LIMIT

  5. Jak zminimalizować RPO dla baz danych PostgreSQL za pomocą odzyskiwania punktu w czasie?