Jonathan

Jonathan Harding's Blog

Colour-coding SQL Server Management Studio Connections

Have you ever wished there was an easier way to tell at a glance what server a particular Management Studio query window was connected to by, say, colouring the query window?   Well it turns out there is a way to do this baked into Management Studio, it’s just fairly well hidden.  Here’s how you do it:

 

  • Open Management Studio and press Ctrl + Alt + G to open the Registered Servers window.
  • Right-click the ‘Local Server Groups’ folder and select ‘New Server Registration’.
  • In the popup dialog, on the ‘General’ tab, enter the login information and give the registered server a name (e.g. ‘Production’).
  • To to the ‘Connection Properties’ tab, the tick the ‘Use Custom Color’ box.  Click ‘Select’ to the right of it.
  • Choose a colour for this server (I like to use green for Test, blue for SIT/UAT and red for Production).
  • Finally, click ‘Save’.

That’s it!  The next time you connect to the server you’ll see that the bottom information bar of the query window is now whichever colour you chose.  I know this won’t be for everyone, but for me it makes it much easier to see at a glance which server I’m connected to.

Removing Duplicates in SSRS SUMs

Just a quick post on something that I recently had to remember how to do and thought would be useful to write down and share.

The Problem

I have a dataset which duplicates some values, something like this:

image

Here I’ve got two product lines that each have two narratives associated with them.  I am required to SUM the price column for a totals rows in a table displaying the product lines, so that I get an output in my report something like this:

image

By default, SSRS will use a SUM expression for the ‘Price’ column (SUM(Fields!Price.Value)), but in this instance it will give the wrong answers (21.00, 41.98, 62.98) because the product lines are being doubled up by their narratives.  Ordinarily you might break the narratives out into their own data set, but sometimes that is not possible.  So how do I get the correct answer?

The Solution

The expressions in the ‘Price’ column should be set to:

=SUM(IIF(Fields!NarrativeNum.Value = 1, Fields!Price.Value, 0))

This expression checks for the value of NarrativeNum on the current row and only SUMs the Price value if NarrativeNum == 1, otherwise we substitute a 0.  In effect, the SUM sees our dataset like this:

image

Summary

We’ve seen how we can use a combination of functions to prevent SSRS from counting duplicate values.  This is a simple technique but can be very useful.

Maintaining Table Width with Hidden Columns in SSRS

Sometimes when using SSRS to create documents such as invoices, it is necessary to include columns in a table which may or may not be shown.  Often with these documents layout is very important, and having a table changing width as columns are shown or hidden is unacceptable.  It is not possible in SSRS to give a table a fixed width, but we can use another method to simulate this without having to dig into the RDL file.

We’ll make a report that contains a table made up of 5 columns:

Product
(Static)
Colour
(Variable)
Weight
(Variable)
Delivery Date
(Variable)
Price
(Static)

Columns labelled as ‘static’ are always shown on the report, and those labelled ‘variable’ may be optionally shown or hidden by the user at run-time.  In order to make it appear that the table maintains a constant width when some columns are hidden

Test Data

First we need some test data for out report.  Use the following script to set up a small table and sample data for use in the example:

   1: CREATE TABLE [dbo].[InvoiceProducts]
   2: (   Product         VARCHAR(20) NOT NULL,
   3:     Colour          VARCHAR(10) NOT NULL,
   4:     Weight          NUMERIC(10,2) NOT NULL,
   5:     DeliveryDate    DATETIME NULL,
   6:     Price           NUMERIC(10,2) NOT NULL);
   7:  
   8: INSERT INTO [dbo].[InvoiceProducts] VALUES ('Milk','White',1500,'2013-05-13',1.45);
   9: INSERT INTO [dbo].[InvoiceProducts] VALUES ('Beans','Orange',450,NULL,0.75);
  10: INSERT INTO [dbo].[InvoiceProducts] VALUES ('Eggs','Yellow',500,'2013-06-01',1.75);

Creating the Report

