F.43. tablefunc â ÑÑнкÑии, возвÑаÑаÑÑие ÑаблиÑÑ (crosstab и не ÑолÑко) #
ÐодÑÐ»Ñ tablefunc ÑодеÑÐ¶Ð¸Ñ ÑÑд ÑÑнкÑий, возвÑаÑаÑÑиÑ
ÑаблиÑÑ (Ñо еÑÑÑ, множеÑÑва ÑÑÑок). ÐÑи ÑÑнкÑии Ð¿Ð¾Ð»ÐµÐ·Ð½Ñ Ð¸ Ñами по Ñебе, и как пÑимеÑÑ Ð½Ð°Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð½Ð° C ÑÑнкÑий, возвÑаÑаÑÑиÑ
набоÑÑ ÑÑÑок.
ÐаннÑй модÑÐ»Ñ ÑÑиÑаеÑÑÑ Â«Ð´Ð¾Ð²ÐµÑеннÑм», Ñо еÑÑÑ ÐµÐ³Ð¾ могÑÑ ÑÑÑанавливаÑÑ Ð¾Ð±ÑÑнÑе полÑзоваÑели, имеÑÑие пÑаво CREATE в ÑекÑÑей базе даннÑÑ
.
F.43.1. ÐÑедоÑÑавлÑемÑе ÑÑнкÑии #
ФÑнкÑии, пÑедоÑÑавлÑемÑе модÑлем tablefunc, пеÑеÑиÑÐ»ÐµÐ½Ñ Ð² ТаблиÑе F.32.
ТаблиÑа F.32. ФÑнкÑии tablefunc
F.43.1.1. normal_rand #
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)F.43.1.2. crosstab(text) #
crosstab(text sql) crosstab(text sql, int N)
ФÑнкÑÐ¸Ñ crosstab пÑименÑеÑÑÑ Ð´Ð»Ñ ÑоÑмиÑÐ¾Ð²Ð°Ð½Ð¸Ñ Â«Ð¿Ð¾Ð²ÑÑнÑÑÑÑ
» оÑобÑажений, в коÑоÑÑÑ
даннÑе идÑÑ Ð²Ð´Ð¾Ð»Ñ ÑÑÑок, а не ÑвеÑÑ
Ñ Ð²Ð½Ð¸Ð·. ÐапÑимеÑ, Ð¼Ñ Ð¼Ð¾Ð¶ÐµÐ¼ имеÑÑ Ñакие даннÑе:
row1 val11 row1 val12 row1 val13 ... row2 val21 row2 val22 row2 val23 ...
и Ñ Ð¾Ñим видеÑÑ Ð¸Ñ Ñак:
row1 val11 val12 val13 ... row2 val21 val22 val23 ... ...
ФÑнкÑÐ¸Ñ 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, Ñак ÑÑо ÑакÑиÑеÑкие имена и ÑÐ¸Ð¿Ñ ÑÑолбÑов Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¾Ð¿ÑеделÑÑÑÑÑ Ð² пÑедложении FROM вÑзÑваÑÑего опеÑаÑоÑа SELECT, напÑÐ¸Ð¼ÐµÑ Ñак:
SELECT * FROM crosstab('...') AS ct(row_name text, category_1 text, category_2 text);ÐÑÐ¾Ñ Ð·Ð°Ð¿ÑÐ¾Ñ Ð²ÑдаÑÑ Ð¿ÑимеÑно Ñакой ÑезÑлÑÑаÑ:
<== ÑÑолбÑÑ Ð·Ð½Ð°Ñений ==> row_name category_1 category_2 ----------+------------+------------ row1 val1 val2 row2 val5 val6
ÐÑедложение FROM должно опÑеделÑÑÑ ÑезÑлÑÑÐ°Ñ Ñо ÑÑолбÑом row_name (Ñого же Ñипа даннÑÑ
, ÑÑо Ñ Ð¿ÐµÑвого ÑезÑлÑÑиÑÑÑÑего ÑÑолбÑа SQL-запÑоÑа), за коÑоÑÑм ÑледÑÑÑ N ÑÑолбÑов знаÑений (вÑе Ñого же Ñипа даннÑÑ
, ÑÑо и ÑÑеÑий ÑезÑлÑÑиÑÑÑÑий ÑÑÐ¾Ð»Ð±ÐµÑ SQL-запÑоÑа). ÐолиÑеÑÑво вÑÑ
однÑÑ
ÑÑолбÑов знаÑений Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÑоизволÑнÑм и имена вÑÑ
однÑÑ
ÑÑолбÑов опÑеделÑеÑе Ð²Ñ Ñами.
ФÑнкÑÐ¸Ñ crosstab вÑдаÑÑ Ð¾Ð´Ð½Ñ Ð²ÑÑ
однÑÑ ÑÑÑÐ¾ÐºÑ Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ поÑледоваÑелÑной гÑÑÐ¿Ð¿Ñ Ñ Ð¾Ð´Ð½Ð¸Ð¼ знаÑением row_name. Ðна заполнÑÐµÑ ÑÑолбÑÑ Ð·Ð½Ð°Ñений Ñлева напÑаво полÑми 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, опÑеделÑÑÑего вÑÑ
однÑе ÑÑолбÑÑ, Ñоздав ÑобÑÑвеннÑÑ ÑÑнкÑÐ¸Ñ crosstab, в опÑеделении коÑоÑой бÑÐ´ÐµÑ Ð·Ð°ÑÐ¸Ñ Ð¶ÐµÐ»Ð°ÑелÑнÑй Ñип вÑÑ
одной ÑÑÑоки. ÐÑо опиÑÑваеÑÑÑ Ð² ÑледÑÑÑем Ñазделе. Также имееÑÑÑ Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ÑÑÑ Ð²ÐºÐ»ÑÑиÑÑ ÑÑебÑемое пÑедложение FROM в опÑеделение пÑедÑÑавлениÑ.
ÐÑимеÑание
Также изÑÑиÑе ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ \crosstabview в psql, ÑеализÑÑÑÑÑ ÑÑнкÑионалÑноÑÑÑ, подобнÑÑ crosstab().
F.43.1.3. crosstabN(text) #
N(text)crosstabN(text sql)ФÑнкÑии crosstab ÑвлÑÑÑÑÑ Ð¿ÑимеÑами Ñого, как можно ÑоздаÑÑ ÑобÑÑвеннÑе обÑÑÑки ÑнивеÑÑалÑной ÑÑнкÑии Ncrosstab, ÑÑÐ¾Ð±Ñ Ð½Ðµ пÑиÑ
одилоÑÑ Ð²ÑпиÑÑваÑÑ Ð¸Ð¼ÐµÐ½Ð° и ÑÐ¸Ð¿Ñ ÑÑолбÑов в вÑзÑваÑÑем запÑоÑе SELECT. ÐодÑÐ»Ñ tablefunc вклÑÑÐ°ÐµÑ ÑÑнкÑии crosstab2, crosstab3 и crosstab4, опÑеделÑÑÑие ÑÐ¸Ð¿Ñ Ð²ÑÑ
однÑÑ
ÑÑÑок Ñак:
CREATE TYPE tablefunc_crosstab_N AS (
row_name TEXT,
category_1 TEXT,
category_2 TEXT,
.
.
.
category_N TEXT
);Таким обÑазом, ÑÑи ÑÑнкÑии могÑÑ Ð¿ÑименÑÑÑÑÑ Ð½ÐµÐ¿Ð¾ÑÑедÑÑвенно, когда вÑ
одной запÑÐ¾Ñ Ð²ÑдаÑÑ ÑÑолбÑÑ row_name и value Ñипа text и Ð²Ñ Ñ
оÑиÑе полÑÑиÑÑ Ð½Ð° вÑÑ
оде 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. ÐаÑем нÑжно вÑбÑаÑÑ ÑникалÑное Ð¸Ð¼Ñ Ð´Ð»Ñ ÑÑнкÑии, пÑинимаÑÑей один паÑамеÑÑtextи возвÑаÑаÑÑейsetof имÑ_ваÑего_Ñипа, и ÑвÑзаÑÑ ÐµÐ³Ð¾ Ñ Ñой же нижележаÑей ÑÑнкÑиейcrosstabна C. ÐапÑимеÑ, еÑли Ð²Ð°Ñ Ð¸ÑÑоÑник даннÑÑ Ð²ÑдаÑÑ Ð¸Ð¼ÐµÐ½Ð° ÑÑÑок Ñипаtextи знаÑÐµÐ½Ð¸Ñ Ñипаfloat8, и Ð²Ñ Ñ Ð¾ÑиÑе полÑÑиÑÑ 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;
F.43.1.4. crosstab(text, text) #
crosstab(text source_sql, text category_sql)
ÐÑновное огÑаниÑение ÑоÑÐ¼Ñ crosstab Ñ Ð¾Ð´Ð½Ð¸Ð¼ паÑамеÑÑом ÑоÑÑÐ¾Ð¸Ñ Ð² Ñом, ÑÑо она воÑпÑÐ¸Ð½Ð¸Ð¼Ð°ÐµÑ Ð²Ñе знаÑÐµÐ½Ð¸Ñ Ð² гÑÑппе одинаково и вÑÑавлÑÐµÑ Ð¾ÑеÑедное знаÑение в пеÑвÑй ÑвободнÑй ÑÑолбеÑ. ÐÑли Ð²Ñ Ñ
оÑиÑе, ÑÑÐ¾Ð±Ñ ÑÑолбÑÑ Ð·Ð½Ð°Ñений ÑооÑвеÑÑÑвовали опÑеделÑннÑм каÑегоÑиÑм даннÑÑ
и некоÑоÑÑе гÑÑÐ¿Ð¿Ñ Ð¼Ð¾Ð³Ð»Ð¸ ÑодеÑжаÑÑ Ð´Ð°Ð½Ð½Ñе не Ð´Ð»Ñ Ð²ÑеÑ
каÑегоÑий, ÑÑÐ¾Ñ Ð¿Ð¾Ð´Ñ
од не бÑÐ´ÐµÑ ÑабоÑаÑÑ. ФоÑма crosstab Ñ Ð´Ð²ÑÐ¼Ñ Ð¿Ð°ÑамеÑÑами ÑеÑÐ°ÐµÑ ÑÑÑ Ð·Ð°Ð´Ð°ÑÑ, пÑÐ¸Ð½Ð¸Ð¼Ð°Ñ ÑвнÑй ÑпиÑок каÑегоÑий, ÑооÑвеÑÑÑвÑÑÑиÑ
вÑÑ
однÑм ÑÑолбÑам.
РпаÑамеÑÑе source_sql пеÑедаÑÑÑÑ SQL-опеÑаÑоÑ, вÑдаÑÑий иÑÑ
однÑй Ð½Ð°Ð±Ð¾Ñ Ð´Ð°Ð½Ð½ÑÑ
. ÐÑÐ¾Ñ Ð¾Ð¿ÐµÑаÑÐ¾Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ вÑдаваÑÑ ÑÑÑоки Ñо ÑÑолбÑом row_name, ÑÑолбÑом category и ÑÑолбÑом value. Также он Ð¼Ð¾Ð¶ÐµÑ Ð²ÑдаÑÑ Ð¾Ð´Ð¸Ð½ или неÑколÑко «дополниÑелÑнÑÑ
» ÑÑолбÑов. СÑÐ¾Ð»Ð±ÐµÑ row_name должен бÑÑÑ Ð¿ÐµÑвÑм, а ÑÑолбÑÑ category и value â поÑледними двÑмÑ, именно в ÑÑом поÑÑдке. ÐÑе ÑÑолбÑÑ Ð¼ÐµÐ¶Ð´Ñ row_name и category воÑпÑинимаÑÑÑÑ ÐºÐ°Ðº «дополниÑелÑнÑе». ÐжидаеÑÑÑ, ÑÑо «дополниÑелÑнÑе» ÑÑолбÑÑ Ð±ÑдÑÑ ÑодеÑжаÑÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñе знаÑÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð²ÑеÑ
ÑÑÑок Ñ Ð¾Ð´Ð½Ð¸Ð¼ знаÑением 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;
cat
-------
cat1
cat2
cat3
cat4ФÑнкÑÐ¸Ñ crosstab обÑÑвлена как возвÑаÑаÑÑÐ°Ñ Ñип setof record, Ñак ÑÑо ÑакÑиÑеÑкие имена и ÑÐ¸Ð¿Ñ Ð²ÑÑ
однÑÑ
ÑÑолбÑов Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¾Ð¿ÑеделÑÑÑÑÑ Ð² пÑедложении FROM вÑзÑваÑÑего опеÑаÑоÑа SELECT, напÑÐ¸Ð¼ÐµÑ Ñак:
SELECT * FROM crosstab('...', '...')
AS ct(row_name text, extra text, cat1 text, cat2 text, cat3 text, cat4 text);ÐÑи ÑÑом бÑÐ´ÐµÑ Ð¿Ð¾Ð»ÑÑен пÑимеÑно Ñакой ÑезÑлÑÑаÑ:
<== ÑÑолбÑÑ Ð·Ð½Ð°Ñений ==> 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. ÐÑли в поле category оказÑваеÑÑÑ Ð·Ð½Ð°Ñение, оÑÑÑÑÑÑвÑÑÑее в ÑезÑлÑÑаÑе запÑоÑа category_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'
)
AS
(
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)ÐÑ Ð¼Ð¾Ð¶ÐµÑе ÑоздаÑÑ Ð¿ÑедопÑеделÑннÑе ÑÑнкÑии, ÑÑÐ¾Ð±Ñ Ð½Ðµ вÑпиÑÑваÑÑ Ð¸Ð¼ÐµÐ½Ð° и ÑÐ¸Ð¿Ñ ÑезÑлÑÑиÑÑÑÑиÑ
ÑÑолбÑов в каждом запÑоÑе. ÐÑимеÑÑ Ð¿ÑÐ¸Ð²ÐµÐ´ÐµÐ½Ñ Ð² пÑедÑдÑÑем Ñазделе. ÐижележаÑÐ°Ñ ÑÑнкÑÐ¸Ñ C Ð´Ð»Ñ ÑÑой ÑоÑÐ¼Ñ crosstab назÑваеÑÑÑ crosstab_hash.
F.43.1.5. connectby #
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.33.
ТаблиÑа F.33. ÐаÑамеÑÑÑ connectby
| ÐаÑамеÑÑ | ÐпиÑание |
|---|---|
relname | ÐÐ¼Ñ Ð¸ÑÑ Ð¾Ð´Ð½Ð¾Ð³Ð¾ оÑноÑÐµÐ½Ð¸Ñ |
keyid_fld | ÐÐ¼Ñ Ð¿Ð¾Ð»Ñ ÐºÐ»ÑÑа |
parent_keyid_fld | ÐÐ¼Ñ Ð¿Ð¾Ð»Ñ, ÑодеÑжаÑего клÑÑ ÑодиÑÐµÐ»Ñ |
orderby_fld | ÐÐ¼Ñ Ð¿Ð¾Ð»Ñ, по коÑоÑÐ¾Ð¼Ñ ÑоÑÑиÑÑÑÑÑÑ Ð¿Ð¾Ñомки (необÑзаÑелÑно) |
start_with | ÐнаÑение клÑÑа оÑпÑавной ÑÑÑоки |
max_depth | ÐакÑималÑÐ½Ð°Ñ Ð³Ð»Ñбина, на коÑоÑÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ погÑÑзиÑÑÑÑ, либо Ð½Ð¾Ð»Ñ Ð´Ð»Ñ Ð½ÐµÐ¾Ð³ÑаниÑенного погÑÑÐ¶ÐµÐ½Ð¸Ñ |
branch_delim | СÑÑока, ÑазделÑÑÑÐ°Ñ ÐºÐ»ÑÑи в вÑводе веÑви (необÑзаÑелÑно) |
ÐÐ¾Ð»Ñ ÐºÐ»ÑÑа и клÑÑа ÑодиÑÐµÐ»Ñ Ð¼Ð¾Ð³ÑÑ Ð±ÑÑÑ Ð»Ñбого Ñипа, но Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¸Ð¼ÐµÑÑ Ð¾Ð±Ñий Ñип. ÐамеÑÑÑе, ÑÑо знаÑение start_with должно задаваÑÑÑÑ ÑекÑÑовой ÑÑÑокой, вне завиÑимоÑÑи Ð¾Ñ Ñипа Ð¿Ð¾Ð»Ñ ÐºÐ»ÑÑа.
ФÑнкÑÐ¸Ñ connectby обÑÑвлена как возвÑаÑаÑÑÐ°Ñ setof record, Ñак ÑÑо ÑакÑиÑеÑкие имена и ÑÐ¸Ð¿Ñ Ð²ÑÑ
однÑÑ
ÑÑолбÑов Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¾Ð¿ÑеделÑÑÑÑÑ Ð² пÑедложении FROM вÑзÑваÑÑего опеÑаÑоÑа SELECT, напÑÐ¸Ð¼ÐµÑ Ñак:
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 и ÑÑÐ¾Ð»Ð±ÐµÑ branch в ÑпиÑке вÑÑ
однÑÑ
ÑÑолбÑов.
ÐÑли поÑÑдок поÑомков одного ÑодиÑÐµÐ»Ñ Ð¸Ð¼ÐµÐµÑ Ð·Ð½Ð°Ñение, добавÑÑе паÑамеÑÑ orderby_fld, ÑказÑваÑÑий поле Ð´Ð»Ñ ÑпоÑÑдоÑÐ¸Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾Ñомков. ÐÑо поле Ð¼Ð¾Ð¶ÐµÑ Ð¸Ð¼ÐµÑÑ Ð»Ñбой Ñип, допÑÑкаÑÑий ÑоÑÑиÑовкÑ. СпиÑок вÑÑ
однÑÑ
ÑÑолбÑов должен вклÑÑаÑÑ Ð¿Ð¾Ñледним ÑÑолбÑом ÑелоÑиÑленнÑй ÑÑÐ¾Ð»Ð±ÐµÑ Ñ Ð¿Ð¾ÑледоваÑелÑнÑми знаÑениÑми, еÑли и ÑолÑко еÑли пеÑедаÑÑÑÑ Ð¿Ð°ÑамеÑÑ orderby_fld.
ÐаÑамеÑÑÑ, пÑедÑÑавлÑÑÑие имена ÑаблиÑÑ Ð¸ полей, копиÑÑÑÑÑÑ ÐºÐ°Ðº еÑÑÑ Ð² SQL-запÑоÑÑ, коÑоÑÑе connectby генеÑиÑÑÐµÑ Ð²Ð½ÑÑÑи. Таким обÑазом, иÑ
нÑжно заклÑÑиÑÑ Ð² двойнÑе кавÑÑки, еÑли они ÑодеÑÐ¶Ð°Ñ Ð±ÑÐºÐ²Ñ Ð² Ñазном ÑегиÑÑÑе или ÑпеÑиалÑнÑе ÑимволÑ. Также Ð¼Ð¾Ð¶ÐµÑ Ð¿Ð¾Ð½Ð°Ð´Ð¾Ð±Ð¸ÑÑÑÑ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸ÑÑ Ð¸Ð¼Ñ ÑаблиÑÑ ÑÑ
емой.
С болÑÑими ÑаблиÑами пÑоизводиÑелÑноÑÑÑ Ð±ÑÐ´ÐµÑ Ð½ÐµÑдовлеÑвоÑиÑелÑной, еÑли не ÑоздаÑÑ Ð¸Ð½Ð´ÐµÐºÑ Ð¿Ð¾ Ð¿Ð¾Ð»Ñ Ñ ÐºÐ»ÑÑом ÑодиÑелÑ.
Ðажно, ÑÑÐ¾Ð±Ñ ÑÑÑока 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);
-- Ñ Ð²ÐµÑвÑми без orderby_fld (поÑÑдок ÑезÑлÑÑаÑов не гаÑанÑиÑÑеÑÑÑ)
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)
-- без веÑвей и без orderby_fld (поÑÑдок ÑезÑлÑÑаÑов не гаÑанÑиÑÑеÑÑÑ)
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)
-- Ñ Ð²ÐµÑвÑми и Ñ orderby_fld (замеÑÑÑе, ÑÑо row5 идÑÑ Ð¿ÐµÑед 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)
-- без веÑвей, Ñ orderby_fld (замеÑÑÑе, ÑÑо row5 идÑÑ Ð¿ÐµÑед 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)F.43.2. ÐвÑÐ¾Ñ #
Ðжо Ðонвей (Joe Conway)