简体   繁体   English

在PostgreSQL中动态生成列

[英]Dynamically generate columns in PostgreSQL

I have seen that there are quit a few similar questions like this one, but I havent understood how to code it myself. 我已经看到有一些像这样的类似问题,但我不知道如何自己编码。 Please have in mind that I am just a beginner in this field. 请记住,我只是这个领域的初学者。

Basically I want to pivot the table like this: 基本上我想像这样转动表格:

zoom |    day     | point         zoom | 2015-10-01 |  2015-10-02 | ......
------+-----------+-------  ---> ------+------------+-------------+
   1 | 2015-10-01 |   201            1 |    201     |     685     |
   2 | 2015-10-01 |    43            2 |     43     |     346     | 
   3 | 2015-10-01 |    80            3 |     80     |     534     | 
   4 | 2015-10-01 |   324            4 |    324     |     786     | 
   5 | 2015-10-01 |    25            5 |     25     |     685     |
   1 | 2015-10-02 |   685 
   2 | 2015-10-02 |   346 
   3 | 2015-10-02 |   534 
   4 | 2015-10-02 |   555 
   5 | 2015-10-02 |   786
   :
   :
   :

Time can vary. 时间可以变化。

Results on left I get with: 左边的结果我得到:

SELECT 
zoom,
to_char(date_trunc('day', time), 'YYYY-MM-DD') AS day,
count(*) as point
FROM province
WHERE time >= '2015-05-01' AND time < '2015-06-01'
GROUP BY to_char(date_trunc('day', time), 'YYYY-MM-DD'), zoom;

I have read that there are some issues if I use count and also that it would be better if I use CASE and GROUP BY , however I have no idea how to CASE this. 我已经读过如果我使用count会有一些问题,而且如果我使用CASEGROUP BY会更好,但是我不知道如何使用CASE

Crosstab itself doesnt support dynamic creation of column names, but that can be achieved with crosstab_hash , if I understood it correctly. Crosstab本身不支持动态创建列名,但如果我理解正确的话,可以使用crosstab_hash实现。

This might be probably nice solution: http://okbob.blogspot.ca/2008/08/using-cursors-for-generating-cross.html however I am stucked with it trying to program it myself. 这可能是一个很好的解决方案: http ://okbob.blogspot.ca/2008/08/using-cursors-for-generating-cross.html但我仍然坚持尝试自己编程。

I have to use this kind of pivoting quite often, so I would appriciate any kind of help and additional explanation behind it. 我必须经常使用这种旋转,所以我会在其背后提供任何帮助和其他解释。

Edit1 EDIT1

I am trying to figure out how crosstab works with dates, currently without returning dynamic names of columns. 我试图弄清楚交叉表如何与日期一起工作,目前没有返回列的动态名称。 Later on I will explain why. 稍后我将解释原因。 It is realted to the main question. 这是主要问题。 For this example I am using only period of 2 dates. 对于此示例,我仅使用2个日期。

Based on @Erwin Brandstetter answer: 基于@Erwin Brandstetter回答:

SELECT * FROM crosstab(
       'SELECT zoom, day, point
        FROM   province
        ORDER  BY 1, 2'
      , $$VALUES ('2015-10-01'::date), ('2015-10-02')$$)
AS ct (zoom text, day1 int, day2 int);

returned results are: 返回的结果是:

zoom |    day1    |    day2     | 
-----+------------+-------------+
   1 |    201     |     685     |
   2 |     43     |     346     | 
   3 |     80     |     534     | 
   4 |    324     |     786     | 

I am trying to get this 我想要得到这个

zoom | 2015-10-01 |  2015-10-02 | 
-----+------------+-------------+
   1 |    201     |     685     |
   2 |     43     |     346     | 
   3 |     80     |     534     | 
   4 |    324     |     786     | 

but my query doesnt work: 但我的查询不起作用:

SELECT *
FROM crosstab(
      'SELECT *
       FROM province
       ORDER  BY 1,2')
AS ct (zoom text, "2015-10-01" date, "2015-10-02" date);

ERROR:  return and sql tuple descriptions are incompatible

Edit1, Q1. Edit1,Q1。 Why does this doesnt work and how can I return results like that? 为什么这不起作用,我怎么能返回那样的结果呢?

I have read links that @Erwin Brandstetter provided me, especially this one: Execute a dynamic crosstab query . 我已经阅读了@Erwin Brandstetter为我提供的链接,特别是这一个: 执行动态交叉表查询 I have copied/pasted his function: 我复制/粘贴了他的功能:

CREATE OR REPLACE FUNCTION pivottab(_tbl regclass, 
                                    _row text, _cat text, 
                                    _expr text,
                                    _type regtype)  
RETURNS text AS
$func$
DECLARE
   _cat_list text;
   _col_list text;
BEGIN
-- generate categories for xtab param and col definition list    
EXECUTE format(
 $$SELECT string_agg(quote_literal(x.cat), '), (')
        , string_agg(quote_ident  (x.cat), %L)
   FROM  (SELECT DISTINCT %I AS cat FROM %s ORDER BY 1) x$$
 , ' ' || _type || ', ', _cat, _tbl)
INTO  _cat_list, _col_list;

-- generate query string
RETURN format(
'SELECT * FROM crosstab(
   $q$SELECT %I, %I, %s
      FROM   %I
      GROUP  BY 1, 2
      ORDER  BY 1, 2$q$
 , $c$VALUES (%5$s)$c$
   ) ct(%1$I text, %6$s %7$s)'
, _row, _cat, _expr, _tbl, _cat_list, _col_list, _type
);

