SQL Server 常用开发技术

Ø  简介

本文记录笔者在开发中遇到的MSSQL常见的开发技术分享给大家,包括以下内容:

1.   T-SQL 运行时生成语句

2.   使用RAISERROR函数抛出异常,及异常捕获

3.   查询数据表的容量/大小

4.   替换非法字符

5.   存储多国语言

6.  数据库存储特殊字符

7.   在查询结果中添加自增列的两种方法

8.   创建表语法中ON [PRIMARY]是什么意思

9.   使用触发器注意事项

10.  设置主表主键字段修改或删除时,子表外键字段一同修改或删除

11.  存储过程中使用游标,将当前数据库的所有用户存储过程加密

12.  使用游标遍历,将同一数据库服务中的A数据库数据表复制到B数据库中

13.  选择性查询字段

14.  同义词

 

1.  T-SQL 运行时生成语句

1)  使用EXECUTE执行动态命令

EXECUTE命令:执行Transact-SQL 批中的命令字符串、字符串或执行下列模块之一:系统存储过程、用户定义存储过程、标量值用户定义函数或扩展存储过程。

语法:{ EXEC | EXECUTE }( { @string_variable | [N]'tsql_string } [+...n] )[ AS { LOGIN | USER } = 'name' ] [;]

参数说明:

EXEC:是EXECUTE的简写,两者皆可使用。

@string_variable:局部变量的名称,可以是任意charvarcharncharnvarchar数据类型,其中包括(max)数据类型。可以将T-SQL代码封装在局部变量中被执行。

[N]'tsql_string':常量字符串,可以使任意nvarcharvarchar数据类型。如果包含N,则字符串将解释成nvarchar数据类型。如果不是动态生成的字符串命令,直接将其写成常量字符串也可以直接被执行。

[ AS { LOGIN | USER } = 'name' ]LOGIN指定执行的上下文(Context)为登录名,所以其执行范围为服务器级;USER指定执行的上下文为用户,所以其执行范围为数据库级。

注意事项:EXECUTE在使用中可能导致SQL注入式攻击,即超越用户本身权限的SQL语句可能会被执行,这样在生成动态命令字符串时,可以对字符串的内容进行检查。通过指定执行上下文,即使用[ AS { LOGIN | USER } = 'name' ]的语法形式,可以限定EXECUTE语句的执行环境,确保安全。

EXEC命令有两种用法,一种是执行一个存储过程,另一种是执行一个动态的批处理。以下所讲的都是第二种用法。

示例:

A.  拼接SQL语句存入变量执行

DECLARE @TableName varchar(50),@Sql varchar(MAX),@Id int;

SET @TableName = 'Student';

SET @Id = 3;

SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) +' WHERE Id = '+CAST(@Id AS varchar(10));

EXEC(@sql);

结果集:

B.  EXEC括号中只允许包含一个字符串常量,但是可以串联多个变量

a)  多个字符串常量

EXEC('SELECT * FROM '+QUOTENAME('Student')+' WHERE Id = '+CAST(3 AS varchar(10)));    --语法报错

b)  多个字符串变量

DECLARE @sql1 varchar(MAX), @sql2 varchar(MAX);

SET @sql1 = 'SELECT * FROM '+QUOTENAME('Student');

SET @sql2 = ' WHERE Id = '+CAST(3 AS varchar(10));

EXEC(@sql1+@sql2);  --正常执行

 

2)  使用sp_executesql执行动态命令

sp_executesql是一个系统存储过程,其功能和EXECUTE大致相同,不同的是其支持参数替换功能。在使用sp_executesql执行时,可以使用参数。参数的使用有两种类型,一种是输入参数,一种是输出参数。而EXECUTE不支持参数替换功能。

功能:执行可以多次重复使用或动态生成的T-SQL语句或批处理,可以包含嵌入的参数。在批处理、临时变量作用域和数据库上下文上,SP_EXECUTESQLEXECUTE相同。

