他又又来了,c#开源sql解析引擎类库【SqlParser.Net 1.0】正式发布,它可以帮助你简单快速高效的解析和处理sql

背景

hi 大家好,我是三合,在过往的岁月中,我曾经想过要写以下这些工具

  1. 写一个通过拦截业务系统所有sql,然后根据这些sql自动分析表与表,字段与字段之间是如何关联的工具,即sql血缘分析工具
  2. 想着动态改写sql,比如给where动态添加一个条件。
  3. 写一个sql格式化工具
  4. 写一个像mycat那样的分库分表中间件
  5. 写一个sql防火墙,防止出现where 1=1后没有其他条件导致查询全表
  6. 写一个数据库之间的sql翻译工具,比如把sqlserver的sql自动翻译为oracle的sql

但是无一例外,都失败了,因为要实现以上这些需求,都需要一个核心的类库,即sql解析引擎,遗憾的是,我没有找到合适的,这是我当初寻找的轨迹

  1. 我发现了tsql-parser,但他只支持sql server,所以只能pass。
  2. 然后我又发现了SqlParser-cs,
    他的语法树解析出来像这样,
JsonConvert.SerializeObject(statements.First(), Formatting.Indented)
// Elided for readability
{
   "Query": {
      "Body": {
         "Select": {
            "Projection": [
               {
                  "Expression": {
                     "Ident": {
                        "Value": "a",
                        "QuoteStyle": null
                     }
                  }
               }
	...

额,怎么说呢,这语法树也太丑了点吧,同时非常难以理解,跟我想象中的完全不一样啊,于是也只能pass。

  1. 接下来我又发现了另外一些基于antlr来解析sql的类库,比如SQLParser,因为代码是antlr自动生成的,比较难以进行手动优化,所以还是pass。

  2. 最后我还发现了另外一个gsp的sqlparser,但它是收费的,而且巨贵无比,也pass。

找了一圈下来,我发现符合我要求的类库并不存在,所以我上面的那些想法,也一度搁浅了,但每一次的搁浅,都会使我内心的不甘加重一分,终于有一天,我下定决心,自己动手,丰衣足食,所以最近花了大概3个月时间,从头开始写了一个sql解析引擎,包括词法解析器到语法分析器,不依赖任何第三方组件,纯c#代码,在通过了156个各种各样场景的单元测试以及各种真实的业务环境验证后,今天它SqlParser.Net1.0.0正式发布了,本项目基于MIT协议开源,有以下优点,

  1. 支持5大数据库,oracle,sqlserver,mysql,pgsql以及sqlite。
  2. 极致的速度,解析普通sql,时间基本在0.3毫秒以下,当然了,sql越长,解析需要的时间就越长。
  3. 文档完善,众所周知,我三合的开源项目,一向是文档齐全且简单易懂,做到看完就能上手,同时,我也会根据用户的反馈不断的补充以及完善文档。
  4. 代码简洁易懂

SqlParser.Net存在的意义

SqlParser.Net是一个免费,功能全面且高性能的sql解析引擎类库,它可以帮助你简单快速高效的解析和处理sql。

Getting Started

接下来,我将介绍SqlParser.Net的用法

通过Nuget安装

你可以运行以下命令在你的项目中安装 SqlParser.Net 。

PM> Install-Package SqlParser.Net

支持框架

netstandard2.0

从最简单的demo开始

让我们一起看一个最简单的select语句是如何解析的

var sql = "select * from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            }
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test"
            }
        }
    }
};

以上面为例子,抽象语法树的所有叶子节点均为sqlExpression的子类,且各种sqlExpression节点可以互相嵌套,组成一颗复杂无比的树,其他sql解析引擎还分为statement和expression,我认为过于复杂,所以全部统一为sqlExpression,*的sqlExpression总共分为4种,

  1. 查询语句(SqlSelectExpression)
  2. 插入语句(SqlInsertExpression)
  3. 删除语句(SqlDeleteExpression)
  4. 更新语句(SqlUpdateExpression)

这4种*语句中,我认为最复杂的是查询语句,因为查询组合非常多,要兼容各种各样的情况,其他3种反而非常简单。现阶段,sqlExpression的子类一共有38种,我将在下面的演示中,结合例子给大家讲解一下。

1. Select查询语句

如上例子,SqlSelectExpression代表一个查询语句,SqlSelectQueryExpression则是真正具体的查询语句,他包括了

  1. 要查询的所有列(Columns字段)
  2. 数据源(From字段)
  3. 条件过滤语句(Where字段)
  4. 分组语句(GroupBy字段)
  5. 排序语句(OrderBy字段)
  6. 分页语句(Limit字段)
  7. Into语句(sql server专用,如SELECT id,name into test14 from TEST t)
  8. ConnectBy语句(oracle专用,如SELECT LEVEL l FROM DUAL CONNECT BY NOCYCLE LEVEL<=100)
  9. WithSubQuerys语句,公用表表达式,即CTE

其中Columns是一个列表,他的每一个子项都是一个SqlSelectItemExpression,他的body代表一个逻辑子句,逻辑子句的值,可以为以下这些

  1. 字段,如name,
  2. 二元表达式,如t.age+3
  3. 函数调用,如LOWER(t.NAME)
  4. 一个完整的查询语句,如SELECT name FROM TEST2 t2

包括order by,partition by,group by,between,in,case when后面跟着的都是逻辑子句,这个稍后会演示,在这个例子中,因为是要查询所有列,所以仅有一个SqlSelectItemExpression,他的body是SqlAllColumnExpression(代表所有列),From代表要查询的数据源,在这里仅单表查询,所以From的值为SqlTableExpression(代表单表),表名是一个SqlIdentifierExpression,即标识符表达式,表示这是一个标识符,在SQL中,标识符(Identifier)是用于命名数据库对象的名称。这些对象可以包括表、列、索引、视图、模式、数据库等。标识符使得我们能够引用和操作这些对象,在这里,标识符的值为test,表示表名为test。

1.1 查询返回列的各种情形

1.1.1 查询指定字段

var sql = "select id AS bid,t.NAME testName  from test t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "id",
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "bid",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "NAME",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "testName",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};


