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

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介:

前言

之前有园友一直关注着我快点出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(200)

SET @AccountNumber = 'AW00000002'

SELECT 
    StoreID, CustomerID, ModifiedDate, PersonID
FROM Sales.Customer
WHERE AccountNumber = @AccountNumber
复制代码

EXEC动态SQL语句动态查询 

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

复制代码
USE TSQL2012

DECLARE @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

EXEC(@sqlCommand)
复制代码

 

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

SET @shipcity = '''Lyon'''

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

复制代码
USE TSQL2012

DECLARE @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, PersonID
    FROM 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.Customers

EXEC('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]
GO

CREATE 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 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(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 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!





本文转自Jeffcky博客园博客,原文链接:http://www.cnblogs.com/CreateMyself/p/6411676.html,如需转载请自行联系原作者

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情: https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
6天前
|
SQL 数据库 开发者
功能发布-自定义SQL查询
本期主要为大家介绍ClkLog九月上线的新功能-自定义SQL查询。
|
13天前
|
SQL 移动开发 Oracle
SQL语句实现查询连续六天数据的方法与技巧
在数据库查询中,有时需要筛选出符合特定时间连续性条件的数据记录
|
20天前
|
SQL Java 数据库连接
如何使用`DriverManager.getConnection()`连接数据库,并利用`PreparedStatement`执行参数化查询,有效防止SQL注入。
【10月更文挑战第6天】在代码与逻辑交织的世界中,我从一名数据库新手出发,通过不断探索与实践,最终成为熟练掌握JDBC的开发者。这段旅程充满挑战与惊喜,从建立数据库连接到执行SQL语句,再到理解事务管理和批处理等高级功能,每一步都让我对JDBC有了更深的认识。示例代码展示了如何使用`DriverManager.getConnection()`连接数据库,并利用`PreparedStatement`执行参数化查询,有效防止SQL注入。
62 5
|
20天前
|
SQL 数据挖掘 数据库
SQL查询每秒的数据:技巧、方法与性能优化
id="">SQL查询功能详解 SQL(Structured Query Language,结构化查询语言)是一种专门用于与数据库进行沟通和操作的语言
|
20天前
|
SQL 移动开发 大数据
SQL语句查询连续六天满足条件的记录
在数据库管理和数据分析中,我们经常需要查询符合特定时间范围内连续几天的记录
|
22天前
|
SQL 存储 安全
SQL查询数据库:基础概念与操作指南
在数字化时代,数据库已成为信息管理的重要工具之一。作为管理和操作数据库的核心语言,SQL(结构化查询语言)已成为数据管理和查询的关键技能。本文将全面介绍SQL查询数据库的基本概念、语句和操作指南,以帮助初学者快速上手,同时为进阶用户提供有价值的参考。一、数据库与SQL简介数据库是一种存储、管理和检索
30 3
|
2天前
|
SQL 关系型数据库 MySQL
mysql编写sql脚本:要求表没有主键,但是想查询没有相同值的时候才进行插入
mysql编写sql脚本:要求表没有主键,但是想查询没有相同值的时候才进行插入
6 0
|
13天前
|
SQL 数据可视化 BI
SQL语句及查询结果解析:技巧与方法
在数据库管理和数据分析中,SQL语句扮演着至关重要的角色
|
18天前
|
SQL 监控 关系型数据库
使用SQL语句查询操作耗时的技巧与方法
在数据库管理和优化过程中,了解SQL查询操作的耗时是至关重要的
|
18天前
|
SQL
创建分组总计查询的SQL技巧与方法
在SQL中,创建分组总计查询(也称为聚合查询)是一项非常基础且重要的技能