语法、示例,参考 SQL Server 系统存储过程

 

3)  EXECUTEsp_executesql比较

A.  使用sp_executesql效率比EXECUTE要高,同一类型的语句,只需编译一次即可,而EXECUTE执行几次就需要编译几次。

B.   参数替换带来的高效率:其最大的特想在于sp_executesql的参数替换,而EXECUTE语句不支持该功能。如果对于只是参数不同的操作,使用sp_executesqSQLServer只需要编译一次;而如果用EXECUTESQLServer需要编译两次。

C.   执行计划重用带来的高效率:使用sp_executesql可以重用SQL Server的执行计划。多次执行T-SQL语句且只更改了提供给T-SQL语句的参数值时,可以使用sp_executesql而不要使用存储过程。因为T-SQL语句本身保持不变,仅参数值发生更改,所以SQLServer查询优化器可能会重用第一次执行时生成的执行计划。

 

2.  使用RAISERROR函数抛出异常,及异常捕获

生成错误消息并启动会话的错误处理。RAISERROR 可以引用 sys.messages 目录视图中存储的用户定义消息,也可以动态建立消息。该消息作为服务器错误消息返回到调用应用程序,或返回到TRY…CATCH 构造的关联CATCH 块。

语法:RAISERROR ( { msg_id | msg_str | @local_variable }

    { ,severity ,state }

    [ ,argument [ ,...n ] ] )

    [ WITH option [ ,...n ] ]

示例:

A.  抛出自定义异常并捕获异常

BEGIN TRY

    RAISERROR('你的语法出错了', 16, 1);

END TRY

BEGIN CATCH

    SELECT

         ERROR_NUMBER() AS 错误编号,

        ERROR_SEVERITY() AS ErrorSeverity,

        ERROR_STATE() AS ErrorState,

        ERROR_PROCEDURE() AS ErrorProcedure,

        ERROR_LINE() AS 错误行号,

        ERROR_MESSAGE() AS 错误信息;

END CATCH;

 

 

3.  查询数据表的容量/大小

1)  查询数据库中所有表的容量

CREATE TABLE ##Temp(

    Name VARCHAR(50),

    Rows INT,

    Reserved VARCHAR(50),

    Data VARCHAR(50),

    Index_Size VARCHAR(50),

    Unused VARCHAR(50)

);

INSERT INTO ##Temp(Name,Rows,Reserved,Data,Index_Size,Unused)

exec sp_msforeachTable @Command1="sp_spaceused'?'";

SELECT * FROM ##Temp ORDER BY Data DESC;

2)  查询指定数据表的容量

EXEC sp_spaceused Student;

 

4.  替换非法字符

REPLACE('我是被替换的字符串',char(30),'');

 

5.  存储多国语言

存储多国语言时,字段需要使用nvarchar 类型,并且在写入数据时,指定为"N",例如:

CREATE TABLE T1(Id int, Name nvarchar(256));

INSERT INTO T1 VALUES(1, N' اﻟﻤﺠﻴﺪ ﻋﺰﻳﺰ اﻟﺰﻧﺪاﻧﻲ');

INSERT INTO T1 VALUES(2, N'中国삼성abc');

SELECT * FROM T1;

DROP TABLE T1;

 

6.  数据库存储特殊字符,例如音标:'sə:vis

1)  设置字段的类型为nchar 或者nvarchar 类型

