Postgresql数据库中通过函数实现将不确定列的数据插入到表中

在数据库操作中,经常会遇到需要将不确定列的数据插入到表中的情况。这个文章将详细介绍如何在 PostgreSQL 中通过函数来实现这一需求,记录一下。

一、需求概述

在实际应用中,可能会接收到一个包含数据的 JSON 字符串,需要将其插入到指定表中。同时,要处理表的主键问题,确保在数据中没有主键值时能自动生成并添加。此外,还需要考虑表可能位于不同的模式(schema)下,以及后续的数据更新操作。

二、实现分析

为了实现上述需求,需要利用 PostgreSQL 丰富的函数和操作符来处理 JSON 数据、获取表结构信息、生成主键值以及构建和执行 SQL 语句。

三、实现步骤

第一步:获取表的所有字段

使用 information_schema.columns 表获取指定表和模式下的所有列名,并存储在 all_column_names 数组中。

第二步:查找主键字段及数据类型

通过查询 information_schema.key_column_usage 和相关表来确定主键列名和数据类型。

第三步:如果 Json 没有主键字段,则给 Json 增加主键字段

将输入的 JSON 字符串转换为 JSONb 类型,并检查主键列在数据中是否存在。如果不存在且主键为整数类型,从表中获取最大值并加 1 作为主键值;如果主键为其他类型,则生成一个随机 UUID 作为主键值。之后创建包含主键的新 JSON 数据并与原数据合并。

第四步:构建插入语句的列和值

接下来,构建插入语句的列和值部分。遍历 all_column_names 数组,只添加数据中存在值的列到插入语句中。

第五步:插入

最后,执行构建好的插入语句,将数据插入到表中。

四、测试

为了验证函数的正确性,进行如下测试操作:

sql 复制代码
CREATE TABLE test_table (
    id VARCHAR(50) PRIMARY KEY,
    name VARCHAR(50),
    age INT
);

CREATE TABLE test_table2 (
    id int PRIMARY KEY,
    name VARCHAR(50),
    age INT
);

SELECT insert_data_to_table('{"name": "Alice", "age": 25,"id": "55655655" }', 'test_table');
SELECT insert_json_data('{"name": "Aliceb", "age": 26}', 'test_table');
SELECT insert_json_data('{"name": "Alicec", "age": 27}', 'test_table');
SELECT insert_json_data('{}', 'test_table2');

五、结果

经过测试,上述函数能够成功地将不确定列的数据插入到表中,并根据给定的条件进行更新操作。在实际应用中,我们可以根据具体的业务需求灵活调整函数的参数和逻辑,以满足更多复杂的场景。

六、附:完整的函数

sql 复制代码
-- 插入数据函数
CREATE OR REPLACE FUNCTION insert_data_to_table(json_string TEXT, tName TEXT, SchemaName TEXT DEFAULT 'public')
RETURNS VOID AS $$
DECLARE
    all_column_names TEXT[];
    primary_key_column_name TEXT;
    primary_key_data_type TEXT;
    json_data JSONb;
    new_json_data jsonb;
    insert_statement TEXT;
    key_value TEXT;
    max_value INT;  -- 用于存储主键的最大值
BEGIN
    -- 第一步:获取表的所有字段
    SELECT array_agg(column_name) INTO all_column_names
    FROM information_schema.columns
    WHERE table_name = tName AND table_schema = SchemaName;

    -- 第二步:查找主键字段及数据类型
    SELECT kcu.column_name, data_type INTO primary_key_column_name, primary_key_data_type
    FROM information_schema.key_column_usage kcu
    JOIN information_schema.columns col ON kcu.column_name = col.column_name
    WHERE kcu.table_name = tName AND col.table_schema = SchemaName AND kcu.constraint_name IN 
        (SELECT constraint_name FROM information_schema.table_constraints WHERE table_name = tName AND table_schema = SchemaName AND constraint_type = 'PRIMARY KEY');

    -- 第三步:如果 Json 没有主键字段,则给 Json 增加主键字段
    json_data := json_string::JSONb;
    IF primary_key_column_name IS NOT NULL AND json_data ->> primary_key_column_name IS NULL THEN
        IF primary_key_data_type = 'integer' THEN
            -- 获取主键的最大值
            EXECUTE 'SELECT coalesce(max(' || primary_key_column_name || '), 0) + 1 FROM ' || SchemaName || '.' || tName INTO max_value;
            key_value := max_value;  -- 使用获取到的最大值加 1 作为主键值
        ELSE
            key_value := gen_random_uuid()::TEXT;
        END IF;
        new_json_data := concat('{', '"', primary_key_column_name, '": "', key_value, '"}')::jsonb;
        json_data := (new_json_data || json_data);
    END IF;

    -- 第四步:构建插入语句的列和值
    insert_statement := 'INSERT INTO ' || SchemaName || '.' || tName || ' (';
    FOR i IN 1..array_length(all_column_names, 1) LOOP
        IF json_data ->> all_column_names[i] IS NOT NULL THEN
            insert_statement := insert_statement || all_column_names[i] || ', ';
        END IF;
    END LOOP;
    insert_statement := substring(insert_statement, 1, length(insert_statement) - 2) || ') VALUES (';
    FOR i IN 1..array_length(all_column_names, 1) LOOP
        IF json_data ->> all_column_names[i] IS NOT NULL THEN
            insert_statement := insert_statement || quote_literal(json_data ->> all_column_names[i]) || ', ';
        END IF;
    END LOOP;
    insert_statement := substring(insert_statement, 1, length(insert_statement) - 2) || ')';

    -- 第五步:插入
    RAISE NOTICE '%', insert_statement;
    EXECUTE insert_statement;
END;
$$ LANGUAGE plpgsql;
相关推荐
闻道且行之11 分钟前
图片处理助手|泊松融合原理 + C++ 工程实现,seamlessClone 三模式一次讲透
数据库·c++·人工智能·opencv
夏炳辉.1 小时前
PostgreSQL 高可用集群核心配置参数全解:从原生流复制到 Patroni 企业级方案
数据库·postgresql
努力的小雨2 小时前
KES 开启 SSL 前,证书、端口和客户端要一起验
数据库
DevOps老兵2 小时前
AI全栈知识07:向量数据库 - Milvus/Chroma实战
数据库·ai·milvus
这个DBA有点耶2 小时前
当数据库从“存储”走向“决策”:金仓数据库的融合架构之路
数据库·架构·aigc
意疏3 小时前
2026年远控软件安全横评:六款主流工具逐项核查——官方文档、一手实测与安全事件,全摊开
大数据·前端·数据库
名不经传的养虾人4 小时前
从0到1:企业级AI项目迭代日记 Vol.90|Agent变快了,Judge定下来了
大数据·数据库·人工智能·ai编程·企业ai
冰之杍4 小时前
MySQL utf8mb3 → utf8mb4 完整修改方案
数据库·mysql
老纪的技术唠嗑局4 小时前
端侧智能爆火之后,为何模型反而不是主角了?
数据库·人工智能
这个DBA有点耶4 小时前
一文讲透数据库分类:关系型、非关系型、OLTP、OLAP、分布式、多模……
数据库·mysql·架构