如何有条件地更新SQL表?

如何有条件地更新SQL表?,sql,postgresql,Sql,Postgresql,我有两个这样的数据库表: CREATE TABLE IF NOT EXISTS accounts ( id INT PRIMARY KEY NOT NULL, balance INT NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS transactions ( from INT FOREIGN KEY REFERENCES accounts(id), to INT FOREIGN KEY

我有两个这样的数据库表:

CREATE TABLE IF NOT EXISTS accounts (
  id        INT PRIMARY KEY NOT NULL,
  balance   INT NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS transactions (
  from      INT FOREIGN KEY REFERENCES accounts(id),
  to        INT FOREIGN KEY REFERENCES accounts(id),
  amount    INT NOT NULL
)
在我的应用程序逻辑中,每当一个新的事务请求出现时,我想首先检查from帐户是否有足够的余额,然后我想从from帐户中减去金额,将金额添加到to帐户,并将新行添加到transactions表中

然而,我想以原子方式完成所有这些工作,原因有二:

  • 防止比赛条件。如果有两个请求将5美元从帐户1转移到帐户2,而帐户1总共只有5美元,我想防止余额的双重支出。因此,对足够余额的检查和随后的余额更新必须以原子方式进行
  • 防止应用程序崩溃时出现不一致状态
是否可以在单个SQL语句中执行此操作?假设我们正在使用Postgres

例如,我知道我可以做
更新账户集balance=balance-6,其中id=1,balance>6
检查账户是否有足够的余额,同时更新余额

我也听说过一种叫做
select。。。用于更新
,但我不确定这是否对我有帮助


此外,查询的结果应该表明事务是否成功,因此如果不成功,我可以向客户端显示一些错误消息。

解决方案不是将所有内容都塞进一条SQL语句中。除了复杂之外,它不一定能保护你不受比赛条件的影响

使用数据库事务。若要避免异常,请使用
SELECT。。。用于针对并发修改进行更新,或者使用
可重复读取
隔离级别,并在出现序列化错误时重复事务

为了保护自己免于死锁,请确保始终首先使用较低的
id
锁定帐户

下面是一个伪代码示例:

EXEC SQL START TRANSACTION;

if (id1 < id2) {
    EXEC SQL SELECT balance INTO :bal1 FROM accounts
         WHERE id = :id1 FOR UPDATE;
    EXEC SQL SELECT balance INTO :bal2 FROM accounts
         WHERE id = :id2 FOR UPDATE;
} else {
    EXEC SQL SELECT balance INTO :bal2 FROM accounts
         WHERE id = :id2 FOR UPDATE;
    EXEC SQL SELECT balance INTO :bal1 FROM accounts
         WHERE id = :id1 FOR UPDATE;
}

if (bal1 < amount) {
    EXEC SQL ROLLBACK;
    throw_error('too little money on account');
}

EXEC SQL UPDATE accounts SET balance = :bal1 - :amount WHERE id = :id1;
EXEC SQL UPDATE accounts SET balance = :bal1 + :amount WHERE id = :id2;
EXEC SQL INSERT INTO transaction ("from", "to", amount)
    VALUES (:id1, :id2, :amount);

EXEC SQL COMMIT;
execsql启动事务;
如果(id1
这有帮助吗?假设我们正在使用PostGres。如果是这样的话,那么标记[postgresql]和untag[mysql]。@kmoser不是真的,这只是展示了如何使用事务。这如何解决我的问题?在添加到to之前,我仍然需要有一些方法来检查余额是否足够account@shmth事务允许您将多个SQL语句视为一个原子操作。这不是你想做的吗?@kmoser,但我也想阻止比赛条件,就像我在问题中提到的那样。由于两个事务可以交叉进行,因此事务处理对此没有帮助。谢谢,下面有几个问题:1。在继续更新表之前,如何首先检查第一个用户的余额是否足够?2.sql查询是否返回一些状态,以便我知道操作是否实际成功?1。运行
选择
。2.如果语句失败,它总是会导致错误。您能提供SQL语句的示例吗?我还是不明白我该怎么做。在伪代码中,我需要类似这样的代码:``从id=I的帐户中选择*;如果余额>6:update accounts set balance=balance-6其中id=1 update accounts set balance=balance+6其中id=2插入到事务1、2、6``我添加了一些伪代码。另一个选项是不检查代码中是否有足够的余额。而是在列上放置一个检查约束。So:更改表科目添加约束余额0检查(余额>=0);