如何创建一个返回逗号分隔值列表的函数?

发布于 2024-10-28 01:42:09 字数 618 浏览 3 评论 0原文

我正在尝试创建一个函数,该函数根据表名和列名参数返回逗号分隔的值列表。

这个想法是动态构建动态 SQL 语句,然后返回结果。但是,我遇到了一个问题,它不允许我在函数内部运行 execute 语句。这就是我的情况:

ALTER FUNCTION fn_HearingAttendeesToCSV (@TableName varchar(100), 
                                         @ColumnName varchar(100))
RETURNS VARCHAR(MAX)
AS
BEGIN
declare @sql varchar(max)

select @sql = 
'DECLARE @listStr VARCHAR(MAX)
    SELECT @listStr = COALESCE(@listStr + '', '' ,'''') + ' 
          + @ColumnName + ' FROM ' + @TableName + ' RETURN @listStr '

Exec (@sql)
END
GO

我很确定我走错了路。有人可以建议如何解决 execute 问题吗?或者有更好的方法来做到这一点?

I am trying to create a function that returns a comma separated list of values based on a table name and column name parameters.

The idea was to build a dynamic SQL statement on the fly and then return the result. However, I ran into a problem where it won't let me run the execute statement inside of a function. Here is what I had:

ALTER FUNCTION fn_HearingAttendeesToCSV (@TableName varchar(100), 
                                         @ColumnName varchar(100))
RETURNS VARCHAR(MAX)
AS
BEGIN
declare @sql varchar(max)

select @sql = 
'DECLARE @listStr VARCHAR(MAX)
    SELECT @listStr = COALESCE(@listStr + '', '' ,'''') + ' 
          + @ColumnName + ' FROM ' + @TableName + ' RETURN @listStr '

Exec (@sql)
END
GO

I am pretty sure that I am going down the wrong road. Can someone suggest how to get around the execute issue? Or a better way of doing this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烦人精 2024-11-04 01:42:10

您不能在函数期间执行此操作。函数无法执行动态 SQL,因此使用任意表/列名称(非确定性)与 SQL Server 函数背道而驰。

You cannot do this in a function - period. Functions cannot execute dynamic SQL, so using an arbitrary table/column name (non-deterministic) flies in the face of SQL Server functions.

纵性 2024-11-04 01:42:10

我认为您无法通过 T-SQL 来完成此操作,但您绝对可以通过用 C# 编写的 UDF 来完成同样的事情。代码如下:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions {
   [Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read)]
   public static SqlString fn_HearingAttendeesToCSV(SqlString tableName, SqlString columnName) {
      System.Text.StringBuilder result = null;
      using (var cnn = new SqlConnection("context connection=true")) {
         var cmd = new SqlCommand();
         cmd.CommandText = String.Format("select [{0}] from [{1}]", SqlEscape(columnName.Value), SqlEscape(tableName.Value));
         cmd.Connection = cnn;

         cnn.Open();
         using (var rdr = cmd.ExecuteReader()) {
            while (rdr.Read()) {
               if (result == null) {
                  result = new System.Text.StringBuilder();
               }
               else {
                  result.Append(",");
               }
               result.Append(CsvEscape(rdr[0]));
            }
         }
         cnn.Close();
      }

      if (result == null) {
         return SqlString.Null;
      }
      else {
         return result.ToString();
      }
   }

   private static string SqlEscape(string s) {
      s = s ?? "";
      return s.Replace("[", "[[").Replace("]", "]]");
   }

   private static string CsvEscape(object o) {
      var s = o == null ? "" : o.ToString();
      return "\"" + s.Replace("\"", "\"\"") + "\"";
   }
};

为了使用 CLR UDF,您需要确保已在服务器上启用它们,如下所示:

exec sp_configure 'clr enabled', 1
RECONFIGURE

您还需要编译为旧版本的 .NET 框架(我测试的是 2.0): SQL 2005 不允许 4.0 程序集。

即使您的 UDF 是用 C# 编写的,您仍然可以按照习惯的方式通过 T-SQL 访问它:

select fn_HearingAttendeesToCSV('table', 'column')

如果您需要支持“dbo”以外的架构,或者不需要纯 CSV,请修改为满足您的需求,但这应该可以让您实现 99% 的目标。也就是说,假设您是 DBA 或者您与他/她是足够好的朋友来启用 CLR。

