在PostgreSQL中实现update | delete limit

背景

使用MySQL的用户可能会比较熟悉这样的用法,更新或删除时可以指定限制更新或删除多少条记录。

达到限定的记录数后不再继续进行下去,而是返回。

delete from tbl where xxx limit 100;

update tbl set xxx=xxx where xxx limit 100;

目前PostgreSQL没有类似的语法,但是可以通过其他手段来达到同样的效果。

with语法实现

创建测试表

postgres=# create table t(id int primary key, info text);
CREATE TABLE
postgres=# insert into t select generate_series(1,1000000);
INSERT 0 1000000

update | delete limit 用法如下

postgres=# with t1 as (select id from t where id between 1 and 1000 limit 10) 
                  delete from t where id in (select * from t1);
DELETE 10

postgres=# with t1 as (select id from t where id between 1 and 1000 limit 10) 
                   update t set info='new' where id in (select * from t1);
UPDATE 10

postgres=# explain with t1 as (select id from t where id between 1 and 1000 limit 10) 
postgres-#                    update t set info='new' where id in (select * from t1);
                                      QUERY PLAN                                       
---------------------------------------------------------------------------------------
 Update on t  (cost=4.89..12.93 rows=1 width=38)
   CTE t1
     ->  Limit  (cost=0.42..4.44 rows=1 width=4)
           ->  Index Only Scan using t_pkey on t t_1  (cost=0.42..4.44 rows=1 width=4)
                 Index Cond: ((id >= 1) AND (id <= 1000))
   ->  Nested Loop  (cost=0.45..8.48 rows=1 width=38)
         ->  HashAggregate  (cost=0.02..0.03 rows=1 width=32)
               Group Key: t1.id
               ->  CTE Scan on t1  (cost=0.00..0.02 rows=1 width=32)
         ->  Index Scan using t_pkey on t  (cost=0.42..8.44 rows=1 width=10)
               Index Cond: (id = t1.id)
(11 rows)

使用with模拟必须有PK或者非空UK,否则需要用行号,但是行号来扫就慢了,没走tid scan方法。

postgres=#  with t1 as (select ctid from t where id between 1 and 100000 limit 10) delete from t where ctid in (select ctid from t1);
DELETE 10

postgres=#  with t1 as (select ctid from t where id between 1 and 100000 limit 10) update t set info='new' where ctid in (select ctid from t1);
UPDATE 10

postgres=# explain with t1 as (select ctid from t where id between 1 and 100000 limit 10) update t set info='new' where ctid in (select ctid from t1);
                                       QUERY PLAN                                        
-----------------------------------------------------------------------------------------
 Update on t  (cost=129834.64..134784.79 rows=10 width=40)
   CTE t1
     ->  Limit  (cost=0.42..0.78 rows=10 width=6)
           ->  Index Scan using t_pkey on t t_1  (cost=0.42..3178.93 rows=89225 width=6)
                 Index Cond: ((id >= 1) AND (id <= 100000))
   ->  Merge Semi Join  (cost=129833.86..134784.01 rows=10 width=40)
         Merge Cond: (t.ctid = t1.ctid)
         ->  Sort  (cost=129833.49..132308.49 rows=990000 width=10)
               Sort Key: t.ctid
               ->  Seq Scan on t  (cost=0.00..14325.00 rows=990000 width=10)  -- 这个NODE非常慢
         ->  Sort  (cost=0.37..0.39 rows=10 width=36)
               Sort Key: t1.ctid
               ->  CTE Scan on t1  (cost=0.00..0.20 rows=10 width=36)
(13 rows)

这应该也是可以优化的地方,已反馈给社区。

函数+游标实现

do language plpgsql 
$$

declare 
  rowvar record;
 cur cursor for select * from t where id between 1 and 1000000 limit 10;
begin
  open cur; 
  loop 
    fetch cur into rowvar;
    if found then 
      update t set info='new' where current of cur returning * into rowvar; raise notice '%', rowvar;
    else
      return;
    end if;
  end loop; 
  close cur;
end;

$$
;

