前言

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

动态SQL语句查询

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

参数化SQL语句动态查询

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

USE AdventureWorks2012

DECLARE @AccountNumber AS VARCHAR()

SET @AccountNumber = 'AW00000002'

SELECT
StoreID, CustomerID, ModifiedDate, PersonID
FROM Sales.Customer
WHERE AccountNumber = @AccountNumber

EXEC动态SQL语句动态查询

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

USE TSQL2012

DECLARE @shipcity AS VARCHAR()
DECLARE @sqlCommand AS VARCHAR()
DECLARE @columnList AS VARCHAR() SET @shipcity = 'Lyon'
SET @columnList = 'orderid, custid, orderdate, shipname, shipaddress, shipcity'
SET @sqlCommand = 'SELECT '+ @columnList + ' FROM Sales.Orders WHERE shipcity = '+ @shipcity EXEC(@sqlCommand)

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

SET @shipcity = '''Lyon'''

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

USE TSQL2012

DECLARE @shipcity AS VARCHAR()
DECLARE @sqlCommand AS VARCHAR()
DECLARE @columnList AS VARCHAR() 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()
DECLARE @TableName AS VARCHAR() 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, PersonID
FROM Sales.Customerl
)

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

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

DECLARE @SQL NVarChar()

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 

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

EXEC('SET ROWCOUNT 3')

SELECT * FROM Sales.Customers

EXEC('SET ROWCOUNT 0')

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

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

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]
GO CREATE PROCEDURE [Sales].[GetSalesOrders] (
@CustomerID INT = NULL,
@CreditCardID INT = NULL)
AS
SET NOCOUNT ON; DECLARE @SQL NVARCHAR();
DECLARE @ParameterDefinition NVARCHAR(); SELECT @ParameterDefinition = '
@CustomerParameter INT,
@CreditCardIDParameter INT
'; SELECT @SQL = N'
SELECT [SalesOrderID], [OrderDate], [Status],
[CustomerID], [CreditCardID]
FROM [Sales].[SalesOrderHeader]
WHERE =
'; IF @CustomerID IS NOT NULL
SELECT @SQL = @SQL + N'
AND CustomerID = @CustomerParameter '; IF @CreditCardID IS NOT NULL
SELECT @SQL = @SQL + N'
AND CreditCardID = @CreditCardIDParameter '; EXECUTE sp_executesql
@SQL,
@ParameterDefinition,
@CustomerParameter = @CustomerID,
@CreditCardIDParameter = @CreditCardID;
GO SET 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]
GO CREATE PROCEDURE [Sales].[GetSalesOrders] (
@CustomerID INT = NULL,
@CreditCardID INT = NULL,
@debug bit = 0)
AS
SET NOCOUNT ON; DECLARE @SQL NVARCHAR();
DECLARE @ParameterDefinition NVARCHAR(); SELECT @ParameterDefinition = '
@CustomerParameter INT,
@CreditCardIDParameter INT
'; SELECT @SQL = N'
SELECT [SalesOrderID], [OrderDate], [Status],
[CustomerID], [CreditCardID]
FROM [Sales].[SalesOrderHeader]
WHERE =
'; IF @CustomerID IS NOT NULL
SELECT @SQL = @SQL + N'
AND CustomerID = @CustomerParameter '; IF @CreditCardID IS NOT NULL
SELECT @SQL = @SQL + N'
AND CreditCardID = @CreditCardIDParameter '; IF @debug = 1
PRINT @SQL EXECUTE sp_executesql
@SQL,
@ParameterDefinition,
@CustomerParameter = @CustomerID,
@CreditCardIDParameter = @CreditCardID;
GO EXEC [Sales].[GetSalesOrders] @debug = 1, @CustomerID = 29565 SET NOCOUNT OFF;

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

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

总结

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

