### 结合使用 `INSTR` 和 `SUBSTR` 进行字符串处理
在数据库查询中,`INSTR` 和 `SUBSTR` 是两个常用的字符串函数,它们的结合使用可以实现灵活且高效的字符串截取和提取操作。`INSTR` 用于查找子字符串在源字符串中的位置,而 `SUBSTR` 则用于根据该位置截取特定长度的字符串[^1]。
#### 基本函数说明
- `INSTR(sourceString, destString)` 返回子字符串 `destString` 在 `sourceString` 中首次出现的位置索引,索引从 1 开始计数。
- `SUBSTR(sourceString, start, length)` 从 `sourceString` 的第 `start` 个字符开始截取长度为 `length` 的子字符串。若 `start` 为负数,则从字符串末尾倒数开始截取[^3]。
#### 结合使用示例
##### 示例 1:提取域名中的主机名部分
假设有一个字段 `url`,其值为 `'https://www.example.com/path/to/page'`,需要提取主机名 `'www.example.com'`。
```sql
SELECT SUBSTR(url, INSTR(url, '://') + 3, INSTR(url, '/', INSTR(url, '://') + 4) - INSTR(url, '://') - 3) AS host
FROM dual;
```
解释:
- `INSTR(url, '://')` 查找协议分隔符 `'://'` 的位置。
- `INSTR(url, '/', INSTR(url, '://') + 4)` 查找路径起始的斜杠 `/` 的位置。
- `SUBSTR` 从协议之后开始截取,直到路径开始前的位置。
##### 示例 2:提取以逗号分隔的字符串中的第二个字段
假设有字段 `data`,其值为 `'apple,banana,orange,grape'`,需要提取第二个字段 `'banana'`。
```sql
SELECT SUBSTR(data,
INSTR(data, ',', 1, 1) + 1,
INSTR(data, ',', 1, 2) - INSTR(data, ',', 1, 1) - 1
) AS second_field
FROM dual;
```
解释:
- `INSTR(data, ',', 1, 1)` 查找第一个逗号的位置。
- `INSTR(data, ',', 1, 2)` 查找第二个逗号的位置。
- `SUBSTR` 从第一个逗号之后开始截取,直到第二个逗号之前。
##### 示例 3:从路径中提取文件名
假设有字段 `file_path`,其值为 `'/home/user/documents/report.pdf'`,需要提取文件名 `'report.pdf'`。
```sql
SELECT SUBSTR(file_path,
INSTR(file_path, '/', -1) + 1
) AS filename
FROM dual;
```
解释:
- `INSTR(file_path, '/', -1)` 查找最后一个斜杠 `/` 的位置(从右往左查找)。
- `SUBSTR` 从最后一个斜杠后开始截取,直到字符串结束。
#### 应用场景
- **数据清洗**:从非结构化文本中提取结构化字段,如日志分析、URL解析等。
- **字段拆分**:从逗号、分号等分隔符分隔的字符串中提取特定字段。
- **路径处理**:从文件路径中提取文件名或目录名。
- **数据库安全**:在 SQL 注入测试中结合布尔盲注,逐字符提取敏感信息。
#### 注意事项
- `INSTR` 和 `SUBSTR` 的位置索引从 1 开始,不是从 0 开始。
- 若目标子字符串不存在,`INSTR` 返回 0,可能导致 `SUBSTR` 截取异常,需结合 `CASE` 或 `DECODE` 进行容错处理。
- 若 `SUBSTR` 的起始位置为负数,则从字符串末尾倒序计算起始点。
#### 示例代码
```sql
-- 提取主机名
SELECT SUBSTR('https://www.example.com/path',
INSTR('https://www.example.com/path', '://') + 3,
INSTR('https://www.example.com/path', '/', INSTR('https://www.example.com/path', '://') + 4) - INSTR('https://www.example.com/path', '://') - 3
) AS host FROM dual;
-- 提取第二个字段
SELECT SUBSTR('apple,banana,orange',
INSTR('apple,banana,orange', ',', 1, 1) + 1,
INSTR('apple,banana,orange', ',', 1, 2) - INSTR('apple,banana,orange', ',', 1, 1) - 1
) AS second_field FROM dual;
-- 提取文件名
SELECT SUBSTR('/home/user/report.txt',
INSTR('/home/user/report.txt', '/', -1) + 1
) AS filename FROM dual;
```