在检查约束中使用 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 语句的主要内容,如果未能解决你的问题,请参考以下文章