PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Obtenir des lignes pour les 10 dernières dates

Cela n'a pas l'air suspect, mais c'est une sacrée question .

Hypothèses

crosstab() requêtes

Pour obtenir des performances optimales et des chaînes de requête courtes (surtout si vous exécutez souvent cette requête), je suggère le module supplémentaire tablefunc fournissant divers crosstab() les fonctions. Instructions de base :

Requêtes de base

Vous devez d'abord les obtenir correctement.

Les 10 derniers jours :

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

Chiffres des 10 derniers jours en utilisant la fonction de fenêtre 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;

(Non compris les dates réelles dans cette requête.)

Noms de colonne pour les colonnes de sortie (pour la solution complète) :

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;

Résultat simple avec des noms de colonnes statiques

Cela peut vous suffire, mais nous ne voyons pas les dates réelles dans le résultat :

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

Pour une utilisation répétée, je vous suggère de créer une fois cette fonction C générique (très rapide) pour 10 colonnes d'entiers, pour simplifier un peu les choses :

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

Détails dans cette réponse connexe :

Alors votre appel devient :

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!

Solution complète avec noms de colonnes dynamiques

Votre question réelle est plus compliquée, vous voulez également des noms de colonnes dynamiques.
Pour une table donnée, la requête résultante pourrait alors ressembler à ceci :

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");

La difficulté est de distiller des noms de colonnes dynamiques. Soit assemblez la chaîne de requête à la main, soit (plutôt) laissez cette fonction le faire pour vous :

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

Appel :

SELECT f_generate_date10_sql(1);

Cela génère la requête souhaitée , que vous exécutez à tour de rôle.

db<>violon ici