SQL Server报表服务(SSRS)对于开发人员和用户来说是一个非常有用的设计和浏览报表的工具。但是,有些特性是在SSRS中没有提供为直接可以使用的。 这些特性是开发人员和用户经常需要的。
在一个报表中显示服务器名称或IP地址
当报表数据是运行在几个报表服务器上时,用户经常会搞错。你可能有一个测试和开发环境,但是你不知道你的报表数据是来自于哪个服务器。
很明显,解决这个问题的一个方法是在报表中显示报表服务器名称或它的IP地址。不幸的是,没有直接的函数或方法来获取服务器名称。但是有这个工作环境。
在报表服务中,有一个全局参数叫做 ReportServerUrl,它可以显示报表URL。这个值的开始部分是来自于C:\Program Files\Microsoft SQL Server\MSSQL.#\Reporting Services\ReportServer 文件夹下的RSReportServer.config 文件。在这个文件里,有叫做URLRoot 的结点。它是http://{IPAddress}/reportserver或http://{Server Name}/reportserver。你需要找到方法来提取这个IP地址或服务器名称,你可以使用下面的表达式:
Mid(Globals!ReportServerUrl,8,InStr(9,Globals!ReportServerUrl,"/") - 8)
这个表达式所做的,是提取从//位置后的字母到/位置后的字母间的名称,这是服务器的服务器名称或IP地址。
将数字类型转化为文本
这是在SSRS论坛上经常看到的问题,因为许多用户需要将数字类型的数据显示为文本。尽管在水晶报表中有一个函数,但是在SSRS中没有函数。不过,你可以在SSRS中自己写个函数。
让我们用下面的查询来举个例子:
SELECT Sales.SalesPerson.SalesPersonID AS SalesPerson,
SUM(Sales.SalesOrderDetail.OrderQty * Sales.SalesOrderDetail.UnitPrice) AS Amount
FROM Sales.SalesOrderDetail
INNER JOIN Sales.SalesOrderHeader ON Sales.SalesOrderDetail.SalesOrderID = Sales.SalesOrderHeader.SalesOrderID
INNER JOIN Sales.SalesPerson ON Sales.SalesOrderHeader.SalesPersonID = Sales.SalesPerson.SalesPersonID
GROUP BY Sales.SalesPerson.SalesPersonID
为了使用这个例子,让我们假设我们需要显示销售人员的id和数量,数量要以文本形式显示。
按照下面的步骤来创建一个报表。
1. 创建一个SSRS项目。
2. 添加新报表到这个项目中。
3. 添加一个数据源,它的数据库指向adventureworks。
4. 用上面的查询创建一个数据集。
5. 从工具箱拖拉一个表来进行显示,并拖拉字段到这个表上。
下一步是创建一个函数来转换数字类型为文本。
到报表菜单选项中选择报表属性。选择对话框中的代码标签页并复制和黏贴下面的代码:
' Source
' http://cc.msnscache.com/cache.aspx?q=72465960679242&mkt=en-US&lang=en-US&w=577f5001&FORM=CVRE8
SHARED suffixes AS String() = _
{"Thousand ", "Million ", "Billion ", "Trillion ", _
"Quadrillion ", "Quintillion ", "Sextillion "}
SHARED units AS String() = _
{"","One ", "Two ", "Three ", "Four ", "Five ", _
"Six ", "Seven ", "Eight ", "Nine "}
SHARED tens AS String() = _
{"Twenty ", "Thirty ", "Forty ", "Fifty ", "Sixty ", _
"Seventy ", "Eighty ", "Ninety "}
SHARED digits AS String() = _
{"Ten ","Eleven ", "Twelve ", "Thirteen ", "Fourteen ", _
"Fifteen ", "Sixteen ", "Seventeen ", "Eighteen ", "Nineteen"}
SHARED expr AS NEW _
System.Text.RegularExpressions.Regex("^-?\d+(\.\d{2})?$", _
System.Text.RegularExpressions.RegexOptions.None)
PUBLIC Function ExpandPrice(Price AS Double, _
Optional pSeparator AS String = ".") _
AS String
Dim pPrice As String
pPrice = FORMAT(Price,"##############.00")
Dim temp AS New System.Text.StringBuilder()
If Not expr.IsMatch(pPrice) Then
' temp.Append(pPrice) or whatever you want to do here
Else
Dim parts AS String() = pPrice.Split(pSeparator)
Dim dollars AS String = parts(0)
Dim cents AS String = parts(1)
If CDbl(dollars) > 1 Then
temp.Append(ExpandIntegerNumber(dollars) & "Dollars ")
If CInt(cents) > 0 Then
temp.Append("And ")
End If
ElseIf CDbl(dollars) = 0 Then
temp.Append(ExpandIntegerNumber(dollars) & "Zero Dollars ")
If CInt(cents) >= 0 Then
temp.Append("And ")
End If
ElseIf CDbl(dollars) = 1 Then
temp.Append(ExpandIntegerNumber(dollars) & "Dollar " )
End If
If CDbl(cents) > 1 Then
temp.Append(ExpandIntegerNumber(cents) & "Cents")
ElseIf CDbl(cents) = 0 Then
temp.Append(ExpandIntegerNumber(cents) & "Zero Cents ")
ElseIf CDbl(cents) = 1 Then
temp.Append(ExpandIntegerNumber(cents) & "Cent " )
End If
End If
RETURN temp.ToString()
End Function
Function ExpandIntegerNumber(pNumberStr AS String) AS String
Dim temp2 AS New System.Text.StringBuilder()
Dim number AS String = _
StrDup(3 - Len(pNumberStr) Mod 3, "0") & pNumberStr
Dim i AS Integer, j AS Integer = -1
Dim numPart AS String
For i = Len(number) - 2 To 1 Step -3
numPart = Mid(number, i, 3)
If Clng(numPart > 0) Then
If j > -1 Then
temp2.Insert(0,suffixes(j),1)
End If
End If
temp2.Insert(0,GetNumberUnder1000Str(numPart),1)
j += 1
Next
RETURN temp2.ToString()
End Function
Function GetNumberUnder1000Str(pNumber AS String) AS String
Dim temp1 AS New System.Text.StringBuilder()
If Len(pNumber) = 3 Then
If CLng(Left(pNumber, 1)) > 0 Then
temp1.Append(GetNumberUnder100Str(Left(pNumber, 1)) & "Hundred ")
End If
End If
temp1.Append(GetNumberUnder100Str(Right("0" & pNumber, 2)))
RETURN temp1.ToString()
End Function
Function GetNumberUnder100Str(pNumber AS String) AS String
If pNumber > 19 Then
RETURN tens(Left(pNumber, 1) - 2) & units(Right(pNumber, 1))
ElseIF pNumber >= 10 and pNumber <= 19 Then
RETURN digits(Right(pNumber, 1))
Else
RETURN units(Right(pNumber, 1))
End If
End Function
上面的代码是从网站上代码片段那里得到的。
接下来,你需要在你的表中调用这个函数。你需要在你需要转换你的数字为文本的字段输入下面的函数调用。
=Code.ExpandPrice(Fields!Amount.Value,".")
最后,你将看到下面的屏幕。
SQL Server报表服务中的一些常见小问题
以选择的颜色显示网格
在SSRS中,没有直接以选择的颜色来显示网格的方法(以可选择的颜色显示网格会提高报表的可读性。在Excel 2007中你可以很容易地做到,但是在SSRS中却不行)。
通过将背景颜色中的一个表达式和RowNumber函数结合起来,你可以添加选择颜色到你的报表中。RowNumber(无)返回当前记录的记录编号。对于每个文本框的背景属性,你需要给出下面的表达式。
软件开发网 =iif(RowNumber(Nothing)Mod2,"LightBlue","SkyBlue")
由上面的函数,偶数编号的记录会显示为淡蓝色,而奇数编号的记录会显示为天蓝色,如下所示:
SQL Server报表服务中的一些常见小问题
每页显示给定数目的记录
如果你想只在每页显示一组记录,那么这不是简单的工作,因为记录数目将依赖于字体大小和其它的打印机默认设置。除了这个问题,还有如果一行的记录长度太长,那么它会移到另一行,而这会扰乱你报表中记录的数目。所以有必要添加一些代码来添加这个功能到你的报表中。假设你想每页显示20行,那么添加一个组到你的报表中,它具有以下表达式:
=int((RowNumber(Nothing)-1)/20)
然后为这个组选择page break at the end选项。
SQL Server报表服务中的一些常见小问题
阻止向下钻取矩阵中总列数
当你在报表中有个矩阵时,你可以像下面这样显示总行数和列数:
SQL Server报表服务中的一些常见小问题
如果你想在你点击任何详细单元时导航到另一个报表,那么你可以配置导航标签。但是,这个配置的问题是这个配置使得用户可以点击总列数并钻取到导航报表。你不能通过使用表达式来阻止,因为总列数可以看作是一个其它字段。
下面是我们将用于上面这个例子的查询:
SELECT YEAR(Sales.SalesOrderHeader.OrderDate) AS Year,
Production.Product.Name AS Name,
SUM(Sales.SalesOrderDetail.LineTotal) AS Amt
FROM Sales.SalesOrderDetail
INNER JOIN Sales.SalesOrderHeader ON Sales.SalesOrderDetail.SalesOrderID = Sales.SalesOrderHeader.SalesOrderID
INNER JOIN Production.Product ON Sales.SalesOrderDetail.ProductID = Production.Product.ProductID
WHERE ( Production.Product.Name LIKE 'HL R%' )
AND ( YEAR(Sales.SalesOrderHeader.OrderDate) IN ( 2001, 2002 ) )
GROUP BY Production.Product.Name,
YEAR(Sales.SalesOrderHeader.OrderDate)
ORDER BY YEAR,
Name
注意,我包含了where以便可以很容易地看到总列数。
为了解决上面的问题,我们要做的是在查询里包含总的值而不是从报表服务中获取这个值。
SELECT ISNULL(YEAR(Sales.SalesOrderHeader.OrderDate), '9999') AS YEAR,
ISNULL(Production.Product.Name, 'Total') AS Name,
SUM(Sales.SalesOrderDetail.LineTotal) AS Amt
FROM Sales.SalesOrderDetail
INNER JOIN Sales.SalesOrderHeader ON Sales.SalesOrderDetail.SalesOrderID = Sales.SalesOrderHeader.SalesOrderID
INNER JOIN Production.Product ON Sales.SalesOrderDetail.ProductID = Production.Product.ProductID
WHERE ( Production.Product.Name LIKE 'HL R%' )
AND ( YEAR(Sales.SalesOrderHeader.OrderDate) IN ( 2001, 2002 ) )
GROUP BY Production.Product.Name,
YEAR(Sales.SalesOrderHeader.OrderDate)
WITH CUBE
ORDER BY YEAR,
Name
CUBE操作器将为你提供一年的总值和产品名称。这个查询的问题是我们不能有一个“总”标签用于年和总值,因为年是整数。因此,在上面的查询中包括了一个随意的9999。
对于总值,你可能需要写一个简单的表达式,例如IIF(Fields!Year.Value="9999","Total",Fields!Year.Value)。
这时你将获得的与上面相同的输出。
SQL Server报表服务中的一些常见小问题
接下来是编写一个表达式用于导航。
http://www.mscto.com
=SWITCH(Fields!Year.Value<>9999ANDFields!Name.Value<>"Total","SubReport")
在上面的表达式中,检查了年和名称总值,以便它不会导航到子报表。