SQL Server-聚焦深入理解动态SQL查询(三十二)的更多相关文章

  1. Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor

    Webservice WCF WebApi   注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...

  2. c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程

    c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...

  3. [转帖]SQL Server 10分钟理解游标

    SQL Server 10分钟理解游标 https://www.cnblogs.com/VicLiu/p/11671776.html 概述 游标是邪恶的! 在关系数据库中,我们对于查询的思考是面向集合 ...

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

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

  5. 如何用asp.net MVC框架、highChart库从sql server数据库获取数据动态生成柱状图

    如何用asp.net MVC框架.highChart库从sql server数据库获取数据动态生成柱状图?效果大概是这样的,如图: 请问大侠这个这么实现呢?

  6. 不同SQL Server数据库之间的跨数据库查询

    --不同SQL Server数据库之间的跨数据库查询 EXEC sp_addlinkedserver @server=N'OldDatabase', --自己定义别名 @srvproduct=N'', ...

  7. SQL Server中INNER JOIN与子查询IN的性能测试

    这个月碰到几个人问我关于"SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?"这个问题.其实这个概括起来就是SQL Server中INNER JOIN与子 ...

  8. SQL Server中Table字典数据的查询SQL示例代码

    SQL Server中Table字典数据的查询SQL示例代码 前言 在数据库系统原理与设计(第3版)教科书中这样写道: 数据库包含4类数据: 1.用户数据 2.元数据 3.索引 4.应用元数据 其中, ...

  9. SQL Server 2008 R2 性能计数器详细列表(三)

    原文:SQL Server 2008 R2 性能计数器详细列表(三) SQL Server,Deprecated Features 对象: 监视指定为不推荐使用的功能: SQL Server Depr ...

随机推荐

  1. 如何使用UDP进行跨网段广播(转)

    源:如何使用UDP进行跨网段广播 广播域首先我们来了解一下广播域的概念.广播域是网络中能接收任一台主机发出的广播帧的所有主机集合.也就是说,如果广播域内的其中一台主机发出一个广播帧,同一广播域内所有的 ...

  2. ubuntu16安装KVM

    apt install qemu-kvm libvirt-bin apt install openvswitch-switch

  3. LPC2478中断控制器以及串口详解

    LPC2478的中断系统 LPC2478使用的是ARM PrimeCell向量中断控制器,一共支持32个中断向量,处于AHB空间便于系统快速访问,在中断向量的硬件优先级上还有一层可以用户自己设计的软件 ...

  4. STM8的GPIO驱动

    芯片的外设一般按照这么几个流程来进行,GPIO,外部中断,定时器,串口,ADC,IIC,SPI,下面我就按照各个模式来写 首先是GPIO,STM8的GPIO拥有复用功能,这句话告诉我们必然需要配置IO ...

  5. 批量检查APK是否具有指定的权限。

    为测试组的妹子提供的. 效果如下: 目录结构如下: 源代码思路: 1.将apk文件变为zip文件.这里是修改后缀 2.解压文件到指定目录.可以只解压其中mainfest.xml文件 3.移动xml文件 ...

  6. php __set()和__get()函数

    <?php /* 总结: 1. 从一个难以访问的属性读取数据的时候 __get() 方法被调用 2. 向一个难以访问的属性赋值的时候 __set() 方法被调用 3. 难以访问包括:(1)私有属 ...

  7. 从底层谈WebGIS 原理设计与实现(三):WebGIS前端地图显示之根据地理范围换算出瓦片行列号的原理(转载)

    从底层谈WebGIS 原理设计与实现(三):WebGIS前端地图显示之根据地理范围换算出瓦片行列号的原理 1.前言   在上一节中我们知道了屏幕上一像素等于实际中多少单位长度(米或经纬度)的换算方法, ...

  8. 1.1.Core Data是什么(Core Data 应用程序实践指南)

    Core Data是个框架,把数据当作对象来操作. 由Core Data提供的数据对象叫托管对象(managed objecgt),而Core Data 位于程序和持久化存储区之间. 托管对象模型里有 ...

  9. 阿里CEO张勇公开信:把眼光从股市回到客户身上

     8月25日消息,面对全球资本市场的剧烈波动,阿里巴巴集团CEO张勇今日发表致员工信,倡议全体阿里员工把眼光从股市回到客户身上,脚踏实地的服务帮助客户,为客户创造价值,继而为股东和自己创造价值.  张 ...

  10. 排序问题思考(要求时间和空间复杂度尽可能的低)【Part 2】

    继上篇博文,今天我将先介绍一下什么是计数排序,将计数排序描述清楚后,再进行后续的桶排序方法解决这个问题. 通常情况下,一提到排序,大家第一反应就是比较,其实,今天我要说的这个计数排序,不是基于比较的排 ...