前言

之前有园友一直关注着我快点出SQL Server性能优化系列,博主我也对性能优化系列也有点小期待,本来打算利用周末写死锁以及避免死锁系列的接着进入SQL Server优化系列,但是在工作中长时间都是利用EF来操作SQL,不免对写SQL语句有些生疏,在某些场景下还是只能利用底层的SQL语句或者写存储过程来实现,很久没写存储过程都忘记怎么写了,所以本节穿插动态SQL查询的文章,别着急,博主说过不会烂尾,博主再忙也会抽空将整个SQL Server系列梳理完毕,那样的话,无论对初级还是中级者都可以从中受益匪浅,至少我是这么认为,呵呵。

动态SQL语句查询

前前篇我们简短叙述了利用EXEC和EXECUTE来进行动态SQL语句查询,并未深入去讲解,借博主工作中重新回到写原生SQL语句的机会,我们再来回顾下动态SQL语句查询。既然是动态SQL查询,说明在某些场景下利用硬编码SQL语句查询的方式是不可行的,比如查询条件的不固定,这是最常见的情景,那么动态SQL语句查询有哪几种方式呢?万变不离其宗,只有以下三种方式,请往下看。

参数化SQL语句动态查询

参数化SQL查询是动态SQL查询中最简单的一种,因为我们只需要传递参数即可,查询条件是固定的,我们一起来温故而知新。

USE AdventureWorks2012DECLARE @AccountNumber AS VARCHAR(200)SET @AccountNumber = 'AW00000002'SELECT StoreID, CustomerID, ModifiedDate, PersonID
FROM Sales.Customer
WHERE AccountNumber = @AccountNumber

EXEC动态SQL语句动态查询

这个相对来说比上一个要略微复杂一点,对于复杂的SQL语句拼接,我们来看看。

USE TSQL2012DECLARE @shipcity AS VARCHAR(50)
DECLARE @sqlCommand AS VARCHAR(500)
DECLARE @columnList AS VARCHAR(200)SET @shipcity = 'Lyon'
SET @columnList = 'orderid, custid, orderdate, shipname, shipaddress, shipcity'
SET @sqlCommand = 'SELECT '+ @columnList + ' FROM Sales.Orders WHERE shipcity = '+ @shipcityEXEC(@sqlCommand)

居然出错了,让人始料未及,当设置参数值时我们应该将 SET @shipcity = 'Lyon' 进行如下修改:

SET @shipcity = '''Lyon'''

sp_executesql动态SQL语句查询(推荐)

USE TSQL2012DECLARE @shipcity AS VARCHAR(50)
DECLARE @sqlCommand AS VARCHAR(500)
DECLARE @columnList AS VARCHAR(200)SET @shipcity = 'Lyon'
SET @columnList = 'orderid, custid, orderdate, shipname, shipaddress, shipcity'
SET @sqlCommand = 'SELECT '+ @columnList + ' FROM Sales.Orders WHERE shipcity = @shipcity'EXECUTE sp_executesql @sqlCommand, N'@shipcity VARCHAR(50)', @shipcity = @shipcity

之前从未遇到过这种情况,查此错误居然发现要:sp_executesql执行的SQL必须定义为NVARCHAR类型即必须定义为UNICODE,这里算是学习了。

对于利用sp_executesql来执行动态sql语句查询的方式作为推荐最主要是因为其在查询执行计划中,无论其变量值是否改变查询计划都会进行重用,当然还有其他好处,比如利用EXEC来执行查询,此时进行拼接非常容易出错。同时在写动态SQL时个人一般推崇将查询的列作为一个列表进行定义,对于参数也进行定义,这样可维护性强不至于看起来乱糟糟的利于后续排查问题。

讲到这里是不是叙述完毕了呢,那就太没意思了,相信看过博主所写的内容一般都是由浅入深,下面我们继续往下看。

一直讲的动态SQL语句查询,你难道就没有怀疑过仅仅只能进行查询,难道不能创建表或者创建视图么,答案当然是可以的。我们简单看下创建表。

Declare @SQL VarChar(1000)
DECLARE @TableName AS VARCHAR(10)SET @TableName = 'Test'SELECT @SQL = 'Create Table ' + @TableName + '('
SELECT @SQL = @SQL + 'ID int NOT NULL Primary Key, FieldName VarChar(10))'Exec (@SQL)

利用EXEC或者sp_executesql居然还能创建表,还能最高级一点么,答案当然是可以,请继续往下看。

当我们在不是当前打开会话中的数据库中去创建另外数据库的视图时可行不可行呢?

Create View AdventureWorks2012.dbo.Auths AS (SELECT StoreID, CustomerID, ModifiedDate, PersonIDFROM Sales.Customerl)

此时创建视图你会发现不能正常创建视图,说明创建视图必须是在当前会话指定的数据库中才可以。

但是利用sp_executesql就能解决跨数据库创建视图的问题,如下:

