Example of use of the CrossTab stored procedure

February 10, 2009 by libertatis

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:        Fernando Fernandez
-- Create date: 2/10/09
-- Description:    Cross Tab summary report by Business Unit
-- =============================================
ALTER PROCEDURE BUSummaryReportCrossTab
@BusinessUnitId int = 0,
@BeginDt datetime,
@EndDt datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

DECLARE @SqlSelect    varchar(8000)

– embed the parameters into the sql
SET @SqlSelect = ‘SELECT oeb.LAST_NAME , oeb.FIRST_NAME
, t.ID, t.DateOfService, t.Score, t.HoldFailed
, qs.Score AS QuadrantScore
, it.Description, it.SortOrder
FROM TRIP t
, ORACLE_EMPLOYEE_BU oeb
, QUADRANT_SCORE qs
, INFORMATION_TYPE it
WHERE t.AuditedFlag = 1
AND t.BUSINESS_UNIT_ID = ‘ + CONVERT( VARCHAR(5), @BusinessUnitId ) + ‘
AND t.DateOfService BETWEEN ”’ + CONVERT( VARCHAR(10), @BeginDt, 101 ) + ”’ AND ”’ + CONVERT( VARCHAR(10), @EndDt, 101 ) + ”’
AND t.EMPLOYEE_NUMBER = oeb.EMPLOYEE_NUMBER
AND t.ID = qs.TRIP_ID
AND qs.INFORMATION_TYPE_ID = it.ID
UNION
SELECT oeb.LAST_NAME , oeb.FIRST_NAME
, t.ID, t.DateOfService, t.Score, t.HoldFailed
, t.Score AS QuadrantScore
, ”Overall”, -1
FROM TRIP t
, ORACLE_EMPLOYEE_BU oeb
WHERE t.AuditedFlag = 1
AND t.BUSINESS_UNIT_ID = ‘ + CONVERT( VARCHAR(5), @BusinessUnitId ) + ‘
AND t.DateOfService BETWEEN ”’ + CONVERT( VARCHAR(10), @BeginDt, 101 ) + ”’ AND ”’ + CONVERT( VARCHAR(10), @EndDt, 101 ) + ”’
AND t.EMPLOYEE_NUMBER = oeb.EMPLOYEE_NUMBER ‘;

– the stored proc below converts the result set of the given SQL into a crosstab
execute CrossTab
@Select= @SqlSelect
, @PivotCol=’YEAR( DateOfService ) * 100 + MONTH( DateOfService )’
, @Summaries=’AVG( QuadrantScore ELSE 0 )[], AVG( Score )[Overall], COUNT( ID )[TotalPCRsScored] ‘
, @GroupBy=’LAST_NAME, FIRST_NAME, Description’
, @NumberPivotCols = 0
–, @OtherCols=”

– version using PIVOT keyword – it doesn’t work as good as the stored proc version
–SELECT LAST_NAME, FIRST_NAME, Description, SortOrder, [200809], [200810], [200811], [200812], [200901], [200902]
–    –, Score–, HoldFailed
–FROM (

–SELECT oeb.LAST_NAME, oeb.FIRST_NAME
–    , YEAR( t.DateOfService ) * 100 + MONTH( t.DateOfService ) AS YearMonth
–    , qs.Score AS QuadrantScore
–    , it.Description, it.SortOrder
–FROM TRIP t
–    , ORACLE_EMPLOYEE_BU oeb
–    , QUADRANT_SCORE qs
–    , INFORMATION_TYPE it
–WHERE t.AuditedFlag = 1
–    AND t.BUSINESS_UNIT_ID = 175 –{0}
–    AND t.DateOfService BETWEEN ‘11/1/08′ AND ‘2/28/09′ –’{1}’ AND ‘{2}’
–    AND t.EMPLOYEE_NUMBER = oeb.EMPLOYEE_NUMBER
–    AND t.ID = qs.TRIP_ID
–    AND qs.INFORMATION_TYPE_ID = it.ID
–UNION
–SELECT oeb.LAST_NAME, oeb.FIRST_NAME
–    , YEAR( t.DateOfService ) * 100 + MONTH( t.DateOfService ) AS YearMonth
–    , t.Score AS QuadrantScore
–    , ‘Overall’ AS Description, -1
–FROM TRIP t
–    , ORACLE_EMPLOYEE_BU oeb
–WHERE t.AuditedFlag = 1
–    AND t.BUSINESS_UNIT_ID = 175 –{0}
–    AND t.DateOfService BETWEEN ‘11/1/08′ AND ‘2/28/09′ –’{1}’ AND ‘{2}’
–    AND t.EMPLOYEE_NUMBER = oeb.EMPLOYEE_NUMBER
–) AS pv
–    PIVOT ( AVG( QuadrantScore ) FOR YearMonth IN ( [200809], [200810], [200811], [200812], [200901], [200902] ) ) AS ScoresPivot
–ORDER BY LAST_NAME, FIRST_NAME, SortOrder

