【问题标题】:SQL select row into a string variable without knowing columnsSQL在不知道列的情况下将行选择为字符串变量
【发布时间】:2011-01-20 16:29:27
【问题描述】:

我是编写 SQL 的新手,非常感谢有关此问题的帮助。 :)

我正在尝试将整行选择成一个字符串,最好用空格或逗号分隔。我想以一种通用的方式完成此操作,而不必了解表中列的详细信息。

我想做的是:

DECLARE @MyStringVar NVARCHAR(MAX) = ''
@MyStringVar = SELECT * FROM MyTable WHERE ID = @ID AS STRING

但我最终做的是:

DECLARE @MyStringVar = ''
DECLARE @SecificField1 INT
DECLARE @SpecificField2 NVARCHAR(255)
DECLARE @SpecificField3 NVARCHAR(1000)
...
SELECT @SpecificField1 = Field1, @SpecificField2 = Field2, @SpecificField3 = Field3 FROM MyTable WHERE ID = @ID
SELECT @StringBuilder = @StringBuilder + CONVERT(nvarchar(10), @Field1) + ' ' +  @Field2 + ' ' + @Field3

哎呀。 :(

我看到一些人发布了关于 COALESCE 函数的内容,但同样,我还没有看到有人在没有特定列名的情况下使用它。

另外,我在想,也许有一种方法可以通过以下方式动态使用列名:

SELECT [COLUMN_NAME] FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'MyTable' 

这看起来真的不应该这么复杂。 :(

我所做的工作目前有效,但提前感谢任何可以为我指出更好解决方案的人。 :)

编辑: 已解决,感谢所有回答的人。 :)