在上面这个例子中,我们指定了要查询2个字段,分别是id和t.NAME,此时Columns列表里有2个值,
第一个SqlSelectItemExpression包含了

  1. 主体,即body字段,在本例子中他的值是一个SqlIdentifierExpression表达式,值为id,表示列名为id,
  2. 别名,即Alias字段,在本例子中他也是一个SqlIdentifierExpression,值为bid,代表列别名为bid,

第二个SqlSelectItemExpression的body里是一个SqlPropertyExpression,代表这是一个属性表达式,SqlPropertyExpression它包含了

  1. 表名,即Table字段,值为t,即表名为t
  2. 属性名,即Name字段,值为Name,即属性名为name

合起来则代表t表的name字段,而第二个SqlSelectItemExpression也有列别名,即testName,这个查询也是单表查询,但SqlTableExpression他多了一个Alias别名字段,即表示,表别名为t。

1.1.2 查询列为二元表达式的情况

var sql = "select 1+2 from test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlBinaryExpression()
                {
                    Left = new SqlNumberExpression()
                    {
                        Value = 1M,
                    },
                    Operator = SqlBinaryOperator.Add,
                    Right = new SqlNumberExpression()
                    {
                        Value = 2M,
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};

在这个例子中,要查询的字段的值为一个二元表达式SqlBinaryExpression,他包含了

  1. 左边部分,即Left字段,值为一个SqlNumberExpression,即数字表达式,它的值为1
  2. 右边部分,即Right字段,值为一个SqlNumberExpression,即数字表达式,它的值为2
  3. 中间符号,即Operator字段,值为add,即加法

这个例子证明了,SqlSelectItemExpression代表一个逻辑子句,而不仅仅是某个字段。

1.1.3 查询列为字符串/数字/布尔值的情况

var sql = "select ''' ''',3,true FROM test";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlStringExpression()
                {
                    Value = "' '"
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlNumberExpression()
                {
                    Value = 3M,
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlBoolExpression()
                {
                    Value = true
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};


在这个例子中,要查询的3个字段为字符串,数字和布尔值,字符串表达式即SqlStringExpression,body里即字符串的值' ',数字表达式即SqlNumberExpression,值为3,布尔表达式即SqlBoolExpression,值为true;

1.1.4 查询列为函数调用的情况

1.1.4.1 简单的函数调用
var sql = "select LOWER(name)  FROM test";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlFunctionCallExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "LOWER",
                    },
                    Arguments = new List<SqlExpression>()
                    {
                        new SqlIdentifierExpression()
                        {
                            Value = "name",
                        },
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
    },
};


在这个例子中,要查询的表达式是一个函数调用,函数调用表达式即SqlFunctionCallExpression,它包含了

  1. 函数名,即Name字段,值为LOWER,
  2. 函数参数列表,即Arguments字段,列表里只有一个值,即函数只有一个参数,且参数的值为name
1.1.4.2 带有over子句的函数调用
var sql = "SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY t.ID  ORDER BY t.NAME,t.ID) as rnum FROM TEST t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "*",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlFunctionCallExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "ROW_NUMBER",
                    },
                    Over = new SqlOverExpression()
                    {
                        PartitionBy = new SqlPartitionByExpression()
                        {
                            Items = new List<SqlExpression>()
                            {
                                new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "ID",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "t",
                                    },
                                },
                            },
                        },
                        OrderBy = new SqlOrderByExpression()
                        {
                            Items = new List<SqlOrderByItemExpression>()
                            {
                                new SqlOrderByItemExpression()
                                {
                                    Body = new SqlPropertyExpression()
                                    {
                                        Name = new SqlIdentifierExpression()
                                        {
                                            Value = "NAME",
                                        },
                                        Table = new SqlIdentifierExpression()
                                        {
                                            Value = "t",
                                        },
                                    },
                                },
                                new SqlOrderByItemExpression()
                                {
                                    Body = new SqlPropertyExpression()
                                    {
                                        Name = new SqlIdentifierExpression()
                                        {
                                            Value = "ID",
                                        },
                                        Table = new SqlIdentifierExpression()
                                        {
                                            Value = "t",
                                        },
                                    },
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "rnum",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};



在这个例子中,SqlFunctionCallExpression它除了常规字段外,还包含了Over子句,具体有以下这些

  1. 函数名,即Name字段,值为ROW_NUMBER,
  2. 函数参数列表,即Arguments字段,值为null,即无参数
  3. Over子句,即Over字段,他的值为一个SqlOverExpression表达式,SqlOverExpression本身又包含了以下内容
    1. PartitionBy分区子句,值为一个SqlPartitionByExpression表达式,表达式的内容也非常简单,只有一个Items,即一个分区表达式的列表,在这个例子中,列表里只有一个值SqlPropertyExpression,即根据t.id分区
    2. OrderBy排序子句,值为SqlOrderByExpression表达式,表达式的内容也非常简单,只有一个Items,即一个排序表达式的列表,列表里的值为SqlOrderByItemExpression,即排序子项表达式,排序子项表达式里又包含了以下内容
      1. 排序依据,即Body字段,在这个例子中,排序依据是2个SqlPropertyExpression表达式,即根据t.NAME,t.ID排序
      2. 排序类型,即OrderByType字段,值为Asc或者Desc,默认为asc,在这2个例子中,默认排序类型都是asc
1.1.4.3 带有within group子句的函数调用
var sql = "select name,PERCENTILE_CONT(0.5) within group(order by \"number\") from TEST5 group by name";
var sqlAst = DbUtils.Parse(sql, DbType.Pgsql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlIdentifierExpression()
                {
                    Value = "name",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlFunctionCallExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "PERCENTILE_CONT",
                    },
                    WithinGroup = new SqlWithinGroupExpression()
                    {
                        OrderBy = new SqlOrderByExpression()
                        {
                            Items = new List<SqlOrderByItemExpression>()
                            {
                                new SqlOrderByItemExpression()
                                {
                                    Body = new SqlIdentifierExpression()
                                    {
                                        Value = "number",
                                        LeftQualifiers = "\"",
                                        RightQualifiers = "\"",
                                    },
                                },
                            },
                        },
                    },
                    Arguments = new List<SqlExpression>()
                    {
                        new SqlNumberExpression()
                        {
                            Value = 0.5M,
                        },
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST5",
            },
        },
        GroupBy = new SqlGroupByExpression()
        {
            Items = new List<SqlExpression>()
            {
            new SqlIdentifierExpression()
            {
                Value = "name",
            },
            },
        },
    },
};