END
GO

Stored Procedure to return results as a CrossTab

February 10, 2009 by libertatis

-- this proc executes your sql and transform the result set into a crosstab
alter procedure CrossTab (@Select varchar(1000),    -- your select statement
@PivotCol varchar(100),        -- expression you want to display as the column in the crosstab
@Summaries varchar(100),    -- expressions you want to display as the cells in the crosstab
@GroupBy varchar(100),        -- expressions you want to display as the rows (left side) in the crosstab
@NumberPivotCols bit = 0,    -- set to 1 if you want to number the pivot columns (Col_1Prod, Col2_Prod,... instead of TinfoilProd, BaseballProd,...)
@OtherCols varchar(100) = Null)    -- (OPTIONAL) other expressions you want to display as the rows (left side) in the crosstab but not grouped by them

– please see http://weblogs.sqlteam.com/jeffs/archive/2005/05/02/4842.aspx

– see diagram below:
–        CROSS TAB
–    ———————————————————————————————
–    |                |                | @PivotCol    #1        | @PivotCol    #2        | @PivotCol    #3        |
–    ———————————————————————————————
–    | @GroupBy cols    | @OtherCols    | @Summaries values    | @Summaries values    | @Summaries values    |
–    ———————————————————————————————
–    | @GroupBy cols    | @OtherCols    | @Summaries values    | @Summaries values    | @Summaries values    |
–    ———————————————————————————————
–    | @GroupBy cols    | @OtherCols    | @Summaries values    | @Summaries values    | @Summaries values    |
–    ———————————————————————————————

–    EXAMPLE (the original SELECT returned Products in rows):
–    ———————————————————————
–    |                    | Product 1        | Product 2        | Product 3        |
–    ———————————————————————
–    | 2008    | January    | Sum Sales        | Sum Sales        | Sum Sales        |
–    ———————————————————————
–    | 2008    | February    | Sum Sales        | Sum Sales        | Sum Sales        |
–    ———————————————————————
–    | 2008    | March        | Sum Sales        | Sum Sales        | Sum Sales        |
–    ———————————————————————
AS

set nocount on
set ansi_warnings off

– declare variable to hold each of the pivot values (eg.:  01_2008, 02_2008, …)
declare @Vals varchar(8000);
set @Vals = ”;

– @OtherCols is optional
set @OtherCols= ISNULL( ‘, ‘ + @OtherCols, ” )

– this table will hold all pivot values in order to have a name for each pivot column
create table #temp (ID int identity, PivotCol varchar(100) )

– collect all distinct pivot values it can find on the original SELECT @Select
– by running the following statement:  select distinct convert(varchar(100), Product ) as PivotColumn FROM ( … my select ) A
insert into #temp
exec (’select distinct convert(varchar(100),’ + @PivotCol + ‘) as PivotColumn FROM (‘ + @Select + ‘) A’)

– this is to take care of any single quotes and NULLs in the data (eg.:  O’Brien)
update #temp
set PivotCol = REPLACE( ISNULL( PivotCol, ” ), ””, ””” )

