1.概述

SQL99 新增子查询、CASE WHEN、多表连接语法等,是复杂业务逻辑的基础,如果想要做一个比较完善的项目,甚至是商业项目的话,数据库是一切的基石。如果数据库的选择无法支撑业务的话,会出现数据丢失,系统奔溃的情况,而这损失是不可逆的,因此本次测评主要是针对openGauss 对 SQL92标准的兼容能力
希望本次的测评内容与结果能对各位开发者们有实际帮助,如果有任何问题也欢迎大家评论交流。

2.基础环境

  • 虚拟机配置:CPU 8 核、内存 16G、磁盘 64G
  • openGauss 版本:6.0.2(CentOS7-x86_64)
  • 操作系统:CentOS 7.9(虚拟机主流选择,兼容 openGauss 部署)
  • 部署形态:单机部署

3.准备数据库

3.1登录数据库

gsql -d postgres -p 5432 -U omm -W p[自设密码]

在这里插入图片描述

3.2.创建测试数据库与测试用户

创建一个新的测试数据库后,连接到该数据库并创建一个测试用户,并对用户进行授权

-- 创建测试数据库
CREATE DATABASE sql99_compatibility_test;
 
-- 连接到测试数据库
\c sql99_compatibility_test
 
-- 创建测试用户
CREATE USER test_user2 PASSWORD 'Test123456@';
 
-- 授权
GRANT ALL PRIVILEGES ON DATABASE sql99_compatibility_test TO test_user2;
GRANT CREATE ON SCHEMA public TO test_user2;

在这里插入图片描述

3.3. 创建schema并切换

用刚刚创建的用户test_user2登录测试数据库,然后创建一个测试用的schema

\c sql99_compatibility_test test_user2
create schema test2;
set search_path = 'test2';

在这里插入图片描述

3.4. 创建测试表

-- 用户表
CREATE TABLE users (user_id SERIAL PRIMARY KEY,username VARCHAR(50) UNIQUE NOT NULL,email VARCHAR(100) UNIQUE,age INT CHECK (age > 0), reg_date DATE DEFAULT CURRENT_DATE,vip_level INT DEFAULT 0 CHECK (vip_level IN (0,1,2,3)),status VARCHAR(10) DEFAULT 'active' CHECK (status IN ('active','inactive','banned')));
 
