1 CREATE PROCEDURE [dbo].[SP_DynamicCrosstab]
  2  /* SP_DynamicCrosstab
  3   * 描述:生成交叉报表
  4   */
  5  --@tableSpec
  6  --A、表或视图
  7  --B、存储过程句称
  8  @tableSpec    NVARCHAR(4000= '',
  9 
 10  --@sqlSelect
 11  --A、表SELECT语句:'SELECT a.au_id, a.au_fname, a.au_lname, a.phone, LEFT(t.title, 10) AS [title], LEFT(s.stor_name, 10) AS [store], sales.qty'
 12  --B、存储过程:'EXEC dbo.myStoredProcedure <param1>, <param2>'
 13  @sqlSelect    NVARCHAR(512),
 14 
 15  --@sqlFromWhere
 16  --如果数据源是存储过程时可为空
 17  @sqlFromWhere NVARCHAR(512)  = '',
 18 
 19  --@pivotExpr
 20  --表头分组字段
 21  @pivotExpr    NVARCHAR(255),
 22 
 23  --@valueExpr
 24  --交叉表计算的字段
 25  @valueExpr    NVARCHAR(512),
 26 
 27  --@function
 28  --统计函数 如:SUM,MIN等
 29  @function     NVARCHAR(20)   = '',
 30 
 31  --@groupBy
 32  --表行分组字段
 33  @groupBy      NVARCHAR(512),
 34 
 35  --@sortLookup
 36  --排序字段
 37  @sortLookup   NVARCHAR(40)   = '',
 38 
 39  --@collation
 40  --SQL语句执行环境标识
 41  @collation   NVARCHAR(40)   = 'Chinese_PRC_CI_AS',
 42  
 43  --@verbose
 44  --消息标识 0:不显示消息,1:显示消息
 45  @verbose     BIT            = 0 
 46 
 47 AS
 48 BEGIN
 49 
 50  -- 检测命令参数不为空
 51  IF LEN(RTRIM(ISNULL(@sqlSelect''))) = 0
 52  BEGIN
 53    RAISERROR ('The @sqlSelect parameter cannot be an empty string.'161)
 54    ROLLBACK TRANSACTION
 55  END
 56 
 57  IF LEN(RTRIM(ISNULL(@pivotExpr''))) = 0
 58  BEGIN
 59    RAISERROR ('The @pivotExpr parameter cannot be an empty string.'161)
 60    ROLLBACK TRANSACTION
 61  END
 62 
 63  IF LEN(RTRIM(ISNULL(@valueExpr''))) = 0
 64  BEGIN
 65    RAISERROR ('The @valueExpr parameter cannot be an empty string.'161)
 66    ROLLBACK TRANSACTION
 67  END
 68 
 69  IF LEN(RTRIM(ISNULL(@groupBy''))) = 0
 70  BEGIN
 71    RAISERROR ('The @groupBy parameter cannot be an empty string.'161)
 72    ROLLBACK TRANSACTION
 73  END
 74 
 75  --本地变量
 76  DECLARE @i      INTEGER
 77  DECLARE @sql    NVARCHAR(4000)
 78  DECLARE @sqlX   NVARCHAR(4000)
 79  DECLARE @col    NVARCHAR(4000)
 80  DECLARE @pivot  NVARCHAR(4000)
 81  DECLARE @indx   NVARCHAR(10)
 82  DECLARE @cols   NVARCHAR(4000)
 83  DECLARE @where  NVARCHAR(4000)
 84  DECLARE @update NVARCHAR(4000)
 85  DECLARE @value  NVARCHAR(532)
 86  DECLARE @select NVARCHAR(4000)
 87 
 88  --删除临时表(如果存在)
 89  SELECT @sql = N'IF EXISTS (SELECT 1 FROM tempdb.dbo.sysobjects WHERE name = ''@table'' AND xtype = ''U'') DROP TABLE @table'
 90  SELECT @sqlX = REPLACE(@sql, N'@table', N'##input')
 91  EXEC sp_executesql @sqlX
 92  SELECT @sqlX = REPLACE(@sql, N'@table', N'##grpCount')
 93  EXEC sp_executesql @sqlX
 94  SELECT @sqlX = REPLACE(@sql, N'@table', N'##colList')
 95  EXEC sp_executesql @sqlX
 96  SELECT @sqlX = REPLACE(@sql, N'@table', N'##lookup')
 97  EXEC sp_executesql @sqlX
 98  SELECT @sqlX = REPLACE(@sql, N'@table', N'##results')
 99  EXEC sp_executesql @sqlX
100  SELECT @sqlX = REPLACE(@sql, N'@table', N'##temp')
101  EXEC sp_executesql @sqlX
102 
103  IF @verbose = 1 SELECT '删除全局临时表(如果存在)' AS [消息]
104 
105   -- 存储输入数据集
106  IF LEN(RTRIM(@tableSpec)) = 0
107  BEGIN
108    SELECT @sql = @sqlSelect + ' INTO ##input ' + @sqlFromWhere
109    IF @verbose = 1 SELECT @sql AS [创建临时表,并填充数据]
110    EXEC sp_executesql @sql
111  END
112  ELSE
113  BEGIN
114    --如果输入数据来自存储过程时创建临时表
115    SELECT @sql = 'CREATE TABLE ##input (' + @tableSpec + ')'
116    IF @verbose = 1 SELECT @sql AS [创建临时表]
117    EXEC sp_executesql @sql
118    -- Populate it
119    SELECT @sql = 'INSERT INTO ##input ' + @sqlSelect + ' ' + @sqlFromWhere
120    IF @verbose = 1 SELECT @sql AS [填充临时表数据]
121    EXEC sp_executesql @sql
122  END
123  IF @verbose = 1 SELECT * FROM ##input
124 
125  -- Does the query we want to cross-tab already have a column called 'crossTabRowID'? This is a reserved column name!
126  IF EXISTS(SELECT 1 FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c WHERE o.id = c.id AND o.name = '##input' AND c.name = 'crossTabRowID')
127  BEGIN
128    RAISERROR ('The query passed to the [genericCrosstab] procedure uses a reserved column name (''crossTabRowID'').'161)
129    ROLLBACK TRANSACTION
130  END
131 
132  -- Add an identity column (we need a 'row ID')
133  ALTER TABLE ##input ADD crossTabRowID NUMERIC(90IDENTITY NOT NULL
134 
135  -- Generate the column list
136  SELECT @sql = 'SELECT ' + @groupBy + ' INTO ##colList FROM ##input WHERE 1 = 0'
137  IF @verbose = 1 SELECT @sql AS [生成列清单]
138  EXEC sp_executesql @sql
139  IF @verbose = 1 SELECT * FROM ##colList
140 
141  -- If no aggregation function is specified, we have to check first that there are no duplicates, and - if there are - we need to handle them
142  IF RTRIM(ISNULL(@function'')) = ''
143  BEGIN
144    SELECT @sql = 'SELECT ' + @groupBy + '' + @pivotExpr + ' COLLATE ' + @collation + ' AS [pivot], COUNT(1) AS [count] INTO ##grpCount FROM ##input GROUP BY ' + @groupBy + '' + @pivotExpr + ' COLLATE ' + @collation
145  END
146  ELSE
147  BEGIN
148    SELECT @sql = 'SELECT ' + @groupBy + '' + @pivotExpr + ' COLLATE ' + @collation + ' AS [pivot], 1 AS [count] INTO ##grpCount FROM ##input GROUP BY ' + @groupBy + '' + @pivotExpr + ' COLLATE ' + @collation
149  END
150  IF @verbose = 1 SELECT @sql AS [创建##grpCount临时表]
151  EXEC sp_executesql @sql
152  IF @verbose = 1 SELECT * FROM ##grpCount
153 
154  -- Create a temporary table that will act as a lookup (containing all of the non-pivot / non-group columns names)
155  SELECT [pivot][count] AS [index][pivot] AS [column_name] INTO ##lookup FROM ##grpCount WHERE 1 = 0
156 
157  -- Build the results table; one row per group
158  SELECT @sql = 'SELECT ' + @groupBy + ' INTO ##results FROM ##grpCount GROUP BY ' + @groupBy
159  IF @verbose = 1 SELECT @sql AS [创建##results临时表]
160  EXEC sp_executesql @sql
161  IF @verbose = 1 SELECT * FROM ##results
162 
163  -- Build the column list, taking into account duplicate occurences of pivotal values
164  DECLARE xcursor CURSOR FOR SELECT [pivot]MAX([count]FROM ##grpCount GROUP BY [pivot]
165  OPEN xcursor
166 
167  FETCH NEXT FROM xcursor INTO @pivot@indx
168 
169  WHILE @@FETCH_STATUS = 0
170  BEGIN
171 
172   SELECT @i = 1
173 
174   -- Loop over indx
175   WHILE @i <= @indx
176   BEGIN
177 
178     -- Build the column list
179     SELECT @col = CASE @i WHEN 1 THEN @pivot ELSE @pivot + ' (' + CAST(@i AS VARCHAR(10)) + ')' END
180     INSERT INTO ##lookup VALUES (@pivot@i@col)
181     SELECT @col = '[' + @col + '] NVARCHAR(255) NULL'
182     SELECT @cols =  ISNULL(@cols + ''''+ @col
183 
184     -- Add the column to the results table
185     SELECT @sql = 'ALTER TABLE ##results ADD ' + @col
186     IF @verbose = 1 SELECT @sql AS [增加临时表##results列]
187     EXEC sp_executesql @sql
188 
189     -- Continue
190     SELECT @i = @i + 1
191 
192   END
193   
194   FETCH NEXT FROM xcursor INTO @pivot@indx
195 
196  END
197 
198  CLOSE xcursor
199  DEALLOCATE xcursor 
200 
201  IF @verbose = 1 SELECT * FROM ##lookup
202  IF @verbose = 1 SELECT * FROM ##results
203 
204  -- Loop over the column list (using the syscolumns table in the temp database) to build the WHERE clause
205  DECLARE xcursor CURSOR FOR SELECT DISTINCT c.[colid], c.[name] FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c WHERE o.[id] = c.[id] AND o.[name] = '##colList' ORDER BY c.[colid]
206  OPEN xcursor
207 
208  FETCH NEXT FROM xcursor INTO @indx@col
209 
210  WHILE @@FETCH_STATUS = 0
211  BEGIN
212   
213    -- Build the WHERE clause
214    SELECT @where = ISNULL(@where + ' AND '''+ 'ISNULL(t1.[' + @col + '], '''') = ISNULL(t3.[' + @col + '], '''')'
215   
216    FETCH NEXT FROM xcursor INTO @indx@col
217 
218  END
219 
220  CLOSE xcursor
221  DEALLOCATE xcursor 
222 
223  IF @verbose = 1 SELECT @where AS [WHERE语句]
224 
225  -- Create a temp table that will help us build the UPDATE statements to set the values in the pivot table, and the SELECT statement (with columns in preferred order) afterwards
226  IF RTRIM(ISNULL(@sortLookup'')) = ''
227  BEGIN
228 
229    -- If no sorting table was specified, just select all columns
230    SELECT @select = '*'
231 
232    -- Sort alphabetically
233    SELECT @sql = 'SELECT DISTINCT t5.[index], c.[name], t5.[pivot] INTO ##temp FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c, ##lookup t5 WHERE o.[id] = c.[id] AND o.[name] = ''##results'' AND c.[name] COLLATE ' + @collation + ' = t5.[column_name] COLLATE ' + @collation + ' ORDER BY c.[name], t5.[index]'
234 
235  END
236  ELSE
237  BEGIN
238 
239    -- The 'group by' column(s) always come(s) first
240    SELECT @select = @groupBy
241 
242    -- Sort in proscribed order
243    SELECT @sql = 'SELECT DISTINCT t5.[index], c.[name], t5.[pivot], l.[sort_order] INTO ##temp FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c, ##lookup t5, ' + @sortLookup + ' l WHERE o.[id] = c.[id] AND o.[name] = ''##results'' AND t5.[pivot] COLLATE ' + @collation + ' *= l.[label] COLLATE ' + @collation + ' AND c.[name] COLLATE ' + @collation + ' = t5.[column_name] COLLATE ' + @collation + ' ORDER BY l.[sort_order], c.[name], t5.[index]'
244 
245  END
246 
247  IF @verbose = 1 SELECT @sql AS [构建##temp临时表]
248  EXEC sp_executesql @sql 
249  IF @verbose = 1 SELECT * FROM ##temp
250 
251  -- The value expression
252  SELECT @value = @valueExpr
253 
254  -- If a function has been specified
255  IF RTRIM(ISNULL(@function'')) != ''
256  BEGIN
257    SELECT @value = @function + '(' + @value + ')'
258  END
259 
260  DECLARE xcursor CURSOR FOR SELECT [index][name][pivot] FROM ##temp
261  OPEN xcursor
262 
263  FETCH NEXT FROM xcursor INTO @indx@col@pivot
264 
265  WHILE @@FETCH_STATUS = 0
266  BEGIN
267 
268   -- Build the SELECT expression
269   IF @select != '*' SELECT @select = @select + '' + @col
270 
271   -- Create the SET clause of the UPDATE sql
272   IF RTRIM(ISNULL(@function'')) = ''
273   BEGIN
274     -- No function specified
275     SELECT @update = '[' + @col + '] = (SELECT ' + @value + ' FROM ##input t1 WHERE ' + @where + ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + ''' COLLATE ' + @collation + ' AND t1.crossTabRowID = (SELECT MIN(t0.crossTabRowID) FROM ##input t0 WHERE ' + REPLACE(@where't3.''t0.'+ ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + '''' + ' COLLATE ' + @collation + ') + (' + CAST(@indx AS VARCHAR(3)) + ' - 1))'
276   END
277   ELSE
278   BEGIN
279     -- Function specified
280     SELECT @update = '[' + @col + '] = (SELECT ' + @value + ' FROM ##input t1 WHERE ' + @where + ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + ''' COLLATE ' + @collation + ' AND t1.crossTabRowID IN (SELECT t0.crossTabRowID FROM ##input t0 WHERE ' + REPLACE(@where't3.''t0.'+ ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + '''' + ' COLLATE ' + @collation + '))'
281   END
282   SELECT @sql = 'UPDATE ##results SET ' + @update + ' FROM ##results t3'
283   IF @verbose = 1 SELECT @sql AS [创建UPDATE的SET语句]
284   EXEC sp_executesql @sql
285   
286   FETCH NEXT FROM xcursor INTO @indx@col@pivot
287 
288  END
289 
290  CLOSE xcursor
291  DEALLOCATE xcursor 
292 
293  -- Return the results
294  SELECT @sql = 'SELECT ' + @select + ' FROM ##results'
295  IF @verbose = 1 SELECT @sql AS [创建SELECT语句返回结果集]
296  EXEC sp_executesql @sql
297 
298  -- Tidy up: drop the global temporary tables
299  SELECT @sql = N'IF EXISTS (SELECT 1 FROM tempdb.dbo.sysobjects WHERE name = ''@table'' AND xtype = ''U'') DROP TABLE @table'
300  SELECT @sqlX = REPLACE(@sql, N'@table', N'##input')
301  EXEC sp_executesql @sqlX
302  SELECT @sqlX = REPLACE(@sql, N'@table', N'##grpCount')
303  EXEC sp_executesql @sqlX
304  SELECT @sqlX = REPLACE(@sql, N'@table', N'##colList')
305  EXEC sp_executesql @sqlX
306  SELECT @sqlX = REPLACE(@sql, N'@table', N'##lookup')
307  EXEC sp_executesql @sqlX
308  SELECT @sqlX = REPLACE(@sql, N'@table', N'##results')
309  EXEC sp_executesql @sqlX
310  SELECT @sqlX = REPLACE(@sql, N'@table', N'##temp')
311  EXEC sp_executesql @sqlX
312 
313 END
314 
posted on 2006-09-23 10:58  蚂蚁  阅读(425)  评论(0)    收藏  举报