一、开始之前
在进行单表查询之前,请确保:
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 的可读性:
SELECT c_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;
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(使用索引)或使用堆排序优化
21、分页优化建议
深度分页问题:
— 性能差:需要扫描并跳过前 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_orderkey FROM orders ORDER BY o_orderkey LIMIT 1000000, 20) tmp ON o.o_orderkey = tmp.o_orderkey;
22、聚合查询
聚合查询用于对数据进行统计分析,常用的聚合函数包括 COUNT、SUM、AVG、MAX、MIN 等。
23、基础聚合函数
统计总订单数:
SELECT COUNT(*) AS total_ordersFROM orders;
统计总销售额:
SELECT SUM(o_totalprice) AS total_salesFROM orders;
查询最大和最小订单金额:
SELECT MAX(o_totalprice) AS max_order_amount, MIN(o_totalprice) AS min_order_amount, AVG(o_totalprice) AS avg_order_amountFROM orders;
24、分组聚合
使用 GROUP BY 对数据进行分组统计。
统计每个市场细分的客户数量:
SELECT c_mktsegment, COUNT(*) AS customer_countFROM customerGROUP BY c_mktsegmentORDER BY customer_count DESC;
统计每年的订单数量和总销售额:
SELECT YEAR(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;
统计每个订单状态的订单数量:
SELECT o_orderstatus, COUNT(*) AS status_count, SUM(o_totalprice) AS total_amountFROM ordersGROUP BY o_orderstatus;
25、HAVING 子句
HAVING 用于过滤分组后的结果(与 WHERE 过滤原始行不同)。
查询订单数量超过1000的客户:
SELECT o_custkey, COUNT(*) AS order_count, SUM(o_totalprice) AS total_spentFROM ordersGROUP BY o_custkeyHAVING COUNT(*) > 1000ORDER BY total_spent DESC;
查询平均订单金额大于10000的年份:
SELECT YEAR(o_orderdate) AS order_year, AVG(o_totalprice) AS avg_amountFROM ordersGROUP BY YEAR(o_orderdate)HAVING AVG(o_totalprice) > 10000ORDER BY order_year;
26、去重统计
使用 DISTINCT 进行去重统计:
统计不同市场细分的数量:
SELECT COUNT(DISTINCT c_mktsegment) AS segment_countFROM customer;
统计有订单的客户数量:
SELECT COUNT(DISTINCT o_custkey) AS active_customersFROM orders;
27、聚合查询优化
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);– 此查询无需回表,直接从索引读取SELECT YEAR(o_orderdate) AS order_year, SUM(o_totalprice) AS total_salesFROM ordersGROUP BY YEAR(o_orderdate);
4. 分区表加速:
·如果表按日期分区,聚合查询可以并行在各分区上执行。
·TDSQL Boundless 会自动进行分区裁剪,只扫描相关分区。
— 假设 orders 表按年份分区– 此查询只扫描 1995 年的分区SELECT COUNT(*) AS order_count, SUM(o_totalprice) AS total_salesFROM ordersWHERE o_orderdate BETWEEN '1995-01-01' AND '1995-12-31';