这个语句是如何在 Postgres 中更新 3 行的?

Chl*_*loe 4 postgresql random update

我很好奇这个语句是如何在 Postgres 中更新 3 行的。在我运行它的所有其他时间,它会更新 0 或 1。有没有办法找出哪些行?

bestsales=# update keyword set revenue = random()*10 where id = cast(random()*99999 as int);
UPDATE 3
Run Code Online (Sandbox Code Playgroud)

id 是主键。

 id               | integer                        | not null default nextval('keyword_id_seq'::regclass)
    "keyword_pkey" PRIMARY KEY, btree (id)
Run Code Online (Sandbox Code Playgroud)

我尝试将其运行为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)
Run Code Online (Sandbox Code Playgroud)

有时它会返回不止一个。这怎么可能?

PostgreSQL 9.5.3

ype*_*eᵀᴹ 5

似乎random()每行执行一次,而不是对所有语句执行一次。因此,每个id值都根据不同的随机值进行检查。您可能会得到 0、1、2 或更多,甚至是表格​​的所有行,尽管即使有 10 行,这种可能性也会非常小。

如果您希望它执行一次,从而只获取一个值并只更新零或一行,则可以使用 CTE,已知在主查询之前对其进行评估:

with rnd as
  ( select random() as random )               -- get just one value
select * 
from keyword, rnd 
where id = cast(rnd.random * 99999 as int) ;  -- and use it everywhere
Run Code Online (Sandbox Code Playgroud)

和相同的UPDATE

with rnd as
  ( select random() as random ) 
update keyword
set revenue = random()*10                    -- different random value
from rnd 
where id = cast(rnd.random * 99999 as int);
Run Code Online (Sandbox Code Playgroud)


Erw*_*ter 5

即使是一个简单的子查询也能完成这项工作:

UPDATE keyword
SET    revenue = random() * 10
WHERE  id = (SELECT (random() * 99999)::int);
Run Code Online (Sandbox Code Playgroud)

子查询被评估一次并且只更新一行(如果该id值存在)。比使用 CTE 便宜一点。

如果使用不带子查询包装器的表达式,random()则对每一行求值,因为它是一个volatile函数。这是设计使然,而且通常是期望的行为。

至于你的补充问题:

有没有办法找出哪些行?

不能保证安全,但如果您在 之后立即执行此查询UPDATE,则共享最高事务 IDxmin值的行是最近更新或插入的行。

在同一事务中插入/更新的所有行共享相同的事务 ID。如果在一个事务中对同一个表有多个INSERT/UPDATE操作,则需要cmin另外查看命令 ID :

SELECT *, xmin, cmin
FROM   keyword
ORDER  BY xmin::text::bigint DESC;
Run Code Online (Sandbox Code Playgroud)

没有任何保证,因为xmin受可能的交易 ID 环绕影响。但它通常有效。

xmincmin是行标题中的系统列。
关于对象标识符类型的手册。

虽然仍在同一个事务中(更新尚未提交),但还有一种更安全的方法:

有关的: