【问题标题】:Get rows for the last 10 dates获取最近 10 个日期的行
【发布时间】:2015-10-02 10:39:38
【问题描述】:

我在 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

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

我必须根据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】:

这看起来并不可疑,但这是一个问题

假设

crosstab() 查询

为了获得最佳性能和简短的查询字符串(特别是如果您经常运行此查询),我建议使用附加模块 tablefunc 提供各种 crosstab() 函数。 基本说明:

基本查询

你需要先把这些做好。

过去 10 天:

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

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

呼叫:

SELECT f_generate_date10_sql(1);

生成所需的查询,您依次执行。

db小提琴here

【讨论】:

  • 哇,你是天才。。这件事对我帮助很大。。我的要求还有两个变化。1)用户将传递数字而不是过去的 10 个日期,所以它将是现在最后 N 个日期。 2)即使它没有任何计数,也应该退回该商店的所有书籍。用户可以传递多个存储的ID,结果将被返回。我的问题每天都在增加:(
  • @manthandavda:评论不是地方。为每个新问题开始一个新问题。让每个问题一个问题,提供必要的上下文并展示您尝试过的内容。您可以随时链接到这个以获取上下文。
  • 最后一个问题我如何执行从 f_generate_date10_sql 返回的查询??
  • @manthandavda:执行第一个查询的方式相同。这意味着到数据库服务器的两次往返。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多