NOTICE:  00000: (10011,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10012,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10013,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10014,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10015,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10016,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10017,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10018,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10019,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
NOTICE:  00000: (10020,new)
LOCATION:  exec_stmt_raise, pl_exec.c:3216
DO

期待阿里云PostgreSQL扩展支持这个MySQL语法。

<think>好的,我现在需要帮用户解答如何用泊松分布模型预测拉齐奥和比尔森的比赛结果。首先,我得回忆一下泊松分布的基本概念和应用场景。泊松分布常用于模拟在固定时间或空间内,事件发生的次数的概率,比如足球比赛中的进球数。 用户提到参考了联合概率的计算,即边际概率相乘得到联合概率。这可能指的是两队各自进球数的独立概率,然后将它们结合起来预测比赛结果。所以,我需要分步骤说明如何计算两队的平均进球数,应用泊松分布公式,然后生成所有可能的比分组合及其概率。 首先,收集历史数据是关键。用户需要拉齐奥和比尔森的平均进球数和失球数。例如,拉齐奥场均进1.8球,失0.9球,而比尔森进1.5球,失1.2球。这些数据可能需要用户自行查找最新的赛季数据,或者使用某个数据库的信息。这里可能需要提醒用户确保数据来源的准确性。 接下来,确定每队的进攻和防守强度。比如,拉齐奥的攻击力可能高于联赛平均水平,而比尔森的防守可能较弱。这一步可能需要计算联赛平均进球数,然后调整各队的进攻和防守参数。例如,如果联赛平均每场进1.2球,拉齐奥的进攻强度是1.8/1.2=1.5,而比尔森的防守强度是0.9/1.2=0.75。这样调整后的预期进球数就是进攻强度乘以对手的防守强度再乘以联赛平均。不过,用户提供的示例中可能简化了这一步,直接使用平均进球数作为λ参数,所以可能不需要复杂的调整,除非用户有更详细的数据。 然后,应用泊松分布公式计算各队进球数的概率。例如,拉齐奥的λ=1.8,比尔森的λ=1.5。对于每个可能的进球数k(0,1,2,…),计算P(k) = (λ^k * e^{-λ}) / k!。需要提醒用户选择合理的最大进球数,比如最多5个球,因为更高的情况概率很低。 之后,计算联合概率。例如,拉齐奥进2球,比尔森进1球的概率是P(2)*P(1) = [ (1.8² e^{-1.8}) / 2! ] * [ (1.5¹ e^{-1.5}) / 1! ]。将所有可能的组合计算出来,然后可以汇总出胜负平的概率,或者具体比分的概率。 用户可能需要知道如何具体实施这些计算,比如使用Excel或编程语言。例如,Python中的scipy.stats有泊松分布的函数,可以方便地计算概率。此外,生成所有可能的比分组合可能需要嵌套循环,遍历主队和客队的进球数,然后计算联合概率。 另外,需要注意泊松分布的假设条件,比如事件独立、发生率恒定等。足球比赛中可能存在其他因素,比如主场优势、伤病情况、战术变化等,这些可能影响预测的准确性。因此,提醒用户泊松分布的局限性,可能需要结合其他因素进行修正。 在用户提供的示例中,引用了联合概率的计算,所以需要确保在步骤3中明确提到这点,并在适当的位置添加引用标记。例如,在计算联合概率时,说明这是根据边际概率相乘得到的,引用用户提供的资料。 最后,用户可能需要知道如何应用这些概率来预测结果。比如,比较主胜、平局、客胜的概率,或者找出最可能的比分。此外,可能还需要进行验证,比如与实际比赛结果对比,或者使用历史数据进行回测,评估模型的准确性。 总结步骤:收集数据→确定λ参数→计算单队进球概率→计算联合概率→汇总结果→分析预测。过程中需要确保每个步骤清晰,并且提醒用户注意事项,如数据准确性、假设条件等。</think>要使用泊松分布模型预测拉齐奥与比尔森的比赛结果,需按以下步骤进行: --- ### 1. **数据收集与参数设定** - **历史数据**:获取两队近期比赛的场均进球数和失球数。例如: - 拉齐奥场均进球 $λ_{\text{攻击}}=1.8$,失球 $λ_{\text{防守}}=0.9$; - 比尔森场均进球 $μ_{\text{攻击}}=1.5$,失球 $μ_{\text{防守}}=1.2$。 - **计算预期进球数**: - 拉齐奥本场预期进球 $λ = λ_{\text{攻击}} \times μ_{\text{防守}} / \text{联赛平均失球}$(若联赛平均失球为1.0,则 $λ=1.8 \times 1.2=2.16$); - 比尔森预期进球 $μ = μ_{\text{攻击}} \times λ_{\text{防守}} / \text{联赛平均失球}=1.5 \times 0.9=1.35$[^1]。 --- ### 2. **应用泊松分布公式** 泊松分布概率公式为: $$P(k) = \frac{λ^k e^{-λ}}{k!}$$ - **拉齐奥进球概率计算**($λ=2.16$): - $P(0)=2.16^0 e^{-2.16}/0! ≈ 0.115$ - $P(1)=2.16^1 e^{-2.16}/1! ≈ 0.249$ - 以此类推至 $k=5$。 - **比尔森进球概率计算**($μ=1.35$): - $P(0)=1.35^0 e^{-1.35}/0! ≈ 0.259$ - $P(1)=1.35^1 e^{-1.35}/1! ≈ 0.350$ - 以此类推至 $k=5$。 --- ### 3. **联合概率计算** 对两队的进球数组合计算联合概率,例如拉齐奥进2球且比尔森进1球的概率为: $$P(2,1)=P_{\text{拉齐奥}}(2) \times P_{\text{比尔森}}(1) ≈ 0.216 \times 0.350 ≈ 0.0756$$ 将所有组合的概率相加,可得出比赛结果的分布: - **拉齐奥胜的概率**:所有满足 $k_{\text{拉齐奥}} > k_{\text{比尔森}}$ 的组合概率之和; - **平局的概率**:所有 $k_{\text{拉齐奥}} = k_{\text{比尔森}}$ 的组合概率之和; - **比尔森胜的概率**:所有 $k_{\text{拉齐奥}} < k_{\text{比尔森}}$ 的组合概率之和。 --- ### 4. **结果分析与优化** - **预测示例**:若计算得出拉齐奥胜的概率为48%,平局25%,比尔森胜27%,则可预测拉齐奥更可能获胜。 - **模型优化**:考虑主场优势、伤病情况等修正参数,或使用加权泊松分布提高准确性。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值