Reporting Services, Tablix currency formatting

With Reporting Services is number formatting so easy. You can continue with our series and get report from here, or just open your report and follow bellow instructions. There are 2 basic places in designer surface where to format figures in your report Tablix.

  1. Through Tablix Text Box Properties
  2. Properties Pane
Tip: if you would like to format more text boxes in Tablix you can do it in few ways.
  1. Select text boxes you would like to format by clicking mouse cursor holding Left shift and change formatting in Properties Pane. (this will work in case that all text boxes would have the same formatting, if one text box format differs from others you will not be allowed to change the format).
  2. You can change formatting in one text box and by copy paste action to other textboxes, formats are transferred.
  3. Download report and open it in a texts editor and replace formatting tags

Let’s look on the report we created in post SSRS Tablix – repeating header. We would like to format Sum Sales Amount column with currency.

Sales Report
Picture 1 Sales Report

Go to text box with Sum Sales Amount value. Choose for example the one from EnglishCountryName group, right click and select Text Box Properties from popup menu.

Textbox Properties
Picture 2 Textbox Properties

Text Box Properties window is opened, and you can set Currency formatting options. Click on Currency category and choose format you would like to apply. On picture bellow you can see that you have many options to set currency format: how many decimal places, if use 1000 separator, how to display negative numbers which currency symbol to display and where, etc. Regional setting of machine where Reporting Services are installed has impact on default settings in Text Box Properties window. Reporting Services supports regional formatting in different regional environments. For our report I left the default English settings.

Textbox Properties - Number currency format
Picture 3 Textbox Properties – Number currency format

After formatting the rest of text boxes with Sum Sales Amount you can see formatted report bellow.

Sales report - currency format
Picture 4 Sales report – currency format

In Properties Pane, Number section, Format property you can find currency formatting we set.

Properties Pane - Number format
Picture 5 Properties Pane – Number format

In other posts I will describe other scenarios and approaches to format data in reports.

Stay tuned.

SSRS Tablix – repeating header

When I started developing with Reporting Services I was confused when I would like to make Tablix header repeating on each page. I tried to manage it through Tablix properties and Row headers or Column headers section. But as states on MSDN in case of static groups in Tablix you should use another approach then in case of non-static members. Since I was working mainly with Tablix component, I worked with static members. So, I had to apply approach described in this post. For this scenario I will use report from post Grouping Tablix.

 Grouped Sales Report
Picture 1 Grouped Sales Report

Open the report and in design mode select Tablix, right click and select Tablix Properties from popup menu. In the dialog box you can see menu saying Repeat header rows on each page, Keep header visible while scrolling and the same menu for columns. When you check this checked-boxes in Row Headers or Columns Headers section and run the report again nothing happened. This setting would work in case of Matrix component. Because there is no need to have static groups. In case of Tablix report, headers are static groups. So, let’s close properties window and click on the Tablix again.

 Tablix Properties
Picture 2 Tablix Properties

At the bottom of designer or studio surface, in the Grouping pane, there is an arrow, on the right side and by clicking on it you can enable Advanced Mode (picture below). After enabling this feature you can see static groups in Grouping pane. When you click for example on first static group Row Groups panel you can see that Last Name text box on the left-top corner of the Tablix is selected.

Grouping Pane
Picture 3 Grouping Pane

Having Static Last Name group selected, you can see its properties in the Property pane (picture below). We need to set FixedData, KeepWithGroup and RepeatOnNewPage attribute as on picture below. FixedData = True, KeepWithGroup = After, RepeatOnNewPage=True.

Properties Pane
Picture 4 Properties Pane

Run report again and you can see header repeating on each page and staying on top when scrolling the report page. And that’s it. In next posts I will try on repeating groups in more details.

Sales Report with fixed header
Picture 5 Sales Report with fixed header

Download final report here ReportSalesGroupingFixedHeaders.

Extended Events – usage for logging

Since logging is fundamental functionality for many SQL applications, I still ask myself what the best approach is to make SQL log for your application. In lots of my solutions I got with SQL log table approach. But there could be other ways how to implement SQL logging mechanisms. I decided to use Extended Events functionality to implement such a solution. This is powerful feature of SQL server. There are many examples on internet web sites where you can find this functionality was implemented mainly for maintenance purposes or SQL server counters monitoring. But let’s try to find another usage for this feature.

Problem

Let’s develop error log solution for simple insert query. For our solution we will use extended events.

You can create extended events in SQL Management Studio -> Management->Extended Events section as you can see on picture bellow, in this post I will use T-SQL scripts.

Extended Events
Picture 1 Extended Events

In short:

  • Create testing database
  • Create log table
  • Create extended event session
  • Start extended event session
  • Simulate event which trigger log message
  • Get data from extended event
  • Put data into log table

At first, we prepare database with log table.

