在SQL Server中循环列名称

时间:2015-06-05 19:15:28

标签: sql-server-2008

我有一个名为WorkDetails的表,它具有以下列名: ChargeCode ,, EndShiftDate,A1,B1,C1,A2,B2,C2,......,A10,B10,C10 所有列A,B和C都具有数值,表示特定日期的任务花费的小时数。 我能写一个像这样的插入语句:

insert into myTable (chargeCode, EndShiftCode, [hours]) select chargeCode, DATEADD(dd, -10, EndShiftCode), sum(A1) FROM WorkDetails Where ChargeCode in (1,2,5) group by chargeCode, EndShiftCode, A1

insert into myTable (chargeCode, EndShiftCode, [hours]) select chargeCode, DATEADD(dd, -9, EndShiftCode), sum(A2) FROM WorkDetails Where ChargeCode in (1,2,5) group by chargeCode, EndShiftCode, A2

问题是我必须写30次才能将列名从A1更改为A10,B1更改为B10,C1更改为C10。

有没有更好的方法来编写insert语句,我可以通过循环遍历以A,然后是B和C开头的所有列来将列名指定为“Where ColName like'A%'”? p>

谢谢!

4 个答案:

答案 0 :(得分:2)

您不需要动态SQL。您可以从不同的(也可以是基于集合的)方法处理问题。只需将查询交叉连接到数字/计数表(或通过CTE创建一个内联)以获取列数(例如,在您的情况下为TOP(30)),然后使用CASE语句选择所需的列。数字表/ CTE中的每个单独值将表示一行等同于您的一个硬编码查询。由于每一行都是一个不同的"查询",每一行都可以从不同的A,B或C列中提取。

测试设置:

SET NOCOUNT ON;
IF (OBJECT_ID(N'tempdb..#Bob') IS NOT NULL)
BEGIN
  DROP TABLE #Bob;
END;

CREATE TABLE #Bob (
                ChargeCode INT, EndShiftDate DATETIME, A1 INT, B1 INT, C1 INT, 
                A2 INT, B2 INT, C2 INT, A3 INT, B3 INT, C3 INT, A4 INT, B4 INT, C4 INT,
                A5 INT, B5 INT, C5 INT, A6 INT, B6 INT, C6 INT, A7 INT, B7 INT, C7 INT,
                A8 INT, B8 INT, C8 INT, A9 INT, B9 INT, C9 INT, A10 INT, B10 INT, C10 INT
                  );

INSERT INTO #Bob (ChargeCode, EndShiftDate, A1) VALUES (1, '2015-01-05', 1);
INSERT INTO #Bob (ChargeCode, EndShiftDate, A1) VALUES (1, '2015-01-05', 5);
INSERT INTO #Bob (ChargeCode, EndShiftDate, A1) VALUES (2, '2015-01-05', 3);
INSERT INTO #Bob (ChargeCode, EndShiftDate, A1) VALUES (2, '2015-01-05', 56);
INSERT INTO #Bob (ChargeCode, EndShiftDate, A1) VALUES (2, '2015-01-17', 300);
INSERT INTO #Bob (ChargeCode, EndShiftDate, A1) VALUES (2, '2015-01-17', 6);
INSERT INTO #Bob (ChargeCode, EndShiftDate, A1) VALUES (7, '2015-01-17', 10000);
INSERT INTO #Bob (ChargeCode, EndShiftDate, B1) VALUES (10, '2015-01-05', 11);
INSERT INTO #Bob (ChargeCode, EndShiftDate, B1) VALUES (10, '2015-01-05', 15);
INSERT INTO #Bob (ChargeCode, EndShiftDate, B1) VALUES (50, '2015-01-05', 13);
INSERT INTO #Bob (ChargeCode, EndShiftDate, B1) VALUES (50, '2015-01-05', 156);
INSERT INTO #Bob (ChargeCode, EndShiftDate, B1) VALUES (50, '2015-01-17', 1300);
INSERT INTO #Bob (ChargeCode, EndShiftDate, B1) VALUES (50, '2015-01-17', 16);
INSERT INTO #Bob (ChargeCode, EndShiftDate, B1) VALUES (77, '2015-01-17', 100000);
INSERT INTO #Bob (ChargeCode, EndShiftDate, C2) VALUES (200, '2015-02-05', 51);
INSERT INTO #Bob (ChargeCode, EndShiftDate, C2) VALUES (200, '2015-02-05', 55);
INSERT INTO #Bob (ChargeCode, EndShiftDate, C2) VALUES (100, '2015-02-05', 53);
INSERT INTO #Bob (ChargeCode, EndShiftDate, C2) VALUES (100, '2015-02-05', 556);
INSERT INTO #Bob (ChargeCode, EndShiftDate, C2) VALUES (100, '2015-02-17', 5300);
INSERT INTO #Bob (ChargeCode, EndShiftDate, C2) VALUES (100, '2015-02-17', 56);
INSERT INTO #Bob (ChargeCode, EndShiftDate, C2) VALUES (111, '2015-02-17', 1000000);
SELECT * FROM #Bob;

