SQLServer规范

2018.11.2 SQLServer

常用查询语句

--1.获取当前所有的驱动器
exec master.dbo.xp_availablemedia

执行结果
    name	low free	high free	media type
1    C:\	633081856	3	2
2    D:\	-2144931840	5	2
3    E:\	1789870080	3	2
4    Q:\	-156397568	1	2
5    F:\	0	0	8

--2.获得子目录列表
exec master.dbo.xp_subdirs 'd:\'

--3.获得“所有”子目录的目录树结构
exec master.dbo.xp_dirtree 'd:\'

--4.数据库所有表空间统计脚本
exec sp_MSForEachTable
@precommand=N'
create table ##(
tname sysname,   --表名称
num int,    --行数
rspace nvarchar(10),  --表分配空间总量
uspace nvarchar(10),  --数据使用空间量
uindex varchar(10),   --索引使用空间量
unuser varchar(10))',  --未用空间量
@command1=N'insert ## exec sp_spaceused ''?''',
@postcommand=N'select * from ## order by num desc '
go
drop table ##
go

--5.

1.存储过程格式规范例子

CREATE PROCEDURE PROC_INSERT_DATA_ID
  @DealerID varchar(50)
AS
BEGIN
    DECLARE @COUNT  INT    
    
    SET @COUNT = (SELECT COUNT(*) FROM myDATA_Details WHERE DealerID = @DealerID)
    IF (@COUNT>0)    
        BEGIN    
            DELETE FROM myDATA_Details WHERE DealerID = @DealerID    
            INSERT INTO myDATA_Details (DealerID) VALUES (@DealerID)    
        END    
    ELSE    
        BEGIN    
            INSERT INTO myDATA_Details (DealerID) VALUES (@DealerID)    
        END    
END

2.SQLSERVER merge的简单用法

MERGE INTO @TargetTable AS T           
USING @SourceTable AS S                
   ON T.ID = S.ID                      
WHEN MATCHED         
   THEN UPDATE SET T.DSPT = S.DSPT  
WHEN NOT MATCHED BY TARGET   
   THEN INSERT VALUES(S.ID,S.DSPT)
WHEN NOT MATCHED BY SOURCE            
   THEN DELETE
OUTPUT $ACTION AS [ACTION],
   Deleted.ID AS 'Deleted ID',
   Deleted.DSPT AS 'Deleted Description',
   Inserted.ID AS 'Inserted ID',
   Inserted.DSPT AS 'Inserted Description'
INTO @Log;

3.数据库对象

表 (Table)

字段(Column)

视图 (View)

存储过程 (Stored procedure)

触发器(Trigger)

索引(Index)

主键(Primary key)

外键(Foreign key)

Check 约束(Check Constraint)

Default 约束(Default Constraint)

用户定义数据类型 (User-defined data type)

用户定义函数 (User-defined function) 

4.命名规范

1.标识符必须以字符开头,且不能超过30个字符(标识符:即用户自定义的关键词,比如表名、字段名、视图名、序列名、主键等)
2.标识符全部大写,单词多了可以适当缩写
3.多个单词间用下划线(_)进行连接
4.若库中有多个系统,表名采用系统名称+单词或多个单词
5.视图view:vi_
6.存储过程:sp_
7.索引:idx_
8.触发器:tr_, Insert触发器加'_i',Delete触发器加'_d',Update触发器加'_u'
9.函数:fn_
1.查询数据库所有触发器名称
use msdb
SELECT object_name(a.parent_obj) as [表名] ,a.name as [触发器名称] ,
(case when b.is_disabled=0 then '启用' else '禁用' end) as [状态] ,b.create_date as [创建日期] ,
b.modify_date as [修改日期] ,c.text as [触发器语句] 
FROM sysobjects a
INNER JOIN sys.triggers b 
ON b.object_id=a.id 
INNER JOIN syscomments c
ON c.id=a.id
WHERE a.xtype='tr' 
ORDER BY [表名]

2.查询所有索引名称
SELECT  索引名称=a.name 
,表名=c.name 
,索引字段名=d.name 
,索引字段位置=d.colid 
FROM  sysindexes  a 
JOIN  sysindexkeys  b  ON  a.id=b.id  AND  a.indid=b.indid 
JOIN  sysobjects  c  ON  b.id=c.id 
JOIN  syscolumns  d  ON  b.id=d.id  AND  b.colid=d.colid 
WHERE  a.indid NOT IN(0,255) 
 and  c.xtype='U' 

5.查询数据库字段

-- 简化版
select a.name as col_name,k.remarks,t.name as type,
case when (t.name='date' or t.name='datetime' or t.name='int' or t.name='text') then '' 
     when t.name='decimal' then cast(a.precision as varchar(10))+','+cast(a.scale as varchar(10))     
     else cast(a.max_length as varchar(10))
end as length
from sys.columns a
left join sys.objects b on a.object_id=b.object_id 
left join sys.types t on a.system_type_id=t.system_type_id
left join (
	select a.name as table_name, b.name as column_name, c.value as remarks   
	from sys.tables a 
		left join sys.columns b on a.object_id=b.object_id  
		left join sys.extended_properties c on a.object_id=c.major_id  
	where a.name='MM_BNBase' 
	and c.minor_id<>0 
	and b.column_id=c.minor_id  
	and a.schema_id=(
		select schema_id from sys.schemas where name='dbo'  
	)
) k on k.column_name = a.name
where b.type = 'U' 
and charindex('UDT',t.name,0)<=0 
and charindex('sys',t.name,0)<=0 
and b.name ='MM_BNBase'

--完整版
DECLARE @TableName varchar(20)
SET @TableName='HR_Duty'
SELECT
        (CASE when a.colorder=1 then d.name else '' end) AS 表名,
        a.colorder 字段序号,
        a.name 字段名,
        (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 标识,
        (case when (SELECT count(*)
        FROM sysobjects
        WHERE (name in
                  (SELECT name
                FROM sysindexes
                WHERE (id = a.id) AND (indid in
                          (SELECT indid
                         FROM sysindexkeys
                         WHERE (id = a.id) AND (colid in
                                   (SELECT colid
                                  FROM syscolumns
                                  WHERE (id = a.id) AND (name = a.name))))))) AND
              (xtype = 'PK'))>0 then '√' else '' end) 主键,
       b.name 类型,
       a.length 占用字节数,
       COLUMNPROPERTY(a.id,a.name,'PRECISION') as 长度,
       isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小数位数,
       (case when a.isnullable=1 then '√'else '' end) 允许空,
       isnull(e.text,'') 默认值,
       isnull(g.[value],'') AS 字段说明

FROM syscolumns  a
    LEFT JOIN systypes b on  a.xtype=b.xusertype
    inner join sysobjects d on a.id=d.id  and  d.xtype='U' and  d.name<>'dtproperties'
    left join syscomments e on a.cdefault=e.id
    left join sys.extended_properties g on a.id=g.major_id AND a.colid = g.minor_id
WHERE d.name=@TableName    --如果只查询指定表,加上此条件
order by a.id,a.colorder

SELECT * FROM sys.extended_properties

6.查看SQL SERVER 数据库正在执行的语句和时长等

SELECT session_Id,
--ecid, 
--sp.dbid,
--DB_NAME(sp.dbid) as DB_NM, 
--nt_username,
er.status,
wait_type,
SUBSTRING (qt.text, er.statement_start_offset/2,
(CASE WHEN er.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
ELSE er.statement_end_offset END - er.statement_start_offset)/2) as CurrentSQL,
qt.text,
--program_name,
--Hostname,
start_time,
DATEDIFF ( millisecond,start_time, GETDATE() ) AS execTime
FROM sys.dm_exec_requests er
INNER JOIN sys.sysprocesses sp ON er.session_id = sp.spid
CROSS APPLY sys.dm_exec_sql_text(er.sql_handle)as qt
WHERE session_Id > 50
AND session_Id NOT IN (@@SPID)
--and sp.dbid=7
ORDER BY DATEDIFF ( millisecond,start_time, GETDATE() ) desc

7.SQL监控脚本(适用于SQL Server 2005以上)

SELECT  creation_time  N'语句编译时间'
        ,last_execution_time  N'上次执行时间'
        ,total_physical_reads N'物理读取总次数'
        ,total_logical_reads/execution_count N'每次逻辑读次数'
        ,total_logical_reads  N'逻辑读取总次数'
        ,total_logical_writes N'逻辑写入总次数'
        , execution_count  N'执行次数'
        , total_worker_time/1000 N'所用的CPU总时间ms'
        , total_elapsed_time/1000  N'总花费时间ms'
        , (total_elapsed_time / execution_count)/1000  N'平均时间ms'
        ,SUBSTRING(st.text, (qs.statement_start_offset/2) + 1,
         ((CASE statement_end_offset
          WHEN -1 THEN DATALENGTH(st.text)
          ELSE qs.statement_end_offset END
            - qs.statement_start_offset)/2) + 1) N'执行语句'
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
where SUBSTRING(st.text, (qs.statement_start_offset/2) + 1,
         ((CASE statement_end_offset
          WHEN -1 THEN DATALENGTH(st.text)
          ELSE qs.statement_end_offset END
            - qs.statement_start_offset)/2) + 1) not like '%fetch%'
ORDER BY  total_elapsed_time / execution_count DESC 

8.查询优化

对数据库查询进行优化,应尽量避免全表扫描,首先应考虑在where 及order by 涉及的列上建立索引

应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
select id from t where num is null

应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描

应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描

in 和 not in 也要慎用,否则会导致全表扫描

如果在 where 子句中使用参数,也会导致全表扫描

应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描

应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描

9.查询相关规范


10.sqlserver decimal转换为str,并去掉无效的位数0

create function fun_dec2str(@inValue decimal(38,18), @scale int, @defaultValue varchar(38))
returns varchar(38)
as
begin
	declare @returnValue varchar(38)
	declare @strValue varchar(38)
	
	if (@inValue is null)
		set @strValue = @defaultValue
	else
		if (@scale = 0)
			set @strValue=convert(varchar(38),@inValue)			
		else if (@scale = 1)
			set @strValue=convert(varchar(38),convert(decimal(18,1),@inValue))
		else if(@scale = 2)
			set @strValue=convert(varchar(38),convert(decimal(18,2),@inValue))
		else if(@scale = 3)
			set @strValue=convert(varchar(38),convert(decimal(18,3),@inValue))
		else if(@scale = 4)
			set @strValue=convert(varchar(38),convert(decimal(18,4),@inValue))
		else if(@scale = 5)
			set @strValue=convert(varchar(38),convert(decimal(18,5),@inValue))
		else
			set @strValue=convert(varchar(38),convert(decimal(18,6),@inValue))
		
	if(@scale = 0 and charindex('.', @strValue)>0) -- 有小数点,四舍五入
		if (substring(reverse(@strValue),patindex('%[^0]%',reverse(@strValue)),1)='.')
			set @strValue=substring(@strValue,0,charindex('.', @strValue))
		else
			set @strValue =left(@strValue,len(@strValue)- patindex('%[^0]%.%',reverse(@strValue))+1)
			
	if(@scale = 0 and charindex('.', @strValue)>0)
		set @strValue = convert(varchar(38),convert(decimal(18),@strValue))
	
	if(@strValue is null or @strValue = '') -- null或空字符
		set @returnValue=''
	else if(charindex('.', @strValue)=0) -- 没有小数点
		set @returnValue=@strValue
	else if(substring(reverse(@strValue),patindex('%[^0]%',reverse(@strValue)),1)='.') -- 小数点后全是0的
		set @returnValue=left(@strValue,len(@strValue)-patindex('%[^0]%',reverse(@strValue)))
	else
		set @returnValue =left(@strValue,len(@strValue)- patindex('%[^0]%.%',reverse(@strValue))+1) -- 小数点后有0
			
	return @returnValue	
end

/*
    -- 测试用例
	select dbo.fun_dec2str(null,1,'')        -- expected : 
	select dbo.fun_dec2str('',1,'')          -- expected : error
	select dbo.fun_dec2str(616419,0,'')      -- expected : 616419
	select dbo.fun_dec2str(616419.00,0,'')   -- expected : 616419
	select dbo.fun_dec2str(616419.05,0,'')   -- expected : 616419
	select dbo.fun_dec2str(616419.050,0,'')  -- expected : 616419
	select dbo.fun_dec2str(616419.50,0,'')   -- expected : 616419
	
	select dbo.fun_dec2str(null,1,'')        -- expected : 
	select dbo.fun_dec2str('',1,'')          -- expected : error
	select dbo.fun_dec2str(616419,1,'')      -- expected : 616419
	select dbo.fun_dec2str(616419.00,1,'')   -- expected : 616419
	select dbo.fun_dec2str(616419.05,1,'')   -- expected : 616419.1
	select dbo.fun_dec2str(616419.050,1,'')  -- expected : 616419.1
	select dbo.fun_dec2str(616419.50,1,'')   -- expected : 616419.5
*/

11.sqlserver中按字符分割字符串

创建函数

--方法1:循环截取法
CREATE FUNCTION Fun_split(@s     VARCHAR(8000),--待分拆的字符串
                          @split VARCHAR(10) --数据分隔符
)
RETURNS @re TABLE(
  col VARCHAR(100))
AS
  BEGIN
      DECLARE @splitlen INT

      SET @splitlen=Len(@split + 'a') - 2

      WHILE Charindex(@split, @s) > 0
        BEGIN
            INSERT @re
            VALUES(LEFT(@s, Charindex(@split, @s) - 1))

            SET @s=Stuff(@s, 1, Charindex(@split, @s) + @splitlen, '')
        END

      INSERT @re
      VALUES(@s)

      RETURN
  END

GO

使用方法:因为返回的是一个table,只有一列col,可以直接用

select col as '拆分列表' from dbo.Fun_split('a,b,c,d', ',')

12.SQLServer判断临时表字段是否存在

-查询所有列名
select name from tempdb.sys.columns where object_id = OBJECT_ID('tempdb.dbo.#tempTB')

if col_length('tempdb.dbo.#TempTB','columnName') is not null
  print '存在'
else
  print '不存在'

13.使用动态语句创建游标

http://www.manjuke.com/2012/11/create-cursor-using-dynamic-sql-query.html

CREATE PROCEDURE [dbo].[Gsp_Create_GenericCursor]
    /* Parameters */
    @vQuery        NVARCHAR(MAX)
    ,@Cursor    CURSOR VARYING OUTPUT
AS
BEGIN
    SET NOCOUNT ON
    
    DECLARE 
        @vSQL        AS NVARCHAR(MAX)
    
    SET @vSQL = 'SET @Cursor = CURSOR FORWARD_ONLY STATIC FOR ' + @vQuery + ' OPEN @Cursor;'
    
   
    EXEC sp_executesql
         @vSQL
         ,N'@Cursor cursor output'  
         ,@Cursor OUTPUT;
END


DECLARE @obj AS CURSOR
DECLARE @i AS INT    
 
 
    EXEC dbo.Gsp_Create_GenericCursor 
        @vQuery = N'SELECT 1 AS FLD1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4'
        ,@Cursor = @obj OUTPUT
        
        FETCH NEXT FROM @obj INTO @i
        
        WHILE (@@FETCH_STATUS = 0)
        BEGIN
            PRINT @i
            
            FETCH NEXT FROM @obj INTO @i
        END
        
        CLOSE @obj
        DEALLOCATE @obj

14.查看数据库当前连接数

SELECT * FROM 
[Master].[dbo].[SYSPROCESSES] WHERE [DBID] 
IN 
(
SELECT 
   [DBID]
FROM 
   [Master].[dbo].[SYSDATABASES] 
WHERE 
   NAME='databaseName'
)

15.如何测试一个SQL查询的响应时间

SELECT SUBSTRING(qt.text, ( qs.statement_start_offset / 2 ) + 1,
( ( CASE qs.statement_end_offset
WHEN -1 THEN DATALENGTH(qt.text)
ELSE qs.statement_end_offset
END - qs.statement_start_offset ) / 2 ) + 1) ,
qs.execution_count ,
qs.total_worker_time as total_worker_time_in_s,
qs.last_worker_time as last_worker_time_in_ms,
(qs.total_worker_time /qs.execution_count)/1000 as avg_execution_time_ms,
qs.last_execution_time
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
ORDER BY qs.last_execution_time DESC
--这个里面的total_worker_time在不考虑网络传输的情况下,可以看为是响应时间。
--测试结果:total_worker_time=编译时间+等待时间+执行时间+返回时间

16.删除

DELETE FROM TableA
WHERE EXISTS (SELECT *
              FROM TableB
              WHERE TableB.ID1 = TableA.ID1
                AND TableB.ID2 = TableA.ID2)

17.查询所有表的数据量

SELECT object_name (i.id) TableName, 
	   rows as RowCnt 
FROM sysindexes i 
INNER JOIN sysObjects o 
	ON (o.id = i.id AND o.xType = 'U ') 
WHERE indid < 2 
ORDER BY RowCnt

更新列表:

*

参考文章:

相关阅读