SQL 具有动态列名的存储过程
SQL Stored Procedure with Dynamic Column Names
我正在尝试生成一个 SQL 语句,该语句基于过滤器从数据库中动态获取列名。
我们有一个包含大约 50 列的 table,每列都有一个前缀来表示它适用于哪个 "set"。我创建了一个在 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)
您可以创建一个视图,其中仅包含该查询所需的列。当您想要在查询中更改的列时,您只需更新视图,然后中提琴,您的所有应用程序现在都会获得您想要的新列。您只需确保您的应用可以处理返回的不同潜在列。
好的解决方案是为此使用视图。
另一个使用函数的选项,但我们无法在创建后更新函数。
这是一个例子。
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
所以使用视图而不是函数。
还有另一种创建新过程而不是视图的最佳方法。
举个例子
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'
您可以按原样将查询包装在过程中。然后你可以从你的 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);
}
}
我正在尝试生成一个 SQL 语句,该语句基于过滤器从数据库中动态获取列名。 我们有一个包含大约 50 列的 table,每列都有一个前缀来表示它适用于哪个 "set"。我创建了一个在 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)
您可以创建一个视图,其中仅包含该查询所需的列。当您想要在查询中更改的列时,您只需更新视图,然后中提琴,您的所有应用程序现在都会获得您想要的新列。您只需确保您的应用可以处理返回的不同潜在列。
好的解决方案是为此使用视图。
另一个使用函数的选项,但我们无法在创建后更新函数。 这是一个例子。
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
所以使用视图而不是函数。
还有另一种创建新过程而不是视图的最佳方法。 举个例子
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'
您可以按原样将查询包装在过程中。然后你可以从你的 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);
}
}