sql >> Database teknologi >  >> RDS >> PostgreSQL

Hent rækker for de sidste 10 datoer

Dette ser utænkeligt ud, men det er et surveligt spørgsmål .

Forudsætninger

crosstab() forespørgsler

For at få den bedste ydeevne og korte forespørgselsstrenge (især hvis du kører denne forespørgsel ofte) foreslår jeg det ekstra modul tablefunc leverer forskellige crosstab() funktioner. Grundlæggende instruktioner:

Grundlæggende forespørgsler

Du er nødt til at få disse rigtige først.

De sidste 10 dage:

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

Tal for de sidste 10 dage ved hjælp af vinduesfunktionen 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;

(Inkluderer ikke faktiske datoer i denne forespørgsel.)

Kolonnenavne for outputkolonner (for fuld løsning):

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;

Simpelt resultat med statiske kolonnenavne

Dette kan være godt nok for dig - men vi kan ikke se faktiske datoer i resultatet:

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

Til gentagen brug foreslår jeg, at du opretter denne (meget hurtige) generiske C-funktion for 10 heltalskolonner én gang, for at forenkle tingene en smule:

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

Detaljer i dette relaterede svar:

Så bliver dit opkald:

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!

Fuld løsning med dynamiske kolonnenavne

Dit faktiske spørgsmål er mere kompliceret, du vil også have dynamiske kolonnenavne.
For en given tabel kunne den resulterende forespørgsel se således ud:

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

Vanskeligheden er at destillere dynamiske kolonnenavne. Saml enten forespørgselsstrengen i hånden, eller lad (meget snarere) denne funktion gøre det for dig:

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

Ring til:

SELECT f_generate_date10_sql(1);

Dette genererer den ønskede forespørgsel , som du udfører efter tur.

db<>fiddle her




  1. Hvordan får man id for rækken, der blev valgt af aggregatfunktion?

  2. Tag og grupper efter i EntityFramework

  3. Hvordan kan jeg lave en batch-indsættelse i en Oracle-database ved hjælp af Python?

  4. genstart mysql server på windows 7