如何创建检查条件来检查日期是否为特定日期,然后收入必须 > x?

How to create a check condition that checks for if date is a specific date then income has to be > x?

我想弄清楚如何在 CREATE TABLE 之外编写和添加一般约束,其中当 payDate 是 New Years 2010 (01-01-2010) 时,不同员工的收入必须超过 100,000 美元。该日期的某些收入数据为 NULL,但我只是想查找大于 $100k 的收入。

我正在合作的table:

Employees | Date     | Income
John      |12-01-2009| 50000
Jake      |12-01-2009| 70000  
Jill      |12-21-2009| 85000.75
Jonn      |12-27-2009| 120000.5
June      |01-01-2010| 100000.25
Joey      |01-01-2010| \N
Jack      |01-01-2010| 120000
Jane      |01-01-2010| 110000.75
Jean      |01-01-2010| \N
Joon      |01-01-2010| \N  

我试过:

ALTER TABLE Employees
ADD CONSTRAINT nyIncome
CHECK(payDate = DATE '2010-01-01' AND income > 100000 AND income IS NOT NULL);

这给了我:

"ERROR: check constraint nyIncome is violated by some row"

我如何修改我的支票以满足条件?

编辑:我的初始约束不起作用的原因是 table 中还有其他未在条件中指定的日期。

乔纳斯提供的解决方案:

ALTER TABLE Employees ADD CONSTRAINT nyIncome CHECK (payDate != '2010-01-01' OR (payDate = '2010-01-01' AND (income > 100000 OR income IS NULL)));

此解决方案将遍历所有 payDates 并创建一个特定条件,其中日期“2010-01-01”的收入必须高于 100000,例如,日期为“2010-01-”的元组01' 和 80,000 的收入将不满足此限制条件,因此无法插入,或者无法将日期为 '2010-01-01' 的收入更新为小于 100,000 的收入。

如果您的 table 中已有一条或多条记录未通过检查约束,则会显示您所看到的确切错误消息。要查找此类记录,您可以使用以下查询:

SELECT *
FROM Employees
WHERE payDate <> '2010-01-01' OR income > 100000 OR income IS NOT NULL;

其实你的check constraint很具体,在我看来大部分数据都匹配不上。也许您打算为此检查约束的 negative:

ALTER TABLE Employees
ADD CONSTRAINT nyIncome
CHECK (payDate <> '2010-01-01' OR income > 100000 OR income IS NOT NULL);

当某些条目不满足约束条件时,您无法对现有 table 创建约束。 如果您确实需要检查所有这些条件,则必须在创建约束之前更新已经存在的条目以确保所有行都满足条件。 或者,如果这不是预期的,您需要创建一个插入前触发器而不是检查是否允许新条目的约束。如果您创建了这样的触发器,您还应该检查插入之前是否足够或更新之前是否应该完成。 在您的情况下,您的约束条件似乎不正确。正确的语法(根据您的描述)可能是:

ALTER TABLE Employees
ADD CONSTRAINT nyIncome
CHECK (payDate = '2010-01-01' AND (income > 100000 OR income IS NULL));

如果您不确定约束的正确条件,请先执行 select,并使用您要根据约束检查的 where 子句的确切条件,看看结果是否是您的全部table。或者还原 where 子句并检查结果是否为空。

更新,因为你编辑了你的问题:你不能创建这样的约束,因为你的 table 中还有其他日期的条目。您可能需要一个触发器来禁止 insert/update 收入 <= 100000 且日期为“2010-01-01”的条目。 或者,如果这些限制应仅应用于查询,则只需在查询中创建精确的 where 子句而不是触发器。

when the payDate is New Years 2010 (01-01-2010), then the income of different employees must be more than 0,000. Some of the income data on that date is NULL,

其他人已经指出,您不能对 (paydate = 2010-01-01 then income is not null) 使用检查约束,因为有些行违反了条件。
因此,以下答案适用于以下设置约束:if paydate = 2010-01-01 then income = null or income > 100000.

通过检查约束。含义检查当前行和传入行。如果不符合标准,则将调用错误。

ALTER TABLE emp
    ADD CONSTRAINT nyIncome CHECK (
        (payDate = DATE '2010-01-01' AND income > 100000)
    OR (payDate = DATE '2010-01-01' AND income IS NULL)
    OR (payDate <> DATE '2010-01-01'));
drop trigger trg_special_date on emp;

通过触发器。显然触发器不验证现有行。

CREATE OR REPLACE FUNCTION trg_special_date ()
    RETURNS TRIGGER
    AS $$
BEGIN
    IF NEW.paydate = '2010-01-01' THEN
        IF NEW.income IS NULL THEN
            RETURN new;
        elsif NEW.income > 100000 THEN
            RETURN new;
        ELSE
            RETURN NULL;
        END IF;
    ELSE
        RETURN new;
    END IF;
END;
$$
LANGUAGE plpgsql;

CREATE TRIGGER trg_special_date
    BEFORE INSERT OR UPDATE ON emp FOR EACH ROW
    EXECUTE PROCEDURE trg_special_date ();

注意:此触发器将简单地忽略不符合条件的行,您可以对其进行自定义,因此如果传入的行不符合条件,则会引发错误。

另一种方法是行级安全性,有点难。我不确定这是正确的方法。

通过可更新视图:

SET ROLE alice;

CREATE OR REPLACE VIEW constraint_view (WITH security_barrier = true)  AS
SELECT
    *
FROM
    emp
WHERE (paydate = '2010-01-01'
    AND income > 10000)
    OR (payDate = DATE '2010-01-01'
        AND income IS NULL)
    OR (payDate <> DATE '2010-01-01'
)
    WITH cascaded CHECK option;

GRANT SELECT , UPDATE , INSERT ON constraint_view TO bob;

SET ROLE bob;

INSERT INTO constraint_view
    VALUES ('hello' , '2010-01-01' , 11)
RETURNING
    *;
--will fail.

INSERT INTO constraint_view
    VALUES ('hello' , '2010-01-01' , 100001)
RETURNING
    *;    
--will ok.

UPDATE
    constraint_view
SET
    income = 11
WHERE
    name = 'Jane'
RETURNING
    *;--will fail.

TABLE emp;
--ERROR:  permission denied for table emp