本文档介绍如何在 TDSQL Boundless 数据库中对单表数据进行高效查询,包括基础查询语法、查询优化技巧以及 TDSQL Boundless 特有的优化机制。
开始之前
在进行单表查询之前,请确保:
1. 已创建 TDSQL Boundless 实例。
2. 已导入测试数据:本文档使用 TPC-H 基准测试数据集作为示例,详情请参考 TPC-H 基准测试数据模型。
3. 已建立数据库连接:通过 MySQL 客户端或应用程序连接到 TDSQL Boundless。
简单查询
最基础的查询操作是从表中读取特定列的数据。
基本 SELECT 语句
查询客户的基本信息:
SELECT c_custkey, c_name, c_mktsegment FROM customer;
查询订单的关键字段:
SELECT o_orderkey, o_custkey, o_orderdate, o_totalprice FROM orders;
查询所有列
使用
* 可以查询表中的所有列:SELECT * FROM customer;
实践教程:
在生产环境中,避免使用
SELECT *,应显式指定需要的列。原因:减少网络传输量、提高查询性能、避免读取不必要的大字段(如
c_comment)。使用别名
为列或表指定别名可以提高 SQL 的可读性:
SELECTc_custkey AS customer_id,c_name AS customer_name,c_acctbal AS account_balanceFROM customer AS c;
对结果进行筛选
使用
WHERE 子句可以过滤查询结果,只返回满足条件的数据。等值条件查询
查询特定市场细分的客户:
SELECT c_custkey, c_name, c_acctbalFROM customerWHERE c_mktsegment = 'AUTOMOBILE';
查询特定订单状态的订单:
SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersWHERE o_orderstatus = 'O'; -- O: Open, F: Finished, P: Pending
比较条件查询
查询账户余额大于5000的客户:
SELECT c_custkey, c_name, c_acctbalFROM customerWHERE c_acctbal > 5000.00;
查询1995年之后的订单:
SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersWHERE o_orderdate >= '1995-01-01';
范围查询
使用
BETWEEN 进行范围查询:SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersWHERE o_orderdate BETWEEN '1995-01-01' AND '1995-12-31';
多条件组合
使用
AND、OR 组合多个条件:SELECT c_custkey, c_name, c_acctbal, c_mktsegmentFROM customerWHERE c_mktsegment = 'AUTOMOBILE'AND c_acctbal > 5000.00;
模糊查询
使用
LIKE 进行模糊匹配:SELECT c_custkey, c_nameFROM customerWHERE c_name LIKE 'Customer%';
对结果进行排序
使用
ORDER BY 子句可以对查询结果进行排序。升序排序(默认)
按订单日期升序排列:
SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersORDER BY o_orderdate;
或显式指定
ASC:SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersORDER BY o_orderdate ASC;
降序排序
查询账户余额最高的客户:
SELECT c_custkey, c_name, c_acctbalFROM customerORDER BY c_acctbal DESC;
查询最近的订单:
SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersORDER BY o_orderdate DESC;
多列排序
可以按多个列进行排序,优先级从左到右:
SELECT o_orderkey, o_orderdate, o_totalprice, o_orderpriorityFROM ordersORDER BY o_orderdate DESC, o_totalprice DESC;
排序优化建议
TDSQL Boundless 优化要点:
1. 索引优化:在
ORDER BY 的列上建立索引,避免 filesort。2. 覆盖索引:如果查询的列都在索引中,TDSQL Boundless 可以直接从索引读取,无需回表。
3. 索引顺序:
ORDER BY 的列顺序应与索引定义顺序一致。示例:优化排序性能
-- 创建组合索引CREATE INDEX idx_orderdate_totalprice ON orders(o_orderdate DESC, o_totalprice DESC);-- 此查询可以直接使用索引,避免排序SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersORDER BY o_orderdate DESC, o_totalprice DESC;
通过
EXPLAIN 查看执行计划:EXPLAIN SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersORDER BY o_orderdate DESC;
如果执行计划中没有
Using filesort,说明使用了索引排序,性能最优。限制查询结果数量
使用
LIMIT 子句可以限制返回的结果行数,常用于分页查询或获取 Top N 结果。获取前 N 条记录
查询前10个最大金额的订单:
SELECT o_orderkey, o_orderdate, o_totalpriceFROM ordersORDER BY o_totalprice DESCLIMIT 10;
查询账户余额最高的前20名客户:
SELECT c_custkey, c_name, c_acctbalFROM customerORDER BY c_acctbal DESCLIMIT 20;
分页查询
使用
LIMIT offset, count 或 LIMIT count OFFSET offset 进行分页:方式一:
-- 查询第 2 页数据(每页 20 条)SELECT c_custkey, c_name, c_acctbalFROM customerORDER BY c_custkeyLIMIT 20, 20; -- offset=20, count=20
方式二(推荐):
-- 更清晰的写法SELECT c_custkey, c_name, c_acctbalFROM customerORDER BY c_custkeyLIMIT 20 OFFSET 20;
LIMIT 优化机制
TDSQL Boundless 的 LIMIT 优化:
1. 提前终止扫描:
TDSQL Boundless 在扫描到足够的数据后会立即停止,不会扫描整个表。
例如
LIMIT 10 只需找到10条数据即可返回。2. 索引下推:
如果查询条件和排序字段都有索引,TDSQL Boundless 会在存储引擎层就应用
LIMIT。减少数据传输和内存使用。
3. TopN 优化:
ORDER BY ... LIMIT N 会使用堆排序算法,空间复杂度 O(N)。避免全表排序,性能提升显著。
性能对比示例:
-- 无 LIMIT:需要扫描全表并排序SELECT o_orderkey, o_totalpriceFROM ordersORDER BY o_totalprice DESC;-- 扫描行数: 1,500,000 (全表)-- 有 LIMIT:提前终止SELECT o_orderkey, o_totalpriceFROM ordersORDER BY o_totalprice DESCLIMIT 10;-- 扫描行数: ~10(使用索引)或使用堆排序优化
分页优化建议
深度分页问题:
-- 性能差:需要扫描并跳过前 1,000,000 行SELECT o_orderkey, o_orderdateFROM ordersORDER BY o_orderkeyLIMIT 1000000, 20;
优化方案:使用范围查询代替 OFFSET
-- 方式一:记录上次查询的最大 IDSELECT o_orderkey, o_orderdateFROM ordersWHERE o_orderkey > 1000000 -- 上次最后一条记录的 IDORDER BY o_orderkeyLIMIT 20;-- 方式二:使用覆盖索引 + 延迟关联SELECT o.o_orderkey, o.o_orderdate, o.o_totalpriceFROM orders oINNER JOIN (SELECT o_orderkeyFROM ordersORDER BY o_orderkeyLIMIT 1000000, 20) tmp ON o.o_orderkey = tmp.o_orderkey;
聚合查询
聚合查询用于对数据进行统计分析,常用的聚合函数包括
COUNT、SUM、AVG、MAX、MIN 等。基础聚合函数
统计总订单数:
SELECT COUNT(*) AS total_ordersFROM orders;
统计总销售额:
SELECT SUM(o_totalprice) AS total_salesFROM orders;
查询最大和最小订单金额:
SELECTMAX(o_totalprice) AS max_order_amount,MIN(o_totalprice) AS min_order_amount,AVG(o_totalprice) AS avg_order_amountFROM orders;
分组聚合
使用
GROUP BY 对数据进行分组统计。统计每个市场细分的客户数量:
SELECTc_mktsegment,COUNT(*) AS customer_countFROM customerGROUP BY c_mktsegmentORDER BY customer_count DESC;
统计每年的订单数量和总销售额:
SELECTYEAR(o_orderdate) AS order_year,COUNT(*) AS order_count,SUM(o_totalprice) AS total_sales,AVG(o_totalprice) AS avg_order_amountFROM ordersGROUP BY YEAR(o_orderdate)ORDER BY order_year DESC;
统计每个订单状态的订单数量:
SELECTo_orderstatus,COUNT(*) AS status_count,SUM(o_totalprice) AS total_amountFROM ordersGROUP BY o_orderstatus;
HAVING 子句
HAVING 用于过滤分组后的结果(与 WHERE 过滤原始行不同)。查询订单数量超过1000的客户:
SELECTo_custkey,COUNT(*) AS order_count,SUM(o_totalprice) AS total_spentFROM ordersGROUP BY o_custkeyHAVING COUNT(*) > 1000ORDER BY total_spent DESC;
查询平均订单金额大于10000的年份:
SELECTYEAR(o_orderdate) AS order_year,AVG(o_totalprice) AS avg_amountFROM ordersGROUP BY YEAR(o_orderdate)HAVING AVG(o_totalprice) > 10000ORDER BY order_year;
去重统计
使用
DISTINCT 进行去重统计:统计不同市场细分的数量:
SELECT COUNT(DISTINCT c_mktsegment) AS segment_countFROM customer;
统计有订单的客户数量:
SELECT COUNT(DISTINCT o_custkey) AS active_customersFROM orders;
聚合查询优化
TDSQL Boundless 聚合查询优化要点:
1. 索引加速分组:
-- 在 GROUP BY 列上建立索引CREATE INDEX idx_mktsegment ON customer(c_mktsegment);-- 此查询可以利用索引加速分组SELECT c_mktsegment, COUNT(*)FROM customerGROUP BY c_mktsegment;
2. 松散索引扫描(Loose Index Scan):
当
GROUP BY 的列是索引前缀时,TDSQL Boundless 可以跳过不相关的索引记录。通过
EXPLAIN 查看是否使用了 Using index for group-by。3. 覆盖索引优化:
-- 创建覆盖索引CREATE INDEX idx_orderdate_totalprice ON orders(o_orderdate, o_totalprice);-- 此查询无需回表,直接从索引读取SELECTYEAR(o_orderdate) AS order_year,SUM(o_totalprice) AS total_salesFROM ordersGROUP BY YEAR(o_orderdate);
4. 分区表加速:
如果表按日期分区,聚合查询可以并行在各分区上执行。
TDSQL Boundless 会自动进行分区裁剪,只扫描相关分区。
-- 假设 orders 表按年份分区-- 此查询只扫描 1995 年的分区SELECTCOUNT(*) AS order_count,SUM(o_totalprice) AS total_salesFROM ordersWHERE o_orderdate BETWEEN '1995-01-01' AND '1995-12-31';