【问题标题】:Get 3 tables in SQL Server 2008, avoid using two loops and Dynamic SQL在 SQL Server 2008 中获取 3 个表,避免使用两个循环和动态 SQL
【发布时间】:2011-05-06 02:06:50
【问题描述】:

我想从SQL 2008 中的一个名为@X_Table 的表中获取3 tables(该表的列名称为original table):

原始表有很多行和 5 个属性(我可以有更多的属性,比如 30,但在示例中我有 5 个属性):

x1  x2   x3   x4   x5    
----------------------------
438 498  3625 3645 5000
438 498  3625 3648 5000
438 498  3625 3629 5000
437 501  3625 3626 5000
438 498  3626 3629 5000
439 498  3626 3629 5000
440 5000 3627 3628 5000
444 5021 3631 3634 5000
451 5025 3635 3639 5000
458 5022 3640 3644 5000
465 525  3646 3670 5000
473 533  3652 3676 5000
481 544  3658 3678 5000
484 544  3661 3665 5000
484 532  3669 3662 2945
482 520  3685 3664 2952
481 522  3682 3661 2955
480 525  3694 3664 2948
481 515  5018 3664 2956
479 5000 3696 3661 2953 
**...(EVEN MORE ROWS LIKE 100,000)** ...

首先我在其他表中获取列名(the var @table_name 指上表):

INSERT  @X_Table (ID,NAME) 
SELECT  [ID] = ORDINAL_POSITION, [NAME] = COLUMN_NAME 
        FROM INFORMATION_SCHEMA.COLUMNS 
        WHERE TABLE_NAME = @table_name

所以我得到了@X_Table:

@X_Table:

ID  NAME
------------
1   x1
2   x2
3   x3
4   x4
5   x5