Open Visual Studio and create a new blank SSRS report.  Add a data source connection to the database where you created the InvoiceProducts table, then create a new data set using:

   1: SELECT
   2:     Product
   3:    ,Colour
   4:    ,Weight
   5:    ,DeliveryDate
   6:    ,Price
   7: FROM
   8:     dbo.InvoiceProducts

Add a table to the report body, then map the data set fields to the table in the order of the SELECT statement.  Your table should look like this:

image

Next we need to add parameters to control the visibility on the Colour, Weight and Delivery Date columns.  These should be set to a type of ‘boolean’ with the default value set to true.  Finally, set the visibility on each of the variable columns to be controlled by the parameters you’ve just added.  To do this, left-click into one of the column cells, then right-click on the solid grey bar above the column header.  Select ‘column visibility’, then select the radio button labelled ‘Show or hide based on an expression’.  Click the ‘Fx’ button to bring up the expression editor, then enter an expression like:

   1: =Not(Parameters!ShowColour.Value)

Do this for each of the columns for which we need to control the visibility, using the appropriate parameter for each column.

If we test the report now, firstly with all of our parameters set to ‘true’ we should see all the columns:

image

Now set the ‘Show Colour’ parameter to false and run again.  The column is hidden and the table width is reduced by the column width:

image

To prevent this behaviour and maintain a constant table width, we need to insert and extra blank column for each column that may be conditionally hidden.  In the designer, insert 3 columns to the left of the ‘Colour’ column:

image

By setting the ‘column visibility’ properties of these columns to give the opposite effect to the columns containing our data, we can add a blank column for each data column we hide and thus maintain total table width (given that the widths of a blank column and its corresponding data column are the same).  Starting with the left-most blank column, set the column visibility property to:

   1: =Parameters!ShowColour.Value

Set the other blank columns visibility using the parameters for weight and delivery date.  The effect is the when one of the parameters is set to ‘false’ (i.e. do not show the column), the data column will be hidden, but a blank column of the same width will be shown.  With suitable formatting, this will be invisible to the end user and will give the impression that the table width stays the same.  Below shows the report with all columns showing, and then with the colour column hidden:

image image

You could merge the ‘Product’ data cells with the blank cells between it and the ‘Colour’ data cells and this would allow the product data to expand to fill the blank column.  As far as the end user is concerned, the product column has simply gotten wider.

Conclusion

For occasions when precision layout is important, maintaining the width of a table that contains optionally hidden columns may be necessary.  Using the method outlined in this post, I have shown how it is relatively easy to achieve this using standard SSRS components and simple expressions.  This is a much more sustainable solution than editing the RDL of a report at run-time!

Differences between Regular SSRS and SSRS for Dynamics AX 2012

I’ve spent quite a long time recently developing SSRS reports for Dynamics AX 2012.  In a change from AX 2009, the newer version now uses SSRS as it’s main reporting engine.  No doubt this is so that business with existing experience in the MS SQL stack can get on creating reports for their new AX 2012 installation without the need for re-training or bringing in extra people trained in X++.

So not a bad idea, however my experience has turned up a number of important differences between standard SSRS reports and those in Dynamics AX:

  • Placement of Report Code

In Dynamics AX 2012 SSRS reports you cannot use the code window found by going to the report properties.  The window is still there, and you can still type into it, but as soon as you build the report in AX the code will be stripped out of the report.  This is because when you build a report design within a Dynamics AX report model in Visual Studio, the process adds a custom code block to the report XML (a method to overwrite the report OnInit() method).  This wipes out any code you may have added.

Instead, you must add code as a new Data Method in the report model.  This can then be referenced in your report design like this: =MyMethod(arg1)

  • No ‘ResetPageNumbers’ Property on PageBreak

A great addition to SSRS 2008 was the ability to reset the page number global based on groups within your report.  This was handled by a property found by expanding out the ‘PageBreak’ property for the group.  Unfortunately this option does not exist in SSRS for Dynamics AX 2012.  There may be a good reason for this, and if anyone knows it please let me know!

To get around this I added a data method which keeps track of which group my report is on as it renders and resets the page number appropriately.

  • Conditionally Hidden Elements cause ConsumeContainerWhitespace Property to Fail