在这个例子中,SqlFunctionCallExpression它除了常规字段外,还包含了within group子句,具体有以下这些

  1. 函数名,即Name字段,值为PERCENTILE_CONT,
  2. 函数参数列表,即Arguments字段,列表里只有一项,表示只有1个参数,参数是SqlNumberExpression表达式,值为0.5
  3. within group子句,即WithinGroup字段,他的值为一个SqlWithinGroupExpression表达式,SqlWithinGroupExpression又包含了OrderBy排序子句,这里根据number字段排序

1.1.5 查询列为子查询的情况

var sql = "select c.*, (select a.name as province_name from portal_area a where a.id = c.province_id) as province_name, (select a.name as city_name from portal_area a where a.id = c.city_id) as city_name, (CASE WHEN c.area_id IS NULL THEN NULL ELSE (select a.name as area_name from portal_area a where a.id = c.area_id)  END )as area_name  from portal.portal_company c";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "*",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "c",
                    },
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "a",
                                    },
                                },
                                Alias = new SqlIdentifierExpression()
                                {
                                    Value = "province_name",
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "portal_area",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "a",
                            },
                        },
                        Where = new SqlBinaryExpression()
                        {
                            Left = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "a",
                                },
                            },
                            Operator = SqlBinaryOperator.EqualTo,
                            Right = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "province_id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "c",
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "province_name",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "a",
                                    },
                                },
                                Alias = new SqlIdentifierExpression()
                                {
                                    Value = "city_name",
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "portal_area",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "a",
                            },
                        },
                        Where = new SqlBinaryExpression()
                        {
                            Left = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "a",
                                },
                            },
                            Operator = SqlBinaryOperator.EqualTo,
                            Right = new SqlPropertyExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "city_id",
                                },
                                Table = new SqlIdentifierExpression()
                                {
                                    Value = "c",
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "city_name",
                },
            },
            new SqlSelectItemExpression()
            {
                Body = new SqlCaseExpression()
                {
                    Items = new List<SqlCaseItemExpression>()
                    {
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "area_id",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "c",
                                    },
                                },
                                Operator = SqlBinaryOperator.Is,
                                Right = new SqlNullExpression()
                            },
                            Value = new SqlNullExpression()
                        },
                    },
                    Else = new SqlSelectExpression()
                    {
                        Query = new SqlSelectQueryExpression()
                        {
                            Columns = new List<SqlSelectItemExpression>()
                            {
                                new SqlSelectItemExpression()
                                {
                                    Body = new SqlPropertyExpression()
                                    {
                                        Name = new SqlIdentifierExpression()
                                        {
                                            Value = "name",
                                        },
                                        Table = new SqlIdentifierExpression()
                                        {
                                            Value = "a",
                                        },
                                    },
                                    Alias = new SqlIdentifierExpression()
                                    {
                                        Value = "area_name",
                                    },
                                },
                            },
                            From = new SqlTableExpression()
                            {
                                Name = new SqlIdentifierExpression()
                                {
                                    Value = "portal_area",
                                },
                                Alias = new SqlIdentifierExpression()
                                {
                                    Value = "a",
                                },
                            },
                            Where = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "id",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "a",
                                    },
                                },
                                Operator = SqlBinaryOperator.EqualTo,
                                Right = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "area_id",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "c",
                                    },
                                },
                            },
                        },
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "area_name",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "portal_company",
            },
            Schema = new SqlIdentifierExpression()
            {
                Value = "portal",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "c",
            },
        },
    },
};


