【问题标题】:Duplicate key violation on UPDATE FROM SELECT query in VerticaVertica 中 UPDATE FROM SELECT 查询的重复键违规
【发布时间】:2017-08-10 11:32:14
【问题描述】:

我想根据event表数据UPDATE维度表client中的某列:

UPDATE client AS c
SET 
    some_attr_last_value = last_attr_values.value 
FROM (

  SELECT
    l.client_id,
    value,
    ROW_NUMBER() OVER (PARTITION BY l.client_id ORDER BY timestamp DESC) AS num
  FROM event AS l
  WHERE 
    timestamp > DATE_TRUNC('month', NOW() - INTERVAL '1 month')

) AS last_attr_values

WHERE 
    last_attr_values.num = 1 AND 
    c.client_id = last_attr_values.client_id AND 
    c.some_attr_last_value <> last_attr_values.value;

我已经有一些类似上面的查询:它们以相同的方式更新其他列(仅与其他列一起)并且运行良好。但是一个查询会产生错误:

错误 6745:重复键值:'client_id=...' -- 违反约束 '....client.C_PRIMARY'

但我不会尝试更改client_id。为什么会出现这个错误?

我检查了client 表,但它没有重复。 ANALYZE_CONSTRAINTS('client') 也不会返回任何违规行为。

版本是 7.2.2-1。

【问题讨论】:

  • 那张桌子上有触发器吗?我对vertica不熟悉,所以我只是把它扔在那里。
  • 据我所知 Vertica 不支持触​​发器。
  • 您能否添加 eventclient 的 CREATE TABLE 以及确切的 UPDATE 语句 - 如果适用,您在同一事务中触发的一堆先前的 UPDATE 语句(假设您之前是否关闭了自动提交或触发了 BEGIN TRANSACTION)?
  • event 表中大约有 200 列,client 中大约有 50 列,所以恐怕这个问题太多了。关于事务:我尝试在没有任何先前查询(以及来自不同会话和 DB 用户)的情况下以不同方式执行此查询,并且总是遇到相同的错误。
  • @Timurib 请发布 client.C_PRIMARY 约束?

标签: sql vertica


【解决方案1】:

问题不在于您的客户表,而在于您的内部选择。

SELECT
    l.client_id,
    value,
    ROW_NUMBER() OVER (PARTITION BY l.client_id ORDER BY timestamp DESC) AS num
  FROM event AS l
  WHERE 
    timestamp > DATE_TRUNC('month', NOW() - INTERVAL '1 month')

SELECT 中的什么确保每个客户都有一条记录?

假设您有多个事件,其中有多个客户的多个事件。

  • Client_id 1 10:00
  • Client_id 1 10:05
  • Client_id 9 10:05
  • Client_id 9 10:06
  • Client_id 1 10:07

因为你的窗口函数是按时间戳排序的

ROW_NUMBER() OVER (PARTITION BY l.client_id ORDER BY timestamp DESC) AS num

你会得到

  • Client_id 1 1
  • Client_id 1 2
  • Client_id 9 1
  • Client_id 9 2
  • Client_id 1 1

我相信您需要在 ORDER BY 中包含 Client_id 才能使您的查询正常工作。


更新:一段时间后错误再次发生。我尝试使用上述解决方案,但不幸的是它没有帮助。仅使用临时表即可克服该错误:

CREATE LOCAL TEMPORARY TABLE last_values ON COMMIT PRESERVE ROWS 
AS SELECT client_id, value FROM (
  SELECT
    l.client_id,
    value,
    ROW_NUMBER() OVER (PARTITION BY l.client_id ORDER BY timestamp DESC) AS num
  FROM event AS l WHERE timestamp > DATE_TRUNC('month', NOW() - INTERVAL '1 month')
) last_values WHERE last_values.num = 1;

UPDATE client AS c 
SET some_attr_last_value = last_values.value
FROM last_values 
WHERE c.client_id = last_values.client_id AND c.some_attr_last_value <> last_attr_values.value

DROP TABLE last_values;

所以虽然问题解决了,但我并没有找到它的确切原因。

【讨论】:

  • 对不起,但我无法重现您的示例:pastebin.com/raw/a3kctbei - 所有client_id 都被自己的框架划分并按顺序排列,没有重复的对。也许我错过了什么?不幸的是,我不能只替换从时间戳到 client_id 的排序,因为我需要精确地最后一个值。
  • 尝试按client_id 和timestamp 排序。不是一个而不是另一个。当两个数据集中都没有重复时,我遇到了这个问题,问题是查询优化引擎无法知道键是唯一的,因此抱怨它。
  • 我很困惑,但几个小时前错误刚刚消失,查询没有任何更改。我们一直成功地完成了所有行的更新(上个月没有条件),没有发现重复。我终于明白了你的解决方案,但现在我无法检查它。无论如何,我很感谢你提供的信息,并认为它可能对其他人有帮助,所以我认为答案应该被标记为接受。谢谢!
  • 一段时间后错误再次发生。我尝试使用上述解决方案,但不幸的是它没有帮助。仅使用临时表克服了该错误。我在回答中进一步描述了这一点。
【解决方案2】:

Vertica 通常在SELECT 时间检查约束,而不是在您更新时。检查你的桌子是否有重复。

我认为 Vertica 的最新版本现在确实有一些选项可用于检查 INSERTUPDATE,但这不是默认设置。

【讨论】:

  • 是的,我已经检查了表格并且它不包含重复项(每个client_id 只包含在client 中一次)。
猜你喜欢
  • 2020-03-26
  • 2012-10-28
  • 1970-01-01
  • 1970-01-01
  • 2021-09-03
  • 2013-07-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-14
相关资源
最近更新 更多