9 postgresql null database-design postgresql-9.2 check-constraints
我有一个表,需要添加一个没有默认值的新列:
ALTER TABLE integrations.billables
DROP CONSTRAINT IF EXISTS cc_at_least_one_mapping_needed_billables,
ADD CONSTRAINT cc_at_least_one_mapping_needed_billables
CHECK ((("qb_id" IS NOT NULL) :: INTEGER +
("xero_id" IS NOT NULL) :: INTEGER +
("freshbooks_id" IS NOT NULL) :: INTEGER +
("unleashed_id" IS NOT NULL) :: INTEGER +
("csv_data" IS NOT NULL) :: INTEGER +
("myob_id" IS NOT NULL) :: INTEGER) > 0);
Run Code Online (Sandbox Code Playgroud)
ALTER TABLE integrations.billables
DROP COLUMN IF EXISTS myob_id,
ADD COLUMN myob_id varchar(255);
Run Code Online (Sandbox Code Playgroud)
如何为下一个值添加约束,而不是为那些已经存在的值添加约束?(否则我会得到某些行违反了错误检查约束“”)。
这与我之前的问题有关:错误:某些行违反了检查约束
And*_*y M 10
如果您有一serial
列或一个自动填充的整数nextval
(这样您就永远不应该为该列插入具有显式值的新行),您还可以检查该列的值是否大于特定值:
(
(("qb_id" IS NOT NULL) :: INTEGER +
("xero_id" IS NOT NULL) :: INTEGER +
("freshbooks_id" IS NOT NULL) :: INTEGER +
("unleashed_id" IS NOT NULL) :: INTEGER +
("csv_data" IS NOT NULL) :: INTEGER +
("myob_id" IS NOT NULL) :: INTEGER) > 0
OR
YourSerialColumn <= value
)
Run Code Online (Sandbox Code Playgroud)
其中value
应该currval
在更改/重新创建约束时根据列/相应序列确定。
这样,IS NOT NULL
检查将仅适用于YourSerialColumn
值大于的行value
。
笔记
这可以看作是David Spillet 建议的变体。主要区别在于该解决方案不需要结构更改(分区)。但是,这两个选项都依赖于表中是否存在合适的列。如果没有这样的列,而您可以专门添加它来解决此问题,那么采用分区的想法可能是这两者中更好的选择。
将所有现有行标记为旧:
ALTER TABLE integrations.billables
ADD COLUMN is_old BOOLEAN NOT NULL DEFAULT false;
UPDATE integrations.billables SET is_old = true;
Run Code Online (Sandbox Code Playgroud)
并设置约束以忽略旧行:
ALTER TABLE integrations.billables
ADD CONSTRAINT cc_at_least_one_mapping_needed_billables
CHECK (
NOT(("qb_id", "xero_id", "freshbooks_id", "unleashed_id", "csv_data", "myob_id") IS NULL)
OR is_old
);
Run Code Online (Sandbox Code Playgroud)
(是的,该IS NULL
检查有效。请参阅此处。)
这种机制的优点:
缺点:
boolean
为第二个新列添加第二列或其他一些跳圈。is_old
标志翻转为true
. (不过,这可以解决。见下文。)如果最终用户无法直接访问数据库并且您可以相信开发人员不会对数据做古怪的事情,那么这不是什么值得关注的事情。如果你是担心有人改变的标志,你可以设置一个触发器,以防止任何插入或更新从设置is_old
到true
:
CREATE FUNCTION throw_error_on_illegal_old()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW.is_old THEN
-- Need to make sure we don't try to access
-- OLD in an INSERT
IF TG_OP = 'INSERT' THEN
RAISE 'Cannot create new with is_old = true';
ELSE
IF NOT OLD.is_old THEN
RAISE 'Cannot change is_old from false to true';
END IF;
END IF;
END IF;
-- If we get here, all tests passed
RETURN NEW;
END
$$
;
CREATE TRIGGER billables_prohibit_marking_row_old
BEFORE INSERT OR UPDATE ON integrations.billables
FOR EACH ROW EXECUTE PROCEDURE throw_error_on_illegal_old()
;
Run Code Online (Sandbox Code Playgroud)
您仍然必须相信,没有人会修改数据库架构并删除您的触发器或其他东西,但如果他们要这样做,他们也可以删除约束。
这是一个SQLFiddle 演示。请注意,“应该跳过”行不在输出中(如我们所愿)。
归档时间: |
|
查看次数: |
4331 次 |
最近记录: |