SqlServer SqlParser 介绍及基本使用

SqlServer SqlParser 介绍及使用示例

Intro

最近发现在 Nuget 上有一个 SqlServer 的 SqlParser,利用 SqlParser 我们做到可以解析 SQL 的每一部分 ,nuget 包是公开的,可以拿来即用,只是缺少使用示例,很多功能需要自己去摸索

Nuget 包是 https://www.nuget.org/packages/Microsoft.SqlServer.Management.SqlParser/

下面我们来看使用示例吧

Sample

来看一个简单的使用示例:

var sqlText = "SELECT TOP 100 * FROM dto.tabUsers WHERE Id > 10 ORDER BY Id DESC";
var result = Parser.Parse(sqlText);
Console.WriteLine(result.BatchCount);
Console.WriteLine(result.Script.Sql);Console.WriteLine("-------------------------------");
IterateSqlNode(result.Script);

上面的 IterateSqlNode 方法是一个遍历解析结果的一个方法,定义如下:

static void IterateSqlNode(SqlCodeObject sqlCodeObject, int indent=0)
{if (sqlCodeObject.Children == null) return;foreach (var child in sqlCodeObject.Children){Console.WriteLine($"{new string(' ', indent)}Sql:{child.Sql}, Type:{child.GetType().Name}");IterateSqlNode(child, indent+2);}
}

上面示例的输出结果如下:

从上面的输出结果,我们大概可以看得出来一个 SELECT 查询的 SQL 组成部分大概有以下部分:

  • SqlBatch

    • SqlSelectSpecification

    • SqlSelectClause

    • SqlFromClause

    • SqlWhereClause

    • SqlQuerySpecification

    • SqlOrderByClause

    • SqlSelectStatement

每一个 SQL 语句可能会有多个语句,所以最外层是一个 SqlBatch,如果只有一个语句就对应着一个 SqlBatch,如果是一个 SELECT 查询就是一个 SqlSelectStatement,由 SqlQuery(SqlSelectClause/SqlFromClause/SqlWhereClause)和 SqlOrderBy 组成,还有一些 GroupByClause/HavingClause 等从句,可以自己去尝试一下

Practice

接着我们再来看一个实例,我们的带分页的列表查询接口有几个方法内部都是两个方法,一个查询列表,一个查询总数,这样的查询大家是如何处理的呢?

我觉得有些繁琐,合成一个查询就好了,我尝试着利用 SqlParser 来分析 SQL 语句,根据列表查询的 SQL 自动生成一个查询总数的 SQL,来看下面这个例子:

// 查询列表 SQL
sqlText = @"
SELECT u.Id AS UserId, u.[Name] AS UserName, u.City AS [From] FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id
WHERE u.Id>10
ORDER BY u.Id DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
";
IterateSqlNode(Parser.Parse(sqlText).Script);// 根据查询列表的 SQL 生成 GET COUNT 的 SQL
static string GetCountSql(string sql)
{var result = Parser.Parse(sql);if (result.Script is null){throw new ArgumentException("Invalid query", nameof(sql));}var sqlQuery = result.Script.Batches[0].Children.OfType<SqlSelectStatement>().FirstOrDefault()?.Children.OfType<SqlSelectSpecification>().FirstOrDefault()?.Children.OfType<SqlQuerySpecification>().FirstOrDefault();if (sqlQuery is null){throw new ArgumentException("Invalid query", nameof(sql));}return $@"SELECT COUNT(1) {sqlQuery.FromClause.Sql} {sqlQuery.WhereClause.Sql}";
}

上面这个 SQL 是一个比较典型的我们常用的列表查询 SQL,有的会更简单一些只需要一个表,有些查询条件会比较复杂一些,上面代码输出结果如下(内容有点长):

