获取最近10个日期的行数

问题描述 投票:2回答:1

在Postgres 9.3数据库中,我有一个场景,我需要得到最后10个图书销售的日期。请看下面的例子。

   Store                Book
 ----------        ----------------------
 Id  Name          Id Name Sid Count Date
 1   ABC           1  XYZ  1   20    11/11/2015
 2   DEF           2  JHG  1   10    11/11/2015
                   3  UYH  1   10    15/11/2015
                   4  TRE  1   50    17/11/2015

目前没有 UNIQUE 约束 (name, sid, date) 表中 book但我们有一个服务,每天只插入一个计数。

我必须基于以下几点获得结果 store.id. 当我传递ID时,应该生成报告,包括书名、销售日期和销售册数。

希望的输出。

 BookName  11/11/2015 15/11/2015  17/11/2015
 XYZ       20         --          --
 JHG       10         --          --
 UYH       --         10          --
 TRE       --         --          50
sql postgresql crosstab
1个回答
2
投票

这看起来不可疑,但它是一个 糟糕的问题.

假设

  • 你的数量是 integer.
  • 表册中的所有列都被定义为 NOT NULL.
  • 复合材料 (name, sid, date) 独一无二 book. 你应该有一个 UNIQUE 约束,最好是(为了性能)在列中加入 这个 顺序。

    UNIQUE(sid, date, name)
    

    这将自动提供性能所需的索引。(否则创建一个。)参见。

crosstab() 查询

为了获得最高的性能和较短的查询字符串(特别是当你经常运行这个查询时),我建议使用额外的模块--------。tablefunc 提供各种 crosstab() 功能。基本说明。

基本查询

你要先把这些做好。

过去10天的数据。

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

过去十天的数字,用窗口函数 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;

(本查询中不包括实际日期。)

输出列的列名(完整解决方案)。

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;

使用静态列名的简单结果

这对你来说可能已经足够好了--但我们在结果中看不到实际的日期。

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

如果要重复使用,我建议你为10个整数列创建这个(非常快的)通用C函数一次,以简化事情。

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

详细内容请参考相关答案。

那么你的调用就变成了。

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!

用动态列名的完整解决方案

你的实际问题比较复杂,你还想要动态列名。对于一个给定的表,结果查询可以是这样的,那么。

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

困难的是如何提炼出动态列名。要么手工组装查询字符串,要么(更愿意)让这个函数为你做。

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

Call:

SELECT f_generate_date10_sql(1);

Call: This 产生所需的查询,你依次执行。

db<>提琴。此处

© www.soinside.com 2019 - 2024. All rights reserved.