在函数中按列类型过滤行
Filter a row by column types in a function
现在我有一个通用的通知功能,它在我的数据库中的几个 table 上创建后触发(另一端有一个节点进程正在侦听通知)。这是我的更新触发器的样子:
CREATE OR REPLACE FUNCTION notify_create() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
PERFORM pg_notify('update_watchers',
json_build_object(
'eventType', 'new',
'type', TG_TABLE_NAME,
'payload', row_to_json(NEW)
)::text
);
RETURN NEW;
END;
$$;
问题是,如果 NEW 太大,这会在一些有限的极端情况下溢出 8000 字节的限制(我很少在 table 中有这么大的新项目)。在 notify_update 函数中,我只是通过列出列名来报告哪些列发生了变化。这在这里行得通,但我 而 做的只是 row_to_json 从 NEW 中提取整数类型的条目。
那是因为有时我要通知的是 "hey there's a new entry in an entity table"。新条目可能来自几个不同的 table(文档、配置文件等)。在那种情况下,我真的只需要 id,因为任何对新值感兴趣的人都会在稍后获取它。
有时我会通知 "hey, there's a new entry in a join table",在这种情况下我没有 id 字段,而是有类似 documents_id 和 profiles_id.
的内容
我 可以 为每个场景编写一堆不同的 notify_create 函数。我更喜欢有一个可以做类似
的事情
row_to_json(NEW.filter(t => typeof t === 'number'))
将 plpgsql 和 javascript 符号混合在一起,但我相信您明白了:只包括 NEW 的那些输入数字的字段
这可能吗,还是我应该写一堆不同的通知程序?
您可以轻松地消除 json 个非 number
类型的对象,例如:
with my_table(int1, text1, int2, date1, float1) as (
values
(1, 'text1', 100, '2017-01-01'::date, 123.54)
)
select jsonb_object_agg(key, value) filter (where jsonb_typeof(value) = 'number')
from my_table,
jsonb_each(to_jsonb(my_table))
jsonb_object_agg
--------------------------------------------
{"int1": 1, "int2": 100, "float1": 123.54}
(1 row)
下面的函数只保留整数:
create or replace function leave_integers(jdata jsonb)
returns jsonb language sql as $$
select jsonb_object_agg(key, value)
filter (
where jsonb_typeof(value) = 'number'
and value::text not like '%.%')
from jsonb_each(jdata)
$$;
with my_table(int1, text1, int2, date1, float1) as (
values
(1, 'text1', 100, '2017-01-01'::date, 123.54)
)
select leave_integers(to_jsonb(my_table))
from my_table;
leave_integers
--------------------------
{"int1": 1, "int2": 100}
(1 row)
替代(更好)解决方案
此函数直接检查 Postgres 类型,returns 值严格来自整数列。
create or replace function integer_columns_to_jsonb(anyelement)
returns jsonb language sql as $$
select jsonb_object_agg(key, value)
from jsonb_each(to_jsonb())
where key in (
select attname
from pg_type t
join pg_attribute on typrelid = attrelid
where t.oid = pg_typeof()
and atttypid = 'int'::regtype)
$$;
该示例表明该函数消除了 leave_integers()
错误处理的一些极端情况:
create table my_table (int1 int, int2 int, float1 float, text1 text);
insert into my_table values (1, 2, 3, '4');
select integer_columns_to_jsonb(t), leave_integers(to_jsonb(t))
from my_table t;
integer_columns_to_jsonb | leave_integers
--------------------------+-------------------------------------
{"int1": 1, "int2": 2} | {"int1": 1, "int2": 2, "float1": 3}
(1 row)
现在我有一个通用的通知功能,它在我的数据库中的几个 table 上创建后触发(另一端有一个节点进程正在侦听通知)。这是我的更新触发器的样子:
CREATE OR REPLACE FUNCTION notify_create() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
PERFORM pg_notify('update_watchers',
json_build_object(
'eventType', 'new',
'type', TG_TABLE_NAME,
'payload', row_to_json(NEW)
)::text
);
RETURN NEW;
END;
$$;
问题是,如果 NEW 太大,这会在一些有限的极端情况下溢出 8000 字节的限制(我很少在 table 中有这么大的新项目)。在 notify_update 函数中,我只是通过列出列名来报告哪些列发生了变化。这在这里行得通,但我 而 做的只是 row_to_json 从 NEW 中提取整数类型的条目。
那是因为有时我要通知的是 "hey there's a new entry in an entity table"。新条目可能来自几个不同的 table(文档、配置文件等)。在那种情况下,我真的只需要 id,因为任何对新值感兴趣的人都会在稍后获取它。
有时我会通知 "hey, there's a new entry in a join table",在这种情况下我没有 id 字段,而是有类似 documents_id 和 profiles_id.
的内容我 可以 为每个场景编写一堆不同的 notify_create 函数。我更喜欢有一个可以做类似
的事情row_to_json(NEW.filter(t => typeof t === 'number'))
将 plpgsql 和 javascript 符号混合在一起,但我相信您明白了:只包括 NEW 的那些输入数字的字段
这可能吗,还是我应该写一堆不同的通知程序?
您可以轻松地消除 json 个非 number
类型的对象,例如:
with my_table(int1, text1, int2, date1, float1) as (
values
(1, 'text1', 100, '2017-01-01'::date, 123.54)
)
select jsonb_object_agg(key, value) filter (where jsonb_typeof(value) = 'number')
from my_table,
jsonb_each(to_jsonb(my_table))
jsonb_object_agg
--------------------------------------------
{"int1": 1, "int2": 100, "float1": 123.54}
(1 row)
下面的函数只保留整数:
create or replace function leave_integers(jdata jsonb)
returns jsonb language sql as $$
select jsonb_object_agg(key, value)
filter (
where jsonb_typeof(value) = 'number'
and value::text not like '%.%')
from jsonb_each(jdata)
$$;
with my_table(int1, text1, int2, date1, float1) as (
values
(1, 'text1', 100, '2017-01-01'::date, 123.54)
)
select leave_integers(to_jsonb(my_table))
from my_table;
leave_integers
--------------------------
{"int1": 1, "int2": 100}
(1 row)
替代(更好)解决方案
此函数直接检查 Postgres 类型,returns 值严格来自整数列。
create or replace function integer_columns_to_jsonb(anyelement)
returns jsonb language sql as $$
select jsonb_object_agg(key, value)
from jsonb_each(to_jsonb())
where key in (
select attname
from pg_type t
join pg_attribute on typrelid = attrelid
where t.oid = pg_typeof()
and atttypid = 'int'::regtype)
$$;
该示例表明该函数消除了 leave_integers()
错误处理的一些极端情况:
create table my_table (int1 int, int2 int, float1 float, text1 text);
insert into my_table values (1, 2, 3, '4');
select integer_columns_to_jsonb(t), leave_integers(to_jsonb(t))
from my_table t;
integer_columns_to_jsonb | leave_integers
--------------------------+-------------------------------------
{"int1": 1, "int2": 2} | {"int1": 1, "int2": 2, "float1": 3}
(1 row)