SQL Server

You are currently browsing articles tagged SQL Server.

If you’re interested in seeing the next Service Pack for SQL Server 2008 released, vote for it at this Microsoft Connect page!  Now that the release date for R2 has been announced, hopefully the next one is for SQL 2008 Service Pack 2.

If you’re still on 2005 and thus more interested in an SP4 for that version, it has also been posted at Connect.

How did I find out?  Through the following blog posts at SSQA.NET:

SQL Server 2008 SP2

SQL Server 2005 SP4

Update (27 Feb 2010):

SQL Server 2008 SP2 is scheduled for Q3 2010.

SQL Server 2005 SP4 is scheduled for Q4 2010.

See this post on the Microsoft SQL Server Release Services blog for details.

  • Share/Bookmark

Tags: , ,

image

For the second year in a row, the Belgian SQL Server User Group organizes the SQL Server Day.

Like last year, the event will take place at Utopolis Mechelen.

If you’re ready for a day filled with learning and networking opportunities and you can make yourself available at December 3, register at www.sqlserverday.be!

There will be some interesting speakers such as:

  • Chris Webb – SSAS and Gemini
  • Henk van der Valk – SSIS world record
  • Dirk Gubbels – Change tracking
  • my colleagues Luc Lemaire and Mario Van Hissenhoven – Report Builder 3.0

If you’re attending as well and would like to meet up for a chat, don’t hesitate to post a comment here!

See you there!

  • Share/Bookmark

Tags: , ,

Initially I was going to call this article “Struggling With Collation: The SeQueL”, but it just doesn’t have the same ring to it as “Fun With Strings”.  In that previous article I showed how you might get different results when loading data from a temporary table or table variable and I suggested that one way of solving this is by switching your data type to nvarchar.

Unicode Or Not?

Reason #1 For Not

Today I’m going to show you that nvarchar is not always what we want to use, especially if we don’t need to support Unicode strings.  Imagine a staging scenario when loading a data warehouse.  Often the Business Keys (BK) are strings, and depending on the source system, sometimes very long strings – I’ve seen situations with a combined business key of over 500 bytes!  (You can’t imagine what some data sources look like but that’s another story.)  Do we really want to convert these to Unicode, and thus double their size?  Furthermore, to improve lookups we put indexes on those BKs.  These indexes would double in size as well.  So no, we don’t really want to make these fields Unicode, and certainly not when we want our ETLs to perform as fast as possible.

Reason #2 For Not

That was reason number one why nvarchar is not always the solution.  And here comes reason number two.  In my scenario, the source tables are located in an Oracle database.  And guess what: by default Oracle’s ORDER BY behaves different than SQL Server’s ORDER BY (when using the regular Latin1_General_CI_AS or SQL_Latin1_General_CP1_CI_AS collations)!  By default Oracle uses binary string comparison to sort its data and the reason for it appears to be that that’s the only way to prevent a full table scan.  I’m no Oracle expert but that’s what the documentation states.

Here’s a little demonstration.  The following script prepares a table variable and selects the data from it, sorted ascending.

declare @tbl table( col1 varchar(20));
insert into @tbl select ‘AA’;
insert into @tbl select ‘A’;
insert into @tbl select ‘A-’;
insert into @tbl select ‘A A’;
insert into @tbl select ‘BA’;
insert into @tbl select ‘0′;
insert into @tbl select ‘1′;
insert into @tbl select ‘-0′;
insert into @tbl select ‘-1′;
insert into @tbl select ‘A0′;
insert into @tbl select ‘0A’;
insert into @tbl select ‘-A’;
insert into @tbl select ‘-B’;
insert into @tbl select ‘a’;
insert into @tbl select ‘b’;
insert into @tbl select ‘ ‘;

select * from @tbl
order by col1 asc;

I have executed it once just as stated above (while connected to a database that uses the SQL_Latin1_General_CP1_CI_AS collation) and once more while using nvarchar as data type for the column in the table variable.  The first execution will sort the data using a non-Unicode sorting algorithm, while the second execution will order the data according to the Unicode sorting method.  The results will be shown further below for easier comparison.

On Oracle I performed a similar procedure, as shown in following script.

