设为首页 收藏本站
查看: 928|回复: 0

[经验分享] 重构sql server的sys.sp_helptext存储

[复制链接]

尚未签到

发表于 2017-7-13 18:35:16 | 显示全部楼层 |阅读模式
本文目录列表:

1、sys.sp_helptext存储的功能和效果

2、重构sys.sp_helptext存储(命名为dbo.usp_helptext)提供直观的效果

3、sys.sp_helptext和dbo.usp_helptext的限制以及解决方案

4、总结语

5、参考清单列表







1、sys.sp_helptext存储的功能和效果



近来在研究sql server提供的现实可编程对象定义体的方法包括:sys.syscomments(视图)、sys.all_sql_modules(sys.sql_modules)(视图)、object_definition(函数)和sys.sp_helptext(存储)。针对以上方式的不同以后有时间在写成博文。本文主要研究了sys.sp_helptext的显示效果,感觉有些不太美好。先看该存储的现实效果如下图:

DSC0000.png

DSC0001.png

上图现在看没有什么的,那就将如下图的Text字段列内容复制放入单独的文件中再看其效果如下图:

DSC0002.png

DSC0003.png

上图我红色矩形框标注的地方了吧,每个行后都增加了char(13)和char(10)这两个字符导致的这样的显示效果,如果按照这个结果为基础进行变更,就增加了可编程对象定义的长度(主要是char(13)和char(10))。



2、重构sys.sp_helptext存储(命名为dbo.usp_helptext)提供直观的效果



发现了sys.sp_helptext的显示效果,我自己感觉不太满意,那么就重构嘛。重构后的代码如下:








if object_id(N'dbo.usp_helptext', 'P') IS NOT NULL
begin
drop procedure [dbo].[usp_helptext];
end
go
create procedure [dbo].[usp_helptext]
(
@objname nvarchar(776)
,@columnname sysname = NULL
,@keeporiginal bit = NULL
)
as
begin
set nocount on
set @keeporiginal = ISNULL(@keeporiginal, 1);   
declare @dbname sysname
,@objid    int
,@BlankSpaceAdded   int
,@BasePos       int
,@CurrentPos    int
,@TextLength    int
,@LineId        int
,@AddOnLen      int
,@LFCR          int --lengths of line feed carriage return
,@DefinedLength int
/* NOTE: Length of @SyscomText is 4000 to replace the length of
** text column in syscomments.
** lengths on @Line, #CommentText Text column and
** value for @DefinedLength are all 255. These need to all have
** the same values. 255 was selected in order for the max length
** display using down level clients
*/
,@SyscomText    nvarchar(4000)
,@Line          nvarchar(255)
select @DefinedLength = 255
select @BlankSpaceAdded = 0 /*Keeps track of blank spaces at end of lines. Note Len function ignores
trailing blank spaces*/
CREATE TABLE #CommentText
(LineId    int
,Text  nvarchar(255) collate catalog_default)
/*
**  Make sure the @objname is local to the current database.
*/
select @dbname = parsename(@objname,3)
if @dbname is null
select @dbname = db_name()
else if @dbname <> db_name()
begin
raiserror(15250,-1,-1)
return (1)
end
/*
**  See if @objname exists.
*/
select @objid = object_id(@objname)
if (@objid is null)
begin
raiserror(15009,-1,-1,@objname,@dbname)
return (1)
end
-- If second parameter was given.
if ( @columnname is not null)
begin
-- Check if it is a table
if (select count(*) from sys.objects where object_id = @objid and type in ('S ','U ','TF'))=0
begin
raiserror(15218,-1,-1,@objname)
return(1)
end
-- check if it is a correct column name
if ((select 'count'=count(*) from sys.columns where name = @columnname and object_id = @objid) =0)
begin
raiserror(15645,-1,-1,@columnname)
return(1)
end
if (ColumnProperty(@objid, @columnname, 'IsComputed') = 0)
begin
raiserror(15646,-1,-1,@columnname)
return(1)
end
declare ms_crs_syscom  CURSOR LOCAL
FOR select text from syscomments where id = @objid and encrypted = 0 and number =
(select column_id from sys.columns where name = @columnname and object_id = @objid)
order by number,colid
FOR READ ONLY
end
else if @objid < 0    -- Handle system-objects
begin
-- Check count of rows with text data
if (select count(*) from master.sys.syscomments where id = @objid and text is not null) = 0
begin
raiserror(15197,-1,-1,@objname)
return (1)
end
declare ms_crs_syscom CURSOR LOCAL FOR select text from master.sys.syscomments where id = @objid
ORDER BY number, colid FOR READ ONLY
end
else
begin
/*
**  Find out how many lines of text are coming back,
**  and return if there are none.
*/
if (select count(*) from syscomments c, sysobjects o where o.xtype not in ('S', 'U')
and o.id = c.id and o.id = @objid) = 0
begin
raiserror(15197,-1,-1,@objname)
return (1)
end
if (select count(*) from syscomments where id = @objid and encrypted = 0) = 0
begin
raiserror(15471,-1,-1,@objname)
return (0)
end
declare ms_crs_syscom  CURSOR LOCAL
FOR select text from syscomments where id = @objid and encrypted = 0
ORDER BY number, colid
FOR READ ONLY
end
/*
**  else get the text.
*/
select @LFCR = 2
select @LineId = 1
open ms_crs_syscom
fetch next from ms_crs_syscom into @SyscomText
while @@fetch_status >= 0
begin
select  @BasePos    = 1
select  @CurrentPos = 1
select  @TextLength = LEN(@SyscomText)
while @CurrentPos  != 0
begin
--Looking for end of line followed by carriage return
select @CurrentPos =   CHARINDEX(char(13)+char(10), @SyscomText, @BasePos)
--If carriage return found
IF @CurrentPos != 0
begin
/*If new value for @Lines length will be > then the
**set length then insert current contents of @line
**and proceed.
*/
while (isnull(LEN(@Line),0) + @BlankSpaceAdded + @CurrentPos-@BasePos + @LFCR) > @DefinedLength
begin
select @AddOnLen = @DefinedLength-(isnull(LEN(@Line),0) + @BlankSpaceAdded)
insert #CommentText values
( @LineId,
isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @AddOnLen), N''))
select @Line = NULL, @LineId = @LineId + 1,
@BasePos = @BasePos + @AddOnLen, @BlankSpaceAdded = 0
end
-- 注释系统原来的使用如下修改
--select @Line    = isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @CurrentPos-@BasePos + @LFCR), N'')
select @Line    = isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @CurrentPos-@BasePos + (CASE @keeporiginal WHEN 1 THEN @LFCR ELSE 0 END)), N'')
select @BasePos = @CurrentPos+2
insert #CommentText values( @LineId, @Line )
select @LineId = @LineId + 1
select @Line = NULL
end
else
--else carriage return not found
begin
IF @BasePos <= @TextLength
begin
/*If new value for @Lines length will be > then the
**defined length
*/
while (isnull(LEN(@Line),0) + @BlankSpaceAdded + @TextLength-@BasePos+1 ) > @DefinedLength
begin
select @AddOnLen = @DefinedLength - (isnull(LEN(@Line),0) + @BlankSpaceAdded)
INSERT #CommentText VALUES
( @LineId,
isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @AddOnLen), N''))
select @Line = NULL, @LineId = @LineId + 1,
@BasePos = @BasePos + @AddOnLen, @BlankSpaceAdded = 0
end
select @Line = isnull(@Line, N'') + isnull(SUBSTRING(@SyscomText, @BasePos, @TextLength-@BasePos+1 ), N'')
if LEN(@Line) < @DefinedLength and charindex(' ', @SyscomText, @TextLength+1 ) > 0
begin
select @Line = @Line + ' ', @BlankSpaceAdded = 1
end
end
end
end
FETCH NEXT from ms_crs_syscom into @SyscomText
end
IF @Line is NOT NULL
INSERT #CommentText VALUES( @LineId, @Line )
select Text from #CommentText order by LineId
CLOSE  ms_crs_syscom
DEALLOCATE     ms_crs_syscom
DROP TABLE     #CommentText   
return (0) -- sp_helptext
end
go
  


