检查SQLAlchemy中互斥列的约束

Bri*_*ach 15 python mysql sqlalchemy

如果我有一个SQLAlchemy声明模型如下所示:

class Test(Model):
    __tablename__ = 'tests'

    id = Column(Integer, Sequence('test_id_seq'), primary_key=True)
    ...
    Atest_id = Column(Integer, ForeignKey('Atests.id'), nullable=True)
    Btest_id = Column(Integer, ForeignKey('Btests.id'), nullable=True)
    Ctest_id = Column(Integer, ForeignKey('Ctests.id'), nullable=True)
    Dtest_id = Column(Integer, ForeignKey('Dtests.id'), nullable=True)
    Etest_id = Column(Integer, ForeignKey('Etests.id'), nullable=True)
    ...
    date = Column(DateTime)
    status = Column(String(20))  # pass, fail, needs_review
Run Code Online (Sandbox Code Playgroud)

我想确保*test_id在给定的行中只有一个外键存在,我该如何实现SQLAlchemy呢?

我看到有一个SQLAlchemy CheckConstraint对象(参见docs),但MySQL不支持检查约束.

数据模型之外有交互SQLAlchemy,因此最好是数据库级别的检查(MySQL)

iur*_*niz 6

好吧,考虑到你的要求"数据模型在SQLAlchemy之外有交互,所以最好是数据库级别检查(MySQL)""确保只有一个[..]不为空".我认为最好的方法是编写一个这样的触发器:

DELIMITER $$
CREATE TRIGGER check_null_insert BEFORE INSERT 
ON my_table
FOR EACH ROW BEGIN
IF CHAR_LENGTH(CONCAT_WS('', NEW.a-NEW.a, NEW.b-NEW.b, NEW.c-NEW.c)) = 1 THEN 
    UPDATE `Error: Only one value of *test_id must be not null` SET z=0;
END IF;
END$$
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

一些技巧和考虑因素:

  1. 如果声明:为了避免繁琐的检查写入,每列都不为空,而其他列为空,我做了这个技巧:将每列减少到一个字符并检查存在多少个字符.请注意,NEW.a-NEW.a如果NEW.a是a Integer,则始终返回1个字符,NULL返回0个字符,并且操作在MySQL上NULL-NULL返回NULL.

  2. ERROR TRIGGERING:我想你想提出一个错误,那么如何在MySQL上做这个呢?你没有提到MySQL版本.仅在MySQL 5.5上,您可以使用SIGNAL语法引发异常.因此,更便携的方式是发出一个无效的语句,如:UPDATE xx SET z=0.如果您使用的是MySQL 5.5,则可以使用:signal sqlstate '45000' set message_text = 'Error: Only one value of *test_id must be not null';而不是UPDATE `Error: Only one value of *test_id must be not null` SET z=0;

此外,我想你也想在更新上检查这个,所以使用:

DELIMITER $$
CREATE TRIGGER check_null_update BEFORE UPDATE 
ON my_table
FOR EACH ROW BEGIN
IF CHAR_LENGTH(CONCAT_WS('', NEW.a-NEW.a, NEW.b-NEW.b, NEW.c-NEW.c)) = 1 THEN 
    UPDATE `Error: Only one value of *test_id must be not null` SET z=0;
END IF;
END$$
DELIMITER ;
Run Code Online (Sandbox Code Playgroud)

或者创建一个存储过程并调用它.

更新

对于支持检查约束的数据库,代码更简单,请参阅SQL Server的此示例:

CREATE TABLE MyTable (col1 INT NULL, col2 INT NULL, col3 INT NULL);
GO

ALTER TABLE MyTable
ADD CONSTRAINT CheckOnlyOneColumnIsNull
CHECK (
  LEN(CONCAT(col1-col1, col2-col2, col3-col3)) = 1
)
GO
Run Code Online (Sandbox Code Playgroud)