2)  存储SQL 值前加'N'字符,例如:N'''ɔ:rientid,''əʊ-'

 

7.  在查询结果中添加自增列的两种方法

1)  解决办法一:

SQL Server数据库中表信息会用到Identity关键字来设置自增列。但是当有数据被删除的话,自增列就不连续了。如果想查询出这个表的信息,并添加一列连续自增的ID,可用如下查询语句:

SELECT ROW_NUMBER() OVER (ORDER BY GETDATE()) AS Id, * FROM TableName;

2)  解决办法二:

使用关键字IDENTITY创建临时表

SELECT IDENTITY(int,1,1) AS Id, * INTO MyTable FROM TableName

 

8.  创建表语法中ON [PRIMARY]是什么意思

CREATE TABLE [dbo].[Users](

     [ID] [int] IDENTITY (1, 1) NOT NULL,

     [RegName] [char] (20) NOT NULL,

     [Pwd] [char] (10) NOT NULL,

) ON [PRIMARY];
指定存储表的文件组。如果指定filegroup,则表将存储在指定的文件组中,数据库中必须存在该文件组。如果指定DEFAULT,或者根本未指定ON 参数,则表存储在默认文件组中。 
ON [PRIMARY]就是把表存储在主文件组,在sqlserver里数据文件分为主文件附文件,你可以去安装目录里看下,会发现有MDFndfLDF三种文件,分别是指主文件,附文件,日志文件。

ON {filegroup | DEFAULT} 也可以在 PRIMARY KEY 约束或 UNIQUE 约束中指定。这些约束会创建索引。如果指定 filegroup,则索引将存储在指定的文件组中。如果指定 DEFAULT,则索引将存储在默认文件组中。如果约束中没有指定文件组,则索引将与表存储在同一文件组中。如果 PRIMARY KEY 约束或 UNIQUE 约束创建聚集索引,则表的数据页将与索引存储在同一文件组中。

 

9.  使用触发器注意事项

在对记录同一个UPDATE时,满足条件的会进入deletedinserted逻辑表。有几条数据这两张表就会有几条数据。

如果在同一张表存在两个或多个触发器,会按照顺序执行。而且,在同一个A触发器中,触发再次触发了A触发器,会等待这次触发完之后,在回到第一次触发的状态.

 

10.  设置主表主键字段修改或删除时,子表外键字段一同修改或删除

示例:

A.  Student主表Id字段修改时,Score子表StudentId字段一同修改

DROP TABLE Score, Student, Grade;

--创建Grade年级表

IF(OBJECT_ID('Grade', 'U') IS NOT NULL)

    DROP TABLE Grade;

GO

CREATE TABLE Grade

(

    Id int PRIMARY KEY NOT NULL,

    Name varchar(20) NOT NULL

);

GO

 

--创建Student学生信息表

IF(OBJECT_ID('Student', 'U') IS NOT NULL)

    DROP TABLE Student;

GO

CREATE TABLE Student

(

    Id int PRIMARY KEY NOT NULL,

    GradeId int CONSTRAINT GradeId REFERENCES Grade(Id),

    Name varchar(20) NOT NULL,

    Age int NOT NULL CHECK (Age > 10),

    Sex bit NOT NULL,

    [Address] varchar(50) DEFAULT '地址不详'

);

GO

 

--创建Score成绩表

IF(OBJECT_ID('Score', 'U') IS NOT NULL)

    DROP TABLE Score;

GO

CREATE TABLE Score

(

    Id int PRIMARY KEY NOT NULL,

    --ON UPDATE CASCADE ON DELETE CASCADE 语句,可在Student主表Id字段修改时,StudentId一同被修改

    StudentId int CONSTRAINT StudentId REFERENCES Student(Id) ON UPDATE CASCADE ON DELETE CASCADE,

    SubjectName varchar(20) NOT NULL,

    Score float NOT NULL

);

GO

 

--插入数据

INSERT INTO Grade VALUES(1, '一年级'),(2, '二年级'),(3, '三年级');

INSERT INTO Student VALUES(1, 1, '张小强', 16, 1, DEFAULT),(2, 2, '王美丽', 17, 0, '北京'),(3, 2, '向北京', 18, 1, '上海');

INSERT INTO Score VALUES(1, 1, '语文', 78.6),(2, 2, '数学', 82),(3, 3, '英语', 76);

--查询数据

SELECT * FROM Grade

SELECT * FROM Student

SELECT * FROM Score

 

UPDATE Student SET Id=4 WHERE ID=3;     --修改主表主键字段,子表关联记录一同被修改

DELETE Student WHERE Id=4;  --删除主表记录,子表关联记录一同被删除

 

11. 存储过程中使用游标,将当前数据库的所有用户存储过程加密

DECLARE @sp_name nvarchar(400), @sp_content nvarchar(2000);

DECLARE @asbegin int;

DECLARE @now datetime;

SELECT @now = GETDATE();

DECLARE sp_cursor CURSOR

FOR SELECT OBJECT_NAME(id) FROM sysobjects WHERE xtype = 'P' AND type = 'P' AND crdate < @now

    AND OBJECTPROPERTY(id, 'IsMSShipped')=0;

 

OPEN sp_cursor

FETCH NEXT FROM sp_cursor INTO @sp_name;

WHILE @@FETCH_STATUS = 0

BEGIN

    SELECT @sp_content = text FROM syscomments WHERE id = OBJECT_ID(@sp_name) ;

    SELECT @asbegin = PATINDEX ( '%AS' + char(13) + '%', @sp_content) ;

    SELECT @sp_content = SUBSTRING(@sp_content, 1, @asbegin - 1)+' WITH ENCRYPTION AS'

        +SUBSTRING(@sp_content, @asbegin+2, LEN(@sp_content));

    SELECT @sp_name = 'DROP PROCEDURE [' + @sp_name + ']';

    EXEC sp_executesql @sp_name ;

    EXEC sp_executesql @sp_content;

    FETCH NEXT FROM sp_cursor INTO @sp_name;

END

 

CLOSE sp_cursor;

DEALLOCATE sp_cursor;

 

12. 使用游标遍历,将同一数据库服务中的A数据库数据表复制到B数据库中

示例:

A.  将“RM_DB”数据库中的数据表复制到“MyTestDB”数据库(包含数据)

USE RM_DB;

DECLARE @TableName varchar(100), @sql nvarchar(max);

DECLARE @TableCount int=0;

DECLARE cur_TablesNames CURSOR LOCAL SCROLL SCROLL_LOCKS

    FOR SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME;

OPEN cur_TablesNames;

FETCH FIRST FROM cur_TablesNames INTO @TableName;

WHILE(@@FETCH_STATUS = 0)

BEGIN

    SET @TableCount=@TableCount+1;

    SET @sql = 'SELECT * INTO [MyTestDB].[dbo].['+@TableName+'] FROM [RM_DB].[dbo].['+@TableName+']';

    EXEC(@sql);

    PRINT '执行SQL'+@sql+',数据表:'+@TableName+',计数:'+CAST(@TableCount AS varchar(2));

    FETCH NEXT FROM cur_TablesNames INTO @TableName;

END

CLOSE cur_TablesNames;

DEALLOCATE cur_TablesNames;

 

13. 选择性查询字段

CREATE TABLE #Temp1(Id int, Col1 int, Col2 int, Col3 int, Name nvarchar(25))

INSERT #Temp1 VALUES

(1, 1, 2, 3, 'Test1'),

(2, 4, 5, 6, 'Test2'),

(3, 7, 8, 9, 'Test3');

DECLARE @Columns nvarchar(max) = '';

SELECT @Columns += (CASE WHEN LEN(@Columns) > 0 THEN ',' ELSE '' END) + '[' + name + ']' FROM tempdb..SYSCOLUMNS WHERE id = OBJECT_ID('tempdb..#Temp1') AND name LIKE 'Col%';

DECLARE @Sql nvarchar(max) = 'SELECT ' + @Columns + ' FROM #Temp1';

PRINT @Sql;

EXEC(@Sql);

DROP TABLE #Temp1;

 

14. 同义词

SQL Server 同义词一般用于查询其他数据库。

posted @ 2022-01-04 16:02  Abeam  阅读(435)  评论(0编辑  收藏  举报