我很好奇这条语句是如何在 Postgres 中更新 3 行的。我运行它的所有其他时间,它都会更新 0 或 1。有没有办法找出哪些行?
bestsales=# update keyword set revenue = random()*10 where id = cast(random()*99999 as int);
UPDATE 3
id
是主键。
id | integer | not null default nextval('keyword_id_seq'::regclass)
"keyword_pkey" PRIMARY KEY, btree (id)
我尝试将其运行为SELECT
:
bestsales=# select * from keyword where id = cast(random()*99999 as int);
id | keyword | seed_id | source | search_count | country | language | volume | cpc | competition | modified_on | google_violation | revenue | bing_violation
-------+---------------------+---------+--------+--------------+---------+----------+--------+------+-------------+-------------+------------------+---------+----------------
6833 | vizio m190mv | | GOOGLE | 0 | | | 70 | 0.38 | 0.90 | | | |
65765 | shiatsu massage mat | | SPYFU | 0 | | | 110 | 0.69 | | | | |
87998 | granary flour | | SPYFU | 0 | | | 40 | 0.04 | | | | |
(3 rows)
有时它会返回多个。这怎么可能?
PostgreSQL 9.5.3
似乎
random()
每行执行一次而不是对所有语句执行一次。因此,每个id
值都会根据不同的随机值进行检查。您可能会得到 0、1、2 或更多,甚至是表的所有行,尽管即使有 10 行,这种可能性也很小。如果您希望它执行一次并因此只获得一个值并且只更新零行或一行,您可以使用 CTE,已知它在主查询之前进行评估:
和相同的
UPDATE
:即使是一个简单的子查询也能完成这项工作:
子查询被评估一次并且只更新一行(如果该
id
值存在)。比使用 CTE 便宜一点。如果您使用不带子查询包装器的表达式,
random()
则会对每一行进行评估,因为它是一个易变函数。这是设计使然,而且通常是期望的行为。至于你的附加问题:
不保证安全,但如果您在 之后立即执行此查询
UPDATE
,则共享值中最高事务 ID 的行xmin
是最近更新或插入的行。在同一个事务中插入/更新的所有行共享同一个事务 ID。如果您在一个事务中对同一张表进行了多次
INSERT
/UPDATE
操作,则需要cmin
另外查看命令 ID:没有任何保证,因为
xmin
可能会受到交易 ID 环绕的影响。但它通常有效。xmin
并且cmin
是行标题中的系统列。关于对象标识符类型的手册。
虽然仍在同一个事务中(尚未提交更新),但有一个更安全的方法:
如何查看 PostgreSQL 事务中更改的元组?
交易 ID 回绕后如何比较 xmin 和 txid_current()?
有关的: