我很难理解ON UPDATE CASCADE的所需行为 当一个字段可以为空并且它的引用被更新时。
在下面的表city和statistic的示例中,当我们有一个城市并且它的省没有值(NULL)时触发器ON UPDATE CASCADE无法正常工作,而我期望它更新省值统计数据。
当我们使用EXPLAIN在城市上运行更新时,我们可以很容易地看到这种行为。
注意:我使用的是PostgreSQL 9.4.8
创建表格以重现案例
CREATE TABLE city (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(64) NOT NULL,
country_id BIGINT NOT NULL,
province_id BIGINT,
CONSTRAINT city_city_country_province_un UNIQUE (id, country_id, province_id)
);
CREATE TABLE statistics (
id BIGSERIAL PRIMARY KEY,
city_id BIGINT NOT NULL,
country_id BIGINT NOT NULL,
province_id BIGINT,
some_data INTEGER NOT NULL DEFAULT 0,
CONSTRAINT statistics_city_country_province_fk FOREIGN KEY (city_id, country_id, province_id) REFERENCES city (id, country_id, province_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT statistics_city_un UNIQUE (city_id)
);
填充表格
INSERT INTO city (name, country_id, province_id) VALUES ('SAO CARLOS', 1, 1);
INSERT INTO city (name, country_id, province_id) VALUES ('VATICAN CITY', 2, NULL);
INSERT INTO statistics (city_id, country_id, province_id) VALUES (1, 1, 1);
INSERT INTO statistics (city_id, country_id, province_id) VALUES (2, 1, NULL);
省份非空时更新城市
EXPLAIN ANALYZE VERBOSE UPDATE city SET province_id = 3 WHERE id = 1;
查询计划
Update on public.city (cost=0.15..8.17 rows=1 width=168) (actual time=0.238..0.238 rows=0 loops=1)
-> Index Scan using city_city_country_province_un on public.city (cost=0.15..8.17 rows=1 width=168) (actual time=0.046..0.048 rows=1 loops=1)
Output: id, name, country_id, 3::bigint, ctid
Index Cond: (city.id = 1)
Planning time: 0.510 ms
Trigger RI_ConstraintTrigger_a_41406 for constraint statistics_city_country_province_fk on city: time=0.792 calls=1
Trigger RI_ConstraintTrigger_c_41408 for constraint statistics_city_country_province_fk on statistics: time=0.296 calls=1
Execution time: 1.412 ms
更新后城市
id | city_id | country_id | province_id | some_data
----+---------+------------+-------------+-----------
1 | 1 | 1 | 3 | 0
2 | 2 | 2 | | 0
更新后统计信息
id | city_id | country_id | province_id | some_data
----+---------+------------+-------------+-----------
1 | 1 | 1 | 3 | 0
2 | 2 | 2 | | 0
省份为NULL时更新城市
EXPLAIN ANALYZE VERBOSE UPDATE city SET province_id = 7 WHERE id = 2;
查询计划
Update on public.city (cost=0.15..8.17 rows=1 width=168) (actual time=0.170..0.170 rows=0 loops=1)
-> Index Scan using city_city_country_province_un on public.city (cost=0.15..8.17 rows=1 width=168) (actual time=0.042..0.044 rows=1 loops=1)
Output: id, name, country_id, 7::bigint, ctid
Index Cond: (city.id = 2)
Planning time: 0.423 ms
Execution time: 0.225 ms
更新后城市
id | name | country_id | province_id
----+--------------+------------+-------------
1 | SAO CARLOS | 1 | 3
2 | VATICAN CITY | 2 | 7
更新后统计信息
id | city_id | country_id | province_id | some_data
----+---------+------------+-------------+-----------
1 | 1 | 1 | 3 | 0
2 | 2 | 2 | | 0
编辑:当字段为NULL
时强制更新
创建一个函数,以便在旧值为NULL
CREATE OR REPLACE FUNCTION update_null_province_reference() RETURNS TRIGGER AS $$
BEGIN
IF (OLD.province_id IS NULL AND NEW.province_id IS NOT NULL) THEN
UPDATE statistics SET province_id = NEW.province_id WHERE city_id = NEW.id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
在城市上创建触发器
CREATE TRIGGER update_null_province_reference
AFTER UPDATE ON city
FOR EACH ROW
EXECUTE PROCEDURE update_null_province_reference();
答案 0 :(得分:4)
这里没有意外发生,实际上NULL
值是原因。
执行以下UPDATE
语句时:
UPDATE city SET province_id = 3 WHERE id = 1
Postgre检测到(id, country_id, province_id)
表中city
组合已发生变化,然后在statistics
表中查找具有相同值的记录,在本例中为元组{{ 1}}。找到此记录,因此Postgres也会更新(1, 1, 1)
表。
然而,当您执行此statistics
语句时:
UPDATE
Postgres在记录中执行UPDATE city SET province_id = 7 WHERE id = 2
,UPDATE
元组的值为(id, country_id, province_id)
。但Postgres无法验证(2, 2, NULL)
表中是否存在匹配记录。其原因与statistics
的含义有关。在关系代数中,NULL
表示“未知”。因此,Postgres会看到NULL
并放弃级联,因为它无法验证NULL
表中的NULL
province_id
实际上是否与city
值匹配NULL
表。