【问题标题】:Is it possible to have a DB uniqueness constraint across columns of two tables?是否可以跨两个表的列进行数据库唯一性约束?
【发布时间】:2014-05-30 11:04:49
【问题描述】:

我有一个带有 rails 的 mysql 数据库,以及一个我想在多个表中创建唯一的列“速记”(字符串)。有没有办法在不制作第三张桌子的情况下做到这一点?

Expression
id
shorthand
... 
etc

Variable
id
shorthand
...
etc

我希望两个表的“速记”列中的值在彼此之间是唯一的,即。如果数据库中已经存在速记值为“xyz”的变量,则表达式中的记录速记值“xyz”将被拒绝。

任何想法表示赞赏,甚至“你必须使用第三张桌子”:)

【问题讨论】:

  • 我认为,如果没有第三张桌子,您将无法做到这一点。
  • 基于触发器的约束可以做到。
  • 把它们放在同一个表中,或者使用第三个表加触发器。或者在NOT EXISTS 上使用相互触发组件。

标签: ruby-on-rails mysql2


【解决方案1】:

这里是使用第三个表的示例:

        -- TEMP SCHEMA for testing
DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

CREATE TABLE shorthand
        ( shorthand varchar NOT NULL PRIMARY KEY
        , one_or_two varchar NOT NULL
        );

CREATE TABLE table_one
        ( one_id INTEGER NOT NULL PRIMARY KEY
        , shorthand varchar NOT NULL REFERENCES shorthand(shorthand)
                                ON DELETE CASCADE ON UPDATE CASCADE
                                DEFERRABLE INITIALLY DEFERRED
        , etc_one varchar
        );

CREATE TABLE table_two
        ( two_id INTEGER NOT NULL PRIMARY KEY
        , shorthand varchar NOT NULL REFERENCES shorthand(shorthand)
                                ON DELETE CASCADE ON UPDATE CASCADE
                                DEFERRABLE INITIALLY DEFERRED
        , etc_two varchar
        );

        -- Trigger function for BOTH tables
CREATE FUNCTION set_one_or_two( ) RETURNS TRIGGER
AS $func$
        BEGIN
        IF (TG_OP = 'INSERT') THEN
                INSERT INTO shorthand (shorthand, one_or_two)
                VALUES(new.shorthand, TG_TABLE_NAME)
                        ;
        ELSEIF (TG_OP = 'UPDATE') THEN
                UPDATE shorthand SET shorthand = new.shorthand
                WHERE shorthand = old.shorthand
                        ;
        ELSEIF (TG_OP = 'DELETE') THEN
                DELETE FROM shorthand
                WHERE shorthand = old.shorthand
                        ;
        END IF;
        RETURN NULL;
        END
$func$ LANGUAGE plpgsql
        ;
        -- Triggers for I/U/D
CREATE CONSTRAINT TRIGGER check_one
    AFTER INSERT OR UPDATE OR DELETE
    ON table_one
    FOR EACH ROW
    EXECUTE PROCEDURE set_one_or_two ( )
        ;

CREATE CONSTRAINT TRIGGER check_two
    AFTER INSERT OR UPDATE OR DELETE
    ON table_two
    FOR EACH ROW
    EXECUTE PROCEDURE set_one_or_two ( )
        ;

        -- Some tests (incomplete)
INSERT INTO table_one (one_id,shorthand,etc_one) VALUES (1, 'one' , 'one' );
INSERT INTO table_two (two_id,shorthand,etc_two) VALUES (1, 'two' , 'two' );

SELECT * FROM shorthand;

\echo this should fail
INSERT INTO table_one (one_id,shorthand,etc_one) VALUES (11, 'two' , 'eleven' );

SELECT * FROM shorthand;

UPDATE table_one SET shorthand = 'eleven' WHERE one_id = 1;
SELECT * FROM shorthand;

【讨论】:

    【解决方案2】:

    我认为这篇较旧的文章完全符合您的要求(模拟多表约束): http://classes.soe.ucsc.edu/cmps180/Winter04/constraints.html

    您可能还想使用类似于文章中的 check_nojoin() 函数的函数来研究 postgres CREATE CONSTRAINT TRIGGER

    http://www.postgresql.org/docs/9.0/static/sql-createconstraint.html

    一旦您有了所需的确切 SQL,您就可以使用 execute "the required SQL" 将其放入您的 Rails 迁移中

    另一种方法是使用第三个表“shorthands”,其中包含“shorthand”和“src”列。将速记定义为该表上的唯一主键。在您的其他两个表中的每一个上,将“src”定义为单个字符字段,在每个表上分别默认为“A”和“B”。在包含“shorthand”和“src”以及引用表“shorthands”的两个表中的每一个上添加一个外键约束。在两个表中的任何一个中插入或更新行时,您需要确保“速记”表作为事务的一部分或通过触发器显式更新,并将“速记”和“src”设置为相应的表,即“ A' 或'B'。

    外键约束的作用是确保速记值存在于各个 src 表的速记表中,但是如果另一个表已经定义了速记表中的“速记”列,则存在唯一性约束速记值会发生键违规,从而保证两个(甚至更多)表的唯一性。

    无论您做什么,最好将参照完整性放入数据库中,而不是在 orm/active 记录验证中。

    【讨论】:

      猜你喜欢
      • 2013-01-27
      • 1970-01-01
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      相关资源
      最近更新 更多