Suppose I have a one-page report that should fit onto an A4 page.  The report contains a data table and another section, which should appear at the bottom of the page regardless of the number of rows in the table.

whitespaceexample

To do this, I enclose the data table in a rectangle element which is sized such that the ‘bottom section’ is at the bottom of the page.  By setting the report property ‘consumecontainerwhitespace’ to true, I ensure that as the table grows it fills up it’s containing rectangle, but the ‘bottom section’ stays where it is.

This example would also work in AX, unless you place an element in the rectangle which can be conditionally hidden.  For example I may have a row in the table which should only be shown in certain conditions.  In standard SSRS, the report would continue to function as before, with the table consuming the rectangle and the rectangle staying the same size.  In AX however, the addition of the hidden row causes this behaviour to break and that rectangle to grow as rows are added to the table.

A workaround for this is to make the optional row ‘invisible’ by shrinking it and/or making the text and borders white so they don’t show up.

Absolute Positioning of Report Elements in SSRS

A client recently asked for an invoice document to be designed using SSRS.  The layout was pretty standard but for one important caveat:  The payment details section had to be in a fixed position at the bottom of the last page of the report, so a 3-page invoice should look like this example:

InvPg1 InvPg2 InvPg3

This could be achieved by putting the ‘Payment Details Section’ fields in the report footer, and setting their visibility based on the report page number. Unfortunately in SSRS there is only one footer for every page and it’s height cannot be varied between pages.  The client did not want to waste space in the report body, which is a side-effect of this method.

So, how to fix the position of a report element to the bottom of the report?

The basic premise of my solution was to incorporate a ‘hidden’ element into the report body. This element can grow or shrink dynamically to alter the distance between the last of the invoice lines and the payment details section, and thus appears to keep the payment details ‘fixed’ at the bottom of the report.  To do this, we need to know how many rows of data can fit on the first page of the report (r1):

InvEq1 

And how many we need to add to push the payment details section to the bottom of the next page (r2 + r3):

InvEq2

Once we have values for r1, r2 and r3, we need to add (r2+r3) additional rows to the invoice lines table on our layout.  These rows must be outside of all groupings on the table as we don’t want them repeated with the groups, we just want them to appear at the foot of the table.  To get the desired effect, we need to control the visibility on these additional rows such that given the total number of data rows in the table, enough additional rows are visible to ‘push’ the payment details section to the bottom of the last page.

To control the visibility of the additional rows, we need to add some code to the report:

   1:  Public Function RowHidden(countRows As Integer, thisRow As Integer) As Boolean
   2:   
   3:          Dim maxRowsPageOne, additionalRows As Integer
   4:          Const TOTALADDITIONALROWS As Integer = 35
   5:          maxRowsPageOne = 12
   6:   
   7:          If (countRows <= maxRowsPageOne) Then
   8:              If (thisRow <= (maxRowsPageOne - countRows)) Then 'Current row is inside visible range for first page
   9:                  Return False 'Show the row
  10:              Else
  11:                  Return True 'Hide the row
  12:              End If
  13:          Else
  14:              additionalRows = TOTALADDITIONALROWS - ((countRows - maxRowsPageOne) Mod TOTALADDITIONALROWS) 'This is key, determines count of rows to make visible
  15:              If additionalRows < TOTALADDITIONALROWS Then
  16:                  If thisRow <= additionalRows Then
  17:                      Return False 'Show the row
  18:                  Else
  19:                      Return True 'Hide the row
  20:                  End If
  21:              Else
  22:                  Return True 'Hide all extra columns
  23:              End If
  24:          End If
  25:   
  26:      End Function

This code needs to be called by every additional row in the invoice lines table using =Code.RowHidden(CountRows(), 1) where ‘1’ represents the number of the particular row, starting from the first additional row after the data rows.

And that’s all there is to it!  It should be noted that this method means the cells in the invoice line table cannot be allowed to grow, or the calculations for the number of additional rows to show will not work.  You also need to be very careful with any future changes to the layout of the report, as these could also affect those calculations.  It’s certainly worth seeing if there can be some compromise on the positioning of report elements to something that SSRS can handle better, but if not, then I hope this is useful!

 

Until next time…..