在检查约束中使用 case 语句

Posted

技术标签:

【中文标题】在检查约束中使用 case 语句【英文标题】:Using a case statement in a check constraint 【发布时间】:2015-01-26 20:26:35 【问题描述】:

上周我一直在学习 SQL,但我不确定如何在检查约束中正确添加 case 语句。谁能给我指点?

我有以下成绩表:

CREATE TABLE Grade
(
    salary_grade    char(1) NOT NULL CHECK (salary_grade = UPPER(salary_grade)),
        CONSTRAINT ck_grade_scale CHECK(
        CASE 
            WHEN salary_grade = '[A-D]' 
                THEN salary_scale = 'S1'
            WHEN salary_grade = '[D-G]' 
                THEN salary_scale = 'S2'
        END)

        salary_scale    char(2) DEFAULT 'S1' NOT NULL,

        CONSTRAINT pk_grade PRIMARY KEY (salary_grade),
        CONSTRAINT ck_salary_grade CHECK (REGEXP_LIKE(salary_grade, '[A-G]', 'c')),
        --constraint must be either S1 or S2
        CONSTRAINT ck_salary_scale CHECK (salary_scale IN ('S1', 'S2'))
);

我想检查 salary_grade 是否在 A-D 之间,那么 salary_scale 必须是“S1”,或者如果 salary_grade 在 E-G 之间,那么它是“S2”。

我曾尝试对此进行研究并提出后者,但它不起作用..我的代码结构是否正确?

【问题讨论】:

你有什么错误吗?如果是这样,请更新您的问题,这可能有助于找到问题。 @Hugo Sousa 我在尝试删除时缺少右括号。还说表不存在 我真的是新手,几周前才开始学习,所以请多多包涵 一般来说,你不应该在允许布尔逻辑的地方使用CASE(例如WHERE子句和检查约束)。它的预期用途是允许您在通常不允许的地方使用布尔逻辑(例如SELECT 语句中的列列表)。 @user3414871 你已经有 3 个很好的答案了。您应该通过接受最适合您需求的那一种来给予他们信任。 【参考方案1】:

我认为你可以做到以下几点:

CREATE TABLE Grade
(
  salary_grade    char(1) NOT NULL CHECK (REGEXP_LIKE(salary_grade, '[A-G]', 'c')),
  salary_scale    char(2) DEFAULT 'S1' NOT NULL,
  CONSTRAINT pk_grade PRIMARY KEY (salary_grade),
  CONSTRAINT ck_grade_scale CHECK ( REGEXP_LIKE(salary_grade, '[A-D]', 'c') AND salary_scale = 'S1'
                                 OR REGEXP_LIKE(salary_grade, '[E-G]', 'c') AND salary_scale = 'S2' )
);

Please see SQL Fiddle schema here.

您不需要salary_grade 上的UPPER() 约束,因为正则表达式检查就足够了(您已经在检查以确保它是A 和G 之间的大写字母)。我不认为单独对 salary_scale 的约束是必要的,因为从逻辑上讲,它会包含在最后一个约束中。

更新

以下是您可以使用CASE 声明的方法:

CREATE TABLE Grade
(
  salary_grade    char(1) NOT NULL CHECK (REGEXP_LIKE(salary_grade, '[A-G]', 'c')),
  salary_scale    char(2) DEFAULT 'S1' NOT NULL,  
  CONSTRAINT pk_grade PRIMARY KEY (salary_grade),
  CONSTRAINT ck_grade_scale CHECK ( salary_scale = CASE WHEN REGEXP_LIKE(salary_grade, '[A-D]', 'c') THEN 'S1' ELSE 'S2' END )
);

Please see SQL Fiddle schema here.

【讨论】:

第一次检查salary_grade 也不是真正需要的,因为ck_grade_scale 也涵盖了这一点。 是的,你可能是对的,但是我必须在CASE 中更改我的ELSE。严格来说,正则表达式可能比人们想要的更昂贵; BETWEEN 更好。 @DavidFaber 谢谢老兄.. 你很有帮助。当您刚接触 SQL 时,这是一个很大的学习曲线。我认为只有几个关键字就很容易学习,但事情从来没有看起来那么容易【参考方案2】:

case 必须与某些东西进行比较,这就是为什么您会收到缺少右括号的错误。除非您特别想要 case,否则您可以使用和/或检查组合:

CONSTRAINT ck_grade_scale CHECK(
    (salary_grade BETWEEN 'A' AND 'D' AND salary_scale = 'S1')
    OR (salary_grade BETWEEN 'D' AND 'G' AND salary_scale = 'S2')),

SQL Fiddle demo.

正如 Parado 所说,您不能使用约束有条件地设置列值,而只能限制它们。您可能会使用虚拟列作为比例,但这意味着将查找表的一部分而不是数据放入 DDL,这似乎有点奇怪。

【讨论】:

我已经知道如何做到这一点,如“Oracle SQL 示例”中所述,但本书没有告诉我如何在检查约束中使用案例语句。这就是我想学的 @user3414871:也许那本书没有给你一个这种用法的例子,因为它不是CASE的好用法。【参考方案3】:

Check Constraints 用于在插入之前测试数据,以保护数据结构免受虚假数据的影响。实际上我们在select 语句中使用case您不能将其用于条件插入。如果您想在插入之前更改特定列的数据,您需要使用trigger,或者您也可以使用virtual column,但它有一些限制。

您可以在这里找到更多信息

check constraints triggers virtual columns

【讨论】:

好的。这有帮助.. 谢谢。我的想法是,如果输入等级,那么salary_scale会自动输入。这可能吗? @user3414871 是的,可以使用我上面提到的触发器或虚拟列。 好的.. 我不是想为列分配值,只是想确保列的值是 S1 或 S2。即S1,如果A级 @user3414871 是的,这是 Alex Poole 的解决方案CONSTRAINT ck_grade_scale CHECK((salary_grade BETWEEN 'A' AND 'D' AND salary_scale = 'S1') OR (salary_grade BETWEEN 'D' AND 'G' AND salary_scale = 'S2'))

以上是关于在检查约束中使用 case 语句的主要内容,如果未能解决你的问题,请参考以下文章

oracle sql - 选择具有多个“case when”的语句并检查是不是包含文本

检查 SQL CASE 语句中是不是存在

SQL语句中Case 的用法

SQL语句中case,when,then的用法

为啥在MySQL数据库中建立检查约束不成功呢,语句是这样的

java检查char是不是是switch case语句中的数字[关闭]