SSRS如何获取矩阵行组的第一个和最后一个值?
SSRS How to get the first and last values of a matrix row group?
我基本上有下面的截图作为我的布局。
我的矩阵列是 DCG1 和 DCG2。
在 Matrix 的末尾,我有一个 Total Group,它可以直接找到。但是我想找出我的第一个和最后一个值之间的区别。我已经尝试了 ReportItems 中的所有内容!到价值观。我无法让 SSRS 识别这些值。
所以基本上在下面的屏幕截图中。屏幕截图 1 是矩阵结构。我有一个名为 Test1 的列组,我想要 Test1 的第一个值和 Test 1 的最后一个值并将其放在红色框中。
在屏幕截图 2 中,您可以看到我要比较的值。 table 分组的名称与列 + 组的名称相同。所以dcs1group/dcs2group
好的,这是数据源的 DDL 和 DML
http://pastebin.com/1ySN701D
pastebin 已删除。为什么,不确定所以在下面。
IF EXISTS
(SELECT [name]
FROM tempdb.sys.tables
WHERE [name] LIKE '%tmpHoldingTable%')
BEGIN
DROP TABLE #tmpHoldingTable;
END;
CREATE TABLE #tmpHoldingTable
(
dcs1 NVARCHAR (50),
dcs2 NVARCHAR (50),
Total DECIMAL (10, 2),
Test1 NVARCHAR (50)
)
INSERT INTO #tmpHoldingTable (dcs1,
dcs2,
Total,
Test1)
VALUES ('Contract',
'Breach of Contract',
500.00,
'01/01/2013-12/31/2013'),
('Contract',
'Breach of Contract',
300.00,
'01/01/2014-12/31/2014'),
('Employment',
'Discrimination',
500.00,
'01/01/2013-12/31/2013'),
('Employment',
'Discrimination',
300.00,
'01/01/2014-12/31/2014'),
('Employment',
'Research',
500.00,
'01/01/2013-12/31/2013'),
('Employment',
'Research',
300.00,
'01/01/2014-12/31/2014')
SELECT * FROM #tmpHoldingTable;
据我所知,仅在 SSRS 中这是不可能的。相信我,我试过了。幸运的是,你有一个 SQL 数据源,所以我会在那里解决这个要求,你有(几乎)无限的权力来塑造和操作数据。
例如,我会将您最后的 select 替换为:
; WITH CTE_Base AS (
SELECT * FROM #tmpHoldingTable
)
, CTE_Test1 AS (
SELECT Test1
, ROW_NUMBER () OVER ( ORDER BY Test1 ) AS Column_Number_Test1
FROM CTE_Base
GROUP BY Test1
)
SELECT CTE_Base.*
, CTE_Test1.Column_Number_Test1
, CASE WHEN CTE_Test1.Column_Number_Test1 = 1
THEN Total
WHEN CTE_Test1.Column_Number_Test1 =
( SELECT MAX ( Column_Number_Test1 ) FROM CTE_Test1 )
THEN 0 - Total
ELSE 0
END AS [Difference]
FROM CTE_Base
INNER JOIN CTE_Test1
ON CTE_Base.Test1 = CTE_Test1.Test1
这会添加一个 [Difference] 列,其中第一列的副本是 [Total],最后一列的副本是 0 - [Total]。
SQL 可能会变得更有效率,但希望将其分解为 CTE 更容易理解。
然后在 SSRS 设计器中,您可以在 [Test1] 列组之外添加一个 [Difference] 列并让它求和(默认)。
顺便说一句,您的测试数据似乎有点简单 - 它只会产生 2 列,并且所有单元格都有值。但是你发布 DDL 和 DML 真是太棒了 - 它使扩展数据和代码以及测试它变得容易。
是的,这是可能的,但如您所见,它有点复杂。
为了使这个答案更通用,我创建了自己的 DataSet,其中包含简化的列但更多的数据:
select grp = 1, val = 100, dt = cast('01-jan-2015' as date)
union all select grp = 1, val = 110, dt = cast('01-jan-2015' as date)
union all select grp = 1, val = 200, dt = cast('02-jan-2015' as date)
union all select grp = 1, val = 210, dt = cast('02-jan-2015' as date)
union all select grp = 1, val = 300, dt = cast('03-jan-2015' as date)
union all select grp = 1, val = 310, dt = cast('03-jan-2015' as date)
union all select grp = 1, val = 400, dt = cast('04-jan-2015' as date)
union all select grp = 1, val = 410, dt = cast('04-jan-2015' as date)
union all select grp = 1, val = 500, dt = cast('05-jan-2015' as date)
union all select grp = 1, val = 510, dt = cast('05-jan-2015' as date)
union all select grp = 2, val = 220, dt = cast('02-jan-2015' as date)
union all select grp = 2, val = 230, dt = cast('02-jan-2015' as date)
union all select grp = 2, val = 320, dt = cast('03-jan-2015' as date)
union all select grp = 2, val = 330, dt = cast('03-jan-2015' as date)
union all select grp = 2, val = 420, dt = cast('04-jan-2015' as date)
union all select grp = 2, val = 430, dt = cast('04-jan-2015' as date)
请注意,每个 grp / dt 组合都有两个值,并且 grp 1
在更长的范围内 dt 比 grp 2
.
我基于此创建了一个简单的矩阵:
由于您使用的是 SQL Server 2012,因此您可以使用 LookupSet
函数获取每个行组的 First/Last 个值。
第一个行组TextBox中的表达式是:
=Code.SumLookup(
LookupSet(
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
)
根据我的样本数据,这给出了我需要的结果:
请注意,第二个 grp
行的范围比第一行窄,但它的 first/last 列对于每个组都是独立的,因此在每个 grp
中都是正确的。
这里发生了很多事情。
聚合 LookUpSet
结果的自定义代码
LookupSet
表达式包装在 Code.SumLookup
自定义函数中:
Function SumLookup(ByVal items As Object()) As Decimal
If items Is Nothing Then
Return Nothing
End If
Dim suma As Decimal = New Decimal()
suma = 0
For Each item As Object In items
suma += Convert.ToDecimal(item)
Next
Return suma
End Function
这取自 this SO 问题的答案。
这里假设每个矩阵单元格可以是多个值的总和,所以这个需要求和。 LookupSet
returns 值数组,由 Code.SumLookup
.
聚合
LookupSet
的详细信息
接下来,LoopupSet
表达式本身:
LookupSet(
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
LookupSet
采用以下参数:
LookupSet(source_expression, destination_expression, result_expression, dataset)
在我们的表达式中,我们希望从 DataSet1
中获取与当前 grp
范围内的第一个 dt
匹配的所有值。
对于source_expression
我使用:
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
这将获取行范围中的第一个 dt("grp"
是行组的名称),然后将其附加到当前 grp.当在 DataSet1
.
中搜索时,这会创建一个表达式来匹配类似的表达式
即destination_expression
:
Fields!dt.Value.ToString & Fields!grp.Value.ToString
最后,我们指定我们希望 Fields!val.Value
作为 result_expression
和 DataSet1
作为 dataset
参数。
DataSet1
中所有匹配的Fields!val.Value
值由LookupSet
构造成一个数组,然后由Code.SumLookup
聚合。
更新最后一个值的表达式
Last 文本框的表达式几乎相同;只需将 First
更改为 Last
:
=Code.SumLookup(
LookupSet(
Last(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
)
找不同
最后,要获得它们的差异,只需在 Difference 文本框中从另一个表达式中减去一个表达式,甚至引用 ReportItems
值:
=ReportItems!Last.Value - ReportItems!First.Value
其中 Last
和 First
是文本框的名称。
结论
显然您需要针对您的具体情况进行更新,但您可以看到这是可以做到的。
在你的报告中这样做值得吗?您可以看到涉及的步骤很多,并且通常在生成 DataSet 时更容易解决。但是,如果这不是一个选项,希望这种 LookupSet
方法有用。
我基本上有下面的截图作为我的布局。
我的矩阵列是 DCG1 和 DCG2。
在 Matrix 的末尾,我有一个 Total Group,它可以直接找到。但是我想找出我的第一个和最后一个值之间的区别。我已经尝试了 ReportItems 中的所有内容!到价值观。我无法让 SSRS 识别这些值。
所以基本上在下面的屏幕截图中。屏幕截图 1 是矩阵结构。我有一个名为 Test1 的列组,我想要 Test1 的第一个值和 Test 1 的最后一个值并将其放在红色框中。
在屏幕截图 2 中,您可以看到我要比较的值。 table 分组的名称与列 + 组的名称相同。所以dcs1group/dcs2group
好的,这是数据源的 DDL 和 DML
http://pastebin.com/1ySN701D
pastebin 已删除。为什么,不确定所以在下面。
IF EXISTS (SELECT [name] FROM tempdb.sys.tables WHERE [name] LIKE '%tmpHoldingTable%') BEGIN DROP TABLE #tmpHoldingTable; END; CREATE TABLE #tmpHoldingTable ( dcs1 NVARCHAR (50), dcs2 NVARCHAR (50), Total DECIMAL (10, 2), Test1 NVARCHAR (50) ) INSERT INTO #tmpHoldingTable (dcs1, dcs2, Total, Test1) VALUES ('Contract', 'Breach of Contract', 500.00, '01/01/2013-12/31/2013'), ('Contract', 'Breach of Contract', 300.00, '01/01/2014-12/31/2014'), ('Employment', 'Discrimination', 500.00, '01/01/2013-12/31/2013'), ('Employment', 'Discrimination', 300.00, '01/01/2014-12/31/2014'), ('Employment', 'Research', 500.00, '01/01/2013-12/31/2013'), ('Employment', 'Research', 300.00, '01/01/2014-12/31/2014') SELECT * FROM #tmpHoldingTable;
据我所知,仅在 SSRS 中这是不可能的。相信我,我试过了。幸运的是,你有一个 SQL 数据源,所以我会在那里解决这个要求,你有(几乎)无限的权力来塑造和操作数据。
例如,我会将您最后的 select 替换为:
; WITH CTE_Base AS (
SELECT * FROM #tmpHoldingTable
)
, CTE_Test1 AS (
SELECT Test1
, ROW_NUMBER () OVER ( ORDER BY Test1 ) AS Column_Number_Test1
FROM CTE_Base
GROUP BY Test1
)
SELECT CTE_Base.*
, CTE_Test1.Column_Number_Test1
, CASE WHEN CTE_Test1.Column_Number_Test1 = 1
THEN Total
WHEN CTE_Test1.Column_Number_Test1 =
( SELECT MAX ( Column_Number_Test1 ) FROM CTE_Test1 )
THEN 0 - Total
ELSE 0
END AS [Difference]
FROM CTE_Base
INNER JOIN CTE_Test1
ON CTE_Base.Test1 = CTE_Test1.Test1
这会添加一个 [Difference] 列,其中第一列的副本是 [Total],最后一列的副本是 0 - [Total]。
SQL 可能会变得更有效率,但希望将其分解为 CTE 更容易理解。
然后在 SSRS 设计器中,您可以在 [Test1] 列组之外添加一个 [Difference] 列并让它求和(默认)。
顺便说一句,您的测试数据似乎有点简单 - 它只会产生 2 列,并且所有单元格都有值。但是你发布 DDL 和 DML 真是太棒了 - 它使扩展数据和代码以及测试它变得容易。
是的,这是可能的,但如您所见,它有点复杂。
为了使这个答案更通用,我创建了自己的 DataSet,其中包含简化的列但更多的数据:
select grp = 1, val = 100, dt = cast('01-jan-2015' as date)
union all select grp = 1, val = 110, dt = cast('01-jan-2015' as date)
union all select grp = 1, val = 200, dt = cast('02-jan-2015' as date)
union all select grp = 1, val = 210, dt = cast('02-jan-2015' as date)
union all select grp = 1, val = 300, dt = cast('03-jan-2015' as date)
union all select grp = 1, val = 310, dt = cast('03-jan-2015' as date)
union all select grp = 1, val = 400, dt = cast('04-jan-2015' as date)
union all select grp = 1, val = 410, dt = cast('04-jan-2015' as date)
union all select grp = 1, val = 500, dt = cast('05-jan-2015' as date)
union all select grp = 1, val = 510, dt = cast('05-jan-2015' as date)
union all select grp = 2, val = 220, dt = cast('02-jan-2015' as date)
union all select grp = 2, val = 230, dt = cast('02-jan-2015' as date)
union all select grp = 2, val = 320, dt = cast('03-jan-2015' as date)
union all select grp = 2, val = 330, dt = cast('03-jan-2015' as date)
union all select grp = 2, val = 420, dt = cast('04-jan-2015' as date)
union all select grp = 2, val = 430, dt = cast('04-jan-2015' as date)
请注意,每个 grp / dt 组合都有两个值,并且 grp 1
在更长的范围内 dt 比 grp 2
.
我基于此创建了一个简单的矩阵:
由于您使用的是 SQL Server 2012,因此您可以使用 LookupSet
函数获取每个行组的 First/Last 个值。
第一个行组TextBox中的表达式是:
=Code.SumLookup(
LookupSet(
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
)
根据我的样本数据,这给出了我需要的结果:
请注意,第二个 grp
行的范围比第一行窄,但它的 first/last 列对于每个组都是独立的,因此在每个 grp
中都是正确的。
这里发生了很多事情。
聚合 LookUpSet
结果的自定义代码
LookupSet
表达式包装在 Code.SumLookup
自定义函数中:
Function SumLookup(ByVal items As Object()) As Decimal
If items Is Nothing Then
Return Nothing
End If
Dim suma As Decimal = New Decimal()
suma = 0
For Each item As Object In items
suma += Convert.ToDecimal(item)
Next
Return suma
End Function
这取自 this SO 问题的答案。
这里假设每个矩阵单元格可以是多个值的总和,所以这个需要求和。 LookupSet
returns 值数组,由 Code.SumLookup
.
LookupSet
接下来,LoopupSet
表达式本身:
LookupSet(
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
LookupSet
采用以下参数:
LookupSet(source_expression, destination_expression, result_expression, dataset)
在我们的表达式中,我们希望从 DataSet1
中获取与当前 grp
范围内的第一个 dt
匹配的所有值。
对于source_expression
我使用:
First(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
这将获取行范围中的第一个 dt("grp"
是行组的名称),然后将其附加到当前 grp.当在 DataSet1
.
即destination_expression
:
Fields!dt.Value.ToString & Fields!grp.Value.ToString
最后,我们指定我们希望 Fields!val.Value
作为 result_expression
和 DataSet1
作为 dataset
参数。
DataSet1
中所有匹配的Fields!val.Value
值由LookupSet
构造成一个数组,然后由Code.SumLookup
聚合。
更新最后一个值的表达式
Last 文本框的表达式几乎相同;只需将 First
更改为 Last
:
=Code.SumLookup(
LookupSet(
Last(Fields!dt.Value, "grp").ToString & Fields!grp.Value.ToString
, Fields!dt.Value.ToString & Fields!grp.Value.ToString
, Fields!val.Value
, "DataSet1"
)
)
找不同
最后,要获得它们的差异,只需在 Difference 文本框中从另一个表达式中减去一个表达式,甚至引用 ReportItems
值:
=ReportItems!Last.Value - ReportItems!First.Value
其中 Last
和 First
是文本框的名称。
结论
显然您需要针对您的具体情况进行更新,但您可以看到这是可以做到的。
在你的报告中这样做值得吗?您可以看到涉及的步骤很多,并且通常在生成 DataSet 时更容易解决。但是,如果这不是一个选项,希望这种 LookupSet
方法有用。