CREATE DATABASE LogTest
GO
USE LogTest
GO
IF OBJECT_ID('dbo.LogData') IS  NULL
CREATE TABLE dbo.LogData 
(sql_text NVARCHAR(1000),[log_datetime] DATETIME,[message] NVARCHAR(100))

The next step is to create extended event session. I named it LogTestSession. Since I would like to monitor error messages I will add object sqlserver.error_reported. It handles events connected to error messages triggered on the server. In Action section there are listed attributes we want to get from extended events. Where clause contains filters to our database (we get ID of our database) and then put it to this predicate (Because we would like to test it only on our database) Error number = 50000 means that we would like to check only user messages. As you can see there are many ways how to configure behaviour of extended events. There is not necessary to use filter and you can monitor all databases on the server and error messages. As target we use Ring buffer, it means that data will be written to memory. There is possibility to redirect extended event output to other destinations. I will not describe all possibilities in this post, more info can be found here:  https://bit.ly/2JEVDcV.

The last attribute, I used in this solution is MAX_DISPATCH_LATENCY = { seconds SECONDS | INFINITE } and here is definition directly from Microsoft sites https://bit.ly/2HCIHSD. Specifies the amount of time that events will be buffered in memory before being dispatched to event session targets. By default, this value is set to 30 seconds.

Bellow is T-SQL batch for creating extended event session.

CREATE EVENT SESSION LogTestSession ON SERVER 
    ADD EVENT sqlserver.error_reported  -- ,
      (
      ACTION (sqlserver.sql_text, sqlserver.tsql_stack, sqlserver.database_id,sqlserver.username)
       WHERE sqlserver.database_id = 33  AND [error_number]=50000 /*Filter error no or db number*/
     )   
    ADD TARGET package0.ring_buffer    
        (SET max_memory = 4096 
    ) 
WITH (max_dispatch_latency = 1 seconds)

In next step we start extended events. Since that time, we can test our mechanism.

ALTER EVENT SESSION LogTestSession ON SERVER
STATE = START

Let’s raise user message.

DECLARE @Message VARCHAR(100) = 'Hello word message '+ CONVERT(VARCHAR(20),GETDATE(),20)
RAISERROR(  @Message,1,1);
SELECT CONVERT(VARCHAR(20),GETDATE(),20)
DECLARE @Message VARCHAR(100) = 'Log message '+ CONVERT(VARCHAR(20),GETDATE(),20)
RAISERROR(  @Message,1,1);
SELECT CONVERT(VARCHAR(20),GETDATE(),20)

Now we get data from extended events. There are few useful catalos in SQL server. In the script bellow we get data from tables containing user messages and stage them into temporary table like XML.

IF OBJECT_ID('tempdb.dbo.#LogData') IS NOT NULL
 DROP TABLE #LogData;

SELECT
       CAST(LocksAcquired.TargetXml AS XML)  AS XMLData
   INTO
       #LogData
   FROM
       (
       SELECT
               CAST(t.target_data AS XML)  AS TargetXml 
           FROM
                    sys.dm_xe_session_targets  AS t
               JOIN sys.dm_xe_sessions         AS s

                   ON s.address = t.event_session_address
           WHERE
            
               s.name        = 'LogTestSession'
       )
           AS LocksAcquired;

Now let’s parse our data to our LOG table. In next step we can create one procedure from these two queries. IT would fit better in scenarios of next posts.

 
INSERT INTO  dbo.LogData  	(sql_text ,[log_datetime] ,[message]  )
SELECT
     T.XMLData.value('(action[@name="sql_text"])[1]',
            'varchar(1000)'     )  AS [SqlText],
         T.XMLData.value('(@timestamp)[1]',
            'datetime'     )  AS [OccurredDtTm],
         T.XMLData.value('(data[@name="message"])[1]',
            'varchar(60)'     )  AS [Message]
    FROM
        #LogData
    CROSS APPLY XMLData.nodes('/RingBufferTarget/event[@name="error_reported"]')  AS T(XMLData);

Look to our log table and voila. Log messages are there.

SELECT * FROM dbo.LogData
Result
Picture 2 Result

Clean up our solution.

IF EXISTS (SELECT *
      FROM sys.server_event_sessions  
     WHERE name = 'LogTestSession')
BEGIN
    DROP EVENT SESSION LogTestSession
          ON SERVER;    
END

In this post I demonstrate how Extended Events could be used for logging purposes. Be aware that this approach does not come with real-time solution but with the asynchronous one. So, it cannot fit to every solution. On the other hand Extended Events are not in scope of transaction. Furthermore, here raise other questions which could be answered in other posts.

  • Handling error messages within transactions
  • What about extended events and their performance?
  • Ring buffer is still increasing, do we have solution to clean it up?
  • It could be interesting to compare this solution with classic approach within transaction scope
  • Whatever idea you get….

Stay tuned, stay strong

PS: if you would like to play, you can download the whole script here ext_events_log.