mirror of
https://gitee.com/rulego/streamsql.git
synced 2026-05-06 19:05:43 +00:00
225 lines
5.8 KiB
Markdown
225 lines
5.8 KiB
Markdown
# StreamSQL 负数支持文档
|
||
|
||
## 概述
|
||
|
||
StreamSQL 现在全面支持负数在 CASE 表达式中的使用。本文档总结了负数支持的完善情况、支持范围和使用建议。
|
||
|
||
## ✅ 已支持的负数用法
|
||
|
||
### 1. 基本负数常量
|
||
|
||
```sql
|
||
-- CASE 表达式中的负数常量
|
||
CASE WHEN temperature > 0 THEN 1 ELSE -1 END
|
||
|
||
-- 负数小数
|
||
CASE WHEN temperature > 0 THEN 1.5 ELSE -2.5 END
|
||
|
||
-- 负零
|
||
CASE WHEN temperature = -0 THEN 1 ELSE 0 END
|
||
```
|
||
|
||
### 2. 比较运算符后的负数
|
||
|
||
```sql
|
||
-- 比较运算符后直接跟负数
|
||
CASE WHEN temperature < -10 THEN 'FREEZING' ELSE 'NORMAL' END
|
||
CASE WHEN temperature >= -5.5 THEN 'ABOVE' ELSE 'BELOW' END
|
||
CASE WHEN temperature > -20 THEN 'WARM' ELSE 'COLD' END
|
||
```
|
||
|
||
### 3. 简单 CASE 表达式中的负数
|
||
|
||
```sql
|
||
-- 简单 CASE 中使用负数作为匹配值
|
||
CASE temperature
|
||
WHEN -10 THEN 'FROZEN'
|
||
WHEN -5 THEN 'COLD'
|
||
WHEN 0 THEN 'ZERO'
|
||
ELSE 'OTHER'
|
||
END
|
||
```
|
||
|
||
### 4. 算术表达式中的负数
|
||
|
||
```sql
|
||
-- 括号内的负数运算
|
||
CASE WHEN temperature + (-10) > 0 THEN 1 ELSE 0 END
|
||
CASE WHEN (temperature * -1) > 10 THEN 1 ELSE 0 END
|
||
```
|
||
|
||
## ⚠️ 部分支持或限制
|
||
|
||
### 1. 函数参数中的负数表达式
|
||
|
||
```sql
|
||
-- 当前不完全支持:函数参数中的负数变量
|
||
CASE WHEN ABS(-temperature) > 10 THEN 1 ELSE 0 END -- ❌
|
||
|
||
-- 推荐替代方案:使用括号或先计算
|
||
CASE WHEN ABS(temperature * -1) > 10 THEN 1 ELSE 0 END -- ✅
|
||
```
|
||
|
||
### 2. BETWEEN 语句中的负数范围
|
||
|
||
```sql
|
||
-- 当前不支持:BETWEEN 与负数组合
|
||
CASE WHEN temperature BETWEEN -20 AND -10 THEN 1 ELSE 0 END -- ❌
|
||
|
||
-- 推荐替代方案:使用比较运算符
|
||
CASE WHEN temperature >= -20 AND temperature <= -10 THEN 1 ELSE 0 END -- ✅
|
||
```
|
||
|
||
### 3. SQL 中的空格分隔负数
|
||
|
||
```sql
|
||
-- 避免在 SQL 中使用空格分隔的负数
|
||
SELECT CASE WHEN temperature < - 10 THEN 'COLD' END -- ❌ 解析问题
|
||
|
||
-- 推荐写法:紧密连接或使用括号
|
||
SELECT CASE WHEN temperature < -10 THEN 'COLD' END -- ✅
|
||
SELECT CASE WHEN temperature < (-10) THEN 'COLD' END -- ✅
|
||
```
|
||
|
||
## 🔧 技术实现
|
||
|
||
### 词法分析器增强
|
||
|
||
1. **智能负数识别**:
|
||
- 识别比较运算符后的负数(`<`, `>`, `<=`, `>=`, `==`, `!=`)
|
||
- 支持逻辑运算符后的负数(`AND`, `OR`)
|
||
- 支持 CASE 关键字后的负数(`WHEN`, `THEN`, `ELSE`)
|
||
|
||
2. **连续运算符检查优化**:
|
||
- 允许比较运算符后跟负数的合法组合
|
||
- 智能区分负数与减号运算符
|
||
|
||
3. **空格处理**:
|
||
- 正确处理空格分隔的负数标记
|
||
- 改进 token 化过程以支持各种负数格式
|
||
|
||
### 表达式求值增强
|
||
|
||
1. **负数常量解析**:完全支持负整数和负小数
|
||
2. **类型转换**:正确处理负数的数值转换
|
||
3. **NULL 值处理**:负数与 NULL 值的正确交互
|
||
|
||
## 📊 测试覆盖
|
||
|
||
### 表达式级别测试
|
||
|
||
- ✅ 负数常量在 THEN/ELSE 中
|
||
- ✅ 负数常量在 WHEN 条件中
|
||
- ✅ 负数小数支持
|
||
- ✅ 负数在算术表达式中
|
||
- ✅ 负数在简单 CASE 中
|
||
- ✅ 负零处理
|
||
|
||
### SQL 集成测试
|
||
|
||
- ✅ 完整 SQL 语句中的负数支持
|
||
- ✅ 非聚合查询中的负数表达式
|
||
- ✅ 聚合查询中的负数处理
|
||
|
||
## 🎯 使用建议
|
||
|
||
### 1. 推荐的负数写法
|
||
|
||
```sql
|
||
-- ✅ 推荐:紧密连接的负数
|
||
CASE WHEN temperature < -10 THEN 'FREEZING' END
|
||
|
||
-- ✅ 推荐:括号包围的负数(最安全)
|
||
CASE WHEN temperature < (-10) THEN 'FREEZING' END
|
||
|
||
-- ✅ 推荐:负数小数
|
||
CASE WHEN temperature < -10.5 THEN 'FREEZING' END
|
||
```
|
||
|
||
### 2. 避免的写法
|
||
|
||
```sql
|
||
-- ❌ 避免:空格分隔的负数
|
||
CASE WHEN temperature < - 10 THEN 'FREEZING' END
|
||
|
||
-- ❌ 避免:复杂的负数表达式在函数中
|
||
CASE WHEN ABS(-temperature) > 10 THEN 1 END
|
||
```
|
||
|
||
### 3. 最佳实践
|
||
|
||
1. **使用括号**:当不确定负数解析时,总是使用括号包围负数
|
||
2. **避免空格**:在负号和数字之间不要添加空格
|
||
3. **测试验证**:对包含负数的复杂表达式进行充分测试
|
||
4. **版本兼容**:确保使用的 StreamSQL 版本支持所需的负数功能
|
||
|
||
## 🚀 未来改进计划
|
||
|
||
1. **完全支持函数参数中的负数表达式**
|
||
2. **支持 BETWEEN 语句中的负数范围**
|
||
3. **改进 SQL 解析器对空格分隔负数的处理**
|
||
4. **扩展负数支持到更多数学和字符串函数**
|
||
|
||
## 示例代码
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"github.com/rulego/streamsql"
|
||
)
|
||
|
||
func main() {
|
||
// 创建 StreamSQL 实例
|
||
sql := streamsql.New()
|
||
defer sql.Stop()
|
||
|
||
// 包含负数的 SQL 查询
|
||
query := `
|
||
SELECT deviceId,
|
||
temperature,
|
||
CASE
|
||
WHEN temperature < -10 THEN 'FREEZING'
|
||
WHEN temperature < 0 THEN 'COLD'
|
||
WHEN temperature = 0 THEN 'ZERO'
|
||
ELSE 'POSITIVE'
|
||
END as temp_category,
|
||
CASE
|
||
WHEN temperature > 0 THEN temperature
|
||
ELSE (-1.0)
|
||
END as adjusted_temp
|
||
FROM stream
|
||
`
|
||
|
||
// 执行查询
|
||
err := sql.Execute(query)
|
||
if err != nil {
|
||
fmt.Printf("执行失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
// 添加数据处理器
|
||
sql.AddSink(func(result interface{}) {
|
||
fmt.Printf("结果: %+v\n", result)
|
||
})
|
||
|
||
// 添加测试数据
|
||
testData := []map[string]interface{}{
|
||
{"deviceId": "sensor1", "temperature": -15.0},
|
||
{"deviceId": "sensor2", "temperature": -5.0},
|
||
{"deviceId": "sensor3", "temperature": 0.0},
|
||
{"deviceId": "sensor4", "temperature": 10.0},
|
||
}
|
||
|
||
for _, data := range testData {
|
||
sql.AddData(data)
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
**更新日期**: 2025-06-17
|
||
**版本**: StreamSQL v0.x
|
||
**作者**: StreamSQL 开发团队 |