select cast(‘AA’ as varchar(20)) as col1 from Dual union
select ‘A’ as col1 from Dual union
select ‘A-’ as col1 from Dual union
select ‘A A’ as col1 from Dual union
select ‘BA’ as col1 from Dual union
select ‘0′ as col1 from Dual union
select ‘1′ as col1 from Dual union
select ‘-0′ as col1 from Dual union
select ‘-1′ as col1 from Dual union
select ‘A0′ as col1 from Dual union
select ‘0A’ as col1 from Dual union
select ‘-A’ as col1 from Dual union
select ‘-B’ as col1 from Dual union
select ‘a’ as col1 from Dual union
select ‘b’ as col1 from Dual union
select ‘ ‘ as col1 from Dual
order by col1 asc;

The Oracle script doesn’t use a table variable, it just creates a result set using several select statements with a union in between.  But for our test that doesn’t matter, the results using this method are suitable.

In the table below you can see the result of the three executions.

SQL non-Unicode SQL Unicode Oracle Binary
< space
-0 0 -0
-1 -0 -1
-A 0A -A
-B 1 -B
0 -1 0
0A A 0A
1 a 1
A -A A
a A- A A
A A A A A-
A- A0 A0
A0 AA AA
AA b BA
b -B a
BA BA b

As you can see, they only agree on one thing: space really is the smallest character in my test set!  And that’s not what I want, I want all the data to be sorted consistently, no matter what the source is.

But Why Sorted?

You may wonder why I need to sort the data.  Well, some components in Integration Services expect the incoming data flows to be ordered.  One of the standard components that requires this is the Merge Transformation.  Another (custom!) component is Table Difference.  I could of course add a Sort Transformation to my Data Flow, but that would not be interesting for performance.  I want the data to come from the database server in the expected order.  So now I’ll show you how you can do that.

Taking Control!

SQL Server: ORDER BY … COLLATE …

On SQL Server this was fairly easy.  The ORDER BY clause has a COLLATE part where you can specify what collation should be used to order the data.  Because Oracle sorts its data using a binary algorithm, I’ll tell SQL Server to do that as well.  More precisely I’ll tell SQL Server to use the Latin1_General_BIN collation.  The updated SELECT statement from the T-SQL script above looks like this:

select * from @tbl
order by col1 collate Latin1_General_BIN asc;

Oracle: ORDER BY NLSSORT()

To ensure that results from Oracle are always returned using the same sorting algorithm, I will also tell the Oracle server to sort it’s data using the binary algorithm.

The first way I came up with was to change the NLS_SORT setting on the session.  That can be done by executing the following command before the SELECT statement:

ALTER SESSION SET NLS_SORT=BINARY;

This method is fine when you’re running the queries manually from a client such as Oracle SQL Developer.  However, in SSIS the OLE DB Source component will not accept anything else besides the SELECT statement.

Then I found another way.  There’s a function called NLSSORT() which you can apply to a column in the ORDER BY clause.  The following statement demonstrates how to use this function.  (I only show the ORDER BY clause as it can be applied to the Oracle script mentioned earlier.)

ORDER BY NLSSORT(col1, ‘NLS_SORT=BINARY’)

The following table shows the results from both binary sort queries:

SQL Binary Oracle Binary
-0 -0
-1 -1
-A -A
-B -B
0 0
0A 0A
1 1
A A
A A A A
A- A-
A0 A0
AA AA
BA BA
a a
b b

Finally I am able to get data from both Oracle and SQL Server using a consistent sort order.

But, How Big Is NULL?

However, even on this straightforward request, both database servers do not fully agree!  Here’s what they have to say about the topic:

“NULL is the smallest.”

“No, it’s the largest.”

“No, smallest!”

“Largest!!”

“Smallest.”

“Largest I tell you!!!”

“Bladiebla, not hearing you, anyway, it’s NOTHING!”

“No, it isn’t!”

*discussion goes on and on*

If I add NULL to my test data set, SQL Server will sort it first (thus NULL is the smallest value in my test set), while Oracle will put it last.  In my situation it wasn’t really an issue (the BKs are not supposed to be NULL), but it’s quite important to remember in cases where NULLs are actually possible.

Conclusion

When working with strings, always keep collation in mind.  And even more so when dealing with several different source systems!

Additional reference material:

Database Journal: The Globalization of Language in Oracle – The NLS_COMP and NLS_SORT variables