【问题讨论】:

  • 您能解释一下您的最终目标是什么吗?如果我们能更好地理解这一点,我们或许可以提出更好的解决方案。
  • 我也很好奇这样一个通用例程的目的。我不会在生产代码中使用任何这样的解决方案。
  • 我的总体目标是跟踪数据库中的删除(通过触发器)并将更改写入日志文件。我想要字符串,因为我试图将一行作为参数传递给一个 DLL 函数(用 C# 编写,将行作为字符串),该函数将删除的数据写入日志文件。我想要一个通用的方法,因为我可能必须为数据库中的每个表编写这个触发器。不幸的是,msdn 页面说 DDL 触发器不跟踪行删除,所以看来我必须一遍又一遍地编写这个触发器。如果是这种情况,我至少想动态调整对表格所做的任何更改。
  • @Brandi,我会手动编写触发器以提高速度(根据需要和数据类型为每个触发器编写准确的列和必要的格式),为什么要增加查找的开销每次触发触发时的列?您可以轻松修改我的答案中的查询,该查询使用 INFORMATION_SCHEMA.COLUMNS,为每个表生成 INSERT 代码。
  • 首先,我假设您要跟踪 DML 而不是 DDL。上述解决方案不适用于 DDL。 SQL Server (2k,2k5,2k8) 触发器肯定会在删除时触发。在上述解决方案中,如果列顺序发生变化,您的输出将出现乱码。坦率地说,我认为编写一个脚本来编写触发器(或使用 3rd 方程序,或 2k8 的更改跟踪)比使用这种方法更好,但我不知道你问题的所有细节。

标签: sql sql-server tsql string dynamic


【解决方案1】:

试一试:

DECLARE @SQL nvarchar(MAX), @YourTable nvarchar(200)
SET @YourTable='YourTableName'
SELECT @SQL=
    STUFF(
             (
                  SELECT
                  ' + '','' + COALESCE(''''''''+CONVERT(NVARCHAR(MAX),['+c.COLUMN_NAME+']'+CASE WHEN c.DATA_TYPE='datetime' THEN ',121' ELSE '' END+')+'''''''',''null'')'
                      FROM INFORMATION_SCHEMA.COLUMNS c
                      WHERE c.TABLE_NAME = 'ap_voucher'
                      ORDER BY ORDINAL_POSITION
                      FOR XML PATH('')
             ), 1, 9, ''
         )
SET @SQL = 'SELECT ' + @SQL + ' FROM '+@YourTable
exec (@SQL)

样本输出:

---------------------------------------------------------------------------
'030',null,'I','Zzz0',null,'1000021111          ','2000-03-01 00:00:00.000'
'001',null,'I','zzz0',null,'900099618           ','1999-12-03 00:00:00.000'
'001',null,'I','ET10',null,'KOU557              ','1999-11-01 00:00:00.000'
'001',null,'I','Mzzz',null,'017288              ','1999-11-12 00:00:00.000'

【讨论】:

  • 很好的答案 - 这是我一直在寻找的方法类型。是否可以添加条件来控制输出哪些行?理想情况下,我希望得到这个结果,但要添加条件“WHERE ID=@ID”。
  • @Brandi,只需发出最后的 SET 命令:SET @SQL = 'SELECT ' + @SQL + ' FROM '+@YourTable+' WHERE ID='+CONVERT(nvarchar(100),@ID),一切就绪。
【解决方案2】:

要动态做,只要知道表名,就可以用这种方法:

DECLARE @nSQL NVARCHAR(MAX)
SELECT @nSQL = COALESCE(
     @nSQL + ' + '','' + CAST(ISNULL([' + c.COLUMN_NAME + '], '''') AS NVARCHAR(MAX))', 
    'CAST([' + c.COLUMN_NAME + '] AS NVARCHAR(MAX))')
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_NAME = 'TestTable'

SET @nSQL = 'SELECT ' + @nSQL + ' FROM YourTable'

EXECUTE sp_executesql @nSQL

这取决于你的最终目标是什么。

这将处理列中的空值,但(例如)NULL 整数例如输出为 0。

【讨论】:

  • 您应该为生成的@nSQL 中的每个字段添加一个CONVERT 和一个COALESCE,这样数字和空列就不会破坏最终的连接:Msg 245, Level 16, State 1, Line 1 Conversion failed when converting the varchar value ',' to data type int.
  • @KM - 干杯,好球!我打算处理整数/空值等,但我将 CAST 放在 COLUMN_NAME 周围而不是在要执行的字符串内!更正了 SQL。
  • 查看我的答案,我使用了不同的列连接技术,它可以防止列值的重复格式。我还引用值、格式化日期时间并显示 NULL...
【解决方案3】:

简短的回答是你不能这样做。

你唯一能做的就是和你所拥有的一样。

SELECT @MyField1 = MyField1, @MyField2= MyField2...etc FROM MyTable

然后你可以连接这些字段:

SELECT @MyResult = (SELECT @MyField1 + @MyField2 + MyField3...)

SELECT @MyResult

【讨论】:

  • 为什么不从表中选择 MyField1 + MyField2 + MyField3?
  • @wwosik 我只使用了变量,因为 OP 可能在程序的后期需要这些变量,但这也是有效的。
  • 是的,不知道查询中的结果列是什么听起来有点像创建一个古怪的通用访问方法。或者更确切地说是 Debug.Print
【解决方案4】:

如果您可以处理 XML 格式的输出,这样的方法可能会起作用:

DECLARE @MyStringVar NVARCHAR(MAX) = ''
SET @MyStringVar = (SELECT * FROM MyTable WHERE ID = @ID FOR XML RAW)

SELECT @MyStringVar 

作为额外的奖励,如果您的数据适合它,您可以使用它去除 XML 格式,而不是 SELECT @MyStringVar:

SELECT REPLACE(REPLACE(REPLACE(REPLACE(@MyStringVar, '<row ', ''), '="', ' '), '" ', ', '), '"/>', '')

如果您的数据包含标签中使用的任何字符,当然您必须进行一些额外的转换,但这应该可以让您非常接近您想要的。


刚刚注意到,您实际上并不希望列名与数据一样显示。在这种情况下,其他答案中的 information_schema 查询之一将是获得所需内容的最佳方式。

【讨论】:

    【解决方案5】:

    如果目标是更改跟踪,那么我建议不要使用 KM 建议的串联。

    1. 列的顺序必须保持不变。如果列顺序发生轻微变化,则值的含义充其量将是模糊的,或者更有可能丢失。建议您永远不要依赖列顺序。

    2. 随着表变宽和/或包含大量数据,连接将变慢。 SQL Server 不以高效连接着称。此外,想象一个有几十个 nvarchar(max) 字段的表,每个字段有 2000 个字符。这将产生一个大约 48K 的字符串!

    3. 您可能需要更改分隔符。假设我的值等于“'Pain','Suffing' and 'Trouble'”。这种类型的条目会在您分析数据时产生问题。

    如果您使用的是 SQL Server 2008,我建议您使用 SQL Server 的更改跟踪机制。如果您不使用它,那么我建议您购买第三方审计工具。如果这也不是一个选项,那么我会使用触发器将删除的值写入另一个表。这至少会迫使您确保审计数据架构与源架构同步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      • 2018-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-12
      相关资源
      最近更新 更多