以上修改之处我已经标注了,其他的均来源sys.sp_helptext内容。

那就看看重构后的效果,如下图:

DSC0004.png



以上显示并看不出和sys.sp_helptext的有何不同,继续讲Text内容复制放入单独为文件中效果如下图:





上图红色矩形框就是显示的效果,下部分是为了对比,这部分可以使用如下代码显示器效果:




EXEC [sys].[sp_helptext]
@objname = N'sys.fn_get_sql'    -- nvarchar(776)
,@columnname = NULL -- sysname
GO
EXEC [dbo].[usp_helptext]
@objname = N'sys.fn_get_sql'    -- nvarchar(776)
,@columnname = NULL -- sysname
,@keeporiginal = 1 -- bit
GO




注意:dbo.usp_helptext兼容了sys.sp_helptext的功能。



3、sys.sp_helptext和dbo.usp_helptext的限制以及解决方案



查阅了sys.sp_helptext的源码和其对应的联机帮助文档,发现其输出的字段列Text每行最多255个双字节字符,其输出到客户端最终的大小是4000个双字节字符,这个可以通过编码程序(例如VS程序读取获取等)突破这个限制。



其最大的缺点是每行255个,有可能遇到一行中一个分隔符前一部分属于前一个255个双字节字符,后一部分属于后一个255双字节字符的前部分。

具体的测试代码如下:






IF OBJECT_ID(N'[dbo].[uvm_MyTestView]', 'V') IS NOT NULL
BEGIN
DROP VIEW [dbo].[uvm_MyTestView];
END
GO   
CREATE VIEW [dbo].[uvm_MyTestView]
AS
SELECT
1 AS N'Col_1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111', 2 AS [Col_2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222]
GO



效果展示如下图:





上图显示的效果就是出现了截断。消除这个限制那就使用函数object_definition(不过这个也有缺点的,以后才单独讲解输出可编程对象定义的内容的区别)。



4、总结语



学习sql server提供的系统对象,发现他们写的代码真的很严密的,很多规范以及异常处理等,确实学到了很多的,不过也发现有些sql server的内部限制是不写出来的,如表记录行最大8060字节的限制以及可边长长度溢出到row-overflow索引分配类型的数据页后也在原来的记录行中增加24字节的指针这样也可有能超过行最大8060字节的限制。可能我看理论太少的缘故吧。唯有继续精进,代码编程还是要继续的,有时候sql server客户端输出的最大4000个双字节字符的限制可以通过编程的方式得到完本的解决。



昨天看到园中的一篇博文print、sp_helptext的限制与扩展通过PRINT输出分批次打印超长的字符串,也会遇到某个标识符截断的问题,因为PRINT每次到打印到客户端总增加了char(13)和char(10)这两个字符,这样就可能将一个标识符分割为前后两个批次。



5、参考清单列表



  • https://msdn.microsoft.com/en-us/library/ms176112.aspx


  • http://www.cnblogs.com/fishparadise/p/4797539.html

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-393510-1-1.html 上篇帖子: SQL Server 随机数,随机区间,随机抽取数据rand(),floor(),ceiling(),round(),newid()函数等 下篇帖子: SQL Server触发器的禁用和启用
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表