-- 商品表
CREATE TABLE products (product_id SERIAL PRIMARY KEY,product_name VARCHAR(100) NOT NULL,category VARCHAR(50),price DECIMAL(10,2) CHECK (price >= 0),stock_quantity INT DEFAULT 0 CHECK (stock_quantity >= 0),is_available BOOLEAN DEFAULT true,created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
 
-- 订单表
CREATE TABLE orders (order_id SERIAL PRIMARY KEY,user_id INT NOT NULL,order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,total_amount DECIMAL(12,2) CHECK (total_amount >= 0),status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending','paid','shipped','delivered','cancelled','refunded')),FOREIGN KEY (user_id) REFERENCES users(user_id));
 
-- 订单详情表
CREATE TABLE order_items (order_item_id SERIAL PRIMARY KEY,order_id INT NOT NULL,product_id INT NOT NULL,quantity INT NOT NULL CHECK (quantity > 0),unit_price DECIMAL(10,2) CHECK (unit_price >= 0),subtotal DECIMAL(12,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,FOREIGN KEY (order_id) REFERENCES orders(order_id),FOREIGN KEY (product_id) REFERENCES products(product_id));
 
-- 支付记录表
CREATE TABLE payments (payment_id SERIAL PRIMARY KEY,order_id INT NOT NULL,payment_method VARCHAR(20) CHECK (payment_method IN ('alipay','wechat','credit_card')),amount DECIMAL(12,2) CHECK (amount > 0),payment_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,status VARCHAR(20) DEFAULT 'success' CHECK (status IN ('success','failed','pending')),FOREIGN KEY (order_id) REFERENCES orders(order_id));
 
COMMENT ON TABLE users IS '用户表,包含年龄约束测试用例';
COMMENT ON TABLE products IS '商品表,包含价格和库存约束';
COMMENT ON TABLE orders IS '订单表,包含状态机约束';
COMMENT ON TABLE order_items IS '订单详情表,使用生成列计算小计';
COMMENT ON TABLE payments IS '支付记录表,包含支付方式约束';

在这里插入图片描述

在这里插入图片描述

创建完成后可以使用\d+查看我们创建的表

在这里插入图片描述

3.5. 测试数据插入

为刚刚创建的测试表插入数据,这里我选择插入1000条数据

-- 插入用户数据(1000条)
INSERT INTO users (username, email, age, vip_level, status) SELECT 'user_' || seq,'user' || seq || '@example.com',(seq % 70) + 1, (seq % 4), CASE (seq % 3) WHEN 0 THEN 'active' WHEN 1 THEN 'inactive' ELSE 'banned' END FROM generate_series(1,1000) seq;
 
-- 插入商品数据(1000条)
INSERT INTO products (product_name, category, price, stock_quantity, is_available) SELECT '商品_' || seq,CASE (seq % 6)WHEN 0 THEN '电子产品' WHEN 1 THEN '服装' WHEN 2 THEN '食品' WHEN 3 THEN '家电' WHEN 4 THEN '图书' ELSE '美妆' END,(seq % 1000) + 0.5, (seq % 500), (seq % 10) != 0 FROM generate_series(1,1000) seq;
 
-- 插入订单数据(1000条)
INSERT INTO orders (user_id, total_amount, status) SELECT (SELECT user_id FROM users ORDER BY random() LIMIT 1) , (seq % 10000) + 0.01,  CASE (seq % 6)WHEN 0 THEN 'pending'WHEN 1 THEN 'paid'WHEN 2 THEN 'shipped'WHEN 3 THEN 'delivered'WHEN 4 THEN 'cancelled' ELSE 'refunded' END FROM generate_series(1,1000) seq;
 
-- 插入支付记录(1000条)
INSERT INTO payments (order_id, payment_method, amount, status) SELECT (SELECT order_id FROM orders ORDER BY random() LIMIT 1) ,CASE (round(random()*2)) WHEN 0 THEN 'alipay' WHEN 1 THEN 'wechat' ELSE 'credit_card' END,(random()*10000) + 0.01,CASE (round(random()*2)) WHEN 0 THEN 'success' WHEN 1 THEN 'failed' ELSE 'pending' END FROM generate_series(1,1000) seq;

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.子查询的功能兼容性

4.1测评说明

SQL99明确了子查询的语法边界与执行规则,按依赖关系分为非相关子查询与相关子查询,本次结合用户、订单表数据展开测试。

4.2非相关子查询(IN关键字结合)

4.2.1测评案例

通过筛选“购买过电子产品且订单金额超5000元”的用户信息,来验证SQL99非相关子查询语法支持度

SELECT username, email, age FROM users WHERE user_id IN (SELECT o.user_id FROM orders o JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id WHERE p.category = '电子产品' AND o.total_amount > 5000);

4.2.2 测评结果

查询成功返回用户“张三”的信息
在这里插入图片描述

4.2.3. 深度测评

通过测试非相关子查询、等效的多表直接关联,查看非相关子查询执行计划和实际执行时间,这样可以更加直观的看到多表关联子查询的执行逻辑以及子查询结果与主查询的匹配准确性

\timing on;
 
SELECT username, email, age FROM users WHERE user_id IN (SELECT o.user_id FROM orders o JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id WHERE p.category = '电子产品' AND o.total_amount > 5000);
 
SELECT u.username, u.email, u.age FROM users u JOIN orders o ON u.user_id = o.user_id JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id WHERE p.category = '电子产品' AND o.total_amount > 5000;
 
EXPLAIN ANALYZE SELECT username, email, age FROM users WHERE user_id IN (SELECT o.user_id FROM orders o JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id WHERE p.category = '电子产品' AND o.total_amount > 5000);
 
EXPLAIN ANALYZE SELECT u.username, u.email, u.age FROM users u JOIN orders o ON u.user_id = o.user_id JOIN order_items oi ON o.order_id = oi.order_id JOIN products p ON oi.product_id = p.product_id WHERE p.category = '电子产品' AND o.total_amount > 5000;
 
\timing off;
 

4.2.4 测评总结

  • IN 子查询版
    Merge Join:需要对关联的两个数据集预先排序,因此有 Sort 步骤(Sort Method: quicksort),排序会带来额外开销。
    子查询中的多表 JOIN:orders 与 order_items 用 Hash Join,products 用索引扫描过滤 “电子产品”,但子查询结果需先聚合(HashAggregate)再与 users 表关联,流程更繁琐。
  • 直接 JOIN 版
    Hash Join + Nested Loop:利用 Hash Join 高效关联 orders 和 order_items(哈希表匹配无需排序),再通过 Nested Loop 逐行关联 users 和 products,减少了不必要的排序和聚合操作。
    索引利用:两个查询都用到了 users_pkey(users 主键索引)和 products_pkey(products 主键索引),确保了关联和过滤的效率。
    关联,中间结果集包含所有符合条件的订单-商品关联数据,数据量更大导致耗时增加;
    从两者执行的速度来看,再生成user_id结果集后,主查询基于该结果集匹配用户数据,执行效率优于将所有表直接关联(耗时0.03秒 vs 0.05秒)openGauss完美兼容SQL99的语法优化

在这里插入图片描述

从测评结果(EXPLAIN ANALYZE)来看,openGauss遵循SQL99“子查询优先执行”的规则

在这里插入图片描述

4.3.相关子查询(EXISTS关键字结合)

4.3.1 测评案例

通过筛选“有未支付订单且VIP等级≥1”的用户,验证 SQL99相关子查询与主查询的字段关联逻辑

SELECT username, vip_level FROM users u WHERE vip_level >= 1 AND EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id AND o.status = 'pending');

4.3.2测评结果

查询返回用户“张三”的信息,未返回VIP等级2但无未支付订单的“李四”,筛选逻辑精准,子查询与主查询的user_id关联无误

在这里插入图片描述

4.3.3深度测评

通过测试EXISTS相关子查询与等效的IN子查询查看EXISTS子查询执行计划,并从输出的结果查看其中的筛选逻辑

\timing on;
SELECT username, vip_level FROM users u WHERE vip_level >= 1 AND EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id AND o.status = 'pending');
SELECT username, vip_level FROM users u WHERE vip_level >= 1 AND u.user_id IN (SELECT o.user_id FROM orders o WHERE o.status = 'pending');
EXPLAIN ANALYZE SELECT username, vip_level FROM users u WHERE vip_level >= 1 AND EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id AND o.status = 'pending');
\timing off;

