我正在使用postgres 9.1 with tablefunc:crosstab
我有一个具有以下结构的表:
CREATE TABLE marketdata.instrument_data
(
dt date NOT NULL,
instrument text NOT NULL,
field text NOT NULL,
value numeric,
CONSTRAINT instrument_data_pk PRIMARY KEY (dt , instrument , field )
)
这由每天提取数据的脚本填充。所以它可能看起来像这样:
| dt | instrument | field | value |
|------------+-------------------+-----------+-------|
| 2014-05-23 | SGX.MiniJGB.2014U | PX_VOLUME | 1 |
| 2014-05-23 | SGX.MiniJGB.2014U | OPEN_INT | 2 |
然后,我使用以下交叉表查询来旋转表:
select dt, instrument, vol, oi
FROM crosstab($$
select dt, instrument, field, value
from marketdata.instrument_data
where field = 'PX_VOLUME' or field = 'OPEN_INT'
$$::text, $$VALUES ('PX_VOLUME'),('OPEN_INT')$$::text
) vol(dt date, instrument text, vol numeric, oi numeric);
运行这个我得到的结果:
| dt | instrument | vol | oi |
|------------+-------------------+-----+----|
| 2014-05-23 | SGX.MiniJGB.2014U | 1 | 2 |
问题:当在表中运行大量实际数据时,我注意到对于某些字段,该函数将结果拆分为两行:
| dt | instrument | vol | oi |
|------------+-------------------+-----+----|
| 2014-05-23 | SGX.MiniJGB.2014U | 1 | |
| 2014-05-23 | SGX.MiniJGB.2014U | | 2 |
我检查了dt和仪器字段是否相同,并通过对交叉表的输出进行分组来产生解决方法。
分析我发现输入表中存在另一个条目会导致输出分成2行。如果我输入如下:
| dt | instrument | field | value |
|------------+-------------------+-----------+-------|
| 2014-04-23 | EUX.Bund.2014M | PX_VOLUME | 0 |
| 2014-05-23 | SGX.MiniJGB.2014U | PX_VOLUME | 1 |
| 2014-05-23 | SGX.MiniJGB.2014U | OPEN_INT | 2 |
我明白了:
| dt | instrument | vol | oi |
|------------+-------------------+-----+----|
| 2014-04-23 | EUX.Bund.2014M | 0 | |
| 2014-05-23 | SGX.MiniJGB.2014U | 1 | |
| 2014-05-23 | SGX.MiniJGB.2014U | | 2 |
哪里真的很奇怪......
如果我手动重新创建上面的输入表,那么输出就像我们期望的那样,组合成一行。
如果我跑:
update marketdata.instrument_data
set instrument = instrument
where instrument = 'EUX.Bund.2014M'
然后,输出正如我们所期望的那样,这是令人惊讶的,因为我所做的就是将仪器字段设置为自身。
所以我只能得出结论,该外滩条目中有一些隐藏的字符/编码问题正在破坏交叉表。
关于如何确定破坏交叉表的条目是什么,是否有任何建议?
编辑:我在原始表上运行以下命令以尝试查看任何隐藏的字符:
select instrument, encode(instrument::bytea, 'escape')
from marketdata.bloomberg_future_data_temp
where instrument = 'EUX.Bund.2014M';
得到了:
| instrument | encode |
|----------------+----------------|
| EUX.Bund.2014M | EUX.Bund.2014M |
两个问题。
ORDER BY
is required.在实践中,SQL查询应始终指定
ORDER BY 1,2
以确保输入行正确排序,即具有相同row_name
的值汇集在一起并在行内正确排序。
使用单参数形式的crosstab()
,ORDER BY 1,2
将是必要的。
crosstab(text source_sql, text category_sql)
source_sql
是一个生成源数据集的SQL语句。 ... 该语句必须返回一个row_name
列,一个category
列和一个value
列。它也可能有一个或多个“额外”列。row_name
列必须是第一个。category
和value
列必须按顺序排在最后两列。row_name
和category
之间的任何列都被视为“额外”。对于具有相同row_name
值的所有行,“额外”列应该是相同的。
大胆强调我的。一栏。您似乎想要在两列上形成组,但这并不是您想要的。
相关回答:
解决方案取决于您实际想要实现的目标。这不是你的问题,你默默地认为这个功能会做你想要的。
我想你想在两个主要栏目上分组:(dt, instrument)
。您可以使用连接或数组来玩弄技巧,但这会很慢和/或不可靠。我建议使用window function rank()
or dense_rank()
更清洁,更快速地为每个所需组生成单列唯一值。这是非常便宜的,因为订购行是主要成本,并且框架的顺序无论如何都与所需的顺序相同。如果需要,您可以删除外部查询中添加的列:
SELECT dt, instrument, vol, oi
FROM crosstab(
$$SELECT dense_rank() OVER (ORDER BY dt, instrument) AS rnk
, dt, instrument, field, value
FROM marketdata.instrument_data
WHERE field IN ('PX_VOLUME', 'OPEN_INT')
ORDER BY 1$$
, $$VALUES ('PX_VOLUME'),('OPEN_INT')$$
) vol(rnk int, dt date, instrument text, vol numeric, oi numeric);
更多细节:
您可以运行用星号替换不规则字符的查询:
select regexp_replace(instrument, '[^a-zA-Z0-9]', '*', 'g')
from marketdata.instrument_data
where instrument = 'EUX.Bund.2014M'
也许instrument = instrument
赋值丢弃尾随空格。这也可以解释为什么where instrument = 'EUX.Bund.2014M'
匹配crosstab
认为不同的两个值。