PostgreSQL教程FG046-PG JSON/JSONB类型:JSON数据存储与查询详解
本文档风哥主要介绍PostgreSQL教程046相关内容。风哥教程参考PostgreSQL官方文档Server Administration, SQL Language内容编写,适合DBA人员在学习和测试中使用,如果要应用于生产环境则需要自行确认。
1. JSON与JSONB区别
PostgreSQL提供了两种JSON数据类型:JSON和JSONB。JSONB是JSON的二进制形式,存储时已解析,查询效率更高。
CREATE TABLE fgedu_json_test (
id SERIAL PRIMARY KEY,
json_data JSON,
jsonb_data JSONB
);
— 插入相同的JSON数据
INSERT INTO fgedu_json_test (json_data, jsonb_data) VALUES
(‘{“name”: “风哥1号”, “age”: 30, “city”: “北京”}’, ‘{“name”: “风哥1号”, “age”: 30, “city”: “北京”}’);
— 比较存储差异
SELECT
json_data,
jsonb_data,
pg_column_size(json_data) AS json_size,
pg_column_size(jsonb_data) AS jsonb_size
FROM fgedu_json_test;
执行结果:
———————————+————————-+———–+————
{“name”: “风哥1号”, “age”: 30, “city”: “北京”} | {“age”: 30, “name”: “风哥1号”, “city”: “北京”} | 46 | 76
(1 row)
JSONB会重新排序键:
INSERT INTO fgedu_json_test (json_data, jsonb_data) VALUES
(‘{“z”: 1, “a”: 2, “m”: 3}’, ‘{“z”: 1, “a”: 2, “m”: 3}’);
SELECT json_data, jsonb_data FROM fgedu_json_test WHERE id = 2;
执行结果:
—————-+—————-
{“z”: 1, “a”: 2, “m”: 3} | {“a”: 2, “m”: 3, “z”: 1}
(1 row)
2. JSON数据插入
创建产品表并插入JSON数据:
CREATE TABLE fgedu_products_json (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(100),
attributes JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
— 插入简单JSON数据
INSERT INTO fgedu_products_json (product_name, attributes) VALUES
(‘笔记本电脑’, ‘{“brand”: “ThinkPad”, “cpu”: “i7-12700H”, “ram”: “16GB”, “storage”: “512GB SSD”}’),
(‘无线鼠标’, ‘{“brand”: “Logitech”, “dpi”: 16000, “wireless”: true, “battery”: “AA”}’),
(‘机械键盘’, ‘{“brand”: “Cherry”, “switches”: “MX Brown”, “layout”: “TKL”, “rgb”: true}’);
— 插入嵌套JSON数据
INSERT INTO fgedu_products_json (product_name, attributes) VALUES
(‘智能手机’, ‘{
“brand”: “iPhone”,
“model”: “15 Pro”,
“specs”: {
“cpu”: “A17 Pro”,
“ram”: “8GB”,
“storage”: “256GB”
},
“colors”: [“黑色”, “白色”, “蓝色”],
“features”: [“5G”, “Face ID”, “MagSafe”]
}’);
— 插入数组形式的JSON数据
INSERT INTO fgedu_products_json (product_name, attributes) VALUES
(‘显示器套装’, ‘{
“monitor”: {“brand”: “Dell”, “size”: “27 inch”, “resolution”: “4K”},
“accessories”: [
{“type”: “支架”, “adjustable”: true},
{“type”: “HDMI线”, “length”: “2m”}
]
}’);
— 查询数据
SELECT product_id, product_name, attributes FROM fgedu_products_json;
执行结果:
————-+————–+—————————————————————————————————-
1 | 笔记本电脑 | {“cpu”: “i7-12700H”, “ram”: “16GB”, “brand”: “ThinkPad”, “storage”: “512GB SSD”}
2 | 无线鼠标 | {“dpi”: 16000, “brand”: “Logitech”, “battery”: “AA”, “wireless”: true}
3 | 机械键盘 | {“rgb”: true, “layout”: “TKL”, “brand”: “Cherry”, “switches”: “MX Brown”}
4 | 智能手机 | {“colors”: [“黑色”, “白色”, “蓝色”], “model”: “15 Pro”, “brand”: “iPhone”, “features”: [“5G”, “Face
ID”, “MagSafe”], “specs”: {“cpu”: “A17 Pro”, “ram”: “8GB”, “storage”: “256GB”}}
5 | 显示器套装 | {“monitor”: {“size”: “27 inch”, “brand”: “Dell”, “resolution”: “4K”}, “accessories”: [{“type”:
“支架”, “adjustable”: true}, {“type”: “HDMI线”, “length”: “2m”}]}
(5 rows)
3. JSON数据查询
使用->和->>操作符查询JSON数据:
SELECT
product_name,
attributes -> ‘brand’ AS brand_json,
attributes ->> ‘brand’ AS brand_text,
attributes -> ‘cpu’ AS cpu
FROM fgedu_products_json
WHERE attributes ? ‘brand’;
执行结果:
————–+————–+————+—————-
笔记本电脑 | “ThinkPad” | ThinkPad | “i7-12700H”
无线鼠标 | “Logitech” | Logitech |
机械键盘 | “Cherry” | Cherry |
智能手机 | “iPhone” | iPhone |
显示器套装 | | |
(5 rows)
查询嵌套JSON:
SELECT
product_name,
attributes -> ‘specs’ -> ‘cpu’ AS cpu,
attributes -> ‘specs’ ->> ‘ram’ AS ram,
attributes #> ‘{specs,storage}’ AS storage,
attributes #>> ‘{monitor,size}’ AS monitor_size
FROM fgedu_products_json
WHERE attributes -> ‘specs’ IS NOT NULL OR attributes -> ‘monitor’ IS NOT NULL;
执行结果:
————–+————+——-+————-+————–
智能手机 | “A17 Pro” | 8GB | “256GB” |
显示器套装 | | | | 27 inch
(2 rows)
查询JSON数组:
SELECT
product_name,
attributes -> ‘colors’ AS colors,
attributes -> ‘colors’ -> 0 AS first_color,
attributes -> ‘colors’ ->> 1 AS second_color,
jsonb_array_length(attributes -> ‘colors’) AS colors_count
FROM fgedu_products_json
WHERE attributes ? ‘colors’;
执行结果:
————–+—————————+————-+————–+————–
智能手机 | [“黑色”, “白色”, “蓝色”] | “黑色” | 白色 | 3
(1 row)
JSON条件查询:
SELECT product_name, attributes ->> ‘brand’ AS brand
FROM fgedu_products_json
WHERE attributes @> ‘{“brand”: “ThinkPad”}’;
执行结果:
————–+———-
笔记本电脑 | ThinkPad
(1 row)
SELECT product_name, attributes
FROM fgedu_products_json
WHERE attributes ? ‘wireless’;
— 检查是否包含任意一个键
SELECT product_name, attributes
FROM fgedu_products_json
WHERE attributes ?| ARRAY[‘wireless’, ‘rgb’];
— 检查是否包含所有键
SELECT product_name, attributes
FROM fgedu_products_json
WHERE attributes ?& ARRAY[‘brand’, ‘model’];
执行结果:
————–+————————————————————————–
无线鼠标 | {“dpi”: 16000, “brand”: “Logitech”, “battery”: “AA”, “wireless”: true}
(1 row)
product_name | attributes
————–+———————————————————————————
无线鼠标 | {“dpi”: 16000, “brand”: “Logitech”, “battery”: “AA”, “wireless”: true}
机械键盘 | {“rgb”: true, “layout”: “TKL”, “brand”: “Cherry”, “switches”: “MX Brown”}
(2 rows)
product_name | attributes
————–+—————————————————————————————————-
智能手机 | {“colors”: [“黑色”, “白色”, “蓝色”], “model”: “15 Pro”, “brand”: “iPhone”, “features”: [“5G”, “Face ID”,
“MagSafe”], “specs”: {“cpu”: “A17 Pro”, “ram”: “8GB”, “storage”: “256GB”}}
(1 row)
4. JSON数据修改
更新JSON数据:
UPDATE fgedu_products_json
SET attributes = jsonb_set(attributes, ‘{ram}’, ‘”32GB”‘)
WHERE product_name = ‘笔记本电脑’;
SELECT product_name, attributes FROM fgedu_products_json WHERE product_name = ‘笔记本电脑’;
执行结果:
————–+————————————————————————————
笔记本电脑 | {“cpu”: “i7-12700H”, “ram”: “32GB”, “brand”: “ThinkPad”, “storage”: “512GB SSD”}
(1 row)
添加新字段:
UPDATE fgedu_products_json
SET attributes = attributes || ‘{“price”: 8999.00, “warranty”: “3年”}’
WHERE product_name = ‘笔记本电脑’;
SELECT product_name, attributes FROM fgedu_products_json WHERE product_name = ‘笔记本电脑’;
执行结果:
————–+———————————————————————————————————————
笔记本电脑 | {“cpu”: “i7-12700H”, “ram”: “32GB”, “price”: 8999.00, “brand”: “ThinkPad”, “warranty”: “3年”,
“storage”: “512GB SSD”}
(1 row)
删除字段:
UPDATE fgedu_products_json
SET attributes = attributes – ‘warranty’
WHERE product_name = ‘笔记本电脑’;
— 删除嵌套字段
UPDATE fgedu_products_json
SET attributes = attributes #- ‘{specs,ram}’
WHERE product_name = ‘智能手机’;
SELECT product_name, attributes FROM fgedu_products_json WHERE product_name IN (‘笔记本电脑’, ‘智能手机’);
执行结果:
————–+—————————————————————————————————-
笔记本电脑 | {“cpu”: “i7-12700H”, “ram”: “32GB”, “price”: 8999.00, “brand”: “ThinkPad”, “storage”: “512GB SSD”}
智能手机 | {“colors”: [“黑色”, “白色”, “蓝色”], “model”: “15 Pro”, “brand”: “iPhone”, “features”: [“5G”, “Face ID”,
“MagSafe”], “specs”: {“cpu”: “A17 Pro”, “storage”: “256GB”}}
(2 rows)
5. JSON索引
为JSONB字段创建索引:
CREATE INDEX idx_fgedu_products_attributes ON fgedu_products_json USING GIN (attributes);
— 创建特定字段的索引
CREATE INDEX idx_fgedu_products_brand ON fgedu_products_json ((attributes ->> ‘brand’));
— 查看索引
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = ‘fgedu_products_json’;
执行结果:
———————————-+———————————————————————————————
idx_fgedu_products_attributes | CREATE INDEX idx_fgedu_products_attributes ON fgedu_products_json USING gin
(attributes)
idx_fgedu_products_brand | CREATE INDEX idx_fgedu_products_brand ON fgedu_products_json USING btree
(((attributes ->> ‘brand’::text)))
(2 rows)
索引使用测试:
EXPLAIN ANALYZE
SELECT * FROM fgedu_products_json WHERE attributes @> ‘{“brand”: “ThinkPad”}’;
执行结果:
rows=1 loops=1)
Recheck Cond: (attributes @> ‘{“brand”: “ThinkPad”}’::jsonb)
-> Bitmap Index Scan on idx_fgedu_products_attributes (cost=0.00..12.05 rows=1 width=0) (actual
time=0.010..0.010 rows=1 loops=1)
Index Cond: (attributes @> ‘{“brand”: “ThinkPad”}’::jsonb)
Planning Time: 0.089 ms
Execution Time: 0.035 ms
(6 rows)
6. JSON函数详解
常用JSON函数:
学习交流加群风哥微信: itpux-com
SELECT
product_name,
key,
value
FROM fgedu_products_json,
jsonb_each(attributes)
WHERE product_name = ‘无线鼠标’;
执行结果:
————–+———-+————
无线鼠标 | dpi | 16000
无线鼠标 | brand | “Logitech”
无线鼠标 | battery | “AA”
无线鼠标 | wireless | true
(4 rows)
JSON数组函数:
SELECT
product_name,
jsonb_array_elements(attributes -> ‘colors’) AS color
FROM fgedu_products_json
WHERE attributes ? ‘colors’;
执行结果:
————–+———-
智能手机 | “黑色”
智能手机 | “白色”
智能手机 | “蓝色”
(3 rows)
JSON构建函数:
SELECT jsonb_build_object(
‘name’, ‘风哥1号’,
‘age’, 30,
‘skills’, ARRAY[‘PostgreSQL’, ‘Python’, ‘Linux’]
) AS person_info;
执行结果:
—————————————————————–
{“age”: 30, “name”: “风哥1号”, “skills”: [“PostgreSQL”, “Python”, “Linux”]}
(1 row)
SELECT
product_name,
jsonb_agg(key) AS all_keys
FROM fgedu_products_json,
jsonb_object_keys(attributes) AS key
GROUP BY product_name
LIMIT 3;
执行结果:
风哥提示:更多视频教程www.fgedu.net.cn
————–+————————————-
笔记本电脑 | [“cpu”, “ram”, “price”, “brand”, “storage”]
无线鼠标 | [“dpi”, “brand”, “battery”, “wireless”]
机械键盘 | [“rgb”, “layout”, “brand”, “switches”]
(3 rows)
JSON类型转换:
SELECT
‘{“a”: 1, “b”: 2}’::JSONB AS jsonb_val,
‘{“a”: 1, “b”: 2}’::JSONB -> ‘a’ AS a_json,
(‘{“a”: 1, “b”: 2}’::JSONB ->> ‘a’)::INTEGER AS a_int;
执行结果:
—————-+——–+——-
{“a”: 1, “b”: 2} | 1 | 1
(1 row)
7. JSON路径表达式
PostgreSQL 12+支持JSONPath表达式:
SELECT
product_name,
jsonb_path_query(attributes, ‘$.specs.*’) AS spec_values
FROM fgedu_products_json
WHERE attributes -> ‘specs’ IS NOT NULL;
执行结果:
学习交流加群风哥QQ113257174
————–+—————
智能手机 | “A17 Pro”
智能手机 | “256GB”
(2 rows)
SELECT
product_name,
jsonb_path_query_array(attributes, ‘$.colors[*]’) AS all_colors
FROM fgedu_products_json
WHERE attributes ? ‘colors’;
执行结果:
————–+————————-
智能手机 | [“黑色”, “白色”, “蓝色”]
(1 row)
SELECT
product_name,
jsonb_path_exists(attributes, ‘$.specs.cpu’) AS has_cpu_spec
FROM fgedu_products_json;
执行结果:
————–+————–
笔记本电脑 | f
无线鼠标 | f
机械键盘 | f
智能手机 | t
显示器套装 | f
(5 rows)
8. 实战案例:配置管理
创建配置管理系统:
CREATE TABLE fgedu_fgfgapp_config (
config_id SERIAL PRIMARY KEY,
fgapp_name VARCHAR(100) NOT NULL,
environment VARCHAR(20) NOT NULL,
config_data JSONB NOT NULL,
version INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(fgapp_name, environment, version)
);
— 插入配置数据
INSERT INTO fgedu_fgfgapp_config (fgapp_name, environment, config_data) VALUES
(‘web-fgapp’, ‘development’, ‘{
“fgedudb”: {
“fgedu.net.cn”: “localfgedu.net.cn”,
“port”: 5432,
“name”: “dev_db”,
“pool_size”: 10
},
“cache”: {
“type”: “redis”,
“fgedu.net.cn”: “localfgedu.net.cn”,
“port”: 6379,
“ttl”: 3600
},
“logging”: {
“level”: “debug”,
“output”: [“console”, “file”]
}
}’),
(‘web-fgapp’, ‘production’, ‘{
“fgedudb”: {
“fgedu.net.cn”: “prod-db.fgedu.net.cn”,
“port”: 5432,
“name”: “prod_db”,
“pool_size”: 50
},
“cache”: {
“type”: “redis-cluster”,
“fgedu.net.cns”: [“redis1.fgedu.net.cn”, “redis2.fgedu.net.cn”],
“port”: 6379,
“ttl”: 7200
},
“logging”: {
“level”: “info”,
“output”: [“file”, “syslog”]
}
}’),
(‘api-service’, ‘production’, ‘{
“server”: {
“fgedu.net.cn”: “0.0.0.0”,
“port”: 8080,
“workers”: 4
},
“rate_limit”: {
“enabled”: true,
“requests_per_minute”: 100
},
“features”: {
“auth”: true,
“logging”: true,
“metrics”: true
}
}’);
— 查询配置
SELECT fgapp_name, environment, config_data FROM fgedu_fgfgapp_config;
执行结果:
————–+————–+————————————————————————————————–
web-fgapp | development | {“cache”: {“fgedu.net.cn”: “localfgedu.net.cn”, “port”: 6379, “ttl”: 3600, “type”: “redis”},
“logging”: {“level”: “debug”, “output”: [“console”, “file”]}, “fgedudb”: {“fgedu.net.cn”: “localfgedu.net.cn”, “port”:
5432, “name”: “dev_db”, “pool_size”: 10}}
web-fgapp | production | {“cache”: {“port”: 6379, “ttl”: 7200, “fgedu.net.cns”: [“redis1.fgedu.net.cn”,
“redis2.fgedu.net.cn”], “type”: “redis-cluster”}, “logging”: {“level”: “info”, “output”: [“file”, “syslog”]},
“fgedudb”: {“fgedu.net.cn”: “prod-db.fgedu.net.cn”, “port”: 5432, “name”: “prod_db”, “pool_size”: 50}}
api-service | production | {“server”: {“fgedu.net.cn”: “0.0.0.0”, “port”: 8080, “workers”: 4}, “features”: {“auth”:
true, “logging”: true, “metrics”: true}, “rate_limit”: {“enabled”: true, “requests_per_minute”: 100}}
(3 rows)
查询特定配置项:
SELECT
fgapp_name,
environment,
config_data #> ‘{fgedudb,fgedu.net.cn}’ AS db_fgedu.net.cn,
config_data #> ‘{fgedudb,port}’ AS db_port,
config_data #> ‘{fgedudb,pool_size}’ AS pool_size
FROM fgedu_fgfgapp_config
WHERE config_data ? ‘fgedudb’;
执行结果:
————–+————–+———————-+———+———–
web-fgapp | development | “localfgedu.net.cn” | 5432 | 10
web-fgapp | production | “prod-db.fgedu.net.cn”| 5432 | 50
(2 rows)
比较不同环境配置:
SELECT
d.environment AS dev_env,
p.environment AS prod_env,
d.config_data -> ‘fgedudb’ AS dev_db_config,
p.config_data -> ‘fgedudb’ AS prod_db_config
FROM fgedu_fgfgapp_config d
JOIN fgedu_fgfgapp_config p ON d.fgapp_name = p.fgapp_name
WHERE d.fgapp_name = ‘web-fgapp’
AND d.environment = ‘development’
AND p.environment = ‘production’;
执行结果:
————-+———–+—————————————–+————————————————-
development | production| {“fgedu.net.cn”: “localfgedu.net.cn”, “port”: 5432, “name”: “dev_db”, “pool_size”: 10} | {“fgedu.net.cn”:
“prod-db.fgedu.net.cn”, “port”: 5432, “name”: “prod_db”, “pool_size”: 50}
(1 row)
更新配置:
from oracle:www.itpux.com
INSERT INTO fgedu_fgfgapp_config (fgapp_name, environment, config_data, version)
SELECT
fgapp_name,
environment,
jsonb_set(config_data, ‘{fgedudb,pool_size}’, ‘100’),
version + 1
FROM fgedu_fgfgapp_config
WHERE fgapp_name = ‘web-fgapp’ AND environment = ‘production’
ORDER BY version DESC LIMIT 1;
— 查看配置版本历史
SELECT fgapp_name, environment, version,
config_data #> ‘{fgedudb,pool_size}’ AS pool_size,
created_at
FROM fgedu_fgfgapp_config
WHERE fgapp_name = ‘web-fgapp’ AND environment = ‘production’
ORDER BY version;
执行结果:
———-+————-+———+———–+—————————
web-fgapp | production | 1 | 50 | 2026-04-04 14:30:25.123
web-fgapp | production | 2 | 100 | 2026-04-04 14:30:30.456
(2 rows)
9. 清理环境
清理测试表:
更多学习教程公众号风哥教程itpux_com
DROP TABLE IF EXISTS fgedu_json_test;
DROP TABLE IF EXISTS fgedu_products_json;
DROP TABLE IF EXISTS fgedu_fgfgapp_config;
执行结果:
DROP TABLE
DROP TABLE
本文由风哥教程整理发布,仅用于学习测试使用,转载注明出处:http://www.fgedu.net.cn/10327.html