BOL 2008: How to: Sort Data for the Merge and Merge Join Transformations

  • Share/Bookmark

Tags: , , , , , ,

Recently I was investigating an issue related to ordering data.  As a test, I ran the following script:

– Sorting data from a temporary table 
declare @tbl table ( ProductNumber varchar(25) ); 
insert into @tbl select ‘BBBB’; 
insert into @tbl select ‘AAAA’; 
insert into @tbl select ‘A-B’; 
insert into @tbl select ‘A123′; 
select * from @tbl order by ProductNumber asc;

– Sorting data from a table variable 
create table #tbl ( ProductNumber varchar(25) ); 
insert into #tbl select ‘BBBB’; 
insert into #tbl select ‘AAAA’; 
insert into #tbl select ‘A-B’; 
insert into #tbl select ‘A123′; 
select * from #tbl order by ProductNumber asc; 
drop table #tbl;

 

And it gave me this result:

Results from script

As you can see, the order of the data coming from a temporary table is different than the data from a table variable, even though the same data type is used.  At first I thought, how on earth is this possible?  After spending some time pondering about this problem (and after a colleague reported that when he executed the above script, the result was as expected), it came to me.  Don’t tell me it’s a collation problem?!  Well, it is.  (In case you don’t know collation, in short “Collations specify the rules for how strings of character data are sorted and compared”.  More info through that previous link and here.)

When I executed the script, my Management Studio session was connected to AdventureWorks2008, one of my test databases.  When I switched to tempdb, the result was normal – both queries returned the same result.  Then I had a look at the collations.  My tempdb is using Latin1_General_CI_AS while the AdventureWorks2008 database is apparently using SQL_Latin1_General_CP1_CI_AS.

A temporary table is created in the tempdb and thus uses the collation of the tempdb for its string columns.  And a table variable uses the same collation as the database to which the session is connected.  Which is quite logical because otherwise you would get collation conflicts when using the table variable in combination with a table from the active database in the same query.  I will demonstrate that with the following script:

declare @tbl table ( ProductNumber varchar(25) );
insert into @tbl select ‘BBBB’;
select * from AdventureWorks.Production.Product P
where P.ProductNumber in (select ProductNumber from @tbl);

 

On my (SQL Server 2008) server I also have the old SQL2005 AdventureWorks database up and running, which is using the Latin1_General_CI_AS collation.  Executing the above script (which references that AdventureWorks DB) while being connected to AdventureWorks2008 (or any other DB that uses a different collation from Latin1_General_CI_AS) results in the following error:

Msg 468, Level 16, State 9, Line 3
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_CI_AS" in the equal to operation.

Changing the active database to any other DB that uses the Latin1_General_CI_AS collation and then executing the query results in a positive execution.

Now, to get back to the initial issue of sorting inconsistency, even though the collations are not exactly the same, they are both Latin1, Case Insensitive, Accent Sensitive and still they don’t sort the data in the same way??  For an explanation on that I found the following page on the Microsoft Support site: Comparing SQL collations to Windows collations.  In short: a Windows collation (such as Latin1_General_CI_AS) uses a different comparison algorithm than a SQL collation (SQL_Latin1_General_CP1_CI_AS).  A Windows collation uses the same algorithm as for Unicode data, even when the data is non-Unicode.

All that means is that in our test example, in order to get the data in the right order all the time, we could switch to Unicode fields:

– Sorting data from a temporary table 
declare @tbl table ( ProductNumber nvarchar(25) ); 
insert into @tbl select ‘BBBB’; 
insert into @tbl select ‘AAAA’; 
insert into @tbl select ‘A-B’; 
insert into @tbl select ‘A123′; 
select * from @tbl order by ProductNumber asc;

– Sorting data from a table variable 
create table #tbl ( ProductNumber nvarchar(25) ); 
insert into #tbl select ‘BBBB’; 
insert into #tbl select ‘AAAA’; 
insert into #tbl select ‘A-B’; 
insert into #tbl select ‘A123′; 
select * from #tbl order by ProductNumber asc; 
drop table #tbl;

 

Executing that returns the following result, no matter what the active database is:

Script result when using Unicode fields

