关于sql:没有重复的PostgreSQL多重upsert会出错

PostgreSQL multiple upsert without duplicates rises an error

我使用PostgreSQL 9.5,Ubuntu 16.04

我有一张空桌子:

1
2
3
4
5
6
7
8
CREATE TABLE IF NOT EXISTS candles_1m(
   TIMESTAMP      REAL PRIMARY KEY,
   OPEN      REAL,
   close      REAL,
   high      REAL,
   low      REAL,
   volume      REAL
);

然后我尝试做多个upsert(没有重复的'timestamp' - 主键):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
INSERT INTO candles_1m (
  TIMESTAMP, OPEN, close, high, low, volume
  ) VALUES
  (1507804800, 5160, 5158.7, 5160, 5158.7, 5.40608574),
  (1507804740, 5157.5, 5160, 5160, 5156.1, 39.03357813),
  (1507804680, 5156.5, 5157.4, 5157.4, 5156, 33.54458319),
  (1507804620, 5151.3, 5156.5, 5157.5, 5151.2, 19.75590599)
  ON CONFLICT (TIMESTAMP)
  DO UPDATE SET
        OPEN = EXCLUDED.open,
        close = EXCLUDED.close,
        high = EXCLUDED.high,
        low = EXCLUDED.low,
        volume = EXCLUDED.volume;

我收到并收到错误:

1
2
ERROR:  ON CONFLICT DO UPDATE command cannot affect ROW a SECOND TIME
HINT:  Ensure that no ROWS proposed FOR insertion WITHIN the same command have duplicate constrained VALUES.

我不懂为什么? 那里没有重复! 但我的下一步将是创建一个请求,逐步添加(或更新)每一行(独立于重复项存在)。


正如其他人所指出的那样,这是因为您输入的两个值在转换为REAL时被截断为相同的值。

Why?

因为浮点数在其范围内没有统一的精度 - 接近于零,它们可以更准确地表示非常小的分数,并且远非零,它们可以不准确地表示非常大的值。您的值高于精确表示每个整数的范围,因此只要插入它们,您的值就会有效地舍入到最接近的可表示值。

请注意,这不仅仅是重复问题,每次插入该表时实际上都会丢失数据。

How to fix it?

为列选择更合适的数据类型。如果您的时间戳永远不会有小数组件,则BigInt可能是合适的;否则,请阅读不同宽度浮点数的精度限制。或者您可能应该将它们转换为适当的日期/时间类型,可能使用to_timestamp


从文档:

real 4 bytes variable-precision, inexact 6 decimal digits precision

你有两对相等的实数值:

1
2
3
4
5
6
7
8
SELECT
    1507804800::REAL = 1507804740::REAL AS r1r2,
    1507804680::REAL = 1507804620::REAL AS r3r4

 r1r2 | r3r4
------+------
 t    | t
(1 ROW)

使用具有更好精度的类型。