SQLSERVER用无中生有的思想来替代游标

SQLSERVER用无中生有的思想来替代游标

昨天在MSDN论坛看到一个帖子,帖子中LZ需要根据某列的值把其他列的值插入到额外列

帖子地址:http://social.technet.microsoft.com/Forums/zh-CN/3eac78ca-d071-4c00-afa0-ef48c8501745/sql-statementcolumn-namecolumnsql-


建表脚本

USE tempdb
GO

--建表
CREATE TABLE t1
(
client VARCHAR(10) ,
pay_level INT ,
pay_lv_1 INT ,
pay_lv_2 INT ,
pay_lv_3 INT ,
pay_lv_4 INT ,
pay_lv_5 INT ,
pay_lv_6 INT ,
pay_lv_7 INT ,
pay_lv_8 INT ,
pay_lv_9 INT ,
pay_lv_10 INT ,
pay_lv_11 INT ,
pay_lv_12 INT ,
pay_lv_13 INT ,
pay_lv_14 INT ,
pay_lv_15 INT ,
pay_lv_16 INT ,
pay_lv_17 INT ,
pay_lv_18 INT ,
pay_lv_19 INT ,
pay_lv_20 INT ,
pay_lv_21 INT ,
pay_lv_22 INT ,
pay_lv_23 INT ,
pay_lv_24 INT ,
pay_lv_25 INT,
);

--插入测试数据
DECLARE @i INT
SET @i = 1
WHILE @i < 8
BEGIN
INSERT INTO t1 ( client, pay_level, pay_lv_1, pay_lv_2, pay_lv_3,
pay_lv_4, pay_lv_5, pay_lv_6, pay_lv_7, pay_lv_8,
pay_lv_9, pay_lv_10, pay_lv_11, pay_lv_12,
pay_lv_13, pay_lv_14, pay_lv_15, pay_lv_16,
pay_lv_17, pay_lv_18, pay_lv_19, pay_lv_20,
pay_lv_21, pay_lv_22, pay_lv_23, pay_lv_24,
pay_lv_25 )
SELECT 'client' + CAST(@i AS VARCHAR(10)),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND()
SET @i=@i+1

END

SELECT * FROM t1
GO

  1. 1 USE tempdb
  2. 2 GO
  3. 3
  4. 4 --建表
  5. 5 CREATE TABLE t1
  6. 6 (
  7. 7 client VARCHAR(10) ,
  8. 8 pay_level INT ,
  9. 9 pay_lv_1 INT ,
  10. 10 pay_lv_2 INT ,
  11. 11 pay_lv_3 INT ,
  12. 12 pay_lv_4 INT ,
  13. 13 pay_lv_5 INT ,
  14. 14 pay_lv_6 INT ,
  15. 15 pay_lv_7 INT ,
  16. 16 pay_lv_8 INT ,
  17. 17 pay_lv_9 INT ,
  18. 18 pay_lv_10 INT ,
  19. 19 pay_lv_11 INT ,
  20. 20 pay_lv_12 INT ,
  21. 21 pay_lv_13 INT ,
  22. 22 pay_lv_14 INT ,
  23. 23 pay_lv_15 INT ,
  24. 24 pay_lv_16 INT ,
  25. 25 pay_lv_17 INT ,
  26. 26 pay_lv_18 INT ,
  27. 27 pay_lv_19 INT ,
  28. 28 pay_lv_20 INT ,
  29. 29 pay_lv_21 INT ,
  30. 30 pay_lv_22 INT ,
  31. 31 pay_lv_23 INT ,
  32. 32 pay_lv_24 INT ,
  33. 33 pay_lv_25 INT,
  34. 34 );
  35. 35
  36. 36
  37. 37 --插入测试数据
  38. 38 DECLARE @i INT
  39. 39 SET @i = 1
  40. 40 WHILE @i < 8
  41. 41 BEGIN
  42. 42 INSERT INTO t1 ( client, pay_level, pay_lv_1, pay_lv_2, pay_lv_3,
  43. 43 pay_lv_4, pay_lv_5, pay_lv_6, pay_lv_7, pay_lv_8,
  44. 44 pay_lv_9, pay_lv_10, pay_lv_11, pay_lv_12,
  45. 45 pay_lv_13, pay_lv_14, pay_lv_15, pay_lv_16,
  46. 46 pay_lv_17, pay_lv_18, pay_lv_19, pay_lv_20,
  47. 47 pay_lv_21, pay_lv_22, pay_lv_23, pay_lv_24,
  48. 48 pay_lv_25 )
  49. 49 SELECT 'client' + CAST(@i AS VARCHAR(10)),
  50. 50 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  51. 51 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  52. 52 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  53. 53 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  54. 54 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  55. 55 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  56. 56 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  57. 57 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  58. 58 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  59. 59 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  60. 60 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  61. 61 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  62. 62 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND()
  63. 63 SET @i=@i+1
  64. 64
  65. 65 END
  66. 66
  67. 67 SELECT * FROM t1
  68. 68 GO