然后for each column 我想要sum of all values in that column 所以 xi 中的所有值,所以 sum(x1), sum(x2), sum(x3), sum(x4), sum(x5)`

表 1:INT、VARCHAR、FLOAT

ID Name  value
---------------------------
1   x1  48431
2   x2  138420
3   x3  192071
4   x4  192041
5   x5  204602

然后for each column 我想要sum of all values in that column multiplied by each column 例如
对于第 x1 列:sum(x1*x1), sum(x1*x2), sum(x1*x3), sum(x1*x4), sum(x1*x5)
对于第 x2 列:sum(x2*x2), sum(x2*x3), sum(x2*x4), sum(x2*x5)
对于第 x3 列:sum(x3*x3), sum(x3*x4), sum(x3*x5)
对于第 x4 列:sum(x4*x4), sum(x4*x5)
对于第 x5 列:sum(x5*x5)

表 2:INT、VARCHAR、FLOAT

ID   Name     value
---------------------------
1   SUM(x1*x1)  83926421
2   SUM(x1*x2)  162997894
3   SUM(x1*x3)  186865166
4   SUM(x1*x4)  156224385
5   SUM(x1*x5)  168573557
6   SUM(x2*x2)  598228836
7   SUM(x2*x3)  542331010
8   SUM(x2*x4)  460990820
9   SUM(x2*x5)  512335365
10  SUM(x3*x3)  797430261
11  SUM(x3*x4)  659040569
12  SUM(x3*x5)  723779398
13  SUM(x4*x4)  677091478
14  SUM(x4*x5)  722922237
15  SUM(x5*x5)  808976398

那么对于最后一张表,我想要对角线,或者上表的其他 xii

表 3:INT、VARCHAR、FLOAT

ID   Name     value
---------------------------
1   SUM(x1*x1)  83926421
2   SUM(x2*x2)  598228836
3   SUM(x3*x3)  797430261
4   SUM(x4*x4)  677091478
5   SUM(x5*x5)  808976398

所以为此我正在做这种方法,但我知道这是可以优化的,

SET @d = 5
WHILE (@counterI <= @d) BEGIN
        SELECT @nameThird   = NAME  FROM @X_Table where ID =  @counterI;
        SET    @nameFirst   = @nameThird;

    --INSERT FIRST TABLE
    SET @queryFirst = 'INSERT #FIRST_T (ID,NAME,Value) SELECT '+ CAST(@counterI AS VARCHAR)+' , '''+@nameFirst+''', SUM('+@nameFirst+') FROM '+ @table_name;
    EXEC (@queryFirst);

    --GET VALUE TO INSERT IN THIRD TABLE       
    SET @queryThird = 'INSERT #THIRD_T (ID,NAME,Value) SELECT '+CAST(@counterI AS VARCHAR)+' , '''+@nameThird+'*'+@nameThird+''', SUM('+@nameThird +'*'+@nameThird+') FROM '+  @table_name;
    EXEC (@queryThird);

    --Xij  
    WHILE (@counterJ <= @d) BEGIN           
        SELECT @nameThird2  = NAME  FROM @X_Table where ID =  @counterJ;
        SET @queryThird = 'INSERT #SECOND_T (ID,NAME,Value) SELECT '+CAST(@n AS VARCHAR)+' , '''+@nameThird+'*'+@nameThird2+''', SUM('+@nameThird +'*'+@nameThird2+') FROM '+  @table_name;
        EXEC (@queryThird);         
        SET @counterJ  = @counterJ + 1; 
        SET @n = @n +1  
    END         
    SET @counterI   = @counterI + 1; --reduce space
    SET @counterJ   = @counterI;
END
  • 如果没有这 2 个,您将如何做到这一点 循环?,

(属性超过30个的表需要很多时间...)

--------------编辑-------------- --------------

当我为@table1 获得超过 10 列时(@Thomas 的回答)

ID 名称值


1   x1  8029145
2   x10 15453498
3   x11 13909514
4   x12 11336348
5   x13 11598240
6   x14 11951291
7   x15 12034693
8   x16 6558719
9   x17 5400520
10  x18 4966450
11  x19 5773049
12  x2  12696346
13  x20 5872404
14  x21 5542875
15  x22 9700954
16  x23 8484327
17  x24 8612340
18  x25 129470
19  x3  135818770 

有没有办法按名称对它们进行排序

x1,x2,x3,x4,x5...,x9,x10,x11.... 

而不是

x1,x10,x11,x19,x2,x3....?

所以我们得到

ID Name Value
 -----------------
    1   x1  8029145
    2   x2  12696346
    3   x3  135818770
    ...

【问题讨论】:

    标签: sql sql-server-2008 stored-procedures loops


    【解决方案1】:

    (扩展源数据以说明序数的使用)

    如果你对结构进行规范化,这个问题会容易得多。由于您尝试对列进行算术运算,因此它们需要是行而不是列。

    Declare @X_Table Table
        (
        Id int not null Identity(1,1) Primary Key
        , x1 float not null
        , x2 float not null
        , x3 float not null
        , x4 float not null
        , x5 float not null
        , x111 float not null
        , x112 float not null
        , x113 float not null
        , x114 float not null
        , x115 float not null
        , x11 float not null
        , x12 float not null
        , x13 float not null
        , x14 float not null
        , x15 float not null
        )
    
    Insert @X_Table( x1, x2, x3, x4, x5
        , x111, x112, x113, x114, x115 
        , x11, x12, x13, x14, x15 
        )
    Select 438, 498, 3625, 3645, 5000, 438, 498, 3625, 3645, 5000, 438, 498, 3625, 3645, 5000
    Union All Select 438, 498, 3625, 3648, 5000, 438, 498, 3625, 3648, 5000, 438, 498, 3625, 3648, 5000
    Union All Select 438, 498, 3625, 3629, 5000, 438, 498, 3625, 3629, 5000, 438, 498, 3625, 3629, 5000
    Union All Select 437, 501, 3625, 3626, 5000, 437, 501, 3625, 3626, 5000, 437, 501, 3625, 3626, 5000
    Union All Select 438, 498, 3626, 3629, 5000, 438, 498, 3626, 3629, 5000, 438, 498, 3626, 3629, 5000
    Union All Select 439, 498, 3626, 3629, 5000, 439, 498, 3626, 3629, 5000, 439, 498, 3626, 3629, 5000
    Union All Select 440, 5000, 3627, 3628, 5000, 440, 5000, 3627, 3628, 5000, 440, 5000, 3627, 3628, 5000
    Union All Select 444, 5021, 3631, 3634, 5000, 444, 5021, 3631, 3634, 5000, 444, 5021, 3631, 3634, 5000
    Union All Select 451, 5025, 3635, 3639, 5000, 451, 5025, 3635, 3639, 5000, 451, 5025, 3635, 3639, 5000
    Union All Select 458, 5022, 3640, 3644, 5000, 458, 5022, 3640, 3644, 5000, 458, 5022, 3640, 3644, 5000
    Union All Select 465, 525, 3646, 3670, 5000, 465, 525, 3646, 3670, 5000, 465, 525, 3646, 3670, 5000
    Union All Select 473, 533, 3652, 3676, 5000, 473, 533, 3652, 3676, 5000, 473, 533, 3652, 3676, 5000
    Union All Select 481, 544, 3658, 3678, 5000, 481, 544, 3658, 3678, 5000, 481, 544, 3658, 3678, 5000
    Union All Select 484, 544, 3661, 3665, 5000, 484, 544, 3661, 3665, 5000, 484, 544, 3661, 3665, 5000
    Union All Select 484, 532, 3669, 3662, 2945, 484, 532, 3669, 3662, 2945, 484, 532, 3669, 3662, 2945
    Union All Select 482, 520, 3685, 3664, 2952, 482, 520, 3685, 3664, 2952, 482, 520, 3685, 3664, 2952
    Union All Select 481, 522, 3682, 3661, 2955, 481, 522, 3682, 3661, 2955, 481, 522, 3682, 3661, 2955
    Union All Select 480, 525, 3694, 3664, 2948, 480, 525, 3694, 3664, 2948, 480, 525, 3694, 3664, 2948
    Union All Select 481, 515, 5018, 3664, 2956, 481, 515, 5018, 3664, 2956, 481, 515, 5018, 3664, 2956
    Union All Select 479, 5000, 3696, 3661, 2953, 479, 5000, 3696, 3661, 2953, 479, 5000, 3696, 3661, 2953
    

    下表显然是静态的。但是,如果您要涉及动态 SQL(在 T-SQL 中不推荐,但显然可行),则下一个表的填充将是唯一的部分。

    Declare @X_Table_Normalized Table
        (
        Id int not null
        , Ordinal int not null
        , Name varchar(10) not null
        , Value float not null
        )
    
    Insert @X_Table_Normalized( Id, Ordinal, Name, Value )
    Select Id, 1, 'x1', x1 From @X_Table
    Union All Select Id, 2, 'x2', x2 From @X_Table
    Union All Select Id, 3, 'x3', x3 From @X_Table
    Union All Select Id, 4, 'x4', x4 From @X_Table
    Union All Select Id, 5, 'x5', x5 From @X_Table
    Union All Select Id, 6, 'x111', x111 From @X_Table
    Union All Select Id, 7, 'x112', x112 From @X_Table
    Union All Select Id, 8, 'x113', x113 From @X_Table
    Union All Select Id, 9, 'x114', x114 From @X_Table
    Union All Select Id, 10, 'x115', x115 From @X_Table
    Union All Select Id, 11, 'x11', x11 From @X_Table
    Union All Select Id, 12, 'x12', x12 From @X_Table
    Union All Select Id, 13, 'x13', x13 From @X_Table
    Union All Select Id, 14, 'x14', x14 From @X_Table
    Union All Select Id, 15, 'x15', x15 From @X_Table
    

    表 1 - 按(原始)列求和

    Declare @Table1 Table
        (
        Id int not null
        , Name varchar(25) not null
        , Ordinal int not null
        , Value float not null
        )
    
    Insert @Table1( Id, Name, Ordinal, Value )
    Select Row_Number() Over( Order By Ordinal, Name )
        , Name, Ordinal, Sum(Value)
    From @X_Table_Normalized
    Group By Ordinal, Name
    

    表 2 - 按(原始)列的产品总和

    Declare @Table2 Table
        (
        Id int not null
        , Name varchar(25) not null
        , Ordinal int not null
        , Value float not null
        )
    
    Insert @Table2( Id, Name, Ordinal, Value )
    Select Row_Number() Over ( Order By T1.Ordinal, T1.Name, T2.Ordinal, T2.Name ) As Id
        , 'Sum(' + T1.Name + '*' + T2.Name + ')' As Name
        , T1.Ordinal + 100 * T2.Ordinal
        , Sum( T1.Value * T2.Value ) As Value
    From @X_Table_Normalized As T1
        Join @X_Table_Normalized As T2
            On T2.Id = T1.Id
    Where T1.Ordinal <= T2.Ordinal
    Group By T1.Name, T1.Ordinal, T2.Name, T2.Ordinal
    

    表 3 - (原始)列对角线的乘积总和

    Declare @Table3 Table
        (
        Id int not null
        , Name varchar(25) not null
        , Ordinal int not null
        , Value float not null
        )
    
    Insert @Table3( Id, Name, Ordinal, Value )
    Select Row_Number() Over ( Order By T1.Ordinal, T1.Name, T2.Ordinal, T2.Name ) As Id
        , 'Sum(' + T1.Name + '*' + T2.Name + ')' As Name
        , T1.Ordinal + 100 * T2.Ordinal
        , Sum( T1.Value * T2.Value ) As Value
    From @X_Table_Normalized As T1
        Join @X_Table_Normalized As T2
            On T2.Id = T1.Id
    Where T1.Ordinal = T2.Ordinal
    Group By T1.Name, T1.Ordinal, T2.Name, T2.Ordinal
    
    Select * From @Table1 Order By Ordinal
    Select * From @Table2 Order By Ordinal
    Select * From @Table3 Order By Ordinal
    

    使用动态 SQL 填充规范化表

    根据其使用方式,您可以使用动态 SQL 封装规范化表的填充。如果这样做,您将需要使用真实表或临时表,而不是临时变量。该代码可能类似于:

    Declare @Sql nvarchar(max)
    
    Set @Sql = 'Insert #X_Table_Normalized_Dynamic( Id, Ordinal, Name, Value )|'
    Select @Sql = @Sql + ' Union All Select Id'
        + ', ' + Cast( Row_Number() Over ( Order By ORDINAL_POSITION ) As varchar(10) )
        + ', ' + QuoteName(COLUMN_NAME, '''')
        + ', ' + QuoteName(COLUMN_NAME)
    From INFORMATION_SCHEMA.COLUMNS
    Where TABLE_NAME = 'MySourceTable'
    
    Set @Sql = Replace( @Sql, '| Union All ', ' ')
    
    Exec( @Sql )
    

    【讨论】:

    • 请问,如果我需要在更大的表中进行操作(而不是 5 列,超过 30 列),您建议使用Dynamic SQL 吗?那个动态版本会是什么样子?
    • @darkcminor - 只需要通过动态 SQL 填充一个表(在我的示例中为@X_Table_Normalized,但您当然需要使用临时表而不是临时变量)。是否使用动态 SQL 在很大程度上取决于使用该例程的方式。如果一个晚上在一个批次的自动化工作中进行一次,很好。
    • @darkcminor - 我附加了一些额外的代码来展示动态 SQL 的样子。
    • @Thomas 当有超过 10 列时我得到1 x1 8029145 2 x10 15453498 3 x11 13909514 4 x12 11336348 5 x13 11598240 有没有办法按Name x1,x2,x3,x4,x5...,x9,x10,x11....而不是x1,x10,x11,x19,x2,x3....? /跨度>
    • @darkminor - 当然。请改用01x02x 等。但是,我认为x1 是一个列名,而不是实际使用的东西。另一种解决方案是将列的序号添加到分组和排序中。
    【解决方案2】:

    我在这里采用另一种方法,但您为什么不直接创建一个视图并从中派生其他表。

    优势

    • 速度更快,因为一切仍然是基于设置的。
    • 更具可读性

    动态创建视图

    DECLARE @Statement VARCHAR(MAX) = 'CREATE VIEW VIEW_OriginalTable (ID, Name, Value) AS (' + CHAR(13) + CHAR(10)
    SELECT  @Statement =  @Statement 
                          + 'UNION ALL SELECT ' 
                          + CAST(ORDINAL_POSITION AS VARCHAR(32)) 
                          + ', ''' 
                          + COLUMN_NAME
                          + ''', SUM('
                          + COLUMN_NAME
                          + ') FROM OriginalTable'
                          + CHAR(13) + CHAR(10)
    FROM    INFORMATION_SCHEMA.COLUMNS 
    WHERE   TABLE_NAME = 'OriginalTable'
    SELECT @Statement = REPLACE(@Statement, 'UNION ALL SELECT 1,', 'SELECT 1,') + ')'
    EXEC (@Statement)
    GO        
    

    三个结果表

    /*
      Table1
    */    
    SELECT  *
    FROM    VIEW_OriginalTable
    
    /*
      Table2
    */    
    SELECT  [ID] = ROW_NUMBER() OVER (ORDER BY t1.ID)
            , [Name] = t1.Name + '*' + t2.Name
            , [Value] = t1.Value * t2.Value
    FROM    VIEW_OriginalTable t1
            INNER JOIN VIEW_OriginalTable t2 ON t2.ID >= t1.ID
    
    /*
      Table3
    */    
    SELECT  [ID] = t1.ID
            , [Name] = t1.Name + '*' + t1.Name
            , [Value] = t1.Value * t1.Value
    FROM    VIEW_OriginalTable t1        
    

    【讨论】:

    • 另外,你会如何处理UNION ALL SELECT 1, 'x1', SUM(x1) FROM OriginalTable, x2,x3,x4,...,xn,因为我可以有一个包含很多属性的表格?
    • @darkcminor - 正如我所说,这是缺点。它不是动态的,但您应该问自己一列被添加到原始表中多少次?视图本身很容易扩展和维护。选择不需要调整。
    • @Lieven:是的,我删除了那条评论,但我想要的是一种更通用的方法......对于任意数量的列......
    • 可能使用循环 while,并使用 var 作为 @template,就像我原来的方法一样
    • @darkcminor - 我在解决方案中添加了一个动态创建的视图。
    猜你喜欢
    • 2015-03-05
    • 2013-08-29
    • 1970-01-01
    • 2018-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-07
    • 2018-10-28
    相关资源
    最近更新 更多