PostGIS教程学习二十二:使用触发器追踪历史编辑操作
PostGIS教程学习二十二:使用触发器追踪历史编辑操作
生产环境下数据库的一个常见要求是能够跟踪用户编辑数据的历史:数据在两个日期之间是如何变化的,是谁操作的,以及它们哪些内容变化了?一些GIS系统通过在客户端接口中包含更改管理功能来跟踪用户的编辑数据操作,但这增加了客户端编辑工具的复杂性。
使用数据库和数据库的触发器机制,可以对任何表进行编辑历史跟踪,从而让客户端保持对编辑表的简单“直接编辑”(客户端不用负责追踪编辑历史的功能,只负责CRUD)。
历史跟踪的工作方式是增加一个记录编辑历史的历史表,为每个编辑操作保留历史记录。历史表包含如下信息:
如果编辑表中创建了一条新记录,则保留新记录添加的时间、操作的用户。
如果编辑表中的一条记录被删除,保留记录被删除的时间、操作的用户。
如果编辑表中的一条记录被更新,则添加删除信息(针对旧状态)和创建信息(针对新状态)。
文章目录
- PostGIS教程学习二十二:使用触发器追踪历史编辑操作
- 一、创建历史表
- 二、操作编辑表
- 2.1、SQL编辑
- 三、查找历史表
- 四、参考资料
一、创建历史表
使用历史表的信息,可以恢复任何时间点编辑表的状态。在本例中,我们将针对nyc_streets表创建历史表,从而追踪nyc_streets表的编辑历史。
1)首先,添加一个新的nyc_streets_history表。这是我们将用来存储所有编辑历史信息的历史表。除了包含nyc_streets表的所有字段外,我们还为历史表增加了五个字段:
hid —— 历史表的主码
created —— 编辑表中对应记录被创建的时间
created_by ——编辑表中对应记录被创建的操作用户
deleted —— 编辑表中对应记录被删除的时间
deleted_by —— 编辑表中对应记录被删除的操作用户
CREATE TABLE nyc_streets_history (hid SERIAL PRIMARY KEY,gid INTEGER,id FLOAT8,name VARCHAR(200),oneway VARCHAR(10),type VARCHAR(50),geom GEOMETRY(MultiLinestring,26918),created TIMESTAMP, -- 时间戳(无时区)created_by VARCHAR(32),deleted TIMESTAMP,deleted_by VARCHAR(32)
);
2)接下来,我们将编辑表nyc_streets的当前状态导入到历史表中,这样我们就有一个可以追踪历史编辑的起点。注意,我们插入了创建时间(created)和创建用户(created_by),但删除相关信息为空。
INSERT INTO nyc_streets_history(gid, id, name, oneway, type, geom, created, created_by)SELECT gid, id, name, oneway, type, geom, now(), current_userFROM nyc_streets;
3)现在,我们需要为编辑表创建三个触发器,用于插入、删除和更新操作。首先我们创建触发器函数(PostgreSQL的触发器动作体只能是函数),然后将它们作为触发器动作体绑定到表中。
对于INSERT操作,我们只需记录创建时间(created)和创建用户(created_by)信息并将该新记录添加到历史表中。
CREATE OR REPLACE FUNCTION nyc_streets_insert() RETURNS trigger AS
$$BEGININSERT INTO nyc_streets_history(gid, id, name, oneway, type, geom, created, created_by)VALUES(NEW.gid, NEW.id, NEW.name, NEW.oneway, NEW.type, NEW.geom,current_timestamp, current_user);RETURN NEW;END;
$$
LANGUAGE plpgsql;CREATE TRIGGER nyc_streets_insert_trigger
AFTER INSERT ON nyc_streets
FOR EACH ROW
EXECUTE PROCEDURE nyc_streets_insert();
对于DELETE操作,我们只要将对应的历史记录(且deleted字段是NULL)标记为已删除。
CREATE OR REPLACE FUNCTION nyc_streets_delete() RETURNS trigger AS
$$BEGINUPDATE nyc_streets_historySET deleted = current_timestamp, deleted_by = current_userWHERE deleted IS NULL and gid = OLD.gid;RETURN NULL;END;
$$
LANGUAGE plpgsql;CREATE TRIGGER nyc_streets_delete_trigger
AFTER DELETE ON nyc_streets
FOR EACH ROW
EXECUTE PROCEDURE nyc_streets_delete();
对于UPDATE操作,我们首先将对应的历史记录标记为已删除,然后插入更新状态的新记录。
CREATE OR REPLACE FUNCTION nyc_streets_update() RETURNS trigger AS
$$BEGINUPDATE nyc_streets_historySET deleted = current_timestamp, deleted_by = current_userWHERE deleted IS NULL and gid = OLD.gid;INSERT INTO nyc_streets_history(gid, id, name, oneway, type, geom, created, created_by)VALUES(NEW.gid, NEW.id, NEW.name, NEW.oneway, NEW.type, NEW.geom,current_timestamp, current_user);RETURN NEW;END;
$$
LANGUAGE plpgsql;CREATE TRIGGER nyc_streets_update_trigger
AFTER UPDATE ON nyc_streets
FOR EACH ROW
EXECUTE PROCEDURE nyc_streets_update();
二、操作编辑表
现在启用了历史表,我们可以对编辑表进行编辑,并在历史表中看到日志条目。
请注意这种数据库支持的追踪编辑历史的强大功能:无论使用什么工具进行编辑,比如SQL命令行、基于Web的JDBC工具,还是像QGIS这样的桌面工具,编辑历史都会被持续追踪。
2.1、SQL编辑
让我们把这两条名字叫做“Cumberland Walk”的街道改成更时髦的“Cumberland Wynde”:
UPDATE nyc_streets SET name = 'Cumberland Wynde'
WHERE name = 'Cumberland Walk';
更新这两条街道将会把历史表中原来的街道标记为已删除,删除时间为现在,以及再添加具有新名称的两条新街道。
SELECT hid, gid, name, created, created_by, deleted, deleted_by
FROM nyc_streets_history
WHERE name = 'Cumberland Walk' OR name = 'Cumberland Wynde'
ORDER BY hid;
三、查找历史表
既然我们有了历史表,那还有什么用处呢?它对"时间旅行"很有用!要旅行到特定的时刻T(即查看T时刻历史表的状态),我们需要构造一个查询,查询出符合如下规则的记录:
所有在时刻T之前创建且目前尚未删除的记录。
所有在时刻T之前创建,但在T之后删除的记录。
我们可以使用这个逻辑来创建过去某个时刻T对应的数据状态的视图。假设我们的所有测试编辑都发生在过去30分钟内,那我们可以创建一个30分钟前历史表数据状态的视图:
– 30分钟前的历史表的数据状态
– 记录必须在30分钟前创建,并且现在没被删除(DELETED为NULL)
– 或在过去30分钟内已删除的
CREATE OR REPLACE VIEW nyc_streets_thirty_min_ago ASSELECT * FROM nyc_streets_historyWHERE created < (now() - '30min'::interval)AND ( deleted IS NULL OR deleted > (now() - '30min'::interval) );
我们还可以创建视图来显示被特定用户(比如用户postgres)编辑的记录:
CREATE OR REPLACE VIEW nyc_streets_postgres ASSELECT * FROM nyc_streets_historyWHERE created_by = 'postgres' OR deleted_by = 'postgres';
四、参考资料
PostgreSQL触发器官方文档
PostgreSQL过程化语言(PL/pgSQL)官方文档
相关文章:

PostGIS教程学习二十二:使用触发器追踪历史编辑操作
PostGIS教程学习二十二:使用触发器追踪历史编辑操作 生产环境下数据库的一个常见要求是能够跟踪用户编辑数据的历史:数据在两个日期之间是如何变化的,是谁操作的,以及它们哪些内容变化了?一些GIS系统通过在客户端接口…...

【PTA浙大版《C语言程序设计(第4版)》编程题】练习7-4 找出不是两个数组共有的元素(附测试点)
目录 输入格式: 输出格式: 输入样例: 输出样例: 代码呈现 测试点 给定两个整型数组,本题要求找出不是两者共有的元素。 输入格式: 输入分别在两行中给出两个整型数组,每行先给出正整数N(≤20),随后是N个整数&a…...

C++面试:数据库不同存储引擎的区别以及如何选择
目录 基础 具体选择原则 Mysql如何选择 创建表时指定存储引擎 修改现有表的存储引擎 查看表的存储引擎 注意事项 总结 在数据库管理系统中,不同的存储引擎提供了不同的存储机制、索引技术、锁定水平和其他功能。以MySQL为例,它支持多种存储引擎&…...

HTML -- 常用标签
目录 HTML 标签 单标签 双标签 常见标签的使用 标题和段落 换行、分隔、超链接 列表标签 表单标签 属性 属性的使用 HTML HTML(Hyper Text Markup Language),超文本标记语言,是一门标记语言,不是编程语言&am…...