DECLARE @SQL NVarChar(1000)SELECT @SQL = 'Create View Auths AS (SELECT CustomerID, ModifiedDate FROM Sales.Customer)'EXECUTE AdventureWorks2012.dbo.sp_executesql @sql

此时你会发现利用sp_executesql不仅可以重用查询执行计划并且可以跨数据库创建视图,那么它难道就没有限制么,答案当然是有的,请继续往下看。

存储过程执行动态SQL语句查询

当在存储过程中执行动态SQL语句查询时,此时动态SQL语句是在当前用户的权限下进行而不是调用存储过程的用户的权限,换句话说如果我们对存储过程上的表没有权限,那么运行存储过程将出现问题。还是不理解是什么意思么,也就是说存储过程运行是有其作用域的。我们举一个例子,我们可以利用如下语句来设置查询的行数。

SET ROWCOUNT 3

所以接下来我们利用上述语句来查询返回的行数。

EXEC('SET ROWCOUNT 3')SELECT * FROM Sales.CustomersEXEC('SET ROWCOUNT 0')

从上我们可以发现我们设置限制返回的行数根本不起作用,这是因为设置返回的行数已经超出了查询的作用域。所以我们必须将其设置限制行数放到EXEC中,如下:

EXEC('SET ROWCOUNT 3
SELECT * FROM Sales.Customers SET ROWCOUNT 0')

WHERE 1 = 1问题

对于不确定多条件筛选利用WHERE 1 = 1是否会带来性能问题,但是除此之外似乎没有什么好的办法比利用WHERE 1 = 1来减少条件的判断从而加长SQL语句,可能会导致性能的下降。下面我们来看一个简单的例子

USE  AdventureWorks2012
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Sales].[GetSalesOrders]') AND type in (N'P', N'PC'))
DROP PROCEDURE [Sales].[GetSalesOrders]
GOCREATE PROCEDURE [Sales].[GetSalesOrders] (@CustomerID INT = NULL,@CreditCardID INT = NULL)
AS
SET NOCOUNT ON; DECLARE @SQL NVARCHAR(4000);
DECLARE @ParameterDefinition NVARCHAR(4000);SELECT    @ParameterDefinition = '
    @CustomerParameter INT,@CreditCardIDParameter INT
';

SELECT    @SQL = N'
SELECT    [SalesOrderID], [OrderDate], [Status], [CustomerID], [CreditCardID]
FROM    [Sales].[SalesOrderHeader]
WHERE 1 = 1
';

IF @CustomerID IS NOT NULLSELECT @SQL = @SQL + N'
    AND CustomerID = @CustomerParameter ';

IF @CreditCardID IS NOT NULLSELECT @SQL = @SQL + N'
    AND CreditCardID = @CreditCardIDParameter ';
    EXECUTE sp_executesql@SQL,@ParameterDefinition,@CustomerParameter = @CustomerID,@CreditCardIDParameter = @CreditCardID;
GOSET NOCOUNT OFF;

再复杂也不过就是多表查询和多条件筛选罢了,还是比较简单。得出如下结果。

上述利用存储过程创建动态SQL语句没什么毛病。

那么问题来了,要是如果我们想调试创建过程生成的SQL语句是否有没有错误,我们此时该如何做呢?为了利于调试我们可以将上述修改如下,添加一个debug标识来打印SQL。

USE  AdventureWorks2012
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Sales].[GetSalesOrders]') AND type in (N'P', N'PC'))
DROP PROCEDURE [Sales].[GetSalesOrders]
GOCREATE PROCEDURE [Sales].[GetSalesOrders] (@CustomerID INT = NULL,@CreditCardID INT = NULL,@debug bit = 0)
AS
SET NOCOUNT ON; DECLARE @SQL NVARCHAR(4000);
DECLARE @ParameterDefinition NVARCHAR(4000);SELECT    @ParameterDefinition = '
    @CustomerParameter INT,@CreditCardIDParameter INT
';

SELECT    @SQL = N'
SELECT    [SalesOrderID], [OrderDate], [Status], [CustomerID], [CreditCardID]
FROM    [Sales].[SalesOrderHeader]
WHERE 1 = 1
';

IF @CustomerID IS NOT NULLSELECT @SQL = @SQL + N'
    AND CustomerID = @CustomerParameter ';

IF @CreditCardID IS NOT NULLSELECT @SQL = @SQL + N'
    AND CreditCardID = @CreditCardIDParameter ';

IF @debug = 1PRINT @SQLEXECUTE sp_executesql@SQL,@ParameterDefinition,@CustomerParameter = @CustomerID,@CreditCardIDParameter = @CreditCardID;
GOEXEC [Sales].[GetSalesOrders] @debug = 1, @CustomerID = 29565SET NOCOUNT OFF;

此时我们调试发现SQL语句没有写错并且生成的结果如下:

同时我们在消息中可以看到生成的SQL语句如下,这样能够准确无语的保证所写动态SQL没有问题。