And it also means that you need to be careful when retrieving data from different sources (which is how I came across the issue in the first place).  If you need to have your data sorted in a particular, consistent way and you’re doing that by using an ORDER BY in a SELECT statement on the source system, double-check if all sources are using the same collation!

In case the above left you wondering about what collation to use: the SQL collations are there for backward compatibility.  For new developments use the Windows collations!

 

See my Fun With Strings article for another story related to collation.

 

Additional reference material:

SQL Server Central – Comparing Table Variables with Temporary Tables

  • Share/Bookmark

Tags: , ,

If you don’t have anything planned on next September 2 and you’re interested in some free SQL Server-related learning: it’s the 24 Hours of PASS!

image

You can even stay in your lazy chair at home because it’s an online event, no worrying about bus/train/plane/hotel/…  Just install the software (or browser plug-in, I actually don’t know because I haven’t performed the preparation procedure yet) and off you go.

I have registered for the following 5 sessions myself:

  • Session 10 (Dev) – Working with Spatial Data in SQL Server 2008 (Greg Low)
  • Session 11 (DBA) – Effective Indexing (Gail Shaw)
  • Session 12 (BI) – Reporting Services Inside Out the Things You Should Know (Simon Sabin)
  • Session 13 (Dev) – Query Performance Tuning 101 (Grant Fritchey)
  • Session 16 (DBA) – Database Compatibility Settings: What They Really Do .. and Don’t Do (Don Vilen)

Yep, it will be a busy holiday.  That same day they’ll be delivering our new combi oven, ideally that would be right after session 13 ends.  Fingers crossed.

Anyway, I’ll be seeing you September 2?  Or well, maybe not as it’s an online event…

Happy learning!

  • Share/Bookmark

Tags: , , ,

For this example I’ll be using the Adventure Works cube running on SQL Server Analysis Services 2008 and Reporting Services 2008.

In case you don’t have the AdventureWorks databases and cubes yet, they’re available at CodePlex.

Scenario

The sales department has asked for a report that displays the number of product items sold during a selected period.  As the company is active in two different markets, both the internet and reseller numbers should be shown.  The figures need to be grouped by product category, with drilldown to product level through subcategory.

Besides the period filter, it should be possible to filter on product category to limit the number of items shown.

Also, the background of the numeric cells should get a color depending on the value in the cell.  Colors range from red for low sales figures to green for high sale volumes.  The ranges are variable and should thus be configurable using 3 threshold parameters.  Following table shows the ranges as the department has requested them:

Value X Background Color
X < LowThreshold Red
LowThreshold <= MiddleThreshold Orange
MiddleThreshold <= X < HighThreshold Yellow
HighThreshold < X Green

 

Selecting The Data

At first sight this seems like a fairly simple report.  So you start building your MDX query using the Query Designer:

MDX Query Designer

Two filters have been specified: one of them is a date range and the other is based on product category.

Visualizing The Data

Then you drag a tablix onto the report body and play around with it until you get to the following:

Tablix in design mode

This is what it looks like when rendered, all seems to work fine:

Rendered report

To get the textbox background coloured based on the thresholds, you’ve produced an expression.  This expression is specified in Textbox Properties > Fill > Fill Color and looks like the following:

=Switch
(
    Fields!Internet_Order_Quantity.Value < Parameters!LowThreshold.Value, "#ff0e0e",
    Fields!Internet_Order_Quantity.Value >= Parameters!LowThreshold.Value
        and Fields!Internet_Order_Quantity.Value < Parameters!MiddleThreshold.Value, "#ff922d",
    Fields!Internet_Order_Quantity.Value >= Parameters!MiddleThreshold.Value
        and Fields!Internet_Order_Quantity.Value < Parameters!HighThreshold.Value, "#fff70f",
    Fields!Internet_Order_Quantity.Value >= Parameters!HighThreshold.Value, "#5cff21"
)

It’s a simple Switch statement using the threshold parameters.

A Missing Field Issue

So you deploy your report to the server for the users to test.  All is quiet, until someone starts complaining that the colouring doesn’t always work, for instance when filtering on Components.  Of course, you don’t always believe what the user says and try it out for yourself:

Rendered report with missing field issue

Indeed, the background is no longer coloured for the internet sales.  On top of that, the BIDS shows a couple of warnings in its output window:

[rsMissingFieldInDataSet] The dataset ‘ProductSales’ contains a definition for the Field ‘Internet_Sales_Amount’. This field is missing from the returned result set from the data source.

[rsErrorReadingDataSetField] The dataset ‘ProductSales’ contains a definition for the Field ‘Internet_Sales_Amount’. The data extension returned an error during reading the field. There is no data for the field at position 4.

Hang on, but I am selecting the field in my dataset, how can it be missing?  Except, this is MDX and OLAP, not SQL and OLTP.  By default, the MDX Query Designer uses NON EMPTY in the SELECT statement.  This means that the rows where there are no values for the selected measures will not be contained in the result set.  It also means that the complete measure will be omitted in the case that there are no values for it in any of the rows, which is the reason for our problem.

You could choose to not use NON EMPTY in the query.  To achieve this using the designer, right-click in the results pane and click the Include Empty Cells item.

MDX Query Designer result pane popup menu

Keep in mind that this will result in more rows in your result set because you’re now selecting all the empty measure cells as well.  Depending on your report requirements this may not be the desired effect.  On the other hand, it could be exactly what you want.  If our sales department had asked that the report should always show all products, even when there are no sales for the period, then we’d need to query the cube in this way.

For the sake of the example (and to save some trees in case the sales department is going to print the report :-) ) we will not choose this option.

Attempt to fix #1

As the field does not always exist, you decide that it’s a good idea to test for its existence.  A field in a resultset has an IsMissing property which serves that purpose.  So you adapt your expression to the following:

=IIF(Fields!Internet_Order_Quantity.IsMissing, Nothing,
    Switch
    (
        Fields!Internet_Order_Quantity.Value < Parameters!LowThreshold.Value, "#ff0e0e",
        Fields!Internet_Order_Quantity.Value >= Parameters!LowThreshold.Value
            and Fields!Internet_Order_Quantity.Value < Parameters!MiddleThreshold.Value, "#ff922d",
        Fields!Internet_Order_Quantity.Value >= Parameters!MiddleThreshold.Value
            and Fields!Internet_Order_Quantity.Value < Parameters!HighThreshold.Value, "#fff70f",
        Fields!Internet_Order_Quantity.Value >= Parameters!HighThreshold.Value, "#5cff21"
    )
)

However, when filtering on Components the same problem still occurs.  How can this be?  Expressions in SSRS are built using Visual Basic where expressions are evaluated completely.  In our case both the True and the False part of the IIF function are evaluated even when it will always be true.

On to another attempt to get this working.

(Attempt to) fix #2

The previous fix attempt has shown that it’s not possible to use an expression for the field validity test.  At least, not in the way we’ve tried until now.  Let’s try using custom code.

Custom code can be added to a report through the Code page in the Report Properties dialog box (accessible through the menu Report > Report Properties… or by right-clicking the report’s yellow background).

Report Properties > Code dialog box

Let’s start with a small extra requirement.  When a measure is not present in a row, such as the Internet Order Quantity for the products in the Components category, the report should display a zero instead of blank space.  To get this done we again need to test on whether or not the field exists in the result set.

The following Visual Basic function accepts a Field object and returns the value of the field when the field exists or zero when the field does not exist.

'returns the field's value or zero if the field does not exist
Public Function GetValue(field as Field) as Long
  If (field.IsMissing) Then
    Return 0
  ElseIf (IsNothing(field.Value)) Then
    Return 0
  Else
    Return field.Value
  End If
End Function

This function can now be used in an expression anywhere in the report.  Here’s what the expression looks like for the Value of the textbox that shows the Internet Order Quantity:

=Code.GetValue(Fields!Internet_Order_Quantity)

The same expression is used for the textboxes that display the sum values:

=Sum(Code.GetValue(Fields!Internet_Order_Quantity))

Attention: the function calls above are passing the actual Field object, not the Value property of the field, so not Fields!Internet_Order_Quantity.Value.

So, on to getting our coloring working as required.  For this we need a function that returns the right color for the given amount.  Something like this:

Public Const ColorLow As String = "#ff0e0e"      'red
Public Const ColorLowMid As String = "#ff922d"   'orange
Public Const ColorMidHigh As String = "#fff70f"  'yellow
Public Const ColorHigh As String = "#5cff21"     'green