图1

LZ说原表就是类似上面那样,实际表中pay_lv_会有很多列至少100列,我这里为了测试只建了25个pay_lv_列

而LZ希望select出来的结果是下图那样

图2

client列和pay_level列不变,增加一个pay_cost列

pay_cost列根据pay_level列的值去取pay_lv_列的值,或者我用下面的图片会更加明白

图3

例如第6行,pay_level的值是6,那么就去pay_lv_6这一列的值(值是20)把他放到pay_cost列里

其他也是一样,第二行pay_level的值是10,那就去pay_lv_10这一列的值(值是17)把他放到pay_cost列里

如此类推


要select出图2的结果,有下面几种方法

1、case when

2、UNPIVOT函数

3、游标

我这里再建另外一个表,这个表跟原表是一样的,只是数据没有那么多,pay_lv_列数只有3列

USE tempdb
GO

CREATE TABLE #t
(
client VARCHAR(10) ,
pay_level INT ,
pay_lv_1 INT ,
pay_lv_2 INT ,
pay_lv_3 INT
);

INSERT INTO #t ( client ,
pay_level ,
pay_lv_1 ,
pay_lv_2 ,
pay_lv_3
)
VALUES ( 'client1' , -- client - varchar(10)
1, -- pay_level - int
10 , -- pay_lv_1 - int
12 , -- pay_lv_2 - int
14 -- pay_lv_3 - int
)

INSERT INTO #t ( client ,
pay_level ,
pay_lv_1 ,
pay_lv_2 ,
pay_lv_3
)
VALUES ( 'client2' , -- client - varchar(10)
3, -- pay_level - int
21 , -- pay_lv_1 - int
22 , -- pay_lv_2 - int
23 -- pay_lv_3 - int
)

INSERT INTO #t ( client ,
pay_level ,
pay_lv_1 ,
pay_lv_2 ,
pay_lv_3
)
VALUES ( 'client3' , -- client - varchar(10)
2, -- pay_level - int
30 , -- pay_lv_1 - int
32 , -- pay_lv_2 - int
33 -- pay_lv_3 - int
)

SELECT * FROM #t

  1. 1 USE tempdb
  2. 2 GO
  3. 3
  4. 4
  5. 5 CREATE TABLE #t
  6. 6 (
  7. 7 client VARCHAR(10) ,
  8. 8 pay_level INT ,
  9. 9 pay_lv_1 INT ,
  10. 10 pay_lv_2 INT ,
  11. 11 pay_lv_3 INT
  12. 12 );
  13. 13
  14. 14 INSERT INTO #t ( client ,
  15. 15 pay_level ,
  16. 16 pay_lv_1 ,
  17. 17 pay_lv_2 ,
  18. 18 pay_lv_3
  19. 19 )
  20. 20 VALUES ( 'client1' , -- client - varchar(10)
  21. 21 1, -- pay_level - int
  22. 22 10 , -- pay_lv_1 - int
  23. 23 12 , -- pay_lv_2 - int
  24. 24 14 -- pay_lv_3 - int
  25. 25 )
  26. 26
  27. 27
  28. 28 INSERT INTO #t ( client ,
  29. 29 pay_level ,
  30. 30 pay_lv_1 ,
  31. 31 pay_lv_2 ,
  32. 32 pay_lv_3
  33. 33 )
  34. 34 VALUES ( 'client2' , -- client - varchar(10)
  35. 35 3, -- pay_level - int
  36. 36 21 , -- pay_lv_1 - int
  37. 37 22 , -- pay_lv_2 - int
  38. 38 23 -- pay_lv_3 - int
  39. 39 )
  40. 40
  41. 41 INSERT INTO #t ( client ,
  42. 42 pay_level ,
  43. 43 pay_lv_1 ,
  44. 44 pay_lv_2 ,
  45. 45 pay_lv_3
  46. 46 )
  47. 47 VALUES ( 'client3' , -- client - varchar(10)
  48. 48 2, -- pay_level - int
  49. 49 30 , -- pay_lv_1 - int
  50. 50 32 , -- pay_lv_2 - int
  51. 51 33 -- pay_lv_3 - int
  52. 52 )
  53. 53
  54. 54 SELECT * FROM #t