Sql:
SELECT u.Id AS UserId, u.[Name] AS UserName, u.City AS [From] FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id
WHERE u.Id>10
ORDER BY u.Id DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
, Type:SqlBatchSql:SELECT u.Id AS UserId, u.[Name] AS UserName, u.City AS [From] FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id
WHERE u.Id>10
ORDER BY u.Id DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY, Type:SqlSelectStatementSql:SELECT u.Id AS UserId, u.[Name] AS UserName, u.City AS [From] FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id
WHERE u.Id>10
ORDER BY u.Id DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY, Type:SqlSelectSpecificationSql:SELECT u.Id AS UserId, u.[Name] AS UserName, u.City AS [From] FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id
WHERE u.Id>10, Type:SqlQuerySpecificationSql:SELECT u.Id AS UserId, u.[Name] AS UserName, u.City AS [From], Type:SqlSelectClauseSql:u.Id AS UserId, Type:SqlSelectScalarExpressionSql:u.Id, Type:SqlColumnOrPropertyRefExpressionSql:u.Id, Type:TwoPartObjectIdentifierSql:u, Type:SqlIdentifierSql:Id, Type:SqlIdentifierSql:UserId, Type:SqlIdentifierSql:u.[Name] AS UserName, Type:SqlSelectScalarExpressionSql:u.[Name], Type:SqlColumnOrPropertyRefExpressionSql:u.[Name], Type:TwoPartObjectIdentifierSql:u, Type:SqlIdentifierSql:[Name], Type:SqlIdentifierSql:UserName, Type:SqlIdentifierSql:u.City AS [From], Type:SqlSelectScalarExpressionSql:u.City, Type:SqlColumnOrPropertyRefExpressionSql:u.City, Type:TwoPartObjectIdentifierSql:u, Type:SqlIdentifierSql:City, Type:SqlIdentifierSql:[From], Type:SqlIdentifierSql:FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id, Type:SqlFromClauseSql:dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id, Type:SqlQualifiedJoinTableExpressionSql:dbo.tabUsers AS u WITH(NOLOCK), Type:SqlTableRefExpressionSql:dbo.tabUsers, Type:TwoPartObjectIdentifierSql:dbo, Type:SqlIdentifierSql:tabUsers, Type:SqlIdentifierSql:u, Type:SqlIdentifierSql:NOLOCK, Type:SqlTableHintSql:dbo.tabUserRoles AS r WITH(NOLOCK), Type:SqlTableRefExpressionSql:dbo.tabUserRoles, Type:TwoPartObjectIdentifierSql:dbo, Type:SqlIdentifierSql:tabUserRoles, Type:SqlIdentifierSql:r, Type:SqlIdentifierSql:NOLOCK, Type:SqlTableHintSql:ON r.UserId= u.Id, Type:SqlConditionClauseSql:r.UserId= u.Id, Type:SqlComparisonBooleanExpressionSql:r.UserId, Type:SqlColumnOrPropertyRefExpressionSql:r.UserId, Type:TwoPartObjectIdentifierSql:r, Type:SqlIdentifierSql:UserId, Type:SqlIdentifierSql:u.Id, Type:SqlColumnOrPropertyRefExpressionSql:u.Id, Type:TwoPartObjectIdentifierSql:u, Type:SqlIdentifierSql:Id, Type:SqlIdentifierSql:WHERE u.Id>10, Type:SqlWhereClauseSql:u.Id>10, Type:SqlComparisonBooleanExpressionSql:u.Id, Type:SqlColumnOrPropertyRefExpressionSql:u.Id, Type:TwoPartObjectIdentifierSql:u, Type:SqlIdentifierSql:Id, Type:SqlIdentifierSql:10, Type:IntegerLiteralExpressionSql:ORDER BY u.Id DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY, Type:SqlOrderByClauseSql:u.Id DESC, Type:SqlOrderByItemSql:u.Id, Type:SqlColumnOrPropertyRefExpressionSql:u.Id, Type:TwoPartObjectIdentifierSql:u, Type:SqlIdentifierSql:Id, Type:SqlIdentifierSql:OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY, Type:SqlOffsetFetchClauseSql:0, Type:IntegerLiteralExpressionSql:10, Type:IntegerLiteralExpression

输出的查询 COUNT 的 SQL 语句如下:

SELECT COUNT(1) FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id WHERE u.Id>10

看上去还是比较符合预期的,另外测试了几种稍微比较复杂的情况也都是可以满足我们的需要的

可以自动生成了 COUNT SQL 之后,我们就可以封装一个方法只需要传一个列表查询的接口就可以了

大概实现如下:

public async Task<List<T>> PageListWithTotalAsync<T>(string sql, PageSearchWithTotalDto param)
{var countSql = GetCountSql(sql);var execSql = $@"
SET @TotalCount=({countSql});
{sql}
OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";var parameter = new DynamicParameters(param);parameter.Output(param, x => x.TotalCount);using var conn = new SqlConnection("");return (await conn.QueryAsync<T>(execSql, parameter)).ToList();
}

PageSearchWithTotalDto 是一个 Model,定义如下:

public class PageSearchDto
{private int _pageNumber = 1;private int _pageSize = 20;public int PageNumber{get => _pageNumber;set{if (value > 0){_pageNumber = value;}}}public virtual int PageSize{get => _pageSize;set{if (value > 0){_pageSize = value;}}}public int Offset => (PageNumber - 1) * PageSize;
}public class PageSearchWithTotalDto : PageSearchDto
{public int TotalCount { get; set; }
}

调用方式示例如下:

var sql = @"SELECT u.Id AS UserId, u.[Name] AS UserName, u.City AS [From] FROM dbo.tabUsers AS u WITH(NOLOCK)
INNER JOIN dbo.tabUserRoles AS r WITH(NOLOCK) ON r.UserId= u.Id
WHERE u.Id>10
ORDER BY u.Id DESC";var search = new PageSearchWithTotalDto()
{PageNum=1,PageSize=10,
};
var list = await PageListWithTotalAsync(sql, search);
Console.WriteLine(search.TotalCOunt);

相比之前的代码,已经简洁了不少,又有一大波重复代码可以消灭了,舒服~~

More

使用 SqlParser 来自动生成语句这种方案实际上并没有应用到我们的项目中,但是我觉得这个不一样的思路也许对你有所帮助,在你需要解析 SQL 的时候可以考虑一下这个 SqlParser

前面的示例可以从 Github 上获取 https://github.com/WeihanLi/SamplesInPractice/blob/master/SqlParserSample/Program.cs,有人在 Github 上提了一个关于开源这个 SqlParser 的 issue,有需要可以关注一下 https://github.com/microsoft/sqltoolsservice/issues/623

References

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/SqlParserSample/Program.cs

  • https://www.nuget.org/packages/Microsoft.SqlServer.Management.SqlParser/

  • https://github.com/microsoft/sqltoolsservice/issues/623

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302411.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何使用TensorFlow玩转深度学习?

自 2015 年 11 月 9 号发布之后&#xff0c;TensorFlow 逐渐成为人工智能领域最广泛运用的深度学习框架。那么TensorFlow框架到底是什么&#xff1f;TensorFlow 是一个大规模机器学习的开源框架&#xff0c;提供了多种深度神经网络的支持。不仅 Google 在自己的产品线使用 Tens…

大学,我是怎么边学编程边赚钱的?

我是如何在大学时就靠编程赚钱的&#xff1f;大家好&#xff0c;我是鱼皮&#xff0c;前段时间看到一位朋友的问题&#xff1a;穷极客一枚&#xff0c;正值大学&#xff0c;很想自己解决生活问题&#xff0c;不再向父母要钱。计算机相关专业&#xff0c;喜欢编程&#xff0c;觉…

独占设备的分配与回收_灵魂拷问:Java对象的内存分配过程是如何保证线程安全的?...

点击上方“linkoffer”&#xff0c;选择关注公众号高薪职位第一时间送达作者 l HollisJVM内存结构&#xff0c;是很重要的知识&#xff0c;相信每一个静心准备过面试的程序员都可以清楚的把堆、栈、方法区等介绍的比较清楚。上图&#xff0c;是一张在作者根据《Java虚拟机规范(…

Wtm Blazor来了!

快点关注我们吧BlazorBlazor从诞生到现在也有一段时间了&#xff0c;之前一直在观望&#xff0c;从dotnet5中Blazor的进步以及即将到来的dotnet6中的规划来看&#xff0c;Blazor的前途还是光明的&#xff0c;所以WtmBlazor来了&#xff01;Blazor的优势后台代码的高复用率。不论…

[Netty实践] 简单WebSocket服务实现

目录 一、介绍 二、依赖导入 三、基础类准备 四、Handler实现 五、WebSocketChannelInitializer实现 六、WebSocketServer实现 七、前端实现 八、测试 九、参考链接 一、介绍 关于WebSocket此处不进行过多介绍&#xff0c;本章主要着重通过Netty实现WebSocket通信服务…

这个爱喝酒的酒鬼可真是让人操碎了心

全世界只有3.14 % 的人关注了数据与算法之美最近又有一道数学难题重现江湖&#xff0c;在数学的江湖上掀起了腥风血雨。为了这道题&#xff0c;武林中也衍生出了三个门派&#xff01;分别有75%派&#xff0c;90%派&#xff0c;50%派。打完这么多派字&#xff0c;怎么莫名有点饿…

这几家5月还在急招.NET,都是30k以上!

最近常看到鼓吹财务自由的文章&#xff0c;甚至将5月18号(谐音&#xff1a;我要发)都演变成了财务自由日&#xff0c;号称通过理财快速达到财务自由... 荒谬&#xff01;财务自由本身就是伪命题&#xff0c;更不提啥小白理财就变身财务自由了&#xff0c;完全收智商税&#xff…

mysql binlog oplog_mongodb 学习之oplog

背景&#xff1a;原来一个同事问我主从mongodb数据库为什么数据差距很大,我让他察看一下两边有啥不一样&#xff0c;发现主的local库有13G从却很小&#xff0c;进入local之后du发现有一个collection前缀的文件有13g&#xff0c;说明是local数据库中一个集合太大了&#xff0c;推…

WPF实现Map加载

