代码改变世界

动态sql和长度超过8000的写法 之 交叉表

2010-03-11 16:32  starlet  阅读(563)  评论(0)    收藏  举报
--动态sql和长度超过8000的写法2009-04-03 09:22
/*--化解字符串不能超过8000的方法 

经常有人提到,用动态生成SQL语句的方法处理数据时,处理语句超长,无法处理的问题 
下面就讨论这个问题: 
--邹建 2003.9(引用请保留此信息)--
*/ 

/*-- 测试环境 

--以系统表 syscolumns 为测试数据,要求按xtype为列字段,name为行字段,统计colid的和 
--要求结果 

xtype filedname_1 fieldname_2 ..... fieldname_n 
-------- -------------- -------------- -------- -------------- 
34 0 0 ..... 1 
--
*/ 

/*--常规处理方法(不加行数限制会因生成的字符串益出而出错) 
set rowcount 10--因为syscolumns的记录较,会导致生成的字符串溢出,所以限制一下行数 

declare @s nvarchar(4000) 
set @s= ' ' 
select @s=@s+N ', '+quotename([name]) 
+N '=sum(case name when '+quotename([name], ' ' ' ') 
+N ' then [colid] else 0 end) ' 
from(select distinct [name] from [syscolumns]) a 

set rowcount 0--取消限制 
exec(N 'select [xtype] '+@s+N ' from [syscolumns] group by [xtype] ') 

/*--问题 
不加行数限制时,会因生成的字符串益出而出错 
--
*/ 
--*/ 

/*--方法1. 多个变量处理 

--根据查询结果定义变量(实际处理中,应该是估计需要多少个变量,定义足够多的变量个数,多定义变量并不影响处理,下面就多定义了一个) 
--生成数据处理临时表 
SELECT id=IDENTITY(int,0,1), 
g=0, 
a=CAST(N ', '+QUOTENAME([name]) 
+N '=SUM(CASE [name] WHEN N '+QUOTENAME(name,N ' ' ' ') 
+N ' THEN [colid] ELSE 0 END) ' 
as nvarchar(4000)) 
INTO # FROM syscolumns 
WHERE name> N ' ' 
GROUP BY name 

--分组临时表 
UPDATE a SET G=id/i 
FROM # a,(SELECT i=3800/MAX(LEN(a)) FROM #)b 
SELECT MAX(g)+1 as N '需要的变量个数 ' FROM # 

DECLARE @0 nvarchar(4000),@1 nvarchar(4000),@2 nvarchar(4000),@3 nvarchar(4000),@4 nvarchar(4000) 
SELECT @0=N ' ',@1=N ' ',@2=N ' ',@3=N ' ',@4=N ' ' 
SELECT 
@0=CASE g WHEN 0 THEN @0+a ELSE @0 END, 
@1=CASE g WHEN 1 THEN @1+a ELSE @1 END, 
@2=CASE g WHEN 2 THEN @2+a ELSE @2 END, 
@3=CASE g WHEN 3 THEN @3+a ELSE @3 END, 
@4=CASE g WHEN 4 THEN @4+a ELSE @4 END 
FROM # 
EXEC(N 'SELECT xtype '+@0+@1+@2+@3+@4+N ' FROM syscolumns GROUP BY xtype ') 
DROP TABLE # 

/*--方法说明 

优点:比较灵活,数据量大时只需要增加变量就行了.不用改动其他部分 
缺点:要自行估计处理的数据,估计不足就会出错 
--
*/ 
--*/ 