总结

本节我们阐述了动态SQL以及要注意的地方,同时为了便于调试保证所生成的SQL语句不会出现问题给出的建议。下节我们开始讲死锁问题,欢迎大家持续关注博客和公众号,对于.NET Core也会持续更新。See u!

SQL Server-聚焦深入理解动态SQL查询(三十二)相关推荐

  1. SQL Server-聚焦sp_executesql执行动态SQL查询性能真的比exec好?

    前言 之前我们已经讨论过动态SQL查询呢?这里为何再来探讨一番呢?因为其中还是存在一定问题,如标题所言,很多面试题也好或者有些博客也好都在说在执行动态SQL查询时sp_executesql的性能比ex ...

  2. mysql桦仔_Microsoft SQL Server 2005技术内幕:T-SQL查询笔记

    Microsoft SQL Server 2005技术内幕:T-SQL查询笔记 目录 f f f f f f f f 第二章 物理查询处理 分析,代数化,查询优化 f f f f f. 分析--> ...

  3. SQL Server 中深入理解GRANT、REVOKE 和 DENY 的区别

    在SQL Server数据库登陆账户.数据库用户权限管理中,经常会授予权限.回收权限,有时还会拒绝权限.GRANT.REVOKE是我们常用的,但有时会遇到使用DENY的情形.从英文单词的字面意思来看, ...

  4. 05.SQL Server大数据群集小试牛刀--HDFS查询

    05.SQL Server大数据群集小试牛刀--HDFS查询 SQL Server大数据群集查询HDFS ,利用之前创建好的大数据群集,使用官方提供的测试数据进行测试.脚本是官方的脚本,要知道干了什么 ...

  5. SQL Server 执行计划(8) - 使用 SQL 执行计划进行查询性能调优

    在本系列的前几篇文章(见底部索引)中,我们介绍了SQL 执行计划的多个方面,我们讨论了执行计划是如何在内部生成的,不同类型的计划,主要组件和运算符以及如何阅读和分析使用不同工具生成的计划.在本文中,我 ...

  6. SQL server模拟数据实现稍复杂查询语句

    目录 1 DISTINCT(不允许重复)的用法 2 BETWEEN(在某个范围内)的用法 3 IN(属于若干个孤立的值)的用法 4 NULL(空值)的用法 5 ORDER BY(排序)的用法 6 模糊 ...

  7. sql server死锁_如何报告SQL Server死锁事件

    sql server死锁 介绍 (Introduction) In the previous article entitled "What are SQL Server deadlocks ...

  8. sql server 加密_列级SQL Server加密概述

    sql server 加密 This article gives an overview of column level SQL Server encryption using examples. 本 ...

  9. sql server 锁定_关于锁定SQL Server的全部

    sql server 锁定 .SQLCode { font-size: 13px; font-weight: bold; font-family: monospace;; white-space: p ...

最新文章

  1. jvm两种方式获取对象所占用的内存
  2. [THUWC2017]在美妙的数学王国中畅游
  3. 基于libUSB的USB设备固件更新程序(下载数据)(转)
  4. linux如何生成so文件,新人问个问题,莫见笑:关于如何生成so文件,大家多多捧场啊...
  5. linux 命令api,linux命令行下字典,使用有道API
  6. debian GNU linux 版的docker 安装vi
  7. ftk学习记(combox篇)
  8. 汽车上有哪些很难发现却非常实用的配置?
  9. 【BUG解析】调用PagerAdapter.notifyDataSetChanged()不更新
  10. 计算机 学术论文写作,计算机辅助学术论文写作系统的研制策略与方法.pdf
  11. 分享|2022城市大脑发展白皮书(附PDF)
  12. 图扑数字孪生数据中心机房,助力产业绿色低碳转型
  13. 魔百盒CM211-1-晶晨S905L3B芯片系列-红外蓝牙线刷固件及教程
  14. 积木编程安卓app入门 —— 5 分钟学会 App Inventor
  15. 软件工程 学习笔记 知识梳理
  16. 我的世界java多人不刷溺尸_我的世界:不同版本的三叉戟掉落率不一样?刷了一两百溺尸也没有!...
  17. 总结:阿里云学生服务器地址/条件/续费等问题及解答FAQ
  18. 前端异步请求async/await,axios的错误用法
  19. HCIE课程笔记18-局域网二层技术
  20. python与cgi

热门文章

  1. win32格式化错误消息
  2. Linux字符驱动开发学习总结
  3. 每天一个设计模式,工厂方法模式(Factory Method)
  4. 6.1.2.6 盒子
  5. POJ 3616 Milking Time
  6. .net生成excel并弹出保存提示框(转载)
  7. 《Shell脚本学习指南》第四章 文本处理工具
  8. MFC遍历窗体所有的控件
  9. 用神经网络分类3*3矩阵
  10. 66319d电源使用说明书_海尔洗衣机怎么用 海尔洗衣机使用说明【详细介绍】