当前位置: 首页 > news >正文

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教程学习二十二&#xff1a;使用触发器追踪历史编辑操作 生产环境下数据库的一个常见要求是能够跟踪用户编辑数据的历史&#xff1a;数据在两个日期之间是如何变化的&#xff0c;是谁操作的&#xff0c;以及它们哪些内容变化了&#xff1f;一些GIS系统通过在客户端接口…...

【PTA浙大版《C语言程序设计(第4版)》编程题】练习7-4 找出不是两个数组共有的元素(附测试点)

目录 输入格式: 输出格式: 输入样例: 输出样例: 代码呈现 测试点 给定两个整型数组&#xff0c;本题要求找出不是两者共有的元素。 输入格式: 输入分别在两行中给出两个整型数组&#xff0c;每行先给出正整数N&#xff08;≤20&#xff09;&#xff0c;随后是N个整数&a…...

C++面试:数据库不同存储引擎的区别以及如何选择

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

HTML -- 常用标签

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

【Qt】QInputDialog setGeometry: Unable to set geometry 问题

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

Flink问题解决及性能调优-【Flink rocksDB读写state大对象导致背压问题调优】

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

代码随想录算法训练营第二十四天|● 理论基础 ● 77. 组合

仅做学习笔记&#xff0c;详细请访问代码随想录 ● 理论基础 ● 77. 组合 ● 理论基础 回溯法解决的问题 回溯法&#xff0c;一般可以解决如下几种问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合 切割问题&#xff1a;一个字符串按一定规则有几…...

买保险如何填健康告知

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

云贝教育 | 【技术文章】Oracle 19c RAC修改网络

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

Android SELinux:保护您的移动设备安全的关键

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

第十三章认识Ajax(四)

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

使用 Node.js 和 Cheerio 爬取网站图片

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

2024美赛数学建模E题思路源码

赛题目的 可以将其拆解为以下主要问题&#xff0c;并为每个问题提出解决方案&#xff1a; 如何在极端天气事件越来越多的地区部署财产保险&#xff1f; 保险公司应在何时何地承保保单&#xff1f; 业主如何影响保险公司的承保决定&#xff1f; 如何建立能够评估未来房地产决…...

解决Docker AList本地挂载失效的问题。

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

Emmet常用语法总结

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

Android 12系统源码_页面管理(四)获取系统当前最上层的Activity信息

前言 很多应用开发人员&#xff0c;在日常开发过程中&#xff0c;经常会遇到一些需求&#xff0c;例如需要知道当前最上层的Activity是哪个&#xff0c;并结合这个Activity的名称来完成一些特定场景的需求。最简单的方法&#xff0c;是在创建Activity的时候将该Actvity存储到一…...

RK3588开发板Ubuntu与开发板使用U盘互传

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

【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送达&#xff0c;1回执,channel Int32 DEFAULT 0 COMMENT uid下发的渠道,mode…...

TCP/IP网络模型

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

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…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

LeetCode - 199. 二叉树的右视图

题目 199. 二叉树的右视图 - 力扣&#xff08;LeetCode&#xff09; 思路 右视图是指从树的右侧看&#xff0c;对于每一层&#xff0c;只能看到该层最右边的节点。实现思路是&#xff1a; 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

raid存储技术

1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划&#xff0c;涵盖存储系统的布局、数据存储策略等&#xff0c;它明确数据如何存储、管理与访问&#xff0c;为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...

C++ 类基础:封装、继承、多态与多线程模板实现

前言 C 是一门强大的面向对象编程语言&#xff0c;而类&#xff08;Class&#xff09;作为其核心特性之一&#xff0c;是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性&#xff0c;包括封装、继承和多态&#xff0c;同时讨论类中的权限控制&#xff0c;并展示如何使用类…...

触发DMA传输错误中断问题排查

在STM32项目中&#xff0c;集成BLE模块后触发DMA传输错误中断&#xff08;DMA2_Stream1_IRQHandler进入错误流程&#xff09;&#xff0c;但单独运行BLE模块时正常&#xff0c;表明问题可能源于原有线程与BLE模块的交互冲突。以下是逐步排查与解决方案&#xff1a; 一、问题根源…...