【Qt】QInputDialog setGeometry: Unable to set geometry 问题
【Qt】QInputDialog setGeometry: Unable to set geometry 问题 文章目录 I - 问题背景II - 解决办法III - 参考链接 I - 问题背景 创建了一个 QMainWindow 并在上边创建了布局,尝试调用 QInputDialog 的 getInt 静态方法,结果运行时出现了以下警告 QW…...

Flink问题解决及性能调优-【Flink rocksDB读写state大对象导致背压问题调优】
RocksDB是Flink中用于持久化状态的默认后端,它提供了高性能和可靠的状态存储。然而,当处理大型状态并频繁读写时,可能会导致背压问题,因为RocksDB需要从磁盘读取和写入数据,而这可能成为瓶颈。 遇到的问题 Flink开发…...

代码随想录算法训练营第二十四天|● 理论基础 ● 77. 组合
仅做学习笔记,详细请访问代码随想录 ● 理论基础 ● 77. 组合 ● 理论基础 回溯法解决的问题 回溯法,一般可以解决如下几种问题: 组合问题:N个数里面按一定规则找出k个数的集合 切割问题:一个字符串按一定规则有几…...

买保险如何填健康告知
在投保健康险时,保险公司都有健康告知这一环,那么健康告知怎么机智的填? 人都吃五谷杂粮,身体免不了有各种小毛病,比如甲状腺结节等,健康告知通过不了怎么办? 健康告知是保险公司设计的健康问…...

云贝教育 | 【技术文章】Oracle 19c RAC修改网络
注: 本文为云贝教育 刘峰 原创,请尊重知识产权,转发请注明出处,不接受任何抄袭、和未经注明出处的转载。 原文链接:【Oracle 19c】Oracle 19c RAC修改网络 - 课程体系 - 云贝教育 (yunbee.net) 变更目标 ip类型 节点 原IP 目…...

Android SELinux:保护您的移动设备安全的关键
Android SELinux:保护您的移动设备安全的关键 1 引言 移动设备在我们的生活中扮演着越来越重要的角色,我们几乎把所有重要的信息都存储在这些设备上。然而,随着移动应用程序的数量不断增加,安全性也变得越来越关键。这就是为什么…...

第十三章认识Ajax(四)
认识FormData对象 FormData对象用于创建一个表示HTML表单数据的键值对集合。 它可以用于发送AJAX请求或通过XMLHttpRequest发送表单数据。 以下是FormData对象的一些作用: 收集表单数据:通过将FormData对象与表单元素关联,可以方便地收集表单中的数据。使用FormData对象,…...

使用 Node.js 和 Cheerio 爬取网站图片
写一个关于图片爬取的小案例 爬取效果 使用插件如下: {"dependencies": {"axios": "^1.6.0","cheerio": "^1.0.0-rc.12","request": "^2.88.2"} }新建一个config.js配置文件 // 爬取图片…...

2024美赛数学建模E题思路源码
赛题目的 可以将其拆解为以下主要问题,并为每个问题提出解决方案: 如何在极端天气事件越来越多的地区部署财产保险? 保险公司应在何时何地承保保单? 业主如何影响保险公司的承保决定? 如何建立能够评估未来房地产决…...

解决Docker AList本地挂载失效的问题。
解决Docker AList本地挂载失效的问题。 AList Docker version: 3.3 services:alist:image: xhofe/alist:latestcontainer_name: alistvolumes:- ./etc/alist:/opt/alist/data# 比如我要挂载/home,如果在docker里先挂载,是没法办法映射到linux系统下的/home的- /ho…...

Emmet常用语法总结
Emmet常用语法总结 子元素:>兄弟元素:上级元素:^倍数:*分组:()属性:[]id和类:# .迭代数字:$文本内容:{}注意事项 Emmet是许多流行文本编辑器的…...

Android 12系统源码_页面管理(四)获取系统当前最上层的Activity信息
前言 很多应用开发人员,在日常开发过程中,经常会遇到一些需求,例如需要知道当前最上层的Activity是哪个,并结合这个Activity的名称来完成一些特定场景的需求。最简单的方法,是在创建Activity的时候将该Actvity存储到一…...

RK3588开发板Ubuntu与开发板使用U盘互传
1 将 U 盘(U 盘的格式必须为 FAT32 格式,大小在 32G 以下)插到开发板的 usb 接口,串口打印信息如下所示,U 盘的设备节点是/dev/sdb4。U 盘的设备节点不是固定的,根据实际情况来查看设备节点。 2 输入以下命令挂载 U 盘,…...

