Spanner基础查询 #
一、SELECT语句概述 #
1.1 基本语法 #
sql
SELECT [DISTINCT] expression [, ...]
FROM table_name
[WHERE condition]
[GROUP BY expression [, ...]]
[HAVING condition]
[ORDER BY expression [ASC|DESC] [, ...]]
[LIMIT count [OFFSET skip_count]];
1.2 示例表结构 #
sql
CREATE TABLE users (
user_id INT64 NOT NULL,
name STRING(100) NOT NULL,
email STRING(255),
age INT64,
status STRING(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT (CURRENT_TIMESTAMP())
) PRIMARY KEY (user_id);
CREATE TABLE orders (
order_id INT64 NOT NULL,
user_id INT64 NOT NULL,
product_name STRING(200),
quantity INT64,
price FLOAT64,
order_date DATE
) PRIMARY KEY (order_id);
二、基本查询 #
2.1 查询所有列 #
sql
-- 查询所有列
SELECT * FROM users;
-- 查询指定列
SELECT user_id, name, email FROM users;
-- 查询常量
SELECT 1, 'hello', TRUE;
-- 查询表达式
SELECT user_id, name, age * 2 AS double_age FROM users;
2.2 列别名 #
sql
-- 使用AS设置别名
SELECT
user_id AS id,
name AS user_name,
age AS user_age
FROM users;
-- 别名可以省略AS
SELECT user_id id, name user_name FROM users;
-- 表达式别名
SELECT
UPPER(name) AS upper_name,
age + 10 AS age_plus_10
FROM users;
2.3 DISTINCT去重 #
sql
-- 查询不重复的值
SELECT DISTINCT status FROM users;
-- 多列去重
SELECT DISTINCT status, age FROM users;
-- 去重计数
SELECT COUNT(DISTINCT user_id) FROM orders;
三、WHERE条件 #
3.1 比较运算符 #
sql
-- 等于
SELECT * FROM users WHERE user_id = 1;
-- 不等于
SELECT * FROM users WHERE status != 'inactive';
SELECT * FROM users WHERE status <> 'inactive';
-- 大于、小于
SELECT * FROM users WHERE age > 18;
SELECT * FROM users WHERE age >= 18;
SELECT * FROM users WHERE age < 60;
SELECT * FROM users WHERE age <= 60;
-- 组合比较
SELECT * FROM users WHERE age BETWEEN 18 AND 60;
3.2 逻辑运算符 #
sql
-- AND
SELECT * FROM users
WHERE age >= 18 AND status = 'active';
-- OR
SELECT * FROM users
WHERE status = 'active' OR status = 'premium';
-- NOT
SELECT * FROM users
WHERE NOT status = 'inactive';
-- 组合使用
SELECT * FROM users
WHERE (status = 'active' OR status = 'premium')
AND age >= 18;
3.3 IN条件 #
sql
-- IN列表
SELECT * FROM users WHERE user_id IN (1, 2, 3, 4, 5);
-- IN子查询
SELECT * FROM users
WHERE user_id IN (SELECT user_id FROM orders WHERE quantity > 10);
-- NOT IN
SELECT * FROM users WHERE status NOT IN ('inactive', 'deleted');
3.4 LIKE条件 #
sql
-- 模糊匹配
SELECT * FROM users WHERE name LIKE 'John%'; -- 以John开头
SELECT * FROM users WHERE name LIKE '%Doe'; -- 以Doe结尾
SELECT * FROM users WHERE name LIKE '%mith%'; -- 包含mith
-- 单字符匹配
SELECT * FROM users WHERE name LIKE 'J_hn'; -- J后任意字符后接hn
-- 转义特殊字符
SELECT * FROM users WHERE name LIKE '%\%%' ESCAPE '\'; -- 包含%
3.5 NULL条件 #
sql
-- IS NULL
SELECT * FROM users WHERE email IS NULL;
-- IS NOT NULL
SELECT * FROM users WHERE email IS NOT NULL;
-- IS TRUE / IS FALSE
SELECT * FROM users WHERE is_active IS TRUE;
SELECT * FROM users WHERE is_active IS NOT TRUE;
3.6 时间条件 #
sql
-- 日期比较
SELECT * FROM orders WHERE order_date = DATE '2024-03-27';
-- 日期范围
SELECT * FROM orders
WHERE order_date BETWEEN DATE '2024-01-01' AND DATE '2024-12-31';
-- 时间戳比较
SELECT * FROM users
WHERE created_at > TIMESTAMP '2024-01-01 00:00:00 UTC';
-- 时间函数
SELECT * FROM orders
WHERE order_date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY);
SELECT * FROM users
WHERE created_at >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY);
四、ORDER BY排序 #
4.1 基本排序 #
sql
-- 升序(默认)
SELECT * FROM users ORDER BY user_id ASC;
SELECT * FROM users ORDER BY user_id; -- 默认ASC
-- 降序
SELECT * FROM users ORDER BY user_id DESC;
-- 多列排序
SELECT * FROM users ORDER BY status ASC, age DESC;
-- 使用列别名排序
SELECT user_id, name, age * 2 AS double_age
FROM users
ORDER BY double_age DESC;
-- 使用列位置排序
SELECT user_id, name, age
FROM users
ORDER BY 3 DESC; -- 按第3列(age)排序
4.2 NULL排序 #
sql
-- NULL默认排在最后(ASC)或最前(DESC)
SELECT * FROM users ORDER BY email ASC; -- NULL在最后
-- 显式指定NULL位置(不直接支持NULLS FIRST/LAST)
-- 使用CASE表达式实现
SELECT * FROM users
ORDER BY
CASE WHEN email IS NULL THEN 1 ELSE 0 END,
email;
4.3 表达式排序 #
sql
-- 使用表达式排序
SELECT * FROM users ORDER BY LENGTH(name) DESC;
-- 使用函数排序
SELECT * FROM users ORDER BY UPPER(name);
五、LIMIT和OFFSET #
5.1 LIMIT限制 #
sql
-- 限制返回行数
SELECT * FROM users LIMIT 10;
-- 配合ORDER BY
SELECT * FROM users ORDER BY user_id DESC LIMIT 10;
5.2 OFFSET偏移 #
sql
-- 跳过前10行,返回接下来的10行
SELECT * FROM users ORDER BY user_id LIMIT 10 OFFSET 10;
-- 分页查询
-- 第1页: LIMIT 10 OFFSET 0
-- 第2页: LIMIT 10 OFFSET 10
-- 第3页: LIMIT 10 OFFSET 20
SELECT * FROM users ORDER BY user_id LIMIT 10 OFFSET 0;
5.3 分页优化 #
sql
-- 传统分页(大数据量时性能差)
SELECT * FROM users ORDER BY user_id LIMIT 10 OFFSET 10000;
-- 游标分页(推荐)
-- 记住上一页最后一条记录的主键
SELECT * FROM users
WHERE user_id > 10000 -- 上一页最后的user_id
ORDER BY user_id
LIMIT 10;
六、聚合函数 #
6.1 基本聚合 #
sql
-- 计数
SELECT COUNT(*) FROM users;
SELECT COUNT(email) FROM users; -- 非NULL计数
SELECT COUNT(DISTINCT status) FROM users; -- 去重计数
-- 求和
SELECT SUM(quantity) FROM orders;
SELECT SUM(quantity * price) AS total FROM orders;
-- 平均值
SELECT AVG(age) FROM users;
-- 最大最小值
SELECT MAX(age), MIN(age) FROM users;
-- 组合使用
SELECT
COUNT(*) AS total_users,
AVG(age) AS avg_age,
MAX(age) AS max_age,
MIN(age) AS min_age
FROM users;
6.2 GROUP BY分组 #
sql
-- 按单列分组
SELECT status, COUNT(*) AS count
FROM users
GROUP BY status;
-- 按多列分组
SELECT status, age, COUNT(*) AS count
FROM users
GROUP BY status, age;
-- 使用表达式分组
SELECT
CASE
WHEN age < 18 THEN 'minor'
WHEN age < 60 THEN 'adult'
ELSE 'senior'
END AS age_group,
COUNT(*) AS count
FROM users
GROUP BY age_group;
6.3 HAVING过滤 #
sql
-- HAVING过滤分组结果
SELECT status, COUNT(*) AS count
FROM users
GROUP BY status
HAVING COUNT(*) > 10;
-- HAVING与WHERE组合
SELECT status, AVG(age) AS avg_age
FROM users
WHERE email IS NOT NULL
GROUP BY status
HAVING AVG(age) > 25;
6.4 聚合函数列表 #
| 函数 | 说明 |
|---|---|
| COUNT(*) | 总行数 |
| COUNT(column) | 非NULL行数 |
| SUM(column) | 求和 |
| AVG(column) | 平均值 |
| MAX(column) | 最大值 |
| MIN(column) | 最小值 |
| ARRAY_AGG(column) | 聚合为数组 |
| STRING_AGG(column, delimiter) | 聚合为字符串 |
七、表达式和函数 #
7.1 算术运算 #
sql
-- 加减乘除
SELECT age + 10, age - 5, age * 2, age / 10 FROM users;
-- 取模
SELECT age % 10 FROM users;
-- 一元运算
SELECT -age FROM users;
7.2 字符串函数 #
sql
-- 拼接
SELECT CONCAT(name, ' - ', email) FROM users;
SELECT name || ' (' || email || ')' FROM users;
-- 长度
SELECT name, LENGTH(name) FROM users;
-- 大小写
SELECT UPPER(name), LOWER(name) FROM users;
-- 子字符串
SELECT SUBSTR(name, 1, 3) FROM users;
-- 替换
SELECT REPLACE(name, 'John', 'Jane') FROM users;
-- 去空格
SELECT TRIM(name), LTRIM(name), RTRIM(name) FROM users;
7.3 条件表达式 #
sql
-- CASE WHEN
SELECT
name,
CASE
WHEN age < 18 THEN 'minor'
WHEN age < 60 THEN 'adult'
ELSE 'senior'
END AS age_group
FROM users;
-- CASE表达式(简单形式)
SELECT
status,
CASE status
WHEN 'active' THEN 'Active User'
WHEN 'inactive' THEN 'Inactive User'
ELSE 'Unknown'
END AS status_desc
FROM users;
-- COALESCE
SELECT COALESCE(email, 'no-email') FROM users;
-- IFNULL
SELECT IFNULL(email, 'no-email') FROM users;
-- NULLIF
SELECT NULLIF(status, 'active') FROM users;
7.4 日期时间函数 #
sql
-- 当前日期时间
SELECT CURRENT_DATE();
SELECT CURRENT_TIMESTAMP();
-- 提取部分
SELECT
EXTRACT(YEAR FROM created_at) AS year,
EXTRACT(MONTH FROM created_at) AS month,
EXTRACT(DAY FROM created_at) AS day
FROM users;
-- 日期运算
SELECT
DATE_ADD(CURRENT_DATE(), INTERVAL 1 DAY) AS tomorrow,
DATE_SUB(CURRENT_DATE(), INTERVAL 1 MONTH) AS last_month
FROM users;
-- 格式化
SELECT FORMAT_DATE('%Y-%m-%d', CURRENT_DATE());
SELECT FORMAT_TIMESTAMP('%Y-%m-%d %H:%M:%S', CURRENT_TIMESTAMP());
八、查询执行 #
8.1 使用gcloud CLI #
bash
# 执行查询
gcloud spanner databases execute-sql my-database \
--instance=my-instance \
--sql="SELECT * FROM users LIMIT 10"
# 从文件执行
gcloud spanner databases execute-sql my-database \
--instance=my-instance \
--sql-file=query.sql
8.2 使用客户端库 #
java
// Java查询
DatabaseClient client = spanner.getDatabaseClient(databaseId);
try (ResultSet resultSet = client.singleUse().executeQuery(
Statement.of("SELECT * FROM users WHERE age > 18"))) {
while (resultSet.next()) {
long userId = resultSet.getLong(0);
String name = resultSet.getString(1);
System.out.println(userId + ": " + name);
}
}
python
# Python查询
def query_users(database):
with database.snapshot() as snapshot:
results = snapshot.execute_sql(
"SELECT * FROM users WHERE age > 18"
)
for row in results:
print(row)
go
// Go查询
func queryUsers(ctx context.Context, client *spanner.Client) error {
iter := client.Single().Query(ctx, spanner.Statement{
SQL: "SELECT * FROM users WHERE age > @age",
Params: map[string]interface{}{
"age": 18,
},
})
defer iter.Stop()
for {
row, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return err
}
var userID int64
var name string
if err := row.Columns(&userID, &name); err != nil {
return err
}
fmt.Printf("%d: %s\n", userID, name)
}
return nil
}
九、查询计划 #
9.1 查看执行计划 #
sql
-- 使用EXPLAIN查看执行计划
EXPLAIN SELECT * FROM users WHERE user_id = 1;
-- 使用EXPLAIN ANALYZE查看实际执行统计
EXPLAIN ANALYZE SELECT * FROM users WHERE user_id = 1;
9.2 执行计划解读 #
text
执行计划示例:
┌─────────────────────────────────────────────────────────────┐
│ Distributed Union on users │
│ └── Distributed Cross Apply │
│ ├── Table Scan on users │
│ │ └── Filter: user_id = 1 │
│ └── Index Scan on idx_users_email │
└─────────────────────────────────────────────────────────────┘
关键指标:
├── rows: 返回行数
├── bytes: 返回字节数
├── latency: 执行延迟
└── scan: 扫描方式
十、查询优化 #
10.1 使用索引 #
sql
-- 创建索引
CREATE INDEX idx_users_status ON users(status);
-- 使用索引的查询
SELECT * FROM users WHERE status = 'active';
-- 强制使用索引
SELECT * FROM users@{FORCE_INDEX=idx_users_status}
WHERE status = 'active';
10.2 避免全表扫描 #
sql
-- 不推荐: 全表扫描
SELECT * FROM users;
-- 推荐: 使用主键或索引
SELECT * FROM users WHERE user_id = 1;
-- 不推荐: 使用函数导致索引失效
SELECT * FROM users WHERE UPPER(name) = 'JOHN';
-- 推荐: 避免在条件列上使用函数
SELECT * FROM users WHERE name = 'John';
10.3 查询提示 #
sql
-- 使用查询提示优化
SELECT * FROM users@{FORCE_INDEX=idx_users_status}
WHERE status = 'active';
-- 设置查询超时
SELECT * FROM users@{QUERY_TIMEOUT=10s}
WHERE status = 'active';
十一、查询最佳实践 #
11.1 性能建议 #
text
查询性能建议:
├── 只查询需要的列
├── 使用索引加速查询
├── 避免SELECT *
├── 使用LIMIT限制结果
└── 使用分页避免大量数据
11.2 安全建议 #
text
查询安全建议:
├── 使用参数化查询防止SQL注入
├── 验证用户输入
├── 设置查询超时
└── 限制返回数据量
11.3 可读性建议 #
text
查询可读性建议:
├── 使用有意义的别名
├── 格式化SQL语句
├── 添加注释说明
└── 复杂查询分解为简单查询
十二、总结 #
基础查询要点:
| 子句 | 说明 |
|---|---|
| SELECT | 选择列 |
| FROM | 指定表 |
| WHERE | 过滤条件 |
| GROUP BY | 分组 |
| HAVING | 分组过滤 |
| ORDER BY | 排序 |
| LIMIT | 限制行数 |
最佳实践:
text
1. 使用索引
└── 加速查询
2. 限制结果
└── 使用LIMIT
3. 参数化查询
└── 防止SQL注入
4. 查看执行计划
└── 优化查询
5. 分页查询
└── 使用游标分页
下一步,让我们学习多表连接查询!
最后更新:2026-03-27