(1)case when

  1. 1 SELECT client,[pay_level],( CASE pay_level
  2. 2 WHEN 1 THEN pay_lv_1
  3. 3 WHEN 2 THEN pay_lv_2
  4. 4 WHEN 3 THEN pay_lv_3
  5. 5 ELSE 0
  6. 6 END) AS 'pay_cost'
  7. 7 FROM #t;

图4

(2)UNPIVOT函数

  1. 1 SELECT * INTO #tt
  2. 2 FROM ( SELECT *
  3. 3 FROM #t
  4. 4 ) p UNPIVOT
  5. 5 ( pay_cost FOR pay_lv IN ( pay_lv_1, pay_lv_2, pay_lv_3 ) )AS unpvt
  6. 6 WHERE CAST(RIGHT(pay_lv, 1) AS INT) = pay_level
  7. 7
  8. 8 SELECT [client],[pay_level],[pay_cost] FROM [#tt]
  9. 9
  10. 10 DROP TABLE [#tt]

图5

上面两个方法:CASE WHEN和UNPIVOT函数可以用拼接SQL的方法来做,不过由于本人功力不够,写不出来

(3)游标

我不喜欢使用游标,主要有两个原因

1、每次用的时候,要打开笔记本看语法

2、占用资源

我使用了下面的sql语句来解决LZ的问题

IF object_id('#ttt') IS NOT NULL
DROP TABLE #ttt
IF object_id('#temptb') IS NOT NULL
DROP TABLE #temptb

DECLARE @i INT
--用于循环的
SET @i = 1
DECLARE @pay_level INT
--保存pay_level字段的值
DECLARE @COUNT INT
--保存#t1表的总行数值
DECLARE @pay_lv INT
--用于保存pay_lv的值
DECLARE @sql NVARCHAR(2000)

CREATE TABLE #ttt (ID INT IDENTITY(1,1), pay_cost INT )

SELECT IDENTITY( INT,1,1 ) AS ID, * INTO #temptb FROM t1

--获取#t1表的总行数
SELECT @COUNT = COUNT(*) FROM [#temptb]
WHILE @i <= @COUNT
BEGIN
SELECT @pay_level = [pay_level] FROM [#temptb] WHERE id = @i
--判断列名是否存在,不存在就插入0
IF 'pay_lv_' + CAST(@pay_level AS VARCHAR(200)) IN ( SELECT NAME FROM SYS.[syscolumns] )
BEGIN
--用拼接sql的方法来获得pay_lv列对应的值,然后插入到#ttt表
SET @sql = N'select ' + ' @pay_lv=pay_lv_' + CAST(@pay_level AS NVARCHAR(200)) + ' from #temptb where id=' + CAST(@i AS NVARCHAR(20))
EXEC sp_executesql @sql, N'@pay_lv int output ', @pay_lv OUTPUT
INSERT INTO #ttt VALUES (@pay_lv)
END
ELSE
BEGIN
INSERT INTO #ttt VALUES(0)
END
SET @i = @i + 1
END

SELECT A.[client], A.[pay_level], B.[pay_cost]
FROM [#temptb] AS A
INNER JOIN [#ttt] AS B ON A.[ID] = B.[ID]
ORDER BY A.[ID] ASC

DROP TABLE [#temptb]
DROP TABLE [#ttt]

  1. 1 IF object_id('#ttt') IS NOT NULL
  2. 2 DROP TABLE #ttt
  3. 3 IF object_id('#temptb') IS NOT NULL
  4. 4 DROP TABLE #temptb
  5. 5
  6. 6 DECLARE @i INT
  7. 7 --用于循环的
  8. 8 SET @i = 1
  9. 9 DECLARE @pay_level INT
  10. 10 --保存pay_level字段的值
  11. 11 DECLARE @COUNT INT
  12. 12 --保存#t1表的总行数值
  13. 13 DECLARE @pay_lv INT
  14. 14 --用于保存pay_lv的值
  15. 15 DECLARE @sql NVARCHAR(2000)
  16. 16
  17. 17 CREATE TABLE #ttt (ID INT IDENTITY(1,1), pay_cost INT )
  18. 18
  19. 19 SELECT IDENTITY( INT,1,1 ) AS ID, * INTO #temptb FROM t1
  20. 20
  21. 21
  22. 22 --获取#t1表的总行数
  23. 23 SELECT @COUNT = COUNT(*) FROM [#temptb]
  24. 24 WHILE @i <= @COUNT
  25. 25 BEGIN
  26. 26 SELECT @pay_level = [pay_level] FROM [#temptb] WHERE id = @i
  27. 27 --判断列名是否存在,不存在就插入0
  28. 28 IF 'pay_lv_' + CAST(@pay_level AS VARCHAR(200)) IN ( SELECT NAME FROM SYS.[syscolumns] )
  29. 29 BEGIN
  30. 30 --用拼接sql的方法来获得pay_lv列对应的值,然后插入到#ttt
  31. 31 SET @sql = N'select ' + ' @pay_lv=pay_lv_' + CAST(@pay_level AS NVARCHAR(200)) + ' from #temptb where id=' + CAST(@i AS NVARCHAR(20))
  32. 32 EXEC sp_executesql @sql, N'@pay_lv int output ', @pay_lv OUTPUT
  33. 33 INSERT INTO #ttt VALUES (@pay_lv)
  34. 34 END
  35. 35 ELSE
  36. 36 BEGIN
  37. 37 INSERT INTO #ttt VALUES(0)
  38. 38 END
  39. 39 SET @i = @i + 1
  40. 40 END
  41. 41
  42. 42
  43. 43
  44. 44 SELECT A.[client], A.[pay_level], B.[pay_cost]
  45. 45 FROM [#temptb] AS A
  46. 46 INNER JOIN [#ttt] AS B ON A.[ID] = B.[ID]
  47. 47 ORDER BY A.[ID] ASC
  48. 48
  49. 49 DROP TABLE [#temptb]
  50. 50 DROP TABLE [#ttt]

我这个sql语句也需要拼接sql来达到LZ想要的效果

不过这篇文章的重点不是拼接SQL


重点是怎麽模仿游标

其实这个方法是最原始的方法,之前解决论坛问题的时候用过,想不到这次也能用上

USE tempdb
GO

--建表
CREATE TABLE t1
(
client VARCHAR(10) ,
pay_level INT ,
pay_lv_1 INT ,
pay_lv_2 INT ,
pay_lv_3 INT ,
pay_lv_4 INT ,
pay_lv_5 INT ,
pay_lv_6 INT ,
pay_lv_7 INT ,
pay_lv_8 INT ,
pay_lv_9 INT ,
pay_lv_10 INT ,
pay_lv_11 INT ,
pay_lv_12 INT ,
pay_lv_13 INT ,
pay_lv_14 INT ,
pay_lv_15 INT ,
pay_lv_16 INT ,
pay_lv_17 INT ,
pay_lv_18 INT ,
pay_lv_19 INT ,
pay_lv_20 INT ,
pay_lv_21 INT ,
pay_lv_22 INT ,
pay_lv_23 INT ,
pay_lv_24 INT ,
pay_lv_25 INT,
);

--插入测试数据
DECLARE @i INT
SET @i = 1
WHILE @i < 8
BEGIN
INSERT INTO t1 ( client, pay_level, pay_lv_1, pay_lv_2, pay_lv_3,
pay_lv_4, pay_lv_5, pay_lv_6, pay_lv_7, pay_lv_8,
pay_lv_9, pay_lv_10, pay_lv_11, pay_lv_12,
pay_lv_13, pay_lv_14, pay_lv_15, pay_lv_16,
pay_lv_17, pay_lv_18, pay_lv_19, pay_lv_20,
pay_lv_21, pay_lv_22, pay_lv_23, pay_lv_24,
pay_lv_25 )
SELECT 'client' + CAST(@i AS VARCHAR(10)),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND()
SET @i=@i+1

END

SELECT * FROM t1
GO

--ALTER TABLE [t1] DROP COLUMN [pay_lv_2]

-----------------------------------------------------
IF object_id('#ttt') IS NOT NULL
DROP TABLE #ttt
IF object_id('#temptb') IS NOT NULL
DROP TABLE #temptb

DECLARE @i INT
--用于循环的
SET @i = 1
DECLARE @pay_level INT
--保存pay_level字段的值
DECLARE @COUNT INT
--保存t1表的总行数值
DECLARE @pay_lv INT
--用于保存pay_lv的值
DECLARE @sql NVARCHAR(2000)

CREATE TABLE #ttt (ID INT IDENTITY(1,1), pay_cost INT )

SELECT IDENTITY( INT,1,1 ) AS ID, * INTO #temptb FROM t1

--获取t1表的总行数
SELECT @COUNT = COUNT(*) FROM [#temptb]
WHILE @i <= @COUNT
BEGIN
SELECT @pay_level = [pay_level] FROM [#temptb] WHERE id = @i
--判断列名是否存在,不存在就插入0
IF 'pay_lv_' + CAST(@pay_level AS VARCHAR(200)) IN ( SELECT NAME FROM SYS.[syscolumns] )
BEGIN
--用拼接sql的方法来获得pay_lv列对应的值,然后插入到#ttt表
SET @sql = N'select ' + ' @pay_lv=pay_lv_' + CAST(@pay_level AS NVARCHAR(200)) + ' from #temptb where id=' + CAST(@i AS NVARCHAR(20))
EXEC sp_executesql @sql, N'@pay_lv int output ', @pay_lv OUTPUT
INSERT INTO #ttt VALUES (@pay_lv)
END
ELSE
BEGIN
INSERT INTO #ttt VALUES(0)
END
SET @i = @i + 1
END

SELECT A.[client], A.[pay_level], B.[pay_cost]
FROM [#temptb] AS A
INNER JOIN [#ttt] AS B ON A.[ID] = B.[ID]
ORDER BY A.[ID] ASC

DROP TABLE [#temptb]
DROP TABLE [#ttt]

  1. 1 USE tempdb
  2. 2 GO
  3. 3
  4. 4 --建表
  5. 5 CREATE TABLE t1
  6. 6 (
  7. 7 client VARCHAR(10) ,
  8. 8 pay_level INT ,
  9. 9 pay_lv_1 INT ,
  10. 10 pay_lv_2 INT ,
  11. 11 pay_lv_3 INT ,
  12. 12 pay_lv_4 INT ,
  13. 13 pay_lv_5 INT ,
  14. 14 pay_lv_6 INT ,
  15. 15 pay_lv_7 INT ,
  16. 16 pay_lv_8 INT ,
  17. 17 pay_lv_9 INT ,
  18. 18 pay_lv_10 INT ,
  19. 19 pay_lv_11 INT ,
  20. 20 pay_lv_12 INT ,
  21. 21 pay_lv_13 INT ,
  22. 22 pay_lv_14 INT ,
  23. 23 pay_lv_15 INT ,
  24. 24 pay_lv_16 INT ,
  25. 25 pay_lv_17 INT ,
  26. 26 pay_lv_18 INT ,
  27. 27 pay_lv_19 INT ,
  28. 28 pay_lv_20 INT ,
  29. 29 pay_lv_21 INT ,
  30. 30 pay_lv_22 INT ,
  31. 31 pay_lv_23 INT ,
  32. 32 pay_lv_24 INT ,
  33. 33 pay_lv_25 INT,
  34. 34 );
  35. 35
  36. 36
  37. 37 --插入测试数据
  38. 38 DECLARE @i INT
  39. 39 SET @i = 1
  40. 40 WHILE @i < 8
  41. 41 BEGIN
  42. 42 INSERT INTO t1 ( client, pay_level, pay_lv_1, pay_lv_2, pay_lv_3,
  43. 43 pay_lv_4, pay_lv_5, pay_lv_6, pay_lv_7, pay_lv_8,
  44. 44 pay_lv_9, pay_lv_10, pay_lv_11, pay_lv_12,
  45. 45 pay_lv_13, pay_lv_14, pay_lv_15, pay_lv_16,
  46. 46 pay_lv_17, pay_lv_18, pay_lv_19, pay_lv_20,
  47. 47 pay_lv_21, pay_lv_22, pay_lv_23, pay_lv_24,
  48. 48 pay_lv_25 )
  49. 49 SELECT 'client' + CAST(@i AS VARCHAR(10)),
  50. 50 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  51. 51 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  52. 52 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  53. 53 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  54. 54 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  55. 55 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  56. 56 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  57. 57 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  58. 58 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  59. 59 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  60. 60 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  61. 61 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND(),
  62. 62 ( 20 + 1 ) * RAND(), ( 20 + 1 ) * RAND()
  63. 63 SET @i=@i+1
  64. 64
  65. 65 END
  66. 66
  67. 67 SELECT * FROM t1
  68. 68 GO
  69. 69
  70. 70 --ALTER TABLE [t1] DROP COLUMN [pay_lv_2]
  71. 71
  72. 72
  73. 73 -----------------------------------------------------
  74. 74 IF object_id('#ttt') IS NOT NULL
  75. 75 DROP TABLE #ttt
  76. 76 IF object_id('#temptb') IS NOT NULL
  77. 77 DROP TABLE #temptb
  78. 78
  79. 79 DECLARE @i INT
  80. 80 --用于循环的
  81. 81 SET @i = 1
  82. 82 DECLARE @pay_level INT
  83. 83 --保存pay_level字段的值
  84. 84 DECLARE @COUNT INT
  85. 85 --保存t1表的总行数值
  86. 86 DECLARE @pay_lv INT
  87. 87 --用于保存pay_lv的值
  88. 88 DECLARE @sql NVARCHAR(2000)
  89. 89
  90. 90 CREATE TABLE #ttt (ID INT IDENTITY(1,1), pay_cost INT )
  91. 91
  92. 92 SELECT IDENTITY( INT,1,1 ) AS ID, * INTO #temptb FROM t1
  93. 93
  94. 94
  95. 95 --获取t1表的总行数
  96. 96 SELECT @COUNT = COUNT(*) FROM [#temptb]
  97. 97 WHILE @i <= @COUNT
  98. 98 BEGIN
  99. 99 SELECT @pay_level = [pay_level] FROM [#temptb] WHERE id = @i
  100. 100 --判断列名是否存在,不存在就插入0
  101. 101 IF 'pay_lv_' + CAST(@pay_level AS VARCHAR(200)) IN ( SELECT NAME FROM SYS.[syscolumns] )
  102. 102 BEGIN
  103. 103 --用拼接sql的方法来获得pay_lv列对应的值,然后插入到#ttt
  104. 104 SET @sql = N'select ' + ' @pay_lv=pay_lv_' + CAST(@pay_level AS NVARCHAR(200)) + ' from #temptb where id=' + CAST(@i AS NVARCHAR(20))
  105. 105 EXEC sp_executesql @sql, N'@pay_lv int output ', @pay_lv OUTPUT
  106. 106 INSERT INTO #ttt VALUES (@pay_lv)
  107. 107 END
  108. 108 ELSE
  109. 109 BEGIN
  110. 110 INSERT INTO #ttt VALUES(0)
  111. 111 END
  112. 112 SET @i = @i + 1
  113. 113 END
  114. 114
  115. 115
  116. 116
  117. 117 SELECT A.[client], A.[pay_level], B.[pay_cost]
  118. 118 FROM [#temptb] AS A
  119. 119 INNER JOIN [#ttt] AS B ON A.[ID] = B.[ID]
  120. 120 ORDER BY A.[ID] ASC
  121. 121
  122. 122 DROP TABLE [#temptb]
  123. 123 DROP TABLE [#ttt]

关键代码有以下几句

  1. 1 CREATE TABLE #ttt (ID INT IDENTITY(1,1), pay_cost INT )
  2. 2
  3. 3 SELECT IDENTITY( INT,1,1 ) AS ID, * INTO #temptb FROM t1
  4. 4
  5. 5 --获取#t1表的总行数
  6. 6 SELECT @COUNT = COUNT(*) FROM [#temptb]
  7. 7 WHILE @i <= @COUNT
  8. 8 SELECT @pay_level = [pay_level] FROM [#temptb] WHERE id = @i
  9. 9 SET @i = @i + 1
  10. 10 ----------------------------------
  11. 11 SELECT A.[client], A.[pay_level], B.[pay_cost]
  12. 12 FROM [#temptb] AS A
  13. 13 INNER JOIN [#ttt] AS B ON A.[ID] = B.[ID]
  14. 14 ORDER BY A.[ID] ASC

原表是没有自增id的,我建一个临时表#temptb,临时表有一个自增id,并把原表的数据全部放入临时表

获取临时表的行数,用于循环

每次执行的时候根据 WHERE   id = @i 来逐行逐行获取值,变量@i每次循环都递增1

将获取到的值都插入到#ttt这个临时表里面,然后根据ID的值做两表连接就可以得到LZ的结果

我说的无中生有就是“在原表里增加一个自增id方便循环,既简单又容易理解o(∩_∩)o ”


判断

我这里还用了一句

  1. 1 IF 'pay_lv_' + CAST(@pay_level AS VARCHAR(200)) IN ( SELECT NAME FROM SYS.[syscolumns] )

用于判断要获取值的pay_lv_列是否存在,如果存在就插入pay_lv_列的值,如果不存在就插入0


总结

其实如果觉得某样东西很难去实现,能不能用一个变通的方法呢?多动脑筋,办法会有的

如有不对的地方,欢迎大家拍砖o(∩_∩)o

SQLSERVER用无中生有的思想来替代游标的更多相关文章

  1. SQL优化技巧-批处理替代游标

    通过MSSQL中的用户自定义表类型可以快速将需要处理的数据存储起来,生成新的临时表(这里使用变量表),然后根据表中字段进行批处理替代游标. 用户自定义表类型 0 --创建用户自定义表类型 1 Crea ...

  2. SQL Server 中用While循环替代游标Cursor的解决方案

    在编写SQL批处理或存储过程代码的过程中,经常会碰到有些业务逻辑的处理,需要对满足条件的数据记录逐行进行处理,这个时候,大家首先想到的方案大部分是用“游标”进行处理. 举个例子,在订单管理系统中,客服 ...

  3. SqlServer 临时表、表变量、函数 替代游标

    http://www.cnblogs.com/chongzi/archive/2011/01/19/1939106.html 临时表 存放在tempdb中 --存储过程中将多表连接结果写入到临时表中, ...

  4. [转]SQL Server中用While循环替代游标(Cursor)的解决方案

    本文转自:https://www.cnblogs.com/SunnyZhu/p/5719184.html By行处理数据,推荐2种方式: 1.游标 2.While循环 我们来了解下这两种方案处理1w行 ...

  5. SQL Server中用While循环替代游标(Cursor)的解决方案

    By行处理数据,推荐2种方式: 1.游标 2.While循环 我们来了解下这两种方案处理1w行数据分别需要多长时间. 一.游标. 首先我们填充一个表,用优雅的递归方式填充. ,) ) ;with ct ...

  6. sqlserver数据库安全函数、配置函数、游标函数、行级函数、排名函数、元数据函数、系统统计函数 、文本和图像函数--收藏着有用

    行级函数:下列行集函数将返回一个可用于代替 Transact-SQL 语句中表引用的对象. CONTAINSTABLE 返回具有零行.一行或多行的表,这些行的列中包含的基于字符类型的数据是单个词语和短 ...

  7. SQL 临时表或表变量替代游标

    1.如果表没有自动增长的标识列(int) 使用临时表 SELECT IDENTITY(int) NewID ,.. INTO #tmp FROM YouTable 2.表有标识列 使用表变量 INSE ...

  8. SQL 临时表或表变量替代游标(转)

    1.如果表没有自动增长的标识列(int) 使用临时表 SELECT IDENTITY(int) NewID ,.. INTO #tmp FROM YouTable 2.表有标识列 使用表变量 INSE ...

  9. sqlserver临时表或表变量代替游标

    在很多场合,用临时表或表变量也可以替代游标 临时表用在表没有标识列(int)的情况下. 在表有标识列(int)的情况下可以用表变量,当然也可以用临时表. 利用临时表或表变量的原因时,生成一个连续的列 ...

随机推荐

  1. C# 程序自动批量生成 google maps 的KML文件

    原文:C# 程序自动批量生成 google maps 的KML文件 google maps 的 KML 文件可以用于静态的地图标注,在某些应用中,我们手上往往有成百上千个地址,我们需要把这些地址和描述 ...

  2. MVC 6 写法

    MVC 6 一些不晓得的写法 今天在看 Scott Guthrie 的一篇博文<Introducing ASP.NET 5>,在 MVC 6 中,发现有些之前不晓得的写法,这边简单记录下, ...

  3. Thrift官方安装手冊(译)

    本篇是Thrift官网安装文档的翻译,原地址点击这里.Thrift之前是不支持Windows的.可是似乎0.9版本号以后已经支持Window了.介绍了Thrift安装的环境要求以及在centos,De ...

  4. 日期格式化{0:yyyy-MM-dd HH:mm:ss.fff}和{0:yyyy-MM-dd hh:mm:ss.fff}的区别

    原文:日期格式化{0:yyyy-MM-dd HH:mm:ss.fff}和{0:yyyy-MM-dd hh:mm:ss.fff}的区别 {0:yyyy-MM-dd HH:mm:ss.fff}:使用24小 ...

  5. 如何判断微信内置浏览器(通过User Agent实现)

    在进行微信公众账号开发的时候,其中很大一块是微站点的开发,我们需要知道当前的浏览器是微信内置的浏览器,那么如何判断呢? 微信内置浏览器的 User Agent 如何判断微信内置浏览器,首先需要获取微信 ...

  6. 为Pythonic论坛添加一个“专题”功能

    代码还没读完就踏上了修改功能的深坑.还好思路清晰,通过修改模板和视图,实现了专题模块 原论坛的模式是用户点击节点发帖,然后就归到节点的分类里面了.我需要一个功能,就是右侧需要一个专题区,管理员发帖的话 ...

  7. Kindle使用技巧

    Kindle使用技巧 使用kindle也有几年了,但是好多kindle的使用技巧还不知道,在网上看到了一些,整理了一下. 1. 格式转换 把PDF发送到Kindle的时候,邮件主题写convert,这 ...

  8. 我看PS求职简历照

    看新闻<大学生求职简历照片不PS 连面试机会都没有>,忍不住也谈些自己的看法. 个人意见,尽量求真,这个求真要有平时努力的保证.长相一般的,知道自己是普通一员,按普通的方式行事就可以.即使 ...

  9. 算法课上机实验(一个简单的GUI排序算法比较程序)

    (在家里的电脑上Linux Deepin截的图,屏幕大一点的话,deepin用着还挺不错的说) 这个应该是大二的算法课程上机实验时做的一个小程序,也是我的第一个GUI小程序,实现什么的都记不清了,只记 ...

  10. 【转】 Android项目的mvc模式

    MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器.一个逻辑模型M可以对于多种视图模型V,比如一批统计数据你可以分别用柱状图.饼图V来表示.一种视图模 ...