Public Function GetColor(field as Field, low as Integer, mid as Integer, high as Integer) as String
  If (field.IsMissing) Then
    Return ColorLow
  ElseIf (IsNothing(field.Value)) Then
    Return ColorLow
  Else
    Select Case field.Value
      Case Is < low
        Return ColorLow
      Case Is < mid
        Return ColorLowMid
      Case Is < high
        Return ColorMidHigh
      Case Is >= high
        Return ColorHigh
    End Select
  End If
End Function

This function accepts a field plus the three threshold values.  Depending on the value of the field and the thresholds, the expected color string is returned.  The red color is returned as well when the field does not exist.

As a good coding practice I’ve created constants for the color strings.  This method allows you to define constants that are available in the whole report – could be interesting if the same colors are used in different parts of a report for instance.

This is the expression used for the BackgroundColor property of the TextBox:

=Code.GetColor(Fields!Internet_Order_Quantity,
    Parameters!LowThreshold.Value,
    Parameters!MiddleThreshold.Value,
    Parameters!HighThreshold.Value)

Again the actual Field object gets passed as first parameter, not just the value.

If we now run the report with a filter on Components, the warnings will still appear in the Output window, but the report will function as expected as the following screenshot shows.  (No Photoshop was used in the making of this screenshot.)  Instead of empty cells the report shows zeroes and the background is coloured even when there are no sales.

Rendered report - fully functional

Extra info can be found in the MSDN page about Using Dataset Field Collection References in Expressions.

  • Share/Bookmark

Tags: , , , , , , ,

Have you ever had a table with completely duplicated records?  Of course this can only happen when your table does not have an automatically-increasing identifier nor a primary key or unique constraint.  This does not happen often but occasionally it does.

I came across this problem when working with package configurations in my Integration Services packages.  All the packages are using the same configuration table in an administration database and somehow the BIDS had duplicated a record in that table.  I did not want to delete both records because one of them should stay in there.  So I thought of this workaround.

If you already know the timestamp data type you probably know what I’m going to suggest.  If you don’t: read on!

Unlike what you’d think, timestamp is not a data type that you want to use to store timestamps.  That’s what datetime (or datetime2) is for.  Timestamp is a type that SQL Server uses internally to version-stamp rows in a table.  You can think of it as a counter.  Each database has one and for each modification (Insert or Update) on a record in a table that contains a timestamp column, it is increased by one.

To avoid confusion with this data type, it has been decided to call it RowVersion, thereby deprecating timestamp.

You can look up the current value of your database’s RowVersion counter using the following command:

select @@DBTS;
Now, to get back to the topic: when adding a column of type RowVersion to your table it will get populated automatically with the next available counter values.  To add a RowVersion column to your table you can use a command similar to this one:
alter table MyTable add ts RowVersion;

With this column you now have a way to uniquely identify your duplicated records and the possibility to issue a DELETE statement using the RowVersion column.  You do it like this:

delete from MyTable
where ts = 0×000000000000082B;

Table contents before the DELETE:

Table contents before the DELETE

Table contents after DELETE:

Table contents after DELETE

After having removed your duplicates, don’t forget to drop the new column:

alter table MyTable drop column ts;
  • Share/Bookmark

Tags: ,

Here’s a quick tip on the usage of the Merge, one of the Data Flow Transformation components in Integration Services.  When merging data from two different sources one of the issues that you’ll need to cope with are the data types.  The Merge component may start complaining about certain columns when the metadata has been changed higher in the flow.  The error would look like this:

Validation error. Data Flow Task: Data Flow Task: The metadata for “input column “YourColumn” (590)” does not match the metadata for the associated output column.

A common issue is the length of a string field.  My favorite way to solve these types of issue is by opening up the Advanced Editor, locating the particular field in the Output Columns and increasing its Length property to match the length of the longer field.

But unfortunately the Merge component does not have an Advanced Editor option.  The easiest way to solve it in this case is by first deleting the column in the Merge Transformation Editor and then adding it again.  Luckily the dropdown only shows the input fields that haven’t been matched yet. :-)

  • Share/Bookmark

Tags: , ,

Lately I had been getting annoyed by Visual Studio crashing on me while working on ETL packages.  The crashes seemed somehow related to debugging a package because they usually occurred after I clicked the “Package execution completed. Click here to switch to design mode, or select Stop Debugging from the Debug menu.” link.  But I couldn’t really pinpoint why or when exactly they occurred.  Now I’ve got an explanation, and a solution!