在这个例子中,要查询的列值为一个SqlSelectExpression表达式,即要查询的列是一个子查询

1.2 Where条件过滤语句

1.2.1 二元表达式

var sql = "SELECT * FROM test WHERE ID =1";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlIdentifierExpression()
            {
                Value = "ID",
            },
            Operator = SqlBinaryOperator.EqualTo,
            Right = new SqlNumberExpression()
            {
                Value = 1M,
            },
        },
    },
};


在这个例子中,where字段的值是一个二元表达式SqlBinaryExpression,他包含了

  1. 左边部分,即Left字段,值为一个SqlIdentifierExpression,即标识符表达式,它的值为ID
  2. 右边部分,即Right字段,值为一个SqlNumberExpression,即数字表达式,它的值为1
  3. 中间符号,即Operator字段,值为EqualTo,即等号,当然了,还可以是大于号,小于号,大于等于号,小于等于号,不等号等

二元表达式的两边可以非常灵活,可以是各种其他表达式,同时也可以自我嵌套另一个二元表达式,组成一个非常复杂的二元表达式

1.2.2 between/not between子句

var sql = "SELECT * FROM test WHERE ID BETWEEN 1 AND 2";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
        },
        Where = new SqlBetweenAndExpression()
        {
            Body = new SqlIdentifierExpression()
            {
                Value = "ID",
            },
            Begin = new SqlNumberExpression()
            {
                Value = 1M,
            },
            End = new SqlNumberExpression()
            {
                Value = 2M,
            },
        },
    },
};


