当所有其他列都相等时,对具有不同标签的值的约束
Constraint for values with different labels when all other columns are equal
我想创建一个约束来验证在 'stats_type' 列中分类为 "minimum" 的值(来自 'nominal_value' 列)是否等于或小于分类的值如 "average",当其他列中的所有值都相等时。换句话说,给定相应的元组,除了列 'oid'、'stats_type' 和 'nominal_value',我想确保标记为 "minimum" 的值始终等于或小于标记为 "average".
的值
很难解释,所以我做了下面的例子:
CREATE TABLE price (
oid SERIAL NOT NULL,
product INTEGER NOT NULL,
territory INTEGER NOT NULL,
stats_type INTEGER NOT NULL,
year INTEGER NOT NULL,
nominal_value NUMERIC(6,2) NOT NULL,
data_source INTEGER NOT NULL,
CONSTRAINT pk_price PRIMARY KEY (oid),
CONSTRAINT price_1 UNIQUE (product, territory, stats_type, year, data_source),
CONSTRAINT price_2 CHECK (nominal_value > 0)
);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (1, 55, 5611, 1, 2014, 120, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (2, 55, 5611, 2, 2014, 160, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (3, 55, 5615, 1, 2014, 60, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (4, 55, 5611, 3, 2014, 180, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (5, 62, 5615, 1, 2013, 1500, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (6, 62, 5615, 2, 2013, 1300, 3);
'stats_type' 个标签是:1 = 最小值,2 = 平均值,3 = 最大值。
查看前两行;它们是相等的,除了 'oid'、'stats_type' 和 'nominal_value';所以我想验证:120(第一行标记为最小值)是否小于或等于 160(标记为平均值的值)?本例答案是肯定的,所以通过了验证。
现在,最后两行(5 和 6)也有关于列 'product'、'territory'、'year' 和 'data_source' 的匹配项。但是,碰巧第5行的标称值1500不应该小于第6行的1300(因为1500应该是最小值,而1300是平均值)。
我该怎么做?是否可以使用 'check' 约束来完成此类任务?
考虑对 table 上的所有 inserts/updates(或更新插入)使用 postgresql 函数。然后您可以使用案例逻辑来识别可以进入的内容,并为不正确的输入数据事件指定真实的 API 状态 return 代码。您可以用 PL/pgSQL 或 python 或您熟悉的任何其他可扩展语言编写它table。
例如
CREATE OR REPLACE FUNCTION band.usp_band_add_update(
i_sess TEXT,
i_ban_name TEXT,
i_ban_email TEXT,
i_ban_description TEXT,
i_geo_id BIGINT
)
RETURNS TABLE(
band_id BIGINT,
status_id INT,
status_desc TEXT
)
LANGUAGE plpgsql
AS $$
DECLARE
_user BIGINT;
_ban_id BIGINT;
_status_id INT;
_status_desc TEXT;
_pass TEXT;
BEGIN
SELECT asr_user
INTO _user
FROM sess.usp_sess_check(i_sess);
IF _user IS NOT NULL THEN
SELECT ban_id
FROM band.band
WHERE ban_user = _user
INTO _ban_id;
IF _ban_id IS NOT NULL THEN
UPDATE band.band
SET ban_name = i_ban_name,
ban_description = i_ban_description,
ban_geo_id = i_geo_id,
ban_modified = now()
WHERE ban_user = _user;
_status_id = 200;
_status_desc = 'ban_id updated: ' || _ban_id::text;
ELSE
INSERT INTO band.band (ban_user, ban_name, ban_email, ban_description, ban_geo_id)
VALUES (_user, i_ban_name, i_ban_email, i_ban_description, i_geo_id)
RETURNING ban_id INTO _ban_id;
_status_id = 200;
_status_desc = 'ban_id inserted: ' || _ban_id::text;
END IF;
ELSE
_status_id = 402;
_status_desc = 'User does not exist.';
END IF;
RETURN QUERY SELECT _ban_id, _status_id, _status_desc;
END;
$$;
由于你的场景复杂,可以任意顺序插入,建议你事先在二级table中进行插入,比如price_check.
然后你可以放置一个触发器来执行 SQL 例如:
SELECT pmin.*
FROM price_check AS pmin
JOIN price_check AS pavg
ON (pmin.product, pmin.territory, pmin.year, pmin.data_source) = (pavg.product, pavg.territory, pavg.year, pavg.data_source)
AND pmin.stats_type = 1
AND pavg.stats_type = 2
AND pmin.nominal_value <= pavg.nominal_value
这将 return 仅有效最小值,并使用它在最终 table 上执行插入。请注意,我使用的是 INNER JOIN,因此没有平均对的最小值不会 return,您必须使用 LEFT JOIN 才能做到这一点。
同样,您可以在 price_check 上设计查询,将无效行放入 price_log table 中,以便稍后手动检查它们。
或者,您可以在 table 上放置一个 INSTEAD OF INSERT 触发器,而不需要辅助 table,但即便如此,创建一个 LOG table 是一个很好的做法手动检查并修复无效行。
由于需要查询 table,因此无法使用检查约束来完成此操作。您可以使用触发器:
create or replace function price_trigger()
returns trigger language plpgsql as $$
begin
if exists (
select 1
from price p
where (p.product, p.territory, p.year, p.data_source) =
(new.product, new.territory, new.year, new.data_source)
and (
p.stats_type < new.stats_type and p.nominal_value > new.nominal_value
or
p.stats_type > new.stats_type and p.nominal_value < new.nominal_value
)
)
then
raise exception 'Nominal value error';
end if;
return new;
end $$;
create trigger price_trigger
before insert or update on price
for each row
execute procedure price_trigger();
触发函数检查所有条件 (min < avg < max)。
我想创建一个约束来验证在 'stats_type' 列中分类为 "minimum" 的值(来自 'nominal_value' 列)是否等于或小于分类的值如 "average",当其他列中的所有值都相等时。换句话说,给定相应的元组,除了列 'oid'、'stats_type' 和 'nominal_value',我想确保标记为 "minimum" 的值始终等于或小于标记为 "average".
的值很难解释,所以我做了下面的例子:
CREATE TABLE price (
oid SERIAL NOT NULL,
product INTEGER NOT NULL,
territory INTEGER NOT NULL,
stats_type INTEGER NOT NULL,
year INTEGER NOT NULL,
nominal_value NUMERIC(6,2) NOT NULL,
data_source INTEGER NOT NULL,
CONSTRAINT pk_price PRIMARY KEY (oid),
CONSTRAINT price_1 UNIQUE (product, territory, stats_type, year, data_source),
CONSTRAINT price_2 CHECK (nominal_value > 0)
);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (1, 55, 5611, 1, 2014, 120, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (2, 55, 5611, 2, 2014, 160, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (3, 55, 5615, 1, 2014, 60, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (4, 55, 5611, 3, 2014, 180, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (5, 62, 5615, 1, 2013, 1500, 3);
INSERT INTO price (oid, product, territory, stats_type, year, nominal_value, data_source) VALUES (6, 62, 5615, 2, 2013, 1300, 3);
'stats_type' 个标签是:1 = 最小值,2 = 平均值,3 = 最大值。
查看前两行;它们是相等的,除了 'oid'、'stats_type' 和 'nominal_value';所以我想验证:120(第一行标记为最小值)是否小于或等于 160(标记为平均值的值)?本例答案是肯定的,所以通过了验证。
现在,最后两行(5 和 6)也有关于列 'product'、'territory'、'year' 和 'data_source' 的匹配项。但是,碰巧第5行的标称值1500不应该小于第6行的1300(因为1500应该是最小值,而1300是平均值)。
我该怎么做?是否可以使用 'check' 约束来完成此类任务?
考虑对 table 上的所有 inserts/updates(或更新插入)使用 postgresql 函数。然后您可以使用案例逻辑来识别可以进入的内容,并为不正确的输入数据事件指定真实的 API 状态 return 代码。您可以用 PL/pgSQL 或 python 或您熟悉的任何其他可扩展语言编写它table。
例如
CREATE OR REPLACE FUNCTION band.usp_band_add_update(
i_sess TEXT,
i_ban_name TEXT,
i_ban_email TEXT,
i_ban_description TEXT,
i_geo_id BIGINT
)
RETURNS TABLE(
band_id BIGINT,
status_id INT,
status_desc TEXT
)
LANGUAGE plpgsql
AS $$
DECLARE
_user BIGINT;
_ban_id BIGINT;
_status_id INT;
_status_desc TEXT;
_pass TEXT;
BEGIN
SELECT asr_user
INTO _user
FROM sess.usp_sess_check(i_sess);
IF _user IS NOT NULL THEN
SELECT ban_id
FROM band.band
WHERE ban_user = _user
INTO _ban_id;
IF _ban_id IS NOT NULL THEN
UPDATE band.band
SET ban_name = i_ban_name,
ban_description = i_ban_description,
ban_geo_id = i_geo_id,
ban_modified = now()
WHERE ban_user = _user;
_status_id = 200;
_status_desc = 'ban_id updated: ' || _ban_id::text;
ELSE
INSERT INTO band.band (ban_user, ban_name, ban_email, ban_description, ban_geo_id)
VALUES (_user, i_ban_name, i_ban_email, i_ban_description, i_geo_id)
RETURNING ban_id INTO _ban_id;
_status_id = 200;
_status_desc = 'ban_id inserted: ' || _ban_id::text;
END IF;
ELSE
_status_id = 402;
_status_desc = 'User does not exist.';
END IF;
RETURN QUERY SELECT _ban_id, _status_id, _status_desc;
END;
$$;
由于你的场景复杂,可以任意顺序插入,建议你事先在二级table中进行插入,比如price_check.
然后你可以放置一个触发器来执行 SQL 例如:
SELECT pmin.*
FROM price_check AS pmin
JOIN price_check AS pavg
ON (pmin.product, pmin.territory, pmin.year, pmin.data_source) = (pavg.product, pavg.territory, pavg.year, pavg.data_source)
AND pmin.stats_type = 1
AND pavg.stats_type = 2
AND pmin.nominal_value <= pavg.nominal_value
这将 return 仅有效最小值,并使用它在最终 table 上执行插入。请注意,我使用的是 INNER JOIN,因此没有平均对的最小值不会 return,您必须使用 LEFT JOIN 才能做到这一点。
同样,您可以在 price_check 上设计查询,将无效行放入 price_log table 中,以便稍后手动检查它们。
或者,您可以在 table 上放置一个 INSTEAD OF INSERT 触发器,而不需要辅助 table,但即便如此,创建一个 LOG table 是一个很好的做法手动检查并修复无效行。
由于需要查询 table,因此无法使用检查约束来完成此操作。您可以使用触发器:
create or replace function price_trigger()
returns trigger language plpgsql as $$
begin
if exists (
select 1
from price p
where (p.product, p.territory, p.year, p.data_source) =
(new.product, new.territory, new.year, new.data_source)
and (
p.stats_type < new.stats_type and p.nominal_value > new.nominal_value
or
p.stats_type > new.stats_type and p.nominal_value < new.nominal_value
)
)
then
raise exception 'Nominal value error';
end if;
return new;
end $$;
create trigger price_trigger
before insert or update on price
for each row
execute procedure price_trigger();
触发函数检查所有条件 (min < avg < max)。