Files
streamsql/docs/NEGATIVE_NUMBER_SUPPORT.md
2025-06-17 14:20:40 +08:00

5.8 KiB
Raw Permalink Blame History

StreamSQL 负数支持文档

概述

StreamSQL 现在全面支持负数在 CASE 表达式中的使用。本文档总结了负数支持的完善情况、支持范围和使用建议。

已支持的负数用法

1. 基本负数常量

-- 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. 比较运算符后的负数

-- 比较运算符后直接跟负数
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 表达式中的负数

-- 简单 CASE 中使用负数作为匹配值
CASE temperature 
  WHEN -10 THEN 'FROZEN'
  WHEN -5 THEN 'COLD'
  WHEN 0 THEN 'ZERO'
  ELSE 'OTHER'
END

4. 算术表达式中的负数

-- 括号内的负数运算
CASE WHEN temperature + (-10) > 0 THEN 1 ELSE 0 END
CASE WHEN (temperature * -1) > 10 THEN 1 ELSE 0 END

⚠️ 部分支持或限制

1. 函数参数中的负数表达式

-- 当前不完全支持:函数参数中的负数变量
CASE WHEN ABS(-temperature) > 10 THEN 1 ELSE 0 END  -- ❌ 

-- 推荐替代方案:使用括号或先计算
CASE WHEN ABS(temperature * -1) > 10 THEN 1 ELSE 0 END  -- ✅

2. BETWEEN 语句中的负数范围

-- 当前不支持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 中使用空格分隔的负数
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. 推荐的负数写法

-- ✅ 推荐:紧密连接的负数
CASE WHEN temperature < -10 THEN 'FREEZING' END

-- ✅ 推荐:括号包围的负数(最安全)
CASE WHEN temperature < (-10) THEN 'FREEZING' END

-- ✅ 推荐:负数小数
CASE WHEN temperature < -10.5 THEN 'FREEZING' END

2. 避免的写法

-- ❌ 避免:空格分隔的负数
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. 扩展负数支持到更多数学和字符串函数

示例代码

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 开发团队