between子句包含了

  1. Begin部分,即Begin字段,在这个例子中,值为一个SqlNumberExpression,,它的值为1
  2. End部分,即End字段,在这个例子中,值为一个SqlNumberExpression,它的值为2
  3. Body主体部分,即Body字段,值为SqlIdentifierExpression,即标识符表达式,值为id
  4. 取反部分,即IsNot字段,如果是not between,则IsNot=true

1.2.3 is null/is not null子句

var sql = "select * from test rd where rd.name is null";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "rd",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlPropertyExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "name",
                },
                Table = new SqlIdentifierExpression()
                {
                    Value = "rd",
                },
            },
            Operator = SqlBinaryOperator.Is,
            Right = new SqlNullExpression()
        },
    },
};



is null/is not null子句主要体现在二元表达式里,Operator字段为Is/IsNot,right字段为SqlNullExpression,即null表达式,代表值为null

1.2.4 exists/not exists子句

var sql = "select * from TEST t where EXISTS(select * from TEST2 t2)";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlExistsExpression()
        {
            Body = new SqlSelectExpression()
            {
                Query = new SqlSelectQueryExpression()
                {
                    Columns = new List<SqlSelectItemExpression>()
                    {
                        new SqlSelectItemExpression()
                        {
                            Body = new SqlAllColumnExpression()
                        },
                    },
                    From = new SqlTableExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "TEST2",
                        },
                        Alias = new SqlIdentifierExpression()
                        {
                            Value = "t2",
                        },
                    },
                },
            },
        },
    },
};




exists/not exists子句,主要体现为SqlExistsExpression表达式,

  1. 主体,即body字段,本例子中值为一个SqlSelectExpression表达式
  2. 取反部分,即IsNot字段,如果是not exists,则IsNot=true

1.2.5 like/not like子句

var sql = "SELECT * from TEST t WHERE name LIKE '%a%'";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlIdentifierExpression()
            {
                Value = "name",
            },
            Operator = SqlBinaryOperator.Like,
            Right = new SqlStringExpression()
            {
                Value = "%a%"
            },
        },
    },
};


like子句,主要体现在二元表达式里,Operator字段为Like/NotLike,本例子中right字段为字符串表达式,即SqlStringExpression表达式,值为%a%。

1.2.6 all/any子句

var sql = "select * from customer c where c.Age >all(select o.Quantity  from orderdetail o)";
var sqlAst = DbUtils.Parse(sql, DbType.MySql);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "customer",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "c",
            },
        },
        Where = new SqlBinaryExpression()
        {
            Left = new SqlPropertyExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "Age",
                },
                Table = new SqlIdentifierExpression()
                {
                    Value = "c",
                },
            },
            Operator = SqlBinaryOperator.GreaterThen,
            Right = new SqlAllExpression()
            {
                Body = new SqlSelectExpression()
                {
                    Query = new SqlSelectQueryExpression()
                    {
                        Columns = new List<SqlSelectItemExpression>()
                        {
                            new SqlSelectItemExpression()
                            {
                                Body = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "Quantity",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "o",
                                    },
                                },
                            },
                        },
                        From = new SqlTableExpression()
                        {
                            Name = new SqlIdentifierExpression()
                            {
                                Value = "orderdetail",
                            },
                            Alias = new SqlIdentifierExpression()
                            {
                                Value = "o",
                            },
                        },
                    },
                },
            },
        },
    },
};



all/any子句,主要体现在SqlAllExpression/SqlAnyExpression表达式,它的body里是另一个SqlSelectExpression表达式

1.2.7 in/ not in子句

