【问题标题】:SQL Server query to select all columns of a certain type and also show its max valuesSQL Server 查询以选择特定类型的所有列并显示其最大值
【发布时间】:2016-07-15 05:34:10
【问题描述】:

我使用的是 SQL Server 2012。

thread 已经回答了我查询的第一部分。但我还想要第二列,它会在对应的表中显示该列的对应最大值。

我已经尝试过这种方法:使用一个以表名和列名作为参数并返回最大值的函数。但是从函数中使用动态 SQL 是非法的。此外,我似乎无法从 SELECT 查询中调用函数。

我也尝试过使用存储过程,但我不知道如何调用和使用它。请提出实现此目的的替代方法。

我是 SQL Server 新手。

谢谢

【问题讨论】:

    标签: sql-server sql-server-2012 dynamic-sql sql-function


    【解决方案1】:

    我认为最简单的解决方案是存储过程。据我所知:

    • 动态 SQL 不能放在函数中
    • 动态 SQL 不能放在 OPENROWSET 中

    另外,如果你写这样的程序:

    • 注意名称中包含空格、引号(可能的 SQL 注入)
    • 非索引列上的 MAX(column) 需要完全扫描(可能非常慢)
    • 表名和列名可以重复(放置在不同的架构中)

    id重复和性能不是问题,看看下面的sn-p:

    CREATE PROC FindMaxColumnValues
        @type sysname  = '%',
        @table sysname = '%'
    AS
    DECLARE @result TABLE (TableName sysname, ColumnName sysname, MaxValue NVARCHAR(MAX))
    
    DECLARE @tab sysname
    DECLARE @col sysname
    DECLARE cur CURSOR FOR
      SELECT TABLE_NAME TableName, COLUMN_NAME [Column Name]
      FROM INFORMATION_SCHEMA.COLUMNS
      WHERE DATA_TYPE LIKE @type and TABLE_NAME LIKE @table
    
    OPEN cur
    
    FETCH NEXT FROM cur INTO @tab, @col
    WHILE @@FETCH_STATUS = 0
    BEGIN
        DECLARE @sql nvarchar(MAX) = 'SELECT '+QUOTENAME(@tab,'''')+' [TableName], '+QUOTENAME(@col, '''')+' [ColumnName], MAX('+QUOTENAME(@col)+') FROM '+QUOTENAME(@tab)
        INSERT INTO @result EXEC(@sql)
        FETCH NEXT FROM cur INTO @tab, @col
    END
    
    CLOSE cur
    DEALLOCATE cur
    
    SELECT * FROM @result
    

    样本:

    --MAX of INT's
    EXEC FindMaxColumnValues 'INT'
    --MAX of INT's in tables matching 'TestTab%'
    EXEC FindMaxColumnValues 'INT', 'TestTab%'
    --MAX of ALL columns
    EXEC FindMaxColumnValues
    

    结果:

    TableName   ColumnName   MaxValue
    IdNameTest  ID           2
    TestTable   ID           5
    TestTable   Number       3
    
    TableName   ColumnName   MaxValue
    TestTable   ID           5
    TestTable   Number       3
    
    TableName       ColumnName   MaxValue
    UpdateHistory   UpdateTime   2016-07-14 12:21:37.00
    IdNameTest      ID           2
    IdNameTest      Name         T2
    TestTable       ID           5
    TestTable       Name         F
    TestTable       Number       3
    

    【讨论】:

    • 把你在“另外”部分提到的3点写出来真的很有帮助。我拥有的数据库确实有相同的表名和列名,但在不同的模式中。有很多条目,所以我担心使用 MAX。注入始终是一个问题,但我设置中的所有命名法都没有空格或其他特殊字符。
    【解决方案2】:

    您可以使用以下 SP 并根据您的需要对其进行增强,

    CRETE PROCEDURE Getmaxtablecolval
    AS
      BEGIN
          CREATE TABLE #t
            (
               tablename  VARCHAR(50),
               columnname VARCHAR(50),
               id         INT,
               counts     INT
            )
    
          INSERT INTO #t
          SELECT table_name  [Table Name],
                 column_name [Column Name],
                 NULL,
                 NULL
          FROM   information_schema.columns
          WHERE  data_type = 'INT'
    
          BEGIN TRAN
    
          DECLARE @id INT
    
          SET @id = 0
    
          UPDATE #t
          SET    @id = id = @id + 1
    
          COMMIT TRAN
    
          DECLARE @RowCount INT
    
          SET @RowCount = (SELECT Count(0)
                           FROM   #t)
    
          DECLARE @I INT
    
          SET @I = 1
    
          DECLARE @Counter INT
          DECLARE @TName VARCHAR(50)
          DECLARE @CName VARCHAR(50)
          DECLARE @DynamicSQL AS VARCHAR(500)
    
          WHILE ( @I <= @RowCount )
            BEGIN
                SELECT @TName = tablename
                FROM   #t
                WHERE  id = @I
    
                SELECT @CName = columnname
                FROM   #t
                WHERE  id = @I
    
                SET @DynamicSQL = 'Update #T Set Counts = '
                                  + '(Select ISNull(Max(' + @CName + '), 0) From '
                                  + @TName + ') Where Id = '
                                  + CONVERT(VARCHAR(10), @I)
    
                --PRINT @DynamicSQL
                EXEC (@DynamicSQL)
    
                SET @I = @I + 1
            END
    
          SELECT *
          FROM   #t
      END
    
    go
    
    Getmaxtablecolval  
    

    【讨论】:

    • 好的!所以你使用的是临时表!这是一个不错的方法!但是您首先使用列名和表名创建表,然后运行循环来填充最大值。有没有办法一次性完成?还是没有临时表?
    • 嗯,我们需要一个(临时)表来创建动态查询,因为结果不属于单个表。由于我们需要循环,我们无法创建它一口气。如有更多疑问,请随时留言。
    【解决方案3】:

    您可以由此创建一个过程:

    CREATE PROCEDURE GET_COLUMNS_WITH_MAX_VALUE
    
        @COLUMN_TYPE NVARCHAR(50)
    AS
    BEGIN
        -- SET NOCOUNT ON added to prevent extra result sets from
        -- interfering with SELECT statements.
        SET NOCOUNT ON;
    
        -- DUMMY VARIABLE TO COPY STRUCTURE TO TEMP
            DECLARE @DUMMY TABLE
        (
            TABLE_NAME NVARCHAR(50),
            COLUMN_NAME NVARCHAR(50),
            MAX_VALUE NVARCHAR(MAX)
        )
    
        -- CREATE TEMP TABLE FOR DYNAMIC SQL
        SELECT TOP 0 * INTO #TABLE FROM @DUMMY
    
        INSERT INTO #TABLE
        (TABLE_NAME,  COLUMN_NAME)
        SELECT TABLE_NAME, COLUMN_NAME
        FROM information_schema.columns where data_type = @COLUMN_TYPE
    
        DECLARE @TABLE_NAME VARCHAR(50) -- database name  
        DECLARE @COLUMN_NAME VARCHAR(256) -- path for backup files  
    
        DECLARE db_cursor CURSOR FOR  
        SELECT TABLE_NAME, COLUMN_NAME 
        FROM #TABLE
    
        OPEN db_cursor   
        FETCH NEXT FROM db_cursor INTO @TABLE_NAME, @COLUMN_NAME 
    
        WHILE @@FETCH_STATUS = 0   
        BEGIN   
    
        DECLARE @SQL NVARCHAR(MAX) =  'UPDATE #TABLE SET MAX_VALUE = (SELECT MAX([' + @COLUMN_NAME + ']) FROM [' + @TABLE_NAME + ']) '
                                     + 'WHERE [COLUMN_NAME] = ''' + @COLUMN_NAME + ''' AND TABLE_NAME = ''' + @TABLE_NAME  + '''';
        PRINT @SQL
        EXEC (@SQL)
    
        FETCH NEXT FROM db_cursor INTO @TABLE_NAME, @COLUMN_NAME 
    
        END   
    
        CLOSE db_cursor   
        DEALLOCATE db_cursor
    
    
        SELECT * FROM #TABLE
    
        DROP TABLE #TABLE
    
    END
    GO
    

    用法:

    EXEC GET_COLUMNS_WITH_MAX_VALUE 'INT'
    

    结果:

    TABLE1      ID      50
    TABLE2      ID      100
    TABLE3      CarID       20  
    TABLE4      StudentID   30
    

    【讨论】:

    • 不错!与@Lucky 类似的方法。但我喜欢你使用光标的事实。此外,可以将数据类型作为参数传递给存储过程,这很甜蜜。 :) 再一次,可以一次性完成并且没有临时表吗?
    • @MercuryX 无法根据单个查询中的行值构造动态查询。 SELECT MAX(ColumName) 必须编译为有效的列名。不能基于另一列的值。
    • 是的,确实如此!我想我喜欢你的回答:)
    猜你喜欢
    • 2020-05-03
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-22
    • 2021-12-05
    • 2019-04-03
    相关资源
    最近更新 更多