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

时间:2015-06-02 05:07:13

标签: sql-server stored-procedures

我正在尝试生成一个SQL语句,该语句根据过滤器从数据库中动态获取列名。 我们有一个包含大约50列的表,每列都有一个前缀,表示它适用于哪个“集合”。我创建了一个在SQL Management Studio中运行良好的Query,但是由于我想在.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)

3 个答案:

答案 0 :(得分:1)

您可以将查询包装在过程中。然后,您可以从app / web执行它,并获得DataTable作为结果。将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'

调用程序

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];
    }
}

然后,如果您确实需要了解有关列的信息,可以检查生成的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);
            }
        }

答案 1 :(得分:0)

您可以创建一个只包含该查询所需列的视图。当您在查询中所需的列发生更改时,您只需更新视图和中提琴,您的所有应用程序现在都可以获得所需的新列。您只需确保您的应用可以处理不同的潜在列。

答案 2 :(得分:0)

好的解决方案就是使用视图。

使用函数的另一个选项但我们无法在创建后更新函数。 这是一个例子。

CREATE FUNCTION V_GetWarehouse (@StartDate Date,@EndDate Date,@DepartmentID  varchar(10))
RETURNS TABLE
AS
RETURN
 SELECT [RecordID]
  ,[DateRecord]
  ,[DepartmentID]
  ,[Shift]
  ,[ShiftLength]
  ,[ShiftType]
  ,[Active]
  ,[Comment]      
FROM [Data_Warehouse].[dbo].[tblDetails]
 WHERE DateRecord >= @StartDate AND DateRecord <= @EndDate AND DepartmentID=@DepartmentID

所以使用View代替函数。

还有另一种创建新过程而非视图的最佳方法。 这是一个例子

CREATE PROCEDURE sp_GetGenrealResult
 @ColumnName text,
 @TableName varchar(1000),
 @StartDate Date,
 @EndDate Date,
 @DepartmentID  varchar(10)
  AS
enter code here
        BEGIN

     declare @DateRecordCol char(10)='DateRecord',
      @DepartmentIDCol char(12)='DepartmentID' 
     Select @ColumnName from @TableName where @DateRecordCol >= @StartDate AND @DateRecordCol <= @EndDate AND @DepartmentIDCol =@DepartmentID
    END

执行此操作。

   EXEC sp_GetGenrealResult '[RecordID],[DateRecord],  [DepartmentID]', '[Data_Warehouse].[dbo].  [tblDetails]' , '2015-01-01', '2015-05-01' ,'GMC'