WPF开发者QQ群&#xff1a; 340500857 欢迎转发、分享、点赞&#xff0c;谢谢大家~。 接着上一篇效果预览&#xff1a;一、MainWindow.xaml代码如下&#xff1a;<Window x:Class"WpfBingMap.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml…

和哪个专业的男生谈恋爱最惨?

全世界只有3.14 % 的人关注了数据与算法之美艺术类专业艺术类的男生是最懂女孩们的心思&#xff0c;也是最浪漫的一类人群&#xff0c;弹琴唱歌跳舞画画样样擅长。这类男生所做的一切&#xff0c;皆可以把女孩们的心俘获到。但是呢&#xff0c;这类男孩的身边总是会有很多玩的很…

只能选择分卷文件的第一部分。_为机器学习模型选择正确的度量评估(第一部分)...

作者&#xff1a;Alvira Swalin编译&#xff1a;ronghuaiyang导读对不同的应用场景&#xff0c;需要不同的模型&#xff0c;对于不同的模型&#xff0c;需要不同的度量评估方式。本系列的第一部分主要关注回归的度量在后现代主义的世界里&#xff0c;相对主义的各种形式一直是最…

多项式乘法与快速傅里叶变换

全世界只有3.14 % 的人关注了数据与算法之美第一节、多项式乘法我们知道&#xff0c;有两种表示多项式的方法&#xff0c;即系数表示法和点值表示法。什么是系数表示法?所谓的系数表示法&#xff0c;举个例子如下图所示&#xff0c;A&#xff08;x&#xff09;6x^3 7x^2 - 10…

WPF 模仿QQ音乐首页歌单效果

qq音乐桌面版做的效果感觉很不错&#xff0c;今天就模仿一下它首页歌单的效果&#xff0c;从简单做起。。。看一下效果&#xff1a;&#xff0c;其实也很简单&#xff0c;就是布局和动画&#xff0c;触发器。。。还用到了ItemsControl下面就看看代码&#xff1a;MainWindow的xa…

收藏 : 50个Excel逆天功能,一秒变“表哥”

全世界只有3.14 % 的人关注了数据与算法之美Excel的50个逆天功能&#xff0c;动画教程珍藏版&#xff01;先看几个简单的&#xff1a;1、自动筛选2、在Excel中字符替换3、在Excel中冻结行列标题4、在Excel中为导入外部数据5、在Excel中行列快速转换6、共享Excel工作簿7、在Exce…

实战~~整个网络无法浏览,提示网络不存在或者尚未启动

今天早上接到同事的电脑&#xff0c;说其他人访问不到他的电脑&#xff0c;他电脑上有文件要共享才能进行工作~~故障现象&#xff1a;能上网&#xff0c;能PING通其他电脑&#xff0c;但是通过网上邻居和IP不能访问其他电脑上的资源。 这是在故障本机上的提示~~ 这是其他工作站…

python ctp接口_使用ctp的python接口

在github上查到一个项目ctpwrapper在按照文档按照的时候报错>>>pip install cython --upgrade>>>pip install ctpwrapper --upgrade在安装第二个命令的时候第一个问题安装yum install -y gcc-c 解决第二个问题ctpwrapper/MdApi.cpp:39:20: 致命错误:Python.h…

C# 并行和多线程编程——认识和使用Task

对于多线程&#xff0c;我们经常使用的是Thread。在我们了解Task之前&#xff0c;如果我们要使用多核的功能可能就会自己来开线程&#xff0c;然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击&#xff0c;因为task会比thread具有更小的性能开销&#xf…

Facebook上的一道题,超过50万的评论和1万3500次分享

全世界只有3.14 % 的人关注了数据与算法之美近日&#xff0c;有网友在Facebook发了一道数学题&#xff1a;发布以后&#xff0c;目前已经收到超过50万的评论和1万3500次分享&#xff0c;图中包含四个等式&#xff0c;前面三个已经有答案了&#xff0c;最后一个问题要求你得出相…

从数学入手,3招打破机器学习的边界

全世界只有3.14 % 的人关注了数据与算法之美本文约2007余字&#xff0c;阅读需要约6分钟&#xff1b;系统资料领取见文末&#xff1b;关键词&#xff1a;人工智能&#xff0c;机器学习&#xff0c;深度学习&#xff0c;数学&#xff0c;学习建议01.机器学习工程师的边界是什么&…

.NET Core 基于 Grafana Loki 日志初体验

介绍Loki: like Prometheus, but for logs.Loki是一个轻量级的日志系统&#xff0c;受到Prometheus项目的启发&#xff0c;由Grafana团队设计和开发&#xff0c;所以在Grafana中是原生支持的&#xff0c;具有可水平扩展&#xff0c;高度可用等特性&#xff0c;通过存储压缩的、…