END
$func$ LANGUAGE plpgsql;

and call it with query 并用查询调用它

SELECT pivottab('province','zoom','day','point','date');

Function returned me: 功能归还给我:

                         pivottab                         
----------------------------------------------------------
 SELECT * FROM crosstab(                                 +
    $q$SELECT zoom, day, point                           +
       FROM   province                                   +
       GROUP  BY 1, 2                                    +
       ORDER  BY 1, 2$q$                                 +
  , $c$VALUES ('2015-10-01'), ('2015-10-02')$c$          +
    ) ct(zoom text, "2015-10-01" date, "2015-10-02" date)
(1 row)

So when I edited the query and added ; 所以当我编辑查询并添加时; (it would be nice that ; is already there) I got: (很高兴;已经存在了)我得到了:

ERROR:  column "province.point" must appear in the GROUP BY clause or be used in an aggregate function

Edit1, Q2. Edit1,Q2。 Any ideas how to solove this? 任何想法如何解决这个问题?

Edit1, Q3. Edit1,Q3。 I guess next question will be how to execute function automaticlly , which is also mentioned on the same link, but got stucked on previous steps. 我想接下来的问题将是如何自动执行函数 ,这也是在同一个链接上提到的,但是在前面的步骤中被引用了。

The basic crosstab query for your example is simple: 您的示例的基本交叉表查询很简单:

SELECT * FROM crosstab(
       'SELECT zoom, day, point
        FROM   province
        ORDER  BY 1, 2'

     , $$VALUES ('2015-10-01'::date), ('2015-10-02')$$)
AS ct (zoom text, day1 int, day2 int);

But not with dynamic column names or a dynamic number of columns. 不是动态列名称或动态列数。 As a compromise, you can have a fixed number of columns and only fill the leading ones. 作为妥协,您可以拥有固定数量的列,只填充前导列。 Basics: 基本:

Dynamic? 动态?

crosstab_hash is not going to help you with dynamic column names. crosstab_hash不会帮助您使用动态列名。 It's for repeated use without typing a column definition list, but not for dynamic column names. 它可以在不键入列定义列表的情况下重复使用,但不能用于动态列名。 Examples: 例子:

For truly dynamic column names, you need two round trips to the server. 对于真正动态的列名,您需要两次往返服务器。 Whether you retrieve the column names with a first query to build a second query, or you create a cursor or a temporary table or a prepared statement. 是使用第一个查询检索列名以构建第二个查询,还是创建游标或临时表或预准备语句。 Whatever you try, you need two round trips. 无论您尝试什么,都需要两次往返。 SQL wants to know the return type at call time. SQL希望在调用时知道返回类型。

The closest I could get to a "dynamic" call is with my custom crosstab_n() function defined in this related answer: 我最接近“动态”调用的是我在此相关答案中定义的自定义crosstab_n()函数:


Or you give up the idea of a completely dynamic crosstab query (because, you know, it's impossible) and use a two-step workflow, like mentioned above. 或者你放弃了一个完全动态的交叉表查询的想法(因为,你知道,这是不可能的)并使用两步工作流程,如上所述。

  1. Let a function generate the crosstab query text. 让函数生成交叉表查询文本。 You can use the function provided here (and adapt it to your needs!): 您可以使用此处提供的功能(并根据您的需要进行调整!):

    In particular, remove GROUP BY 1, 2 , since you do not aggregate rows before the cross tabulation. 特别是,删除GROUP BY 1, 2 ,因为您不在交叉列表之前聚合行。

  2. Execute the generated function. 执行生成的功能。


For completeness, there is also the new \\crosstabview metacommand in psql in Postgres 9.6 (just released) - with similar functionality, and it can display dynamic column names (attaching dynamic names happens in the psql client, not in the Postgres server). 为了完整起见, Postgres 9.6 (刚刚发布) 中的psql中还有新的\\crosstabview metacommand - 具有类似的功能,它可以显示动态列名(附加动态名称发生在psql客户端,而不是Postgres服务器中)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM