由于存在不相关的场而导致交叉分裂

时间:2014-07-18 10:11:15

标签: sql postgresql postgresql-9.1 crosstab

我正在使用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 |

2 个答案:

答案 0 :(得分:2)

两个问题。

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列必须是第一个。必须包含categoryvalue列   按顺序排在最后两列。 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);

更多详情:

答案 1 :(得分:0)

您可以运行一个用星号替换不规则字符的查询:

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视为不同的两个值。