我们有一种情况,不同的客户实际上要求相同的报告,但他们:
- 不想要所有的列
- 希望列的顺序与我们自然拥有的顺序不同
- 希望它们被称为不同于我们存储方式的名称(例如“客户编号”与“客户编号”)
目的是减轻适应这些定制请求所需的工作量。我们目前处于这些基本相同报告的数百个实例的位置(不包括这些表面差异)。我想看看我是否需要Dynamic
每组这些基本查询的一个实例,Parameter
或者我是否可以parameter
通过 1 处理所有可能的集合Stored Procedure
。希望也不必拥有一堆特定SSRS
RDL
文件或SSIS
DTSX
包的特定实例来处理这些更改。数据将来自Stored Procedure
我们需要显示/呈现的数据。
让我们假设我构建了一个Dynamic SQL Command
输出看起来像这样的地方:
SELECT
Col1 AS 'Alias1',
Col2 AS 'Alias2',
Col3 AS 'Alias3'
FROM View
WHERE DateCol >= @StartDate
AND DateCol < @EndDate
它是由几个不同的部分使用几个表构建的。下面的表结构是更多的伪代码来理解这些想法,所以请忽略诸如没有声明主键之类的东西......
CREATE TABLE [report].[ReportTemplate]
(
ID INT NOT NULL, --(Primary Key)
ReportName VarChar(100) NOT NULL,
ReportTypeID INT NOT NULL --(FK To report.ReportTemplateType.ID)
)
CREATE TABLE [report].[ReportTemplateType]
(
ID INT NOT NULL, --(Primary Key)
Name VarChar(50), --(Unique Constraint)
BaseCommand VarChar(2000), --Holds FROM and JOIN clauses
WhereCommand VarChar(2000), --Holds WHERE Clause
WhereCommandParameters VarChar(2000), --Holds declaration of the parameters
)
CREATE TABLE [report].[ReportTemplateColumnDetails]
(
ID INT NOT NULL, --(Primary Key)
ReportTemplateID INT NOT NULL, --(FK to report.ReportTemplate.ID)
ColumnName VarChar(256) NOT NULL,
ColumnAlias VarChar(256) NULL, --Have logic handle blank vs NULL values
ColumnOrder SmallInt NOT NULL
)
+----+-------------------+--------------+
| ID | ReportName | ReportTypeID |
+----+-------------------+--------------+
| 1 | Customer 1 Status | 1 |
| 2 | Customer 1 Sales | 2 |
+----+-------------------+--------------+
+----+--------+-----------------+------------------------------------------------------------------------------+-------------------------------------------------------------------+
| ID | Name | BaseCommand | WhereCondition | WhereConditionParameters |
+----+--------+-----------------+------------------------------------------------------------------------------+-------------------------------------------------------------------+
| 1 | Status | FROM StatusView | WHERE DateCol >= @StartDate AND DateCol < @EndDate | @StartDate DATEIME, @EndDate DateTime |
| 2 | Sales | FROM SalesView | WHERE DateCol >= @StartDate AND DateCol < @EndDate AND Col4 = @TypeParameter | @StartDate DATEIME, @EndDate DateTime, @TypeParameter VarChar(20) |
+----+--------+-----------------+------------------------------------------------------------------------------+-------------------------------------------------------------------+
+----+------------------+------------+-------------+-------------+
| ID | ReportTemplateID | ColumnName | ColumnAlias | ColumnOrder |
+----+------------------+------------+-------------+-------------+
| 1 | 1 | Col1 | Alias1 | 1 |
| 2 | 1 | Col2 | Alias2 | 2 |
| 3 | 1 | Col3 | Alias3 | 3 |
| 4 | 2 | Col4 | Alias1 | 1 |
| 5 | 2 | Col5 | Alias2 | 2 |
| 6 | 2 | Col6 | Alias3 | 3 |
+----+------------------+------------+-------------+-------------+
该命令是使用以下代码构建的:
CREATE PROCEDURE [report].[ExecuteReportTemplate] (@ReportName VarChar(50))
AS
BEGIN
DECLARE @SQLCommand VarChar(MAX) = 'SELECT ',
@FirstColumnAdded BIT = 0,
@BaseCommand VarChar(2000),
@WhereCondition VarChar(2000),
@WhereConditionParameters VarChar(2000)
SELECT @BaseCommand = RTT.BaseCommand,
@WhereCondition = RTT.WhereCommand,
@WhereConditionParameters = RTT.WhereCommandParameters
FROM [report].[ReportTemplateType] RTT
INNER JOIN [report].[ReportTemplate] RT
ON RTT.ID = RT.ReportTypeID
WHERE RT.Name = @ReportName
DECLARE @ColumnName VarChar(256),
@ColumnAlias VarChar(256)
DECLARE ColumnCursor CURSOR FOR
SELECT ColumnName,
ColumnAlias
FROM [report].[ReportTemplateColumnDetails]
ORDER BY ColumnOrder
FETCH NEXT FROM ColumnCursor INTO @ColumnName, @ColumnAlias
WHILE (@@FETCH_STATUS = 0)
BEGIN
--Add a comma inbetween columns, does not happen on the first one
IF(@FirstColumnAdded = 1)
BEGIN
SET @SQLCommand = @SQLCommand + ', '
END
ELSE
BEGIN
SET @FirstColumnAdded = 1
END
--Adds the column into the list
SET @SQLCommand = @SQLCommand + @ColumnName
--If we have been provided an alias, set the alias
IF(@ColumnAlias IS NULL OR LTRIM(RTRIM(@ColumnAlias)) = '')
BEGIN
@SQLCommand = @SQLCommand + 'AS ''' + @ColumnAlias + ''' '
END
END
CLOSE ColumnCursor
DEALLOCATE ColumnCursor
--Now Add The Base Command
SELECT @SQLCommand = @SQLCommand + ' ' + @BaseCommand + ' ' + @WhereCommand
EXECUTE sp_executesql @sqlCommand, @WhereConditionParameters
@StartDate = '2019-01-01',
@EndDate = GETDATE()
END
有没有办法动态更改配置和传入的参数而无需构建单独的命令?
我希望能够用不同的and填充[report].[ReportTemplateType].[WhereCondition]
and 。例如在类似的东西中添加第三个。我知道解决这个问题的唯一方法是创建一个不同的地方,其中所有内容都与上述相同,但我们会将最后一块更改为:[report].[ReportTemplateType].[WhereCondition]
WHERE
Parameters
column
WHERE condition
Col4 = @TypeParameter
Stored Procedure
Stored Procedure
EXECUTE sp_executesql @sqlCommand, @WhereConditionParameters
@StartDate = '2019-01-01',
@EndDate = GETDATE(),
@TypeParameter = 'SomeStringValue'
有没有办法动态更改配置和传入的参数而无需构建单独的命令?
评论太长了,所以我把它抛给了一个答案。
我个人的偏好是动态 SQL (DSQL) 不应该被非管理性质的生产代码使用。在管理环境时,我一直使用 DSQL,但这些都不需要任何显着水平的性能。当您开始将 Dynamic SQL 作为生产代码推出时,您将不可避免地遇到性能问题。在那一刻,轮子脱落了,因为 DSQL 是出了名的故障排除。显然,这只是一种意见,你可以自由地做你想做的事,但我强烈反对在你推送到生产的任何代码中使用 DSQL。
在你走得更远之前,我建议你阅读一下我认为是 Erland Sommarskog 撰写的关于动态 SQL 的权威文章:动态 SQL的诅咒和祝福
这真是一本好书;请注意,消化这一切需要一点时间。
我现在要跳下我的肥皂盒......
至于你的问题:
是的,您可能需要嵌套您的 DSQL或相应地添加额外的报告表,但只有您的要求和方法才能决定适当的路径。 如果你是更紧迫的问题,显然我会说不。
来自评论:
可以使用
dynamic Sql
,只要它在"Blessing Part of Dynamic Sql"
.我没有深入了解它。
您对小规模的要求,
您将在其中填充 UI 。填充
UserPrefReport
时不应出现任何错误。您将'FirstName as [FName],MiddleName as [MName]'
按照用户期望的相同顺序填充值。目前没有使用
Reportid
。或者你可以在这里创建另一个表TableorView
并参考Reportid
。这没什么大不了的。您对大规模的要求,
这意味着您还为用户提供了搜索这些字段的便利。并且只应填充这些字段。
在这种情况下,UserPrefReport 的设计将采用这种方式,
在这两种情况下,人口
UserPrefReport
都是非常重要的。告诉我我的脚本中缺少您的哪一部分要求?