PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Holen Sie sich Zeilen für die letzten 10 Daten

Das sieht unverdächtig aus, ist aber eine höllische Frage .

Annahmen

  • Ihre Zählwerte sind integer .
  • Alle Spalten im Tabellenbuch sind NOT NULL definiert .
  • Der zusammengesetzte (name, sid, date) ist in der Tabelle book eindeutig . Sie sollten einen UNIQUE haben Einschränkung, vorzugsweise (aus Performancegründen) mit Spalten in this Bestellung:

    UNIQUE(sid, date, name)
    

    Dadurch wird der für die Performance benötigte Index automatisch bereitgestellt. (Erstellen Sie andernfalls eine.) Siehe:

crosstab() Abfragen

Um Top-Performance und kurze Query-Strings zu erhalten (insbesondere wenn Sie diese Query häufig ausführen), empfehle ich das zusätzliche Modul tablefunc Bereitstellen verschiedener crosstab() Funktionen. Grundlegende Anweisungen:

Grundlegende Abfragen

Sie müssen diese zuerst richtig machen.

Die letzten 10 Tage:

SELECT DISTINCT date
FROM   book
WHERE  sid = 1
ORDER  BY date DESC
LIMIT  10;

Zahlen für die letzten 10 Tage mit der Fensterfunktion dense_rank() :

SELECT *
FROM  (
   SELECT name
        , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
        , count
   FROM   book
   WHERE  sid = 1
   ) sub
WHERE  date_rnk < 11
ORDER  BY name, date_rnk DESC;

(Echte Daten werden in dieser Abfrage nicht berücksichtigt.)

Spaltennamen für Ausgabespalten (für vollständige Lösung):

SELECT 'bookname, "' || string_agg(to_char(date, 'DD/MM/YYYY'), '", "' ORDER BY date) || '"'
FROM  (
   SELECT DISTINCT date
   FROM   book
   WHERE  sid = 1
   ORDER  BY date DESC
   LIMIT  10
   ) sub;

Einfaches Ergebnis mit statischen Spaltennamen

Dies mag für Sie ausreichen - aber wir sehen keine tatsächlichen Daten im Ergebnis:

SELECT * FROM crosstab(
  'SELECT *
   FROM  (
      SELECT name
           , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
           , count
      FROM   book
      WHERE  sid = 1
      ) sub
   WHERE  date_rnk < 11
   ORDER  BY name, date_rnk DESC'
, 'SELECT generate_series(10, 1, -1)'
 ) AS (bookname text
     , date1 int, date2 int, date3 int, date4 int, date5 int
     , date6 int, date7 int, date8 int, date9 int, date10 int);

Zur wiederholten Verwendung schlage ich vor, dass Sie diese (sehr schnelle) generische C-Funktion für 10 Integer-Spalten einmal erstellen, um die Dinge ein wenig zu vereinfachen:

CREATE OR REPLACE FUNCTION crosstab_int10(text, text)
  RETURNS TABLE (bookname text
               , date1 int, date2 int, date3 int, date4 int, date5 int
               , date6 int, date7 int, date8 int, date9 int, date10 int)
  LANGUAGE C STABLE STRICT AS
'$libdir/tablefunc','crosstab_hash';

Details in dieser verwandten Antwort:

Dann wird Ihr Aufruf zu:

SELECT * FROM crosstab(
  'SELECT *
   FROM  (
      SELECT name
           , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
           , count
      FROM   book
      WHERE  sid = 1
      ) sub
   WHERE  date_rnk < 11
   ORDER  BY name, date_rnk DESC'
, 'SELECT generate_series(10, 1, -1)'
 );  -- no column definition list required!

Vollständige Lösung mit dynamischen Spaltennamen

Ihre eigentliche Frage ist komplizierter, Sie möchten auch dynamische Spaltennamen.
Für eine gegebene Tabelle könnte die resultierende Abfrage dann so aussehen:

SELECT * FROM crosstab_int10(
  'SELECT *
   FROM  (
      SELECT name
           , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
           , count
      FROM   book
      WHERE  sid = 1
      ) sub
   WHERE  date_rnk < 11
   ORDER  BY name, date_rnk DESC'
, 'SELECT generate_series(10, 1, -1)'
   ) AS t(bookname
        , "04/11/2015", "05/11/2015", "06/11/2015", "07/11/2015", "08/11/2015"
        , "09/11/2015", "10/11/2015", "11/11/2015", "15/11/2015", "17/11/2015");

Die Schwierigkeit besteht darin, dynamische Spaltennamen zu destillieren. Bauen Sie die Abfragezeichenfolge entweder von Hand zusammen oder lassen Sie (viel besser) diese Funktion für Sie erledigen:

CREATE OR REPLACE FUNCTION f_generate_date10_sql(_sid int = 1) 
  RETURNS text
  LANGUAGE sql AS
$func$
SELECT format(
 $$SELECT * FROM crosstab_int10(
  'SELECT *
   FROM  (
      SELECT name
           , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
           , count
      FROM   book
      WHERE  sid = %1$s
      ) sub
   WHERE  date_rnk < 11
   ORDER  BY name, date_rnk DESC'
, 'SELECT generate_series(10, 1, -1)'
   ) AS ct(bookname, "$$
|| string_agg(to_char(date, 'DD/MM/YYYY'), '", "' ORDER BY date) || '")'
 , _sid)
FROM  (
   SELECT DISTINCT date
   FROM   book
   WHERE  sid = 1
   ORDER  BY date DESC
   LIMIT  10
   ) sub
$func$;

Aufruf:

SELECT f_generate_date10_sql(1);

Dies erzeugt die gewünschte Abfrage , die Sie wiederum ausführen.

db<>fiddle hier