/*--方法2. bcp+isql 

--因为要用到bcp+isql,所以需要这些信息 
declare @servername sysname,@username sysname,@pwd sysname 
select @servername=@@servername --服务器名 
,@username=N ' ' --用户名 
,@pwd=N ' ' --密码 

declare @tbname sysname,@s nvarchar(4000) 

--创建数据处理临时表 
set @tbname=quotename(N '##temp_ '+cast(newid() as varchar(36))) 
set @s=N 'create table '+@tbname+ '(a nvarchar(4000)) 
insert into '+@tbname+N ' 
select N ' 'create view ' 
+stuff(@tbname,2,2,N ' ') 
+N ' as 
select [xtype] ' ' 
union all 
select N ' ', ' '+quotename([name])+ ' '=sum(case [name] when N ' ' 
+quotename([name], ' ' ' ' ' ' ' ') 
+ ' ' then [colid] else 0 end) ' ' 
from(select distinct [name] from [syscolumns] where name <> N ' 'xtype ' ')a 
union all 
select N ' 'from [syscolumns] group by [xtype] ' ' ' 
exec(@s) 

--生成创建视图的文件,注意使用了文件:c:\temp.txt 
set @s=N 'bcp " '+@tbname+N ' " out "c:\ '+@tbname+N ' " /S " ' 
+@servername+N ' " /U " '+@username+N ' " /P " '+@pwd+N ' " /w ' 
exec master..xp_cmdshell @s,no_output 

--调用isql生成数据处理视图 
set @s=N 'osql /S " '+@servername 
+case 
when @username=N ' ' then N ' " /E ' 
else N ' " /U " '+@username+N ' " /P " '+@pwd+N ' " ' 
end 
+N ' /d " '+db_name()+N ' " /i "c:\ '+@tbname+ ' " ' 
exec master..xp_cmdshell @s,no_output 

--删除临时文件 
set @s=N 'del "c:\ '+@tbname+ ' " ' 
exec master..xp_cmdshell @s,no_output 

--调用视图,显示处理结果 
set @s=N 'drop table '+@tbname+N ' 
select * from '+stuff(@tbname,2,2,N ' ')+N ' 
drop view '+stuff(@tbname,2,2,N ' ') 
exec(@s) 

/*--方法总结 

优点:程序自动处理,不存在判断错误的问题 
缺点:复杂,经过的步骤多,容易出错,而且需要一定的操作员权限 
--
*/ 
--*/ 

--/*-- 方法3. 多个变量处理,综合了方法1,2的优点,解决了方法1中需要人为判断,增加变量的问题,排除了方法2,需要权限和过程复杂的问题 
DECLARE @sqlhead nvarchar(4000),@sqlend nvarchar(4000
,
@sql1 nvarchar(4000),@sql2 nvarchar(4000),@sql3 nvarchar(4000),@sql4 nvarchar(4000
,
@i int,@ic nvarchar(10

--生成数据处理临时表 
SELECT id=IDENTITY(int,0,1), 
g
=0
a
=CAST(N '' 
+QUOTENAME([name]
+'=SUM(CASE [name] WHEN N ' 
+QUOTENAME(name,N ' ' ' '
+' THEN [colid] ELSE 0 END) ' 
as nvarchar(4000)) 
INTO # FROM
SELECT DISTINCT name FROM [syscolumns] WHERE name> N ' ')a 

--分组临时表 
UPDATE a SET @i=id/i,g=@i 
FROM # a,(SELECT i=3800/MAX(LEN(a)) FROM #)b 
SET @ic=@i 

--生成数据处理语句 
SELECT 
@sqlhead=' ' ' ' 
+REPLACE(N 'SELECT [xtype] ',N ' ' ' ',N ' ' ' ' ' '
+ ' ' ' '
@sqlend=' ' ' ' 
+REPLACE(N ' FROM [syscolumns] GROUP BY [xtype] ',N ' ' ' ',N ' ' ' ' ' '
+' ' ' '
@sql1=' ',@sql2=' ',@sql3=' ',@sql4=' ' 
WHILE @ic> =0 
SELECT 
@sql1=',@ '+@ic+' nvarchar(4000) '+@sql1
@sql2=',@ '+@ic+'=N ''''+@sql2
@sql3=',@ '+@ic 
+'=CASE g WHEN '+@ic 
+' THEN @ '+@ic+'+a ELSE @ '+@ic 
+' END '+@sql3
@sql4='+@ '+@ic+@sql4
@ic=@ic-1 
SELECT 
@sql1=STUFF(@sql1,1,1,N ' '), 
@sql2=STUFF(@sql2,1,1,N ' '), 
@sql3=STUFF(@sql3,1,1,N ' '), 
@sql4=STUFF(@sql4,1,1,N ' '

--执行 
EXEC(N 'DECLARE '+@sql1+' 
SELECT 
'+@sql2+' 
SELECT 
'+@sql3+' FROM # 
EXEC(N 
'+@sqlhead+''+@sql4+'+N '+@sqlend+''
--删除临时表 
DROP TABLE # 

/*--方法总结 

总结了前两种方法的优点,自动判断需要处理的变量数 
--
*/ 
--*/ 

如果提示错误,请把 N ' 这间的空格删掉