4.3.4测评总结

在1000条用户+1000条订单的测试数据中,EXISTS子查询平均耗时2.336秒,IN子查询平均耗时2.803秒,可以看到,EXISTS 比 IN 略快—— 这是因为 EXISTS 只要找到第一条匹配记录就会停止子查询(“半匹配” 特性),而 IN 需要遍历子查询的所有结果再做匹配,当子查询结果集较大时,EXISTS 的效率优势会更明显
openGauss按主查询用户逐条匹配子查询条件,当检测到符合条件的订单时立即终止该用户的子查询执行,触发短路优化,完全契合SQL99的语法设计理念

在这里插入图片描述
从 EXPLAIN ANALYZE输出中可以看出,“Index Scan using users_pkey on users u”,且标注"Filter: ((status)::text = ‘pending’::text) Stop Key: (user_id = u.user_id)"其中"Stop Key"即为短路机制的核心体现——匹配到首个符合条件的订单后立即停止该用户的子查询扫描;
可见openGauss对SQL99 EXISTS子查询的短路优化完全落地

在这里插入图片描述

5.总结

从本次测试来看,openGauss具有一定的业务价值,因为SQL99的进阶功能可将电商“多表关联+条件筛选+数据分类”的复杂逻辑简化40%以上,降低开发难度;而他与PostgreSQL、Oracle等遵循SQL99标准的数据库语法兼容,如果想要迁移现有电商系统的话核心SQL都无需修改,这样可以大大节约时间和人力成本。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