【BUG】golang gorm导入数据库报错 “unexpected type clause.Expr“
帮同事排查一个gorm导入数据报错的问题 事发现场 ck sql CREATE TABLE ods_api.t_sms_jg_msg_callback_dis (app_key String DEFAULT COMMENT 应用标识,callback_type Int32 DEFAULT 0 COMMENT 0送达,1回执,channel Int32 DEFAULT 0 COMMENT uid下发的渠道,mode…...

TCP/IP网络模型
大家好我是苏麟 , 今天聊聊TCP/IP四层网络模型 . 资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) 应用层 最上层的,也是我们能直接接触到的就是应用层(Application Layer),我们电脑或手机使用的应用软件都…...

github连不上
github连不上 错误提示解决方案steam 采用Hosts加速 错误提示 fatal: unable to access ‘https://github.com/Ada-design/qianduan.git/’: Failed to connect to github.com port 443 after 21073 ms: Couldn’t connect to server 解决方案 下载steam https://steampp.ne…...

Excel计算表达式的值
Excel如何求解表达式 其中第三种方法最简单,通过剪贴板实现 如,单元格A1中输入了 12345 然后在A2输入 “”&A1 然后复制A2,打开剪贴板,点击刚才复制的内容,就会在A2显示计算结果...

26元/月起!腾讯云一键自动搭建4核16G幻兽帕鲁服务器
腾讯云无需任何配置自动搭建幻兽帕鲁游戏联机服务器,游戏24小时在线,4核16G游戏联机服务器低至26元/月起,新手小白也能一键搭建属于自己的幻兽帕鲁游戏联机服务器! 第一步:购买游戏联机服务器 购买入口:htt…...

【C++游戏开发-01】推箱子
C游戏开发 文章目录 C游戏开发[TOC](文章目录) 前言一、逻辑分析1.1地图实现1.2人物的移动1.2.1小人移动1.2.2其他移动 1.3墙壁的碰撞1.4箱子的推动1.4.1什么时候推箱子1.4.2什么情况可以推箱子 1.5胜利的判断1.6卡关的处理1.7关卡的切换 二、DEMO代码2.1游戏框架2.2各功能函数…...

【lesson26】学习MySQL事务前的基础知识
文章目录 CURD不加控制,会有什么问题?CURD满足什么属性,能解决上述问题?什么是事务?为什么会出现事务事务的版本支持 CURD不加控制,会有什么问题? CURD满足什么属性,能解决上述问题&…...

持续积累分享金融知识
持续积累分享金融知识 一、什么是两融余额?二、什么是量化?三、散户可以进行量化投资么? 一、什么是两融余额? 两融余额是指投资者在融资买入和融券卖出交易中,通过向券商借入资金或证券进行交易,并且在交…...

网络协议 UDP协议
网络协议 UDP协议 在之前的文章中有对UDP协议套接字的使用进行讲解,本文主要对UDP协议进行一些理论补充。 文章目录 网络协议 UDP协议1. 概念2. UDP协议格式2.1 数据报长度2.2 校验和/检验和2.2.1 CRC校验2.2.2 MD5算法 1. 概念 UDP,即User Datagram P…...

爬虫笔记(三):实战qq登录
咳咳,再这样下去会进橘子叭hhhhhh 以及,这个我觉得大概率是成功的,因为测试了太多次,登录并且验证之后,qq提醒我要我修改密码才可以登录捏QAQ 1. selenium 有关selenium具体是啥,这里就不再赘述了&#x…...

又涨又跌 近期现货黄金价格波动怎么看?
踏入2024年一月的下旬,现货黄金价格可以说没了之前火热的状态,盘面上是又涨又跌。面对这样的行情,很多投资者不知道如何看了。下面我们就来讨论一下怎么把握近期的行情。 先区分走势类型。在现货黄金市场中有两种主要的走势类型,一…...

软件压力测试:探究其目的与重要性
随着软件应用在各行各业中的广泛应用,确保软件在高负载和极端条件下的稳定性变得至关重要。软件压力测试是一种验证系统在不同负载条件下的性能和稳定性的方法。本文将介绍软件压力测试的目的以及为什么它对软件开发和部署过程至关重要。 验证系统性能的极限&#x…...

Android.bp入门指南之浅析Android.bp文件
文章目录 Android.bp文件是什么?Android.bp的主要作用模块定义依赖关系构建规则模块属性插件支持模块的可配置性 为什么会引入Android.bp语法例子 Android.bp文件是什么? Android.bp 文件是 Android 构建系统(Android Build Systemÿ…...