强制执行数据库约束:代码vs sql

问题描述:

这是跟进到this question强制执行数据库约束:代码vs sql

这里是我的架构

CREATE TABLE A(
    id serial NOT NULL, 
    date timestamp without time zone, 
    type text, 
    sub_type text, 
    filename text, 
    filepath text, 
    filesize integer, 
    lock_status int 
); 

在这个数据库中,用户可以只要“LOCK_STATUS”没有设置更新类型,子类型,文件名,文件路径,文件大小。

因此,在网页代码(php)中,我可以在更新项目之前检查lock_status。

但是,可能会有其他用户在第一次用户检查&更新之间的时间内更新锁定状态的情况。

那么,有没有在SQL更新行之前检查锁状态的方法?

  • 网页代码是在PHP
  • 数据库PostgreSQL的是

编辑添加型,分型以上

当然可编辑的字段列表,使用UPDATE ... WHERE lock_status = 0。或者,您可以尝试使用stored procedures

您可能想要查看行级锁定。它看起来像Postgres有它。

+0

如果你想锁定一个事务之外的行,那么你就不能,因为他们在交易期间只存在使用Postgres的行级锁。 – 2009-02-04 16:08:24

我建议检查和设置lock_status位在同一时间。

发行UPDATE A SET lock_status = 1 WHERE id = ... AND lock_status = 0。该查询是原子而不需要明确的事务。如果这没有返回1个对象的计数更新您的锁不能应用。然后你只需要确认你的主键仍然存在。如果您要从多个位置和/或多个表中调用它,您可能需要考虑将其移至存储过程。

伪PHP:

$result = pg_query_params($conn, "UPDATE A SET lock_status = 1 WHERE id = $1 AND lock_status = 0", $id); 
$tuples = pg_affected_rows($result); 

if ($tuples < 1) { 
    // couldn't lock 
} else { 
    // lock applied 
} 

最彻底的方法,如土豆头先生说,是简单地使用WHERE子句只影响行,其中LOCK_STATUS = 0。因为这是一个SQL语句,它保证是原子的。然后,您可以查看是否有行受到影响(例如,使用@@ rowcount)并作出相应的反应,可以通过无限期地尝试或显示错误消息等。

两步检查和更新的问题除非你将它们包装在一个明确的事务中(例如“BEGIN TRANSACTION ... COMMIT TRANSACTION”),它们是而不是保证是原子的,所以理论上你可以获得多个进程谁认为锁已关闭并继续。我说“理论上”,因为这些语句执行的速度非常快,除非你有一个同时有数千个用户在同一时间敲打这个东西的巨大并发环境,否则就不可能发生。这就是为什么像这样的错误经常被忽视,但随后出现奇怪的不明原因的错误。

要了解有关此类问题的更多信息,您可能需要阅读关于并发编程的书籍,例如:Concurrent Programming by Gregory Andrews

只是一个触发器和PL/pgSQL函数数据库端的单一样品过程:

CREATE OR REPLACE FUNCTION trgfn_ensure_unlocked() RETURNS TRIGGER AS $trig$ 
    DECLARE 
    BEGIN 
    IF (OLD.lock_status <> 0) THEN 
     RAISE EXCEPTION 'Row is locked'; 
    END IF; 
    RETURN NEW; 
    END; 
$trig$ LANGUAGE plpgsql; 

CREATE TRIGGER trg_check_unlocked BEFORE UPDATE ON table_name 
     FOR EACH ROW EXECUTE PROCEDURE trgfn_ensure_unlocked(); 

基本上,你PLPGSQL功能,将检查在旧(更新之前的版本),该行,lock_status为0.如果不是,则会引发异常。

该函数由触发器调用,该函数在对此表执行SQL UPDATE时将自动调用。

希望这有助于...