选择在变量中提供列名称的​​列的最大值

时间:2016-12-18 22:17:31

标签: sql sql-server

我试图将一些非常往返的重C#代码转换为实现表重新种子化的单个SQL脚本块。这是非生产代码,它基本上清除表中的测试数据并将它们作为测试套件的一部分重新安装。

当我尝试选择列名保存在变量中的标识列的最大值时出现问题(' @ IdentityColumnName'在下面的脚本中)。

执行此脚本时,我在包含MAX的行(@IdentityColumnName)上收到以下错误...

  

将数据类型varchar转换为bigint时出错。

...我假设正在发生这种情况,因为SQL假定我要求变量的MAX而不是将其视为列的名称。

-- Assume that some (or all) existing values have been removed from the table prior to this...
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '__Foo' AND COLUMNPROPERTY(OBJECT_ID(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1)
BEGIN
    DECLARE @IdentityColumnName AS VARCHAR(MAX)
    DECLARE @IdentityColumnValue AS BIGINT

    SELECT TOP 1 @IdentityColumnName = [COLUMN_NAME] FROM [INFORMATION_SCHEMA].[COLUMNS] WHERE [TABLE_NAME] = '__Foo'
    SELECT @IdentityColumnValue = ISNULL(MAX(@IdentityColumnName), 0) FROM [__Foo]

    -- Reseed the table with either 0 (if it's empty) or the maximum value already in the table
    DBCC CHECKIDENT ('__Foo', RESEED, @IdentityColumnValue)
END

有没有人知道是否可以让SQL将该变量视为列的名称,以便我的查询有效?我已经尝试构建一个字符串并使用EXEC执行它,但这导致了捕获EXEC结果的严峻考验,我想确认没有更简单的方法来实现这一点。

  • 如果这会产生影响,代码将需要对SQL Server 2008 R2起作用。
  • 这只是非生产测试的东西;它必须坚持任何最佳实践。
  • 通常是DELETE FROM,在重新种植之前立即执行可选的WHERE xyz。有时桌子会空着,有时候桌子里面还有几排。

3 个答案:

答案 0 :(得分:0)

动态SQL代码段示例:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'
DECLARE @IdentityColumnValue AS BIGINT;
SELECT @IdentityColumnValue = ISNULL(MAX(' + QUOTENAME(@IdentityColumnName) + N'), 0) FROM [__Foo];
DBCC CHECKIDENT (N''__Foo'', RESEED, @IdentityColumnValue);';
EXEC (@SQL);

答案 1 :(得分:0)

首先,您需要动态SQL。但是,您的转换问题发生在字符列上。你可以做这样的事情:

DECLARE @SQL NVARCHAR(MAX);

SET @SQL = N'SELECT @IdentityColumnValueStr := CAST(MAX(@IdentityColumnName) as NVARCHAR(MAX)) FROM [__Foo]';
SET @SQL = REPLACE(@SQL, '@IdentityColumnName', QUOTENAME(@IdentityColumnName));

DECLARE @IdentifyColumnValue NVARCHAR(MAX);

EXEC sp_executesql @SQL, N'@IdentityColumnValueStr NVARCHAR(MAX)', @IdentityColumnValueStr = @IdentityColumnValueStr;

PRINT @IdentityColumnValueStr;

如果您确实认为该值是数字,那么您可以非常小心地将值转换回数字。

答案 2 :(得分:0)

正如其他答案中所提到的,动态SQL是必需的,您不能让MAX将变量视为列名。我最终得到的解决方案是Dan和Gordon提供的解决方案的组合,并通过一些调整来实现我想要的目标。

IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '__Foo' AND COLUMNPROPERTY(OBJECT_ID(TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1)
BEGIN
    DECLARE @SQL AS NVARCHAR(MAX), @IdentityColumnName AS NVARCHAR(MAX), @IdentityColumnValue AS BIGINT

    -- Select the name of the first column into @IdentityColumnName (and just assume it's the identity column)
    SELECT TOP 1 @IdentityColumnName = [COLUMN_NAME] FROM [INFORMATION_SCHEMA].[COLUMNS] WHERE [TABLE_NAME] = '__Foo'
    -- Create a dynamic SQL statement to determine the maximum value in that column (or 0 if there are no rows and MAX returns NULL)
    SELECT @SQL = 'SELECT @IdentityColumnValue = ISNULL(MAX(' + QUOTENAME(@IdentityColumnName) + '), 0) FROM [__Foo]'
    -- Execute the dynamic SQL and put the result into the @IdentityColumnValue
    EXEC sp_executesql @SQL, N'@IdentityColumnValue BIGINT OUTPUT', @IdentityColumnValue OUTPUT

    -- Reseed the table using the value determined above
    DBCC CHECKIDENT ('__Foo', RESEED, @IdentityColumnValue)
END