相关文章推荐

connectby relname text keyid_fld text parent_keyid_fld text [ orderby_fld text ], start_with text max_depth integer [ branch_delim text ]) → setof record

生成层级树状结构表示。

normal_rand(int numvals, float8 mean, float8 stddev) returns setof float8
      

normal_rand 生成一组正态分布的随机值(正态分布)。

numvals 参数是要由函数返回的值的数量。mean 是值正态分布的平均值,stddev 是值正态分布的标准差。

例如,该调用请求 1000 个值,平均值为 5,标准差为 3

test=# SELECT * FROM normal_rand(1000, 5, 3);
     normal_rand
----------------------
     1.56556322244898
     9.10040991424657
     5.36957140345079
   -0.369151492880995
    0.283600703686639
     4.82992125404908
     9.71308014517282
     2.49639286969028
(1000 rows)
      

crosstab 函数使用文本参数,该参数是 SQL 查询,按照第一种方式生成格式化的原始数据,并按照第二种方式生成格式化的表。

sql 参数是一个 SQL 语句,用于生成源数据集。此语句必须返回一个 row_name 列、一个 category 列和一个 value 列。N 是一个已过时的参数,如果提供则忽略(以前此参数必须与输出值列的数量匹配,但现在由调用查询确定)。

例如,所提供的查询可能会生成类似于以下内容的集合

 row_name    cat    value
----------+-------+-------
  row1      cat1    val1
  row1      cat2    val2
  row1      cat3    val3
  row1      cat4    val4
  row2      cat1    val5
  row2      cat2    val6
  row2      cat3    val7
  row2      cat4    val8
      

crosstab 函数被声明为返回 setof record,因此输出列的实际名称和类型必须在调用 SELECT 语句的 FROM 子句中定义,例如

SELECT * FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text);
      

此示例生成类似于以下内容的集合

           <== value  columns  ==>
 row_name   category_1   category_2
----------+------------+------------
  row1        val1         val2
  row2        val5         val6
      

FROM 子句必须定义输出为一个 row_name 列(与 SQL 查询的第一个结果列具有相同的数据类型),后跟 N 个 value 列(全部与 SQL 查询的第三个结果列具有相同的数据类型)。你可以设置任意数量的输出值列。输出列的名称由你决定。

crosstab 函数针对具有相同 row_name 值的每个连续输入行组生成一行输出。它使用这些行的 value 字段从左向右填充输出 value 列。如果组中的行数少于输出 value 列,则使用 null 值填充额外的输出列;如果行数多于该数量,则跳过额外的输入行。

在实际中,SQL 查询始终应该指定 ORDER BY 1,2 以确保输入行正确排序,即,具有相同 row_name 的值会聚集在一起并在行中正确排序。注意,crosstab 本身不会关注查询结果的第二列;它只是在那里供排序使用,以控制第三列的值在页面中显示的顺序。

这是一个完整示例

CREATE TABLE ct(id SERIAL, rowid TEXT, attribute TEXT, value TEXT);
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att1','val1');
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att2','val2');
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att3','val3');
INSERT INTO ct(rowid, attribute, value) VALUES('test1','att4','val4');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att1','val5');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att2','val6');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att3','val7');
INSERT INTO ct(rowid, attribute, value) VALUES('test2','att4','val8');
SELECT *
FROM crosstab(
  'select rowid, attribute, value
   from ct
   where attribute = ''att2'' or attribute = ''att3''
   order by 1,2')
AS ct(row_name text, category_1 text, category_2 text, category_3 text);
 row_name | category_1 | category_2 | category_3
----------+------------+------------+------------
 test1    | val2       | val3       |
 test2    | val6       | val7       |
(2 rows)
      

通过设置已将所需输出行类型连接到其定义中的自定义交叉表函数,您可以避免始终必须写出 FROM 从句来定义输出列。这在下一部分中有描述。另一种可能性是将必需的 FROM 从句嵌入到视图定义中。

另请参阅 \crosstabviewpsql 中的命令,其提供类似于 crosstab() 的功能。

crosstabN(text sql)
      

crosstabN 函数是为通用 crosstab 函数设置自定义包装器的示例,这样您不必在调用 SELECT 查询时写出列名和类型。tablefunc 模块包含 crosstab2crosstab3crosstab4,其输出行类型定义为

CREATE TYPE tablefunc_crosstab_N AS (
    row_name TEXT,
    category_1 TEXT,
    category_2 TEXT,
    category_N TEXT
      

因此,当输入查询生成类型为 textrow_namevalue 列,并且您需要 2、3 或 4 个输出值列时,可以直接使用这些函数。在其他所有方面,它们的行为与上面为通用 crosstab 函数描述的完全相同。

例如,上一部分中的示例也会起作用,如下所示

SELECT *
FROM crosstab3(
  'select rowid, attribute, value
   from ct
   where attribute = ''att2'' or attribute = ''att3''
   order by 1,2');
      

这些函数主要用于说明目的。您可以根据底层 crosstab() 函数创建自己的返回类型和函数。这样做有两种方法

contrib/tablefunc/tablefunc--1.0.sql 中类似示例中描述所需输出列的复合类型。然后定义一个唯一的函数名称,接受一个 文本 参数并返回 集合_类型名称,但链接到相同的底层 交叉表 C 函数。例如,如果源数据生成的行名称为 文本,值为 浮点数8,并且需要 5 个值列

CREATE TYPE my_crosstab_float8_5_cols AS (
    my_row_name text,
    my_category_1 float8,
    my_category_2 float8,
    my_category_3 float8,
    my_category_4 float8,
    my_category_5 float8
CREATE OR REPLACE FUNCTION crosstab_float8_5_cols(text)
    RETURNS setof my_crosstab_float8_5_cols
    AS '$libdir/tablefunc','crosstab' LANGUAGE C STABLE STRICT;
            

使用 OUT 参数来隐式定义返回类型。相同的示例还可以通过这种方式完成

CREATE OR REPLACE FUNCTION crosstab_float8_5_cols(
    IN text,
    OUT my_row_name text,
    OUT my_category_1 float8,
    OUT my_category_2 float8,
    OUT my_category_3 float8,
    OUT my_category_4 float8,
    OUT my_category_5 float8)
  RETURNS setof record
  AS '$libdir/tablefunc','crosstab' LANGUAGE C STABLE STRICT;
      
crosstab(text source_sql, text category_sql)
      

crosstab 单参数形式的主要限制在于,它将组中的所有值视为相同,将每个值插入到第一个可用列中。如果需要将值列与特定数据类别相对应,并且某些组可能没有某些类别的相关数据,这种方法就不太适用。crosstab 双参数形式通过提供与输出列相对应的明确类别列表来处理这种情况。

source_sql 是生成源数据集的 SQL 语句。此语句必须返回一个 行名称 列、一个 类别 列和一个 列。它还可能有一个或多个 其他 列。必须将 row_name 列排在首位。必须将 category 列和 value 列按此顺序排在最后的两个位置。所有位于 row_namecategory 之间的列都将被视为 其他。所有具有相同 row_name 值的行都应该采用相同的 其他 列。

例如,source_sql 可能生成类似如下内容的集合

SELECT row_name, extra_col, cat, value FROM foo ORDER BY 1;
 row_name    extra_col   cat    value
----------+------------+-----+---------
  row1         extra1    cat1    val1
  row1         extra1    cat2    val2
  row1         extra1    cat4    val4
  row2         extra2    cat1    val5
  row2         extra2    cat2    val6
  row2         extra2    cat3    val7
  row2         extra2    cat4    val8
      

category_sql 是生成类别集合的 SQL 语句。此语句只能返回一列。它必须生成至少一行,否则将生成错误。此外,它不得生成重复值,否则会生成错误。category_sql 可能是类似以下内容

SELECT DISTINCT cat FROM foo ORDER BY 1;
  -------
      

crosstab 函数被声明为返回 setof record,因此输出列的实际名称和类型必须在调用 SELECT 语句的 FROM 子句中定义,例如

SELECT * FROM crosstab('...', '...')
    AS ct(row_name text, extra text, cat1 text, cat2 text, cat3 text, cat4 text);
      

这会产生类似以下内容的结果

                  <==  value  columns   ==>
row_name   extra   cat1   cat2   cat3   cat4
---------+-------+------+------+------+------
  row1     extra1  val1   val2          val4
  row2     extra2  val5   val6   val7   val8
      

FROM 子句必须定义正确的数据类型及其对应数量的输出列。如果 source_sql 查询结果中包含 N 列,前 N-2 个列必须与前 N-2 个输出列相匹配。剩余输出列必须与 source_sql 查询结果最后一列类型相同,且其数量必须与 category_sql 查询结果中的行数完全一致。

crosstab 函数针对每一连续输入行组生成一行输出行,这些输入行的 row_name 值相同。输出 row_name 列以及任意 额外 列均从该组的第一行复制。输出 value 列填入匹配 category 值的行中的 value 字段。如果某行的 categorycategory_sql 查询的任何输出不匹配,则其 value 将被忽略。在该组中不存在匹配类别的输出列将填入 null 值。

实际上, source_sql 查询应始终指定 ORDER BY 1,以确保具有相同 row_name 的值被汇集到一起。但是,组内类别的排序并不重要。此外,必须确保 category_sql 查询输出的顺序与指定的输出列顺序相匹配。

下面是两个完整示例

create table sales(year int, month int, qty int);
insert into sales values(2007, 1, 1000);
insert into sales values(2007, 2, 1500);
insert into sales values(2007, 7, 500);
insert into sales values(2007, 11, 1500);
insert into sales values(2007, 12, 2000);
insert into sales values(2008, 1, 1000);
select * from crosstab(
  'select year, month, qty from sales order by 1',
  'select m from generate_series(1,12) m'
) as (
  year int,
  "Jan" int,
  "Feb" int,
  "Mar" int,
  "Apr" int,
  "May" int,
  "Jun" int,
  "Jul" int,
  "Aug" int,
  "Sep" int,
  "Oct" int,
  "Nov" int,
  "Dec" int
 year | Jan  | Feb  | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov  | Dec
------+------+------+-----+-----+-----+-----+-----+-----+-----+-----+------+------
 2007 | 1000 | 1500 |     |     |     |     | 500 |     |     |     | 1500 | 2000
 2008 | 1000 |      |     |     |     |     |     |     |     |     |      |
(2 rows)
      
CREATE TABLE cth(rowid text, rowdt timestamp, attribute text, val text);
INSERT INTO cth VALUES('test1','01 March 2003','temperature','42');
INSERT INTO cth VALUES('test1','01 March 2003','test_result','PASS');
INSERT INTO cth VALUES('test1','01 March 2003','volts','2.6987');
INSERT INTO cth VALUES('test2','02 March 2003','temperature','53');
INSERT INTO cth VALUES('test2','02 March 2003','test_result','FAIL');
INSERT INTO cth VALUES('test2','02 March 2003','test_startdate','01 March 2003');
INSERT INTO cth VALUES('test2','02 March 2003','volts','3.1234');
SELECT * FROM crosstab
  'SELECT rowid, rowdt, attribute, val FROM cth ORDER BY 1',
  'SELECT DISTINCT attribute FROM cth ORDER BY 1'
       rowid text,
       rowdt timestamp,
       temperature int4,
       test_result text,
       test_startdate timestamp,
       volts float8
 rowid |          rowdt           | temperature | test_result |      test_startdate      | volts
-------+--------------------------+-------------+-------------+--------------------------+--------
 test1 | Sat Mar 01 00:00:00 2003 |          42 | PASS        |                          | 2.6987
 test2 | Sun Mar 02 00:00:00 2003 |          53 | FAIL        | Sat Mar 01 00:00:00 2003 | 3.1234
(2 rows)
      

您可以创建预定义函数以避免在每个查询中写出结果列的名称和类型。请参见前一部分的示例。此形式 crosstab 的底层 C 函数名为 crosstab_hash

connectby(text relname, text keyid_fld, text parent_keyid_fld
          [, text orderby_fld ], text start_with, int max_depth
          [, text branch_delim ])
      

connectby 函数生成显示存储在表中的分层数据的显示。该表必须具有唯一标识行的键字段和引用每行的父代(如果有)的父键字段。 connectby 可以显示从任意行下降的子树。

表 F.32 说明了这些参数。

表 F.32. connectby 参数

键和父键字段可以是任何数据类型,但它们必须是同一类型。请注意,start_with 值必须以文本字符串形式输入,而无论键字段的类型是什么。

connectby 函数声明返回 setof record,因此实际的输出列名称和类型必须在调用 SELECT 语句的 FROM 子句中定义,例如

SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~')
    AS t(keyid text, parent_keyid text, level int, branch text, pos int);
      

前两列输出用于当前行的键及其父行的键;它们必须与表的键字段类型匹配。第三列输出是树中的深度,并且必须为 integer 类型。如果给出了 branch_delim 参数,则下一列输出是分支显示,并且必须为 text 类型。最后,如果给出了 orderby_fld 参数,则最后一列输出是一个序列号,并且必须为 integer 类型。

branch 输出列显示到达当前行的键所采用的路径。键由指定的 branch_delim 字符串分隔。如果不希望进行分支显示,则同时在输出列列表中省略 branch_delim 参数和分支列。

如果同一父级下的同级顺序很重要,则包括 orderby_fld 参数以指定按哪个字段对同级进行排序。此字段可以是任何可排序数据类型。如果且仅当指定了 orderby_fld 时,输出列列表中必须包含一个最终整数序列号列。

表示表和字段名称的参数按照原样复制到 connectby 在内部生成​​的 SQL 查询中。因此,如果名称包含混合大小写或特殊字符,则包括双引号。您可能还需要对表名称进行模式限定。

在大型表中,除非在父键字段上建立索引,否则性能很差。

重要的是,branch_delim 字符串不能出现在任何键值中,否则 connectby 可能会错误地报告无限递归错误。请注意,如果未提供 branch_delim,则使用默认值 ~ 来进行递归检测。

这里有一个示例

CREATE TABLE connectby_tree(keyid text, parent_keyid text, pos int);
INSERT INTO connectby_tree VALUES('row1',NULL, 0);
INSERT INTO connectby_tree VALUES('row2','row1', 0);
INSERT INTO connectby_tree VALUES('row3','row1', 0);
INSERT INTO connectby_tree VALUES('row4','row2', 1);
INSERT INTO connectby_tree VALUES('row5','row2', 0);
INSERT INTO connectby_tree VALUES('row6','row4', 0);
INSERT INTO connectby_tree VALUES('row7','row3', 0);
INSERT INTO connectby_tree VALUES('row8','row6', 0);
INSERT INTO connectby_tree VALUES('row9','row5', 0);
-- with branch, without orderby_fld (order of results is not guaranteed)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'row2', 0, '~')
 AS t(keyid text, parent_keyid text, level int, branch text);
 keyid | parent_keyid | level |       branch
-------+--------------+-------+---------------------
 row2  |              |     0 | row2
 row4  | row2         |     1 | row2~row4
 row6  | row4         |     2 | row2~row4~row6
 row8  | row6         |     3 | row2~row4~row6~row8
 row5  | row2         |     1 | row2~row5
 row9  | row5         |     2 | row2~row5~row9
(6 rows)
-- without branch, without orderby_fld (order of results is not guaranteed)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'row2', 0)
 AS t(keyid text, parent_keyid text, level int);
 keyid | parent_keyid | level
-------+--------------+-------
 row2  |              |     0
 row4  | row2         |     1
 row6  | row4         |     2
 row8  | row6         |     3
 row5  | row2         |     1
 row9  | row5         |     2
(6 rows)
-- with branch, with orderby_fld (notice that row5 comes before row4)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0, '~')
 AS t(keyid text, parent_keyid text, level int, branch text, pos int);
 keyid | parent_keyid | level |       branch        | pos
-------+--------------+-------+---------------------+-----
 row2  |              |     0 | row2                |   1
 row5  | row2         |     1 | row2~row5           |   2
 row9  | row5         |     2 | row2~row5~row9      |   3
 row4  | row2         |     1 | row2~row4           |   4
 row6  | row4         |     2 | row2~row4~row6      |   5
 row8  | row6         |     3 | row2~row4~row6~row8 |   6
(6 rows)
-- without branch, with orderby_fld (notice that row5 comes before row4)
SELECT * FROM connectby('connectby_tree', 'keyid', 'parent_keyid', 'pos', 'row2', 0)
 AS t(keyid text, parent_keyid text, level int, pos int);
 keyid | parent_keyid | level | pos
-------+--------------+-------+-----
 row2  |              |     0 |   1
 row5  | row2         |     1 |   2
 row9  | row5         |     2 |   3
 row4  | row2         |     1 |   4
 row6  | row4         |     2 |   5
 row8  | row6         |     3 |   6
(6 rows)
 
推荐文章