if @NumberPivotCols = 0
begin
– concatenate a list of pivot column names from the temp table into a single string
– in the format (per Summary column)
– eg.:        AVG( CASE WHEN Product = ‘Tinfoil’ THEN Sales END ) [Tinfoil],
–            AVG( CASE WHEN Product = ‘Baseball’ THEN Sales END ) [Baseball], … and so forth
select @Vals = @Vals + ‘, ‘
+ REPLACE(
REPLACE( @Summaries,
‘(‘,
‘(CASE WHEN ‘ + @PivotCol + ‘=”’ + PivotCol + ”’ THEN ‘ ),
‘)[',
' END) as [' + PivotCol )
from #Temp
order by PivotCol
end
else
-- concatenate a list of pivot column names from the temp table into a single string
-- in the format (per Summary column)
-- eg.:        AVG( CASE WHEN Product = 'Tinfoil' THEN Sales END ) [Col_1],
–            AVG( CASE WHEN Product = ‘Baseball’ THEN Sales END ) [Col_2], … and so forth
select @Vals = @Vals + ‘, ‘
+ REPLACE(
REPLACE( @Summaries,
‘(‘,
‘(CASE WHEN ‘ + @PivotCol + ‘=”’ + PivotCol + ”’ THEN ‘ ),
‘)[‘,
‘ END) as [Col_’ + CONVERT( VARCHAR(2), ID ) )
from #Temp
order by PivotCol

drop table #Temp

– build and execute a SELECT statement that will bring all the required data in a crosstab arrangement
exec ( ’select ‘ + @GroupBy + @OtherCols + @Vals +
‘ from (‘ + @Select + ‘) A GROUP BY ‘ + @GroupBy)

set nocount off
set ansi_warnings on

Installing/creating Merge Modules for Crystal Reports with Visual Studio 2005

January 29, 2009 by libertatis

It used to be a pain to get the instructions below since Business Objects bought Crystal Reports (and in its turn was bought by SAP, I don’t know which was first) and destroyed all the helpful links to forum discussions and downloads.
Now I hope people can quickly find these instructions below and not have to spend too much time searching for it like I had to do. Thanks to Mitesh!

First,  download Merge Modules for VS 2005 from this ridiculously long URL at SAP.com:

www.sdn.sap.com/…/sap(bD1lbiZjPTAwMQ==)/bc/bsp/spn/scn_bosap/notes%7B6163636573733d36393736354636443646363436353344333933393338323636393736354637333631373036453646373436353733354636453735364436323635373233443330333033303331333233323330333133323336%7D.do

OR this simple link at their German web site:

https://smpdl.sap-ag.de/~sapidp/012002523100005853292008E/cr_net_2005_mm_mlb_x86.zip

Unzip the file and extract the CrystalReportsRedist2005_x86.msm file to your Merge Module directory, usually at C:\Program Files\Common Files\Merge Modules

Next, create a Setup project on your Visual Studio solution

Then click Add Merge Module and select the merge module file you extracted earlier.

Build it in Visual Studio.

Now run the .MSI file (or the .EXE, I think either will work) on your server to install the CR modules in it.

Sometimes, the install will not work 100% and the icons will be missing from your report preview page.

In order to fix that, the C:\Inetpub\wwwroot\aspnet_client has to be copied from a good server (that is, a server where the Crystal Reports preview icons are working) to the problem server and a virtual directory has to be created in IIS:  aspnet_client/2_0_50727/CrystalReportWebFormViewer3

This is where the CR setup was supposed to put the icons, scripts and style sheets required to preview a report in the browser. I think the problem happens due to a permissions issue in some servers.

Getting Only the Date Part of a Date/Time Stamp in SQL Server

January 21, 2009 by libertatis

Original article at http://www.bennadel.com/blog/122-Getting-Only-the-Date-Part-of-a-Date-Time-Stamp-in-SQL-Server.htm

I got a question the other about getting the date-only part of date/time stamp in SQL server. In my experience there are two ways to go about doing this. Both require casting data types from one type to another. The first example is how I used to go about performing this task before I understood how dates were represented numerically. The second method is related to my understanding of ColdFusion dates and timespans and fortunately transferred well to SQL.

First, the more verbose method. This requires breaking the date into its year, month, and day parts, putting them together in “yyyy/mm/dd” format, then casting that back to a date. This method involves 7 method calls including the final CAST(), not to mention string concatenation (which we all know is wicked slow).

Launch code in new window » Download code as text file »

  • CAST(
  • (
  • STR( YEAR( GETDATE() ) ) + ‘/’ +
  • STR( MONTH( GETDATE() ) ) + ‘/’ +
  • STR( DAY( GETDATE() ) )
  • )
  • AS DATETIME
  • )

The second method is much much cleaner. It uses only 3 method calls including the final CAST() and performs no string concatenation, which is an automatic plus. Furthermore, there are no huge type casts here. If you can imagine that Date/Time stamps can be represented, then converting from dates to numbers and back to dates is a fairly easy process (hopefully).

Launch code in new window » Download code as text file »

  • CAST(
  • FLOOR( CAST( GETDATE() AS FLOAT ) )
  • AS DATETIME
  • )

In the example above, we are converting the date/time stamp to its float form. So today (when this was posted), this would yield something like 38903.745537114199. Days are not fractional, meaning that decimal places represent fractions of a day (in hours, minutes, seconds). Then, in order to get the days part (trim off the time portion of the date/time stamp) we are FLOOR()’ing the float value. This will give us the numeric representation of the DAY-only date. Then, we simply cast that back to DATETIME format and there you have it, a date-only date/time stamp.

If you want to compare, try running this:

Launch code in new window » Download code as text file »

  • SELECT
  • – Get the full date/time stamp as a base.
  • (
  • GETDATE()
  • ) AS date_time_part,
  • – Trying casting to a string then back to a date.
  • (
  • CAST(
  • (
  • STR( YEAR( GETDATE() ) ) + ‘/’ +
  • STR( MONTH( GETDATE() ) ) + ‘/’ +
  • STR( DAY( GETDATE() ) )
  • )
  • AS DATETIME
  • )
  • ) AS date_only_part,
  • – Try casting to float, rounding, and back to date.
  • (
  • CAST(
  • FLOOR( CAST( GETDATE() AS FLOAT ) )
  • AS DATETIME
  • )
  • ) AS date_only_part2,
  • – Try casting just to float to see what it looks like.
  • (
  • CAST( GETDATE() AS FLOAT )
  • ) AS float_value,
  • – Try flooring to see the intermediary step.
  • (
  • FLOOR( CAST( GETDATE() AS FLOAT ) )
  • ) AS int_value

As far as performance is concerned, there is no big surprise here. The second method has fewer function calls, no string concatenation, and in my opinion is a much more natural casting idea. In my testing on a table with several thousand records, the second method generally executed in a fraction of the time that string concatenation method executed.

On 3,000 rows (based on CFTimer execution time):

Average time of method one: 115ms

Average time of method two: 16ms (HUGE performance increase)

Additionally, in testing, method one has some runs that were all over the place; very large execution times. Method two, on the other hand, consisently performed at the same speed, give or take a few ms.

Construct a comma delimited list with SQL

January 21, 2009 by libertatis

Construct a comma delimited list from table column with SQL

by author Magz
How many times have you had to create a comma delimited list from a table column? Do you ever remember how to do it? I don’t. Here is a little snippet I find useful:

-- this will be a comma delimited list of location ids
DECLARE @LocationIds Varchar(Max)
-- set it to an empty string to start with
SET @LocationIds = ''
 
--add commas
SELECT @LocationIds = COALESCE(@LocationIds + ',','') + CAST(Location_ID AS VARCHAR) FROM Locations_Table
--get rid of the comma at the front of the string
SELECT SUBSTRING(@LocationIds, 2, Len(@LocationIds))

Result: 1234,3445,6778,6789,…

From the web site http://www.objectreference.net/

Missing menu options in Visual Studio 2005?

October 20, 2008 by libertatis

We recently upgraded to Windows Vista, using a new standardized image containing a standard set of developer tools. When I started Visual Studio the first time, I just accepted the default options. I noticed today that the Exceptions… option was missing from the Debug menu:

Debug menu without Exceptions option

Initially, I found this to be a bit of a mystery. How could an important menu item like that just not be there? Was it some sort of configuration issue? After looking into this a bit, I found a post that explained that this is usually due to Visual Studio profile issues. Using the information in this post, I was able to get the option back, as follows.

The issue is that the default profile I selected when I first started Visual Studio is different than the standard C# profile. Some non-C# profiles hide more advanced menu options to make Visual Studio appear simpler. In order to get the menu items back, I needed to change profiles.

In Visual Studio, first click Tools, then click Import and Export Settings… as shown below:

Tools Import and Export Settings option

In the Import and Export Settings Wizard, click Import selected environment settings, and click Next>:

Import and Export settings wizard

Next, tell it to save your existing settings, and click Next >:

Import and Export settings wizard step 2

Now tell it to import the Visual C# Development Settings (the post I found mentioned that everything is available in that settings configuration) and click Next >:

Import and Export settings wizard step 3 - choose new settings

The last step is to choose the settings to import–I took the defaults here, as compromising my environment, computer, or data didn’t sound like good options:

Import and Export settings wizard step 4 - choose settings to import

This completed successfully:

Import and Export settings wizard step 5 -- complete

And now, the Debug menu contains the Exceptions… menu item, as well as a few other additional items:

Debug menu WITH Exceptions option

Note that now all the fonts and other customized settings are all back to the default values, so I need to update these by hand. I could have selected only some of the items in the Visual C# Development Profile to import, but I frankly don’t know what some of the items mean, so I’d rather just rebuild my settings around the C# profile.

Going forward, I’ve exported a copy of my settings, so that I can easily get back to a known good configuration.

IE6 left margin problem

September 29, 2008 by libertatis

ie6 float left margin problem

The “IE6 double margin” bug:  if you have some css like this:

div#example {
    float:left;
    margin-left:100px;
}

IE6 will make the left margin about 200px, NOT 100px. So how do you fix it? Add “display:inline;” and, magically, IE6 decides that you want a 100px margin and not some random arbitrary number that you never wrote. And, yes, this works properly in IE7, Safari, and Firefox.

Great tip from award tour.

Javascript formatter

September 28, 2008 by libertatis

If you paste a copy of the script into the form linked below and then select the “Clean” button then a nicely formatted copy will be displayed in a separate window that uses consistent indenting and has all of the reserved words, predefined objects, comments, and literals all colour coded to make the code easier to follow.

Found at:

http://javascript.about.com/library/blformat.htm

SQL Server 2005 CTE (Common Table Expression)

June 4, 2008 by libertatis

SQL Server 2005 CTE (Common Table Expression)

By Bihag Thaker

CTE is a new feature provided by Microsoft SQL Server 2005. In real world, we often need to query hierarchical data from the database. For example, to get a list of hierarchical list of all the employees, list of product categories etc. CTE fulfills this requirement and let us query the database recursively.

Demo Builder – Create Flash Presentations
Create interactive Flash movies that allow you to show how applications and systems work. Download a FREE trial now.

Let’s us do this practically. Assume that we store different categories of computer books and any category can have sub-categories. For this, we will create a table named tblCategories with the following structure and insert some categories into this table as shown below:

Create Table tblCategories
(
CategoryID Int Constraint PK_tblCategories_CategoryID Primary Key,
CategoryName VarChar(100),
ParentCategoryID Int Constraint FK_tblCategories_ParentCategoryID References tblCategories(CategoryID)
)

GO

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(1,’Languages’,Null)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(2,’Networking’,Null)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(3,’Databases’,Null)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(4,’Visual Basic’,1)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(5,’C#’,1)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(6,’Java’,1)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(7,’VB.Net’,4)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(8,’VB 6.0′,4)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(9,’Desktop Application Development with VB.Net’,7)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(10,’Web Application Development with VB.Net’,7)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(11,’ActiveX Objects and VB 6.0′,8)

Insert Into tblCategories(CategoryID,CategoryName,ParentCategoryID) Values(12,’Network Security’,2)

Now if you query the database with the following SELECT command,

Select * From tblCategories Where CategoryID = 1

You will get the following result:

CategoryID CategoryName ParentCategoryID
———– —————- —————-
1 Languages NULL

(1 row(s) affected)

No surprise! But what, if you want to get the list of all the categories/sub-categories falling under the root category ‘Languages’? To do this, you will need to perform a recursive query and to do that we use CTE.

Let’s do this with the help of CTE. To create CTE we will use the following syntax:

With cteCategories
AS (
Select CategoryID,CategoryName,ParentCategoryID
From tblCategories
Where CategoryID=1

Union All

Select C.CategoryID,C.CategoryName,C.ParentCategoryID
From tblCategories As C Inner Join cteCategories As P On C.ParentCategoryID = P.CategoryID

)

Select CategoryID,CategoryName,ParentCategoryID From cteCategories

Run the above query and see the result as shown below:

CategoryID CategoryName ParentCategoryID
———– —————————————————- —————-
1 Languages NULL
4 Visual Basic 1
5 C# 1
6 Java 1
7 VB.Net 4
8 VB 6.0 4
11 ActiveX Objects and VB 6.0 8
9 Desktop Application Development with VB.Net 7
10 Web Application Development with VB.Net 7

(9 row(s) affected)

SQL Server 2005 Paging Performance Tip

June 4, 2008 by libertatis