是否可以跨两个表的列具有数据库唯一性约束?

时间:2014-05-30 11:04:49

标签: ruby-on-rails mysql2

我有一个带有rails的mysql数据库,还有一个“简写”(字符串)列,我想在多个表中使其独一无二。有没有办法在不制作第三张桌子的情况下做到这一点?

Expression
id
shorthand
... 
etc

Variable
id
shorthand
...
etc

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

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

2 个答案:

答案 0 :(得分:0)

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

您可能还想使用与文章中的CREATE CONSTRAINT TRIGGER功能类似的功能调查postgres check_nojoin()

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

获得所需的确切SQL后,可以使用execute "the required SQL"

将其放入rails迁移中

另一种方法是使用第三个表'短号'用列'简写'和' src'。将速记定义为该表上的唯一主键。在其他两个表格的每一个上定义“src'作为一个单一的char字段默认为' A'和' B'在每张桌子上分别。在两个表中的每一个表上添加一个外键约束,包括'简写'和' src'并引用表'短号'。在两个表中的任何一个表中插入或更新行时,您需要确保'缩写'表格作为交易的一部分或通过触发器明确更新,并设置'简写'和' src'到相应的表格即“A'或者' B'。

外键约束的作用是确保速记值存在于相应src表的速记表中,但由于仅对“ ”速记'的唯一性约束。如果另一个表已经定义了速记值,那么速记表中的列将发生密钥违规,从而保证两个(甚至更多)表的唯一性。

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

答案 1 :(得分:0)

这是一个使用第三个表的例子:

        -- 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;