How did I get to the solution?  Well, today I decided to click the “Send to Microsoft” button on the infamous crash pop-up window.  And from it I actually got a link to a fix for the issue.  Apparently the issue is not related to Integration Services in particular, it is a much broader Visual Studio shell issue that occurs in Visual Studio 2008 with SP1 when you’ve got a combination of undocking windows and changing window layout.  And that is indeed what I usually do when debugging a package!  When the package stops executing I often execute the following scenario:

  • double-click the title bar on the Output window to undock it
  • enlarge the undocked Output window to almost full-screen so I can have a good look at the errors
  • double-click the title bar to dock the window back to its original place
  • click the “Package execution completed.” link (or hit the Stop Debugging button)

And that’s exactly the second scenario described in this Microsoft Support article because when you stop debugging, the IDE switches the window layout back from debug to design.

I have now installed the hotfix and the issue is gone.  Beware though if you also use WPF, better read through the whole article and comments on the download page first.

Hmm, this may also explain some other unexplicable crashes I’ve seen lately.  Makes you wonder doesn’t it :-)

  • Share/Bookmark

Tags: , , , , , , ,

That’s a question that you should always keep in mind when working on a 64-bit machine.  In case you’re wondering why I’ll tell you a little story.

On a server not so long ago the operating system was Windows Server 2008 64-bit.  The database system was SQL Server 2008, 64-bit as well.

That server needed to be able to connect to a DB2 database on another server.  For that purpose someone installed the IBM ODBC 9.5 driver, 32-bit.  As usual when someone asks for connectivity to that DB2 server, the batch script containing some typical DB2 commands was executed.  What this script does is create a couple of ODBC System DSNs.  The person that installed the driver showed that all was working fine because using some command-line tool he was able to connect to the databases.

However, when I opened the ODBC Data Source Administrator through Administrative Tools > Data Sources (ODBC), it didn’t show me any System DSNs and when I clicked the Add… button it didn’t show me any DB2 drivers either.  So it seemed that something went wrong.  Or not?

What I needed to be able to do was to connect from Reporting Services to the DB2 database.  So as next test I decided to create a small report directly on the server (BIDS was installed).  And all went fine, I was able to create a data source connecting to DB2 and dataset querying data from the DB2 database.

Then I decided to deploy the report and run it through the Report Manager.  Deployment went fine but when I ran the report it could not connect to DB2.  Hmm, is it working now or not?

I was fairly convinced that it had something to do with the fact that the ODBC driver was 32-bit.  The Business Intelligence Development Studio is 32-bit, that’s why it worked fine using the 32-bit ODBC driver.  But the Report Server is 64-bit, that’s why it couldn’t connect to DB2 using the 32-bit driver.

But why wasn’t the ODBC DSN showing up?  After all, it must exist because the BIDS showed me the list of DSNs that were created.

And then I found the answer, as usual, on the internet.  And it was fairly logical but sometimes you just don’t think of it.  The ODBC Administrator that you launch from the Start > Administrative Tools menu on a 64-bit server is the 64-bit version of the tool.  And that 64-bit version only shows DSNs using 64-bit ODBC drivers.  To get the 32-bit version of the ODBC Administrator you need to launch it manually through Explorer.

And now you may be wondering where on earth disk you’re supposed to find that 32-bit version.  Here’s the answer:

  • The 32-bit version of the Odbcad32.exe file is located in the %systemdrive%\Windows\SysWoW64 folder.
  • The 64-bit version of the Odbcad32.exe file is located in the %systemdrive%\Windows\System32 folder.

And indeed, that version of the tool showed me the DSNs and the DB2 driver.

(Why they didn’t rename the 64-bit version to odbcad64.exe – after all, that is what the 32 in the filename means – remains a mystery to me.)

Sidenote: there appears to be an issue with this tool where both the 32 and 64-bit version of the tool show both the 32 and 64-bit DSNs.  My server didn’t have this issue but it may be interesting to keep it in mind.  More details about that issue in this Microsoft Support article.

  • Share/Bookmark

Tags: , , , , ,

« Older entries

© 2008-2010 A Developer's Blog All Rights Reserved