var sql = "SELECT  * from TEST t WHERE t.NAME IN ('a','b','c')";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlInExpression()
        {
            Body = new SqlPropertyExpression()
            {
                Name = new SqlIdentifierExpression()
                {
                    Value = "NAME",
                },
                Table = new SqlIdentifierExpression()
                {
                    Value = "t",
                },
            },
            TargetList = new List<SqlExpression>()
            {
                new SqlStringExpression()
                {
                    Value = "a"
                },
                new SqlStringExpression()
                {
                    Value = "b"
                },
                new SqlStringExpression()
                {
                    Value = "c"
                },
            },
        },
    },
};



in/not in子句,主要体现在SqlInExpression表达式,它包含了

  1. body字段,即in的主体,在这里是SqlPropertyExpression,值为t.NAME
  2. TargetList字段,即in的目标列表,在这里是一个SqlExpression的列表,里面包括3个SqlStringExpression,即字符串表达式,a,b,c
  3. 取反部分,即IsNot字段,如果是not in,则IsNot=true

当然了,in也有另一种子查询的类型,即

var sql = "select * from TEST5  WHERE NAME IN (SELECT NAME  FROM TEST3)";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST5",
            },
        },
        Where = new SqlInExpression()
        {
            Body = new SqlIdentifierExpression()
            {
                Value = "NAME",
            },
            SubQuery = new SqlSelectExpression()
            {
                Query = new SqlSelectQueryExpression()
                {
                    Columns = new List<SqlSelectItemExpression>()
                    {
                        new SqlSelectItemExpression()
                        {
                            Body = new SqlIdentifierExpression()
                            {
                                Value = "NAME",
                            },
                        },
                    },
                    From = new SqlTableExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "TEST3",
                        },
                    },
                },
            },
        },
    },
};

在这里的SqlInExpression表达式中,它包含了

  1. body字段,即in的主体,在这里是SqlIdentifierExpression,值为NAME
  2. SubQuery字段,即子查询,值为一个SqlSelectExpression
  3. IsNot字段,如果是not in,则IsNot=true

1.2.8 case when子句

var sql = "SELECT CASE WHEN t.name='1' THEN 'a' WHEN t.name='2' THEN 'b' ELSE 'c' END AS v from TEST t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlCaseExpression()
                {
                    Items = new List<SqlCaseItemExpression>()
                    {
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "t",
                                    },
                                },
                                Operator = SqlBinaryOperator.EqualTo,
                                Right = new SqlStringExpression()
                                {
                                    Value = "1"
                                },
                            },
                            Value = new SqlStringExpression()
                            {
                                Value = "a"
                            },
                        },
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlBinaryExpression()
                            {
                                Left = new SqlPropertyExpression()
                                {
                                    Name = new SqlIdentifierExpression()
                                    {
                                        Value = "name",
                                    },
                                    Table = new SqlIdentifierExpression()
                                    {
                                        Value = "t",
                                    },
                                },
                                Operator = SqlBinaryOperator.EqualTo,
                                Right = new SqlStringExpression()
                                {
                                    Value = "2"
                                },
                            },
                            Value = new SqlStringExpression()
                            {
                                Value = "b"
                            },
                        },
                    },
                    Else = new SqlStringExpression()
                    {
                        Value = "c"
                    },
                },
                Alias = new SqlIdentifierExpression()
                {
                    Value = "v",
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};



case when子句,主要体现在SqlCaseExpression表达式里,他包含了

  1. 各种case when键值对的列表,即Items字段,列表里的每一个元素都是SqlCaseItemExpression表达式,SqlCaseItemExpression表达式,又包含了
    1. 条件,即Condition字段,在本例子中是二元表达式,即SqlBinaryExpression表达式,值为t.name ='1'
    2. 值,即value字段,在本例子中值为字符串a
  2. Else字段,即默认值,本例子中为字符串c

case when还有另外一种句式,如下:

var sql = "select case t.name when 'a' then 1 else 2 end  from test t ";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlCaseExpression()
                {
                    Items = new List<SqlCaseItemExpression>()
                    {
                        new SqlCaseItemExpression()
                        {
                            Condition = new SqlStringExpression()
                            {
                                Value = "a"
                            },
                            Value = new SqlNumberExpression()
                            {
                                Value = 1M,
                            },
                        },
                    },
                    Else = new SqlNumberExpression()
                    {
                        Value = 2M,
                    },
                    Value = new SqlPropertyExpression()
                    {
                        Name = new SqlIdentifierExpression()
                        {
                            Value = "name",
                        },
                        Table = new SqlIdentifierExpression()
                        {
                            Value = "t",
                        },
                    },
                },
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "test",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
    },
};



