Wednesday 27 August 2014

SQL Server: Inline Queries Across Linked Servers

I’ve had to run a scheduled T-SQL query from one database to another linked server for an import operation. I'm finding that I have to do some comparisons between the result of two subqueries in order to get the proper value for one column. So the results I want would correspond to something like this:

Insert into localTable (columns)

Select col1, col2, col3,

CASE 
      WHEN subquery1result > subquery2result THEN subquery1result
      ELSE subquery2result
END As col4 From server.schema.dbo.table1 Where [stuff]

Note the reference to the linked server… First you have to set up the Linked Server in SQL Server, then you can reference the external database table as [LinkedServerName].[SchemaName].[DataBaseOwner].[TableName]

To flesh that out a little more, if I filled in those subqueries in-line:

Insert into localTable (columns)Select col1, col2, col3,

CASE 
    WHEN (select MAX(T2.datecol) from server.schema.dbo.table2 T2 where T2.id = T1.id) > 
         (select MAX(T3.datecol) from server.schema.dbo.table3 T3 where T3.id = T1.id) 
    THEN (select MAX(datecol) from server.schema.dbo.table2 T2 where T2.id = T1.id) 
    ELSE (select MAX(T3.datecol) from server.schema.dbo.table3 T3 where T3.id = T1.id) 

END As col4

From server.schema.dbo.table1 T1

Where [stuff]


But that doesn’t work – using subqueries in a CASE statement like that won’t fly.
I would normally do these subqueries as user-defined scalar functions, but I can't seem to make one that queries tables in a linked server. And I'm not allowed to modify the schema of the database on the linked server (vendor system).

Any ideas on another way of doing this? I suppose I should copy all relevant values into a holding table (on the local server) & then run the import from there instead of importing directly from the linked server. I was just hoping I could keep it as a straight 'Insert Into... Select From' query in a single step.

I think I stumbled upon a "good enough" solution (which suitably horrify a proper SQL-Ninja):

Insert into localTable (columns)

Select col1, col2, col3,
(Select MAX(DC) From 
   (
      select MAX(T2.datecol) as DC from server.schema.dbo.table2 T2 where T2.id = T1.id
      UNION
      select MAX(T3.datecol) as DC from server.schema.dbo.table3 T3 where T3.id = T1.id 
   ) as U
) As col4

From server.schema.dbo.table1 T1

Where [stuff]

It seems to work, though the query is a bit on the slow side (7 seconds to return ~2K rows). Well within reason for a nightly batch job though. Basically the UNION of multiple inline queries forms a set of it’s own, which we can further query from. In this case we’re choosing the Maximum value returned from two different queries.

Friday 1 August 2014

My .NET app is really slow on one PC, fine on all others

So I had this small .NET Windows Forms application that has been running on a large number of our lab computers with no issues. Not that it matters, but this program would run in the background and record logins to each lab computer against the booking system for reserving the labs.

One such computer was recently upgraded to Windows 7 from XP as a result of the end-of-life support of XP (and a bit of common sense, which is known to happen once in a while).

So once the computer was freshly wiped and had Windows 7 installed & patched, my little program was installed... and it was dog slow.

I mean it was really slow. Where it used to go from launch to actually doing anything (such as becoming visible) in under a second, it was now taking 10 to 15 minutes before there was any evidence of it running. On the same machine.

Looking at the installed .NET frameworks on a working PC and the problem one, they were both the same (at the time, 4.5.1), and earlier versions were not installed side-by-side on either machine. The application was written in Visual Studio 2010 and targeted the 4.0 .NET framework.

Now keep in mind that the older machines used to have older versions of the framework which were then upgraded over time (2.0 -> 3.0 -> 3.5 -> 4.0 -> 4.5). However, being a clean install, the re-installed machine was set up with just .NET 4.5.1 without having passed through any of the earlier versions.

Turns out that was the problem.

Removing .NET, then re-installing it with 4.0 only, and then allowing it to update to 4.5.1 afterward on the problem machine fixed it. I don't know what the mechanism is that causes this, but when a .NET app is targeting an earlier framework than was ever installed on the destination PC, problems like this can happen.