Zhonghui

每个不曾起舞的日子,都是对生命的辜负

User Tools

Site Tools


程序:数据库:sql语法

SQL语法


基础语法

P1

# SQL语句(的关键字)不区分大小写
# 语句末尾的分号也是可选的
# 注释好像是使用"--"符号
# 代码好像可以随便换行
 
# 显示所有数据库
SHOW DATABASES;
 
# 选择数据库
USE RUNOOB;
 
# 显示所有的表
SHOW TABLES;
 
# 数据操作
 
# * 是通配符,表示选择所有的「列」
# 这里没有 WHERE 进行筛选,所以会输出所有行
SELECT * FROM Websites;
 
# 查询
SELECT column_name(s)
FROM TABLE_NAME
WHERE condition # 筛选「行」
ORDER BY column_name [ASC|DESC]
 
# 插入数据
INSERT INTO TABLE_NAME (column1, column2, ...)
VALUES (value1, value2, ...)
 
# 更新数据
UPDATE TABLE_NAME
SET column1 = value1, column2 = value2, ...
WHERE condition
 
# 删除(行)
DELETE FROM TABLE_NAME
WHERE condition
 
# 表操作
 
# 创建新TABLE
CREATE TABLE TABLE_NAME (
    column1 data_type CONSTRAINT,
    column2 data_type CONSTRAINT,
    ...
)
 
# 增加「列」
ALTER TABLE TABLE_NAME
ADD column_name data_type
 
# 删除「列」
ALTER TABLE TABLE_NAME
DROP COLUMN column_name
 
# 删除TABLE
DROP TABLE TABLE_NAME

P2

# IF
# 1.流程控制(不同数据库的语法都有些许不同)
# 2.用于根据条件返回不同的值
SELECT 
    name,
    score,
    IF(score >= 60, '及格', '不及格') AS RESULT
FROM students;
 
# 各种 JOIN 关系(聚合),对 SELECT 的结果使用别名
-- 插入数据(BQ语法)
INSERT INTO `ds.results` (id, name, score)
SELECT
    student_data.id,
    student_data.name,
    score_data.score
FROM (
    SELECT id, name
    FROM `ds.students`
    WHERE name LIKE 'G%'
) student_data -- alias for the subquery 一个别名
-- 不同的 JOIN 就是不同类型的集合操作,但本质上都是表的合并
LEFT OUTER JOIN ( -- LEFT OUTER JOIN == LEFT JOIN
    SELECT id, score
    FROM `ds.scores`
) score_data
ON (student_data.id = score_data.id) -- 两张表的拼接规则
WHERE score_data.score > 80; -- filter the final result (after join)

高级语法:聚合&分组

GROUP BY

-- aggregate_function是聚合函数(如 COUNT/SUM/AVG/MIN/MAX)对分组后的多行做汇总
SELECT column_name(s), aggregate_function(column_name)
FROM TABLE_NAME WHERE condition
GROUP BY column_name(s)
id customer_id status amount order_date
1 C001 paid 100.0 2025-09-01
2 C002 paid 200.0 2025-09-01
3 C001 refund 50.0 2025-09-02
4 C001 paid 120.0 2025-09-03
5 C002 paid 80.0 2025-09-03
SELECT
  customer_id,
  COUNT(*)        AS paid_orders,
  SUM(amount)     AS total_amount
FROM orders
WHERE STATUS = 'paid'          -- 先筛选出已支付订单
GROUP BY customer_id;          -- 再按客户分组
customer_id paid_orders total_amount
C001 2 220.0
C002 2 280.0
  1. SELECT 里出现的非聚合列,必须出现在 GROUP BY(少数数据库的宽松模式除外,别依赖它)
  2. WHERE 是分组前过滤;想对“分组后的结果”再过滤,用 HAVING

HAVING

-- WHERE:分组之前过滤“行”(不能用聚合函数)
-- HAVING:分组之后过滤“组”(可以用聚合函数,如 COUNT/SUM/AVG)
 
