这可能吗?我感兴趣的是找出在UPDATE
请求中指定了哪些列,而不管正在发送的新值可能是数据库中已经存储的值,也可能不是。
我这样做的原因是因为我们有一个可以从多个源接收更新的表。以前,我们不记录更新来自哪个源。现在,表存储了哪个源执行了最近的更新。我们可以更改一些源以发送标识符,但这并不是万能的,所以我希望能够识别出UPDATE
请求没有标识符的情况,这样我就可以用一个默认值来代替。
这可能吗?我感兴趣的是找出在UPDATE
请求中指定了哪些列,而不管正在发送的新值可能是数据库中已经存储的值,也可能不是。
我这样做的原因是因为我们有一个可以从多个源接收更新的表。以前,我们不记录更新来自哪个源。现在,表存储了哪个源执行了最近的更新。我们可以更改一些源以发送标识符,但这并不是万能的,所以我希望能够识别出UPDATE
请求没有标识符的情况,这样我就可以用一个默认值来代替。
4条答案
按热度按时间xlpyo6sf1#
如果一个“源”没有“发送标识符”,那么该列将保持不变。这样你就无法检测当前的
UPDATE
是由与上一个相同的源完成的,还是由一个根本没有改变该列的源完成的。换句话说:这不能正常工作。如果“源”可由任何会话信息函数识别,则可以使用该函数。
无条件地进行每次更新。
一般解决方案
我找到了解决原来问题的办法。
如果该列不是
UPDATE
中的目标列(不在SET
列表中),则将其设置为默认值。关键元素是PostgreSQL 9.0引入的每列触发器-使用UPDATE OF
*column_name
* 子句的特定于列的触发器。The manual:仅当列出的列中至少有一列被指定为
UPDATE
命令的目标时,才会触发触发器。这是我发现的唯一一种简单的方法来区分一个列是用与旧值相同的新值更新的,还是根本没有更新的。
人们也可以解析
current_query()
返回的文本,但这看起来很麻烦、棘手而且不可靠。触发函数
我假设列
source
定义为NOT NULL
。**步骤1:**如果未更改,则将
source
设置为NULL
:步骤2:还原为旧值。触发器仅在值实际更新时才被激发(请参阅下面的内容):
**第3步:**现在我们可以确定缺少的更新并设置默认值:
触发器
触发器名称是相关的,因为它们是按字母顺序触发的(全部为
BEFORE UPDATE
)!这个过程可以用类似“per-not-column triggers”的方式来简化,或者用任何其他方法来检查触发器中
UPDATE
的target-list。但是我没有看到这个句柄,目前(Postgres 14没有改变)。如果
source
可以是NULL
,则使用任何其他“不可能”的中间值,并在触发函数1中额外检查NULL
:对其余部分进行相应调整。
j2datikz2#
另一种方法是利用PostgreSQL最新版本中的JSON/JSONB函数,它的优点是可以处理任何可以转换为JSON对象的数据(行或任何其他结构化数据),而且你甚至不需要知道记录类型。
要查找任意两行/记录之间的差异,可以使用以下小技巧:
其中
OLD
和NEW
是在触发器函数中找到的内置记录,分别表示已更改记录的pre和after状态。请注意,我使用了表别名pre
和post
,而不是old
和new
,以避免与OLD和NEW内置对象冲突。还要注意,使用IS DISTINCT FROM
而不是简单的!=
或<>
来适当地处理NULL
值。当然,这也适用于任何ROW构造函数,如
ROW(1,2,3,...)
或它的简写(1,2,3,...)
,也适用于任何两个具有相同键的JSONB对象。例如,考虑一个具有两行的示例(出于示例的目的,已转换为JSONB):
该查询将显示值已更改的列:
这种方法最酷的一点是,按列进行过滤是很简单的。例如,假设您只想检测
col1
和col2
列中的变化:新结果将从结果中排除
col3
,即使其值已更改:很容易看出这种方法可以通过多种方式进行扩展。例如,假设您希望在更新某些列时抛出异常。您可以使用universal触发器函数来实现这一点,也就是说,该函数可以应用于任何/所有表,而不必知道表类型:
然后,您可以通过以下方式将上述触发器函数注册到要控制的任何和所有表:
ssgvzors3#
在plpgsql中,您可以在触发器函数中执行类似以下的操作:
x6492ojm4#
我几乎很自然地得到了类似问题的另一个解决方案,因为我的表包含了一个语义为“上次更新时间戳”(我们称之为UPDT)的列。
因此,我决定在任何更新中一次性包含source和UPDT的新值(或者一个都不包含)。由于UPDT在每次更新时都会改变,因此使用这种策略可以使用条件
new.UPDT = old.UPDT
来推断当前更新没有指定源,并替换默认的源。如果表中已经有了“上次更新时间戳”列,则此解决方案比创建三个触发器简单。如果不需要UPDT,则不确定创建UPDT是否是更好的主意。如果更新非常频繁,存在时间戳相似的风险,则可以使用序列器来代替时间戳。