【问题标题】:Trigger raises message when it's not supposed to触发器在不应该发出消息时引发消息
【发布时间】:2013-10-15 16:27:04
【问题描述】:

我有一个附加到表的触发器,当某些列组合不唯一时应该引发异常。

CREATE TRIGGER uniqueness BEFORE INSERT ON a
  BEGIN
    SELECT RAISE(FAIL, 'Record is not unique !')
    FROM a
    WHERE ((NEW.st <> 1) 
      AND (p = NEW.p)
      AND ((name = NEW.name) OR (safe_name = NEW.safe_name)));
  END;

但它似乎有时会在不必要的时候被触发。

例如,如果 safe_name 是 foo5,并且存在具有 safe_name foo-5 的现有记录。但是foo5 != foo-5 那么为什么会这样呢?

================================================ ===============

好吧,我知道为什么会这样了。这是您在 PHP 中重现它的方法:

$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);      
$pdo->sqliteCreateCollation('NS', 'strnatcasecmp');      

$pdo->query('CREATE TABLE a(
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   st INTEGER NOT NULL,
   p  INTEGER NOT NULL,
   name TEXT NOT NULL COLLATE NS,
   safe_name NOT NULL COLLATE NS)');

$pdo->query('CREATE TRIGGER uniqueness BEFORE INSERT ON a
  BEGIN
    SELECT RAISE(FAIL, "Record is not unique !")
    FROM a
    WHERE ((NEW.st <> 1) 
      AND (p = NEW.p)
      AND ((name = NEW.name) OR (safe_name = NEW.safe_name)));
  END;');

// success
$pdo->query('INSERT INTO a(st, p, name, safe_name) VALUES(2, 2, "Foo 5", "foo-5")');

// fail :(
$pdo->query('INSERT INTO a(st, p, name, safe_name) VALUES(2, 2, "foo  5", "foo5")');

当排序规则处于活动状态时,Foo 5 名称显然等于 foo 5

如果我删除 sqliteCreateCollation 调用和 COLLATE NS 语句,它会起作用:/

是否有任何解决方案可以让我保留整理功能?我只需要它来订购,但显然它也用于比较......

【问题讨论】:

  • 你能发布表格定义和一些意外抛出的示例数据吗?
  • 您是否尝试过明确命名列如a.safe_name = NEW.safe_name
  • 我的猜测是SELECT中的safe_name以外的条件是匹配的。
  • 我添加了更多细节..

标签: php sql sqlite pdo triggers


【解决方案1】:

您应该能够保留排序规则,但仅将其切换为二进制以用于触发器查询。

$pdo = new PDO('sqlite::memory:');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);      
$pdo->sqliteCreateCollation('NS', 'strnatcasecmp');      

$pdo->query('CREATE TABLE a(
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   st INTEGER NOT NULL,
   p  INTEGER NOT NULL,
   name TEXT NOT NULL COLLATE NS,
   safe_name NOT NULL COLLATE NS)');

$pdo->query('CREATE TRIGGER uniqueness BEFORE INSERT ON a
  BEGIN
    SELECT RAISE(FAIL, "Record is not unique !")
    FROM a
    WHERE ((NEW.st <> 1) 
      AND (p = NEW.p)
      AND ((name COLLATE BINARY = NEW.name) OR (safe_name COLLATE BINARY = NEW.safe_name)));
  END;');

// success
$pdo->query('INSERT INTO a(st, p, name, safe_name) VALUES(2, 2, "Foo 5", "foo-5")');

// should work now
$pdo->query('INSERT INTO a(st, p, name, safe_name) VALUES(2, 2, "foo  5", "foo5")');

【讨论】:

  • COLLATE BINARY 是默认排序规则吗?
  • 谢谢!我从 create table 中删除了 COLLATE 语句,并将 COLLATE NS 添加到要使用自然排序的查询中的 ORDER BY 语句中。似乎工作
【解决方案2】:

您已专门设置排序规则以进行不区分大小写的比较。尝试将其更改为:

$pdo->sqliteCreateCollation('NS', 'strnatcmp');

看看有没有帮助。

【讨论】:

  • 我不能这样做,因为我需要对排序结果进行不区分大小写的比较。但我不知道为什么 sqlite 将它用于 x=y 的选择。这没有任何意义......
猜你喜欢
  • 2019-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-31
  • 1970-01-01
  • 2016-02-04
  • 1970-01-01
相关资源
最近更新 更多