SELECT column_name(s), aggregate_function(column_name)
FROM TABLE_NAME
GROUP BY column_name(s)
HAVING condition
-- 只保留“订单数 ≥ 3”的客户
SELECT customer_id, COUNT(*) AS orders_cnt, SUM(amount) AS total_amount
FROM orders
WHERE STATUS = 'paid'                 -- 先把未支付的行排除(行级过滤)
GROUP BY customer_id
-- TODO: 这里能不能用 orders_cnt 这个变量?
HAVING COUNT(*) >= 3;                 -- 再按组过滤(组级过滤)
 
-- 总金额在 [500, 2000) 的客户
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE STATUS = 'paid'
GROUP BY customer_id
HAVING SUM(amount) >= 500 AND SUM(amount) < 2000;
 
-- 同时使用 WHERE 与 HAVING
-- 想统计 2025 年内、状态为已支付的订单里,有至少 2 天下过单的客户
SELECT customer_id, COUNT(DISTINCT DATE(order_date)) AS active_days
FROM orders
WHERE STATUS = 'paid'
  AND order_date >= DATE '2025-01-01'
  AND order_date <  DATE '2026-01-01'   -- 这些是行级条件,放 WHERE
GROUP BY customer_id
HAVING COUNT(DISTINCT DATE(order_date)) >= 2;  -- 聚合条件,放 HAVING
 
-- 不带 GROUP BY 时的 HAVING
-- 有些场景要判断“整张表的聚合是否满足条件”,这时可以没有 GROUP BY
SELECT SUM(amount) AS revenue_2025
FROM orders
WHERE order_date >= DATE '2025-01-01' AND order_date < DATE '2026-01-01'
HAVING SUM(amount) > 100000;  -- 只有在总营收 > 100000 时才返回一行,否则返回空集
  1. 性能:能放到 WHERE 的条件尽量放 WHERE,这样先减小数据量,再分组
  2. 聚合才用 HAVING:凡是和 COUNT/SUM/AVG/MIN/MAX 等聚合相关的过滤放 HAVING;纯列值过滤放 WHERE
  3. 别名在 HAVING:标准 SQL 不保证 HAVING 能用 SELECT 里的别名

JOIN

-- (内)连接(INNER JOIN)的基本写法:把两张表按某个“匹配条件”合在一起,只保留两边都能匹配到的行
-- 不带前缀时,JOIN 等同于 INNER JOIN
SELECT column_name(s)
FROM table_name1 JOIN table_name2
ON table_name1.column_name = table_name2.column_name -- 连接条件(两表的键如何对齐)
customer_id name
C001 Alice
C002 Bob
C003 Carol
order_id customer_id amount
1 C001 100
2 C001 50
3 C002 80
SELECT
  o.order_id,
  c.name,
  o.amount
FROM orders AS o
JOIN customers AS c
  ON o.customer_id = c.customer_id;
order_id name amount
1 Alice 100
2 Alice 50
3 Bob 80

JOIN的种类

  1. INNER JOIN(默认):两边都匹配才保留。
  2. LEFT JOIN:保留左表全部,即使右边没匹配(右表列为 NULL)。
  3. RIGHT JOIN:保留右表全部。
  4. FULL OUTER JOIN:保留两边全部(部分数据库需用 UNION 实现)。

性能分析

EXPLAIN <SQL语句>;
EXPLAIN ANALYZE <SQL语句>;
 
-- 只看计划(估算,不执行)
EXPLAIN
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 20;
 
-- 真正执行并给出实际耗时/行数(8.0.18+)
EXPLAIN ANALYZE
SELECT * FROM orders WHERE user_id = 123 ORDER BY created_at DESC LIMIT 20;
/var/www/DokuWikiStick/dokuwiki/data/pages/程序/数据库/sql语法.txt · Last modified: 2025/10/11 09:18 by zhonghui