具有动态列名的 SQL 存储过程

Ste*_*nis 2 sql-server stored-procedures

我正在尝试生成一个 SQL 语句,该语句根据过滤器从数据库动态获取列名。我们有一个大约有 50 列的表,每列上都有一个前缀来表示它适用于哪个“集合”。我已经创建了一个在 SQL Management Studio 中运行良好的查询,但由于我想在 .Net 应用程序以及 Web 应用程序中使用结果,因此如果有一个存储过程或类似的我可以调用的东西会很棒以获得结果。我知道我可以手动指定查询中的列,但我想尝试动态执行此操作,可能会添加列。我已经制定的查询如下,将其存储在我的 SQL 服务器中以便我可以根据需要使用它的最佳方式是什么?

DECLARE @ColumnList AS Varchar(MAX)
DECLARE @StartDate as Date
DECLARE @EndDate as Date
DECLARE @DepartmentID as Varchar(10)
DECLARE @ColumnFilter as Varchar(3)

SET @StartDate = '2015-01-01' 
SET @EndDate = '2015-05-01'
SET @DepartmentID = 'GMC'
SET @ColumnFilter = 'GM'

SELECT @ColumnList =  COALESCE(@ColumnList, ',') + c.name+',' FROM sys.columns c
WHERE c.object_id = OBJECT_ID('tblDetails') AND c.Name LIKE @ColumnFilter + '%'

SET @ColumnList = Left(@ColumnList,Len(@ColumnList)-1)

DECLARE @Template AS Varchar(max)
SET @Template = 'SELECT [RecordID]
      ,[DateRecord]
      ,[DepartmentID]
      ,[Shift]
      ,[ShiftLength]
      ,[ShiftType]
      ,[Active]
      ,[Comment]
      {ColumnList}
  FROM [Data_Warehouse].[dbo].[tblDetails]
  WHERE DateRecord >= ''{StartDate}'' AND DateRecord <= ''{EndDate}'' AND DepartmentID = ''{DepartmentID}''
  ORDER BY DateRecord'

SET @Template = REPLACE(@Template, '{ColumnList}', @ColumnList) 
SET @Template = REPLACE(@Template, '{StartDate}', @StartDate) 
SET @Template = REPLACE(@Template, '{EndDate}', @EndDate) 
SET @Template = REPLACE(@Template, '{DepartmentID}', @DepartmentID ) 

EXEC (@Template)
Run Code Online (Sandbox Code Playgroud)

Pet*_*ell 5

您可以将查询按原样包装在过程中。然后您可以从您的应用程序/网络执行它并获取数据表作为结果。当您将 DataTable 绑定到 DataGrid 时,它应该自动呈现 DataGrid 中的列

CREATE PROCEDURE GetDynamicReport

     @StartDate as Date
    ,@EndDate as Date
    ,@DepartmentID as Varchar(10)
    ,@ColumnFilter as Varchar(3)
AS
BEGIN
    DECLARE @ColumnList AS Varchar(MAX)

    SELECT @ColumnList =  COALESCE(@ColumnList, ',') + c.name+',' FROM sys.columns c
    WHERE c.object_id = OBJECT_ID('tblDetails') AND c.Name LIKE @ColumnFilter + '%'

    SET @ColumnList = Left(@ColumnList,Len(@ColumnList)-1)

    DECLARE @Template AS Varchar(max)
    SET @Template = 'SELECT [RecordID]
          ,[DateRecord]
          ,[DepartmentID]
          ,[Shift]
          ,[ShiftLength]
          ,[ShiftType]
          ,[Active]
          ,[Comment]
          {ColumnList}
      FROM [dbo].[tblDetails]
      WHERE DateRecord >= ''{StartDate}'' AND DateRecord <= ''{EndDate}'' AND DepartmentID = ''{DepartmentID}''
      ORDER BY DateRecord'

    SET @Template = REPLACE(@Template, '{ColumnList}', @ColumnList) 
    SET @Template = REPLACE(@Template, '{StartDate}', @StartDate) 
    SET @Template = REPLACE(@Template, '{EndDate}', @EndDate) 
    SET @Template = REPLACE(@Template, '{DepartmentID}', @DepartmentID ) 

    EXEC (@Template);

END

GO
-- Execute it like this
EXEC dbo.GetDynamicReport 
    @StartDate = '2015-06-03 06:38:07',
    @EndDate = '2015-06-03 06:38:07',
    @DepartmentID = 'abc',
    @ColumnFilter = 'GM'
Run Code Online (Sandbox Code Playgroud)

调用过程

public static DataTable CallReportProcedure(string connectionString, DateTime startDate, DateTime endDate, string departmentID, string columnFilter)
{
    using(var conn = new SqlConnection(connectionString))
    using(var cmd = new SqlCommand("GetDynamicReport", conn) 
        { CommandType = System.Data.CommandType.StoredProcedure} )
    {
        cmd.Parameters.AddWithValue("@StartDate", startDate);
        cmd.Parameters.AddWithValue("@EndDate", endDate);
        cmd.Parameters.AddWithValue("@DepartmentID", departmentID);
        cmd.Parameters.AddWithValue("@ColumnFilter", columnFilter);

        var da = new SqlDataAdapter(cmd);
        var ds = new DataSet();
        da.Fill(ds);
        return ds.Tables[0];
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,如果您确实需要了解有关列的信息,您可以检查生成的 DataTable

static void Main(string[] args)
        {
            SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
            builder.DataSource = "localhost";
            builder.InitialCatalog = "peter";
            builder.IntegratedSecurity = true;
            var connectionString = builder.ConnectionString;

            var resultTable = p.CallReportProcedure(connectionString, new DateTime(2015, 1, 1), new DateTime(2015, 5, 1), "GMC", "GM");
            // Bind the resultTable to your DataGrid

            // If you need to know the column names then you can loop through the Columns of the resultTable
            foreach (DataColumn col in resultTable.Columns)
            {
                // Print the names of the columns from the result
                Console.WriteLine(col.ColumnName);
            }
        }
Run Code Online (Sandbox Code Playgroud)