I am going to provide detailed step-by-step instructions to create a report, as shown in, needed to satisfy business requirements. In the sections that follow we will discuss, firstly, what the Report Definition Language (RDL) is; secondly, how to bring data to a report; thirdly, how to design the report layout; fourthly, how to enable a report manager (or a database administrator) to modify the report; and finally, how to add an external image to the report.

Figure 1 - AdventureWorks Sales Order Detail report We are going to use data from the AdventureWorks sample database "AdventureWorks2017.bak" [3]. In practice, we usually have some business requirements for creating a report. For demonstration purposes, I extracted some requirements from the "Sales Order Detail" report in the SQL Server Reporting Services Product Samples [4]:
The accounting department at AdventureWorks would like to have a report to list all line items of one sales order. A sales order number is select by business users before they view the report. The report should include detailed information about a line item: line number, quantity, item number, description, tracking number, unit price, subtotal, discount and item total. Below the list is the total amount in the sales order. In addition, business users can print a letter-sized report.
The solution was tested with SQL Server Management Studio V17.4 and Microsoft Visual Studio Community 2017 on windows 10 Home 10.0 <X64>. The DBMS is Microsoft SQL Server 2017 Enterprise Edition (64-bit).
1 - Report Definition Language (RDL) RDL is an XML representation of a SSRS report definition that contains data retrieval and layout information for a report [5]. Like other programming languages that give instructions to a computer to perform specific tasks, the RDL tells the report server how to construct a report with data. Thus, we can apply some best practices from other programming languages to the report development.We can use Report Designer and Report Builder to create a report without knowing anything about RDL. It is noteworthy that the two tools are only RDL generators. We developers should have a programming mindset and need to apply programming principles to make a report flexible and adaptable. For example, we should use views or stored procedures to create datasets. When a table column has been changed, we do not need to update all reports that have referenced this column.
2 - Prepare Data Retrieval for the Report 2.1 Create a data access layer The business requirements asked us to bring a list of line items in a selected sales order number to the report. The line item information should include line number, quantity, item number, description, tracking number, unit price, subtotal, discount and item total. We are going to use two stored procedures, "sp_sales_order_read" and "sp_sales_order_detail_read" to retrieve a list of sales orders and a list of line items in a specific sales order from the database, respectively, on basis of the queries used in [4]: CREATE PROCEDURE [dbo].[sp_sales_order_read]AS
BEGIN
BEGIN TRY
SELECT SOH.SalesOrderID, SOH.SalesOrderNumber
FROM [Sales].[SalesOrderHeader] SOH
INNER JOIN Sales.Customer C ON SOH.CustomerID = C.CustomerID
INNER JOIN Sales.Store S ON C.StoreID = S.BusinessEntityID
ORDER BY SOH.SalesOrderNumber
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState );
RETURN 1
END CATCH
RETURN 0
END
GO
CREATE PROCEDURE [dbo].[sp_sales_order_detail_read]
@sales_order_id int
AS
BEGIN
BEGIN TRY
SELECT SOD.SalesOrderDetailID,
SOD.SalesOrderID,
SOH.SalesOrderNumber,
ROW_NUMBER() OVER(ORDER BY SOD.SalesOrderDetailID ASC) AS Line,
SOD.OrderQty AS Qty,
P.ProductNumber AS [Item Number],
P.[Name] AS [Description],
SOD.CarrierTrackingNumber AS [Tracking #],
SOD.UnitPrice AS [Unit Price],
(SOD.OrderQty*SOD.UnitPrice) AS Subtotal,
SOD.LineTotal,
CASE
WHEN SOD.UnitPriceDiscount IS NULL THEN 0
ELSE 0 - SOD.UnitPrice * SOD.OrderQty * SOD.UnitPriceDiscount
END AS Discount
FROM Sales.SalesOrderDetail SOD INNER JOIN
Production.Product P ON SOD.ProductID = P.ProductID INNER JOIN
Sales.SalesOrderHeader SOH ON SOD.SalesOrderID = SOH.SalesOrderID
WHERE SOH.SalesOrderID = @sales_order_id
ORDER BY SOD.SalesOrderDetailID
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState );
RETURN 1
END CATCH
RETURN 0
END
GO These two stored procedures construct a data access layer which is placed between the SQL server database and the report server. With the data access layer, DBAs only need to grant EXECUTE permissions on these stored procedures without having a risk of exposing underlying tables [6]. In addition, I have created several derived columns in the second stored procedure rather than asking the report to do the calculations. I would like to point out that the report, like the view component in the MVC pattern, should not contain any business logics. 2.2 Create a Shared Data Source
In a similar way as .Net developers put database connection strings in the config file, we will create a shared database source for all reports. This enables us to configurate database connections for all reports in one central place.
2.2.1 Create a Report Server projectCreate an empty Report Server Project in Visual Studio 2017 by selecting the "Report Server Project" template as shown in. In my experience, it is more effective to use the WYSIWYG feature of the Report Designer than the wizard template.

Figure 2 - Create a Report Server project in Visual Studio




