由于存在不相关字段而导致的交叉表拆分结果
Posted
技术标签:
【中文标题】由于存在不相关字段而导致的交叉表拆分结果【英文标题】:Crosstab splitting results due to presence of unrelated field 【发布时间】:2014-07-18 10:11:15 【问题描述】:我正在使用带有 tablefunc:crosstab 的 postgres 9.1
我有一个结构如下的表:
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 和 instrument 字段是否相同,并通过对交叉表的输出进行分组来解决问题。
分析 我发现输入表中存在另一个条目导致输出被分成两行。如果我输入如下:
| 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'
再一次,输出与我们预期的一样,这令人惊讶,因为我所做的只是将仪器字段设置为自身。
所以我只能得出结论,在 Bund 条目中存在一些隐藏的字符/编码问题,正在破坏交叉表。
对于我如何确定破坏交叉表的条目有什么建议吗?
编辑: 我在原始表上运行以下命令来尝试查看任何隐藏的字符:
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 |
【问题讨论】:
您是否尝试比较脚本和手动输入的字符串上的octet_length(string)
?
刚试了一下:两者都是 14。
欧文:很好,谢谢。由于 Clodoaldo 的评论,我更新了查询,但没有更新结果。我还清理了表定义 - 问题仍然存在。
关于text
和varchar
的评论只是旁白。考虑我更新的答案以获得干净的解决方案。顺便说一句,您的问题可以重现,请考虑删除该免责声明。
【参考方案1】:
两个问题。
1。 ORDER BY
为必填项。
The manual:
在实践中,SQL 查询应始终指定
ORDER BY 1,2
以确保输入行正确排序,也就是说,将具有相同row_name
的值放在一起并在行内正确排序。
对于crosstab()
的单参数形式,ORDER BY 1,2
将是必要的。
2。 一个列,每组具有不同的值。
The manual:
crosstab(text source_sql, text category_sql)
source_sql
是一条生成源数据集的 SQL 语句。 ... 该语句必须返回一个row_name
列,一个category
列, 和一个value
列。它也可能有一个或多个“额外”列。row_name
列必须是第一个。category
和value
列必须 是最后两列,按此顺序。row_name
之间的任何列 和category
被视为“额外”。 “额外”列是预期的 对于具有相同row_name
值的所有行都相同。
我的大胆强调。 一个列。您似乎想在 两个 列上形成组,这不能如您所愿。
相关答案:
Pivot on Multiple Columns using Tablefunc解决方案取决于您实际想要实现的目标。这不是你的问题,你默默地假设这个函数会做你希望的事情。
解决方案
我猜你想在两个前导列上分组:(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);
更多细节:
PostgreSQL Crosstab Query【讨论】:
【参考方案2】:您可以运行一个用星号替换不规则字符的查询:
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
认为不同的两个值。
【讨论】:
没有尾随空格,查询的输出是“EUX*Bund*2014M”。正如您所说,这些东西不受仪器 = 仪器修复的影响。 我不确定您所说的“instrument = 'EUX.Bund.2014M' 匹配两个值”是什么意思。在我的精简文本数据库中,只有一个 EUX.Bund 条目。 您的交叉表显示具有相同值的两行 (2014-05-23|SGX.MiniJGB.2014U
)。也许尝试找到一个匹配两者的查询,例如select * from instrument_data where instrument like '%2014M%'
以上是关于由于存在不相关字段而导致的交叉表拆分结果的主要内容,如果未能解决你的问题,请参考以下文章
从 MS Access 中将交叉表查询结果导出到 Excel