在这种SqlCaseExpression表达式里,他包含了

  1. case条件的主体变量,即Value字段,本例子中值为SqlPropertyExpression,它的值为t.name
  2. 各种when then键值对的列表,即Items字段,列表里的每一个元素都是SqlCaseItemExpression表达式,SqlCaseItemExpression表达式,又包含了
    1. 条件,即Condition字段,在本例子中是字符串表达式SqlStringExpression,它的值为a
    2. 值,即Value字段,在本例子中值为SqlNumberExpression,它的值为1
  3. Else字段,即默认值,本例子中为数字2

1.2.9 not子句

var sql = "select * from TEST t WHERE not t.NAME ='abc'";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlNotExpression()
        {
            Body = new SqlBinaryExpression()
            {
                Left = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "NAME",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
                Operator = SqlBinaryOperator.EqualTo,
                Right = new SqlStringExpression()
                {
                    Value = "abc"
                },
            },
        },
    },
};



not子句,主要体现在SqlNotExpression表达式里,它只有一个body字段,即代表否定的部分

1.2.10 变量子句

var sql = "select * from TEST t WHERE not t.NAME =:name";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlTableExpression()
        {
            Name = new SqlIdentifierExpression()
            {
                Value = "TEST",
            },
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
        },
        Where = new SqlNotExpression()
        {
            Body = new SqlBinaryExpression()
            {
                Left = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "NAME",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t",
                    },
                },
                Operator = SqlBinaryOperator.EqualTo,
                Right = new SqlVariableExpression()
                {
                    Name = "name",
                    Prefix = ":",
                },
            },
        },
    },
};


变量子句,主要体现在SqlVariableExpression表达式里,它包括以下部分:

  1. 变量名,即字段Name,这里值为name
  2. 变量前缀,这里值为:

1.3 From数据源

在sql中,From关键字后面有多种形式来指定数据源。主要有以下几种

1.3.1 表名或者视图

select * from test

这个解析结果上面已经演示了。

1.3.2 子查询(子表)

var sql = "select * from (select * from test) t";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlAllColumnExpression()
            },
        },
        From = new SqlSelectExpression()
        {
            Alias = new SqlIdentifierExpression()
            {
                Value = "t",
            },
            Query = new SqlSelectQueryExpression()
            {
                Columns = new List<SqlSelectItemExpression>()
                {
                    new SqlSelectItemExpression()
                    {
                        Body = new SqlAllColumnExpression()
                    },
                },
                From = new SqlTableExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "test",
                    },
                },
            },
        },
    },
};

在这个例子中,数据源From的值为一个SqlSelectExpression,即SqlSelectExpression中可以嵌套SqlSelectExpression,同时我们注意到内部的SqlSelectExpression有一个表别名的字段Alias,标识符的值为t,表示表别名为t;

1.3.3 连表查询(JOIN)

var sql = "select t1.id from test t1 left join test2 t2 on t1.id=t2.id right join test3 t3 on t2.id=t3.id";
var sqlAst = DbUtils.Parse(sql, DbType.Oracle);

解析结果如下:

var expect = new SqlSelectExpression()
{
    Query = new SqlSelectQueryExpression()
    {
        Columns = new List<SqlSelectItemExpression>()
        {
            new SqlSelectItemExpression()
            {
                Body = new SqlPropertyExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "id",
                    },
                    Table = new SqlIdentifierExpression()
                    {
                        Value = "t1",
                    },
                },
            },
        },
        From = new SqlJoinTableExpression()
        {
            Left = new SqlJoinTableExpression()
            {
                Left = new SqlTableExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "test",
                    },
                    Alias = new SqlIdentifierExpression()
                    {
                        Value = "t1",
                    },
                },
                JoinType = SqlJoinType.LeftJoin,
                Right = new SqlTableExpression()
                {
                    Name = new SqlIdentifierExpression()
                    {
                        Value = "test2",
                    },
                    Alias = new SqlIdentifierExpression()
                    {
                        Value = "t2",
                    },
                },
                Conditions = new SqlBinaryExpress
上一篇:TCA9555芯片手册解读(5)