一.关于动态SQL注意的一些事项:
1.exec 是一个能执行动态SQL的函数,但是不提供接口。并且还能执行存储过程。
exec at 远程执行动态SQL
解决了几个以前2000的问题:openquery对于输入的查询字符串必须是静态的,不能是变量并且没有输入参数。另外对于外部查询的from字句中调用 openquery该函数必须返回为一个表。如果查询多个结果,那么只返回第一个集合
语法:OPENQUERY ( linked_server , 'query' )
EXEC sp_addlinkedserver 'OracleSvr', 'Oracle 7.3', 'MSDAORA', 'ORCLDB'
GO --创建远程连接
SELECT * FROM OPENQUERY(OracleSvr, 'SELECT name, id FROM joe.titles')
GO --用OPENQUERY 执行远程SQL
对于OPENROWSET执行远程操作也存在上面的出现的问题
OPENROWSET是一次性的,如果查询出多张表,则它只返回第一张表
对于OPENROWSET的用法:
USE pubs
GO
SELECT a.* FROM OPENROWSET('SQLOLEDB','seattle1';'sa';'MyPass',
'SELECT * FROM pubs.dbo.authors ORDER BY au_lname, au_fname') AS a
GO
其中seattle1是datasource
上面2个函数的限制被exec at打破(可以传入参数,变量,动态SQL,返回值不一定是表或者干脆就不返回)找点代码来展示一下:
EXEC sp_addlinkedserver 'FARAWAYSERVER', 'SQL Server' --创建一个链接服务器
//执行简单的SQL查询
EXEC ('SELECT TOP 10 * FROM AdventureWorksLT.SalesLT.Customer') AT [FARAWAYSERVER];
GO
//在链接服务器上执行多个SELECT语句并得到多个结果集
EXEC ('SELECT TOP 10 * FROM AdventureWorksLT.SalesLT.Customer;
SELECT TOP 10 * FROM AdventureWorksLT.SalesLT.CustomerAddress;') AT [FARAWAYSERVER];
//在链接服务器上执行一个SELECT语句并动态传递两个变量
EXEC ('SELECT TOP 10 * FROM AdventureWorksLT.SalesLT.Customer
WHERE CustomerID = ? AND LastName = ?', 10, 'Garza') AT [FARAWAYSERVER];
GO
//在链接服务器上执行一个SELECT 语句并动态传递两个变量,使用变量
DECLARE @CustomerID AS INT
DECLARE @LastName AS VARCHAR(100)
SET @CustomerID = 10
SET @LastName = 'Garza'
EXEC ('SELECT TOP 10 * FROM AdventureWorksLT.SalesLT.Customer
WHERE CustomerID = ? AND LastName = ?', @CustomerID, @LastName) AT [FARAWAYSERVER];
GO
//在链接服务器上执行一个DDL语句
EXEC ('USE TempDB IF OBJECT_ID(''dbo.Table1'') IS NOT NULL DROP TABLE dbo.Table1
CREATE TABLE dbo.Table1
(Column1 INT)' ) AT [FARAWAYSERVER];
//清除创建的对象
EXEC ( 'USE TempDB IF OBJECT_ID(''dbo.Table1'') IS NOT NULL
DROP TABLE dbo.Table1') AT [FARAWAYSERVER];
EXEC sp_dropserver 'FARAWAYSERVER'
注意事项
你可能得到错误“"Server 'myserver'不能用来配置RPC”,如果这样你需要用下面的命令启用RPC:
exec sp_serveroption @server='myserver', @optname='rpc', @optvalue='true'
exec sp_serveroption @server='myserver', @optname='rpc out', @optvalue='true'
在你调用带有一个字符串的EXECUTE之前,确认该字符串。不要执行一个没有经过验证的用户输入构成的命令。
如果你有一个命名的实例,那么你可能需要使用下面的sp_addlnkedserver来创建没有斜线的链接服务器名称。因此这个链接服务器名称引用的是“SQL2005”,但是它连接的是实例“SERVER1\SQL2005”。
EXEC sp_addlinkedserver @server='SQL2005', @srvproduct='', @provider='SQLNCLI', @datasrc='SERVER1\SQL2005'
我们最好先在用exec at之前调用一下几个系统存储过程:
sp_addlinkedserver 创建远程连接
sp_droplinkedsrvlogin 移除所有本地登录添加的默认自映射(安全考虑)
sp_addlinkedsrvlogin 把本地登录映射到远程服务器的一个安全账户
sp_serveroption 权限启用(远程)
2.sp_executesql优于exec,因为它支持输入输出参数的接口并且能实现计划执行的复用(相同的代码,不同的参数),(而exec对于不同的参数则每次都要重新编译在执行,生成不同的执行计划)相当于存储过程,如果我们用exec,那么执行一次生成一次计划,但是sp_executesql则对与相同的代码只生成一个相同的计划。对与创建足够大的执行语句我们在声明变量的时候采用declare @m nvarchar(max)
sp_executesql [@stmt =] stmt
[
{, [@params =] N'@parameter_name data_type [,...n]' }
{, [@param1 =] 'value1' [,...n] }
]
execute sp_executesql
N'select * from pubs.dbo.employee where job_lvl = @level',
N'@level tinyint',
@level = 35
带输出参数的
DECLARE @IntVariable int;
DECLARE @SQLString nvarchar(500);
DECLARE @ParmDefinition nvarchar(500);
DECLARE @max_title varchar(30);
SET @IntVariable = 197;
SET @SQLString = N'SELECT @max_titleOUT = max(JobTitle)
FROM AdventureWorks2008R2.HumanResources.Employee
WHERE BusinessEntityID = @level';
SET @ParmDefinition = N'@level tinyint, @max_titleOUT varchar(30) OUTPUT';
EXECUTE sp_executesql @SQLString, @ParmDefinition, @level = @IntVariable, @max_titleOUT=@max_title OUTPUT;
SELECT @max_title;
3.动态SQL是作为一个独立的批处理来执行的(还没有测试过在事务中能否成功执行),所以你在批处理中添加一个动态SQL,你将无法在动态SQL语句中处理外部变量。所以在这个时候sp_executesql优势就出来了,把外部变量以参数的方式处理,并且返回处理的结果到参数中。
4.在批处理中修改处理环境,对于动态SQL是有效的(比如用: use 数据库名 修改语句应用于那个数据库),然而在动态SQL中修改批处理环境将只是影响动态SQL语句,对外部环境不会造成影响。
5.和设置环境一样,如果在外部建立临时表则在动态SQL中我们能访问该表,但是在动态SQL中建立的临时表在该动态SQL执行完后将自动删除,所外外部是无法看到的。
6.和临时表以及环境不同,变量在外部声明则只能在外部可见,在内部声明的变量也只能在内部可见。
7.在动态SQL中用''表示输入的是一个',代表字符串。
对于动态SQL中生成的执行计划,当输入的参数值不一样则会为不同的参数生成不同的执行计划,但是当我们用存储过程的时候会对所有的传入参数生成一个相同的执行计划。由于生成执行计划需要耗时间,所以在这方面存储过程优于动态SQL。下面是对于SQL2005以上的数据查看计划的方法
select cacheobjtype,objtype,usecounts,sql
from sys.syscacheobjects
where sql not like '%cache%' and sql not like '%sys.%'
一下是SQL2000的计划查看代码
select cacheobjtype,objtype,usecounts,sql
from master.dbo.syscacheobjects
where sql not like '%cache%' and sql not like '%sys.%'
8.不能再动态SQL的执行函数中(exec的括号中)使用case或者函数。因为括号中只能是一个字符串,字符串变量或者字符串加字符串变量,不能有其他东西。如果真的要用函数应在外部定义一个字符串变量专门用于接收转换后的字符串,然后在执行该变量。
9.在提供一种方法获取动态SQL输出值的方法,首先建立动态SQL,然后建立一张临时表,然后把动态SQL执行的结果插入到临时表中,然后在读取临时表。
例如:创建一个临时表 #table 动态SQL @sql ='select * from table1'
insert into #table exec(@sql)
set @count=select count(*) from #table
10.执行远程的操作函数openquery()和openrowset() 连接存储过程:sp_addlinkedserver,现在用execute at 解决了一部分问题。(具体查找资料)
exec sp_addlinkedserver [dojo], 'sql server' ---建立远程连接
exec('select id,name from table1 where id=?;',@id) at [dojo] ---实现远程操作
11.SQL中出现的一些关键词
QUOTENAME 创建带分隔符的字符串
SELECT QUOTENAME('abc[]def') 返回[abc[]]def]
注意,字符串"abc[]def"中的右括号有两个,用于表示转义符。
12.防止SQL注入
限制输入参数的大小,比如 declare @m varchar(10) --10就是最大长度
13.交叉表的实现方式(举例说明)
销售人员 书籍 销量
----------------------------------------
小王 Excel教材 10
小李 Excel教材 15
小王 Word教材 8
小李 Excel教材 7
小王 Excel教材 9
小李 Excel教材 2
小王 Word教材 3
小李 Excel教材 5
一种数据透视的方法是统计每个销售人员对每种书籍的销量 ,结果如下
----------------------------------------------------------------
姓名 Excel教材 Word教材 总计
---------------------------------------------- -----------------
小王 29 0 29
小李 19 11 30
生成数据透视表
面的查询语句首先是拼接了一条"Sql语句",它的最终结果为:
SELECT [name], sum(case book when 'Excel教材' then saledNumber else 0 end) as [Excel教材],sum(case book when 'Word教材' then saledNumber else 0 end) as [Word教材], sum(saledNumber) as [sum] from s group by [name]
当然,如果表中的数据不同,那么这生成的Sql语句也是不同的。最后它调用了Sql Server的系统存储过程Exec来执行这条语句。截个图吧。
这就是在Sql Server中生成数据透视表的实现,其实它的核心也就是上面拼接成的那条Sql语句。更复杂的透视方式,比如多级透视,也是在这个基础上的实现的。