单个非动态SQL查询:

;WITH nums AS
(
  SELECT TOP (6) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS [TheNum]
  FROM   [sys].[objects]
), agg AS
(
  SELECT b.ChargeCode,
         DATEADD(DAY, (CEILING(nums.TheNum / 3.0) - 11), b.EndShiftDate) AS [EndShiftDate],
         SUM(
                CASE nums.TheNum
                    WHEN 1 THEN b.A1
                    WHEN 2 THEN b.B1
                    WHEN 3 THEN b.C1
                    WHEN 4 THEN b.A2
                    WHEN 5 THEN b.B2
                    WHEN 6 THEN b.C2
                  --repeat for 7, 8, 9 to be [ABC]3; 10, 11, 12 to be [ABC]4; and so on...
                END
            ) AS [Hours]
  FROM   #Bob b
  CROSS JOIN nums
  WHERE 1 = CASE
              WHEN nums.TheNum % 3 = 1 AND chargeCode IN (1, 2, 5) THEN 1       -- A
              WHEN nums.TheNum % 3 = 2 AND chargeCode IN (10, 50, 70) THEN 1    -- B
              WHEN nums.TheNum % 3 = 0 AND chargeCode IN (100, 200, 500) THEN 1 -- C
            END
  GROUP BY b.chargeCode, DATEADD(DAY, (CEILING(nums.TheNum / 3.0) - 11), b.EndShiftDate)
)
-- INSERT INTO myTable (ChargeCode, [EndShiftDate], [Hours])
  SELECT ChargeCode, [EndShiftDate], [Hours]
  FROM   agg
  WHERE  agg.[Hours] IS NOT NULL;

结果(INSERT已注释掉):

ChargeCode  EndShiftDate                Hours
----------  ------------                -----
1           2014-12-26 00:00:00.000     6
2           2014-12-26 00:00:00.000     59
10          2014-12-26 00:00:00.000     26
50          2014-12-26 00:00:00.000     169
2           2015-01-07 00:00:00.000     306
50          2015-01-07 00:00:00.000     1316
100         2015-01-27 00:00:00.000     609
200         2015-01-27 00:00:00.000     106
100         2015-02-08 00:00:00.000     5356

答案 1 :(得分:1)

您可以使用动态sql,如下所示:

declare @sql nvarchar(max) = ''
declare @column varchar(max)

declare cur cursor for select name from sys.columns where object_name(object_id) = 'WorkDetails' and name not in ('chargeCode', 'EndShiftCode')

open cur

fetch next from cur into @column;

while @@fetch_status = 0
begin   
    set @sql = @sql + 'insert into myTable(chargeCode, EndShiftCode, [hours])' + char(13) + char(10) 
        + 'select chargeCode, DATEADD(dd, -9, EndShiftCode), sum({0}) FROM WorkDetails Where ChargeCode in (1,2,5) group by chargeCode, EndShiftCode, {0}'

    set @sql = replace(@sql, '{0}', @column)



   fetch next from cur;
end

select @sql

close cur
deallocate cur  

答案 2 :(得分:0)

您可以使用information_schema最大限度地减少手动编写查询的重复工作。我更喜欢在动态查询上手动编写查询。

declare @var varchar(8000) = ' insert into .... WHERE ... GROUP BY '
declare @resultQuery varchar(8000) = ''
    select @resultQuery += @var + ' ' + COLUMN_NAME + '; '
    FROM information_schema.columns where table_name = 'myTable'

print @resultQuery
exec (@resultQuery)

您正在寻找的只能使用动态查询来实现。

答案 3 :(得分:0)

你有两个选择,一开始就想到我: