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…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...

select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...