I don't think you can do this via T-SQL, BUT you absolutely can accomplish the same thing via a UDF written in C#. Here's the code:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions {
   [Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read)]
   public static SqlString fn_HearingAttendeesToCSV(SqlString tableName, SqlString columnName) {
      System.Text.StringBuilder result = null;
      using (var cnn = new SqlConnection("context connection=true")) {
         var cmd = new SqlCommand();
         cmd.CommandText = String.Format("select [{0}] from [{1}]", SqlEscape(columnName.Value), SqlEscape(tableName.Value));
         cmd.Connection = cnn;

         cnn.Open();
         using (var rdr = cmd.ExecuteReader()) {
            while (rdr.Read()) {
               if (result == null) {
                  result = new System.Text.StringBuilder();
               }
               else {
                  result.Append(",");
               }
               result.Append(CsvEscape(rdr[0]));
            }
         }
         cnn.Close();
      }

      if (result == null) {
         return SqlString.Null;
      }
      else {
         return result.ToString();
      }
   }

   private static string SqlEscape(string s) {
      s = s ?? "";
      return s.Replace("[", "[[").Replace("]", "]]");
   }

   private static string CsvEscape(object o) {
      var s = o == null ? "" : o.ToString();
      return "\"" + s.Replace("\"", "\"\"") + "\"";
   }
};

In order to use CLR UDFs, you need to be sure you've enabled them on the server like this:

exec sp_configure 'clr enabled', 1
RECONFIGURE

You also need to compile to an older version of the .NET framework (2.0 is what I tested with) as SQL 2005 doesn't allow 4.0 assemblies.

Even though your UDF is written in C#, you still access it via T-SQL the same way you're used to:

select fn_HearingAttendeesToCSV('table', 'column')

If you need to support schemas other than 'dbo', or if you don't need pure CSV, modify to fit your needs but this should get you 99% of the way there. That is, assuming you're the DBA or you're good enough buddies with him/her to get CLR enabled.

依 靠 2024-11-04 01:42:10

您好,我认为他们关于函数的说法是正确的,但是您可以使用带有输出参数的 SP 来执行此操作。

检查此代码:

create proc DynCsv 
    @table  varchar(100),
    @column varchar(100),
    @csv    varchar(8000) output
as 
    declare
            @sql    nvarchar(4000),
            @parms  nvarchar(100)

    -- setup parms 
    select  @csv = '',          
            @sql = '
    select top 10 @csv = @csv +'','' + CAST(' + @column + ' as varchar)
    from ' + @table + ' option(maxdop 1)',
            @parms = '@csv varchar(8000) output'

    -- execute dynamic
    exec sp_executesql 
            @sql,
            @parms,
            @csv = @csv output

    -- loose first comma    
    set @csv = substring(@csv, 2, 8000)

您可以使用此代码调用它:

declare @csv varchar(8000)
exec dynCsv 'tObj', 'ObjId', @csv output
print @csv

祝你好运,

GJ

Hi I think they're right about functions, but you can use an SP with an output parameter to do this.

Check this code:

create proc DynCsv 
    @table  varchar(100),
    @column varchar(100),
    @csv    varchar(8000) output
as 
    declare
            @sql    nvarchar(4000),
            @parms  nvarchar(100)

    -- setup parms 
    select  @csv = '',          
            @sql = '
    select top 10 @csv = @csv +'','' + CAST(' + @column + ' as varchar)
    from ' + @table + ' option(maxdop 1)',
            @parms = '@csv varchar(8000) output'

    -- execute dynamic
    exec sp_executesql 
            @sql,
            @parms,
            @csv = @csv output

    -- loose first comma    
    set @csv = substring(@csv, 2, 8000)

You can call it with this code:

declare @csv varchar(8000)
exec dynCsv 'tObj', 'ObjId', @csv output
print @csv

Good luck,

GJ

爱*していゐ 2024-11-04 01:42:10

您不能在函数中执行此操作,但可以在 PROCEDURE 中执行此操作。我已经这样做过很多次来构建动态 SQL 语句。

这就是要走的路。

You can't do this in a function but you can do it in a PROCEDURE. I've done that many times to build a dynamic SQL statement.

That's the way to go.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文