Monday, September 24, 2007

MERGE Statement in SQL Server


Ever had to merge two tables in SQL? Hard work?
The MERGE statement is only available in SQL Server 2008 (and Oracle and DB2 ;) ), but in the meantime - you can mimic it.


If you ever had a large csv file with a new version of data already contained in a table in SQL, you dreamed of something like this, didn't you:

MERGE t1
USING (SELECT @id AS id, @name1 AS name1) AS t2 ON t1.id = t2.id
WHEN MATCHED
THEN UPDATE SET t1.name1 = t2.name1
WHEN NOT MATCHED
THEN INSERT VALUES(@id, @name1)
In other words: if the id of a row in the new version matches one in the old version - update the old row; otherwise - insert a new row.

[Of course we're assuming here that you managed to load the contents of the csv file into a staging table].

Well, if you have the luxury of working with SQL Server 2008, the construct above is exactly what you could use there. Have a look here: Using MERGE statement.


Now, since SQL Server 2008 is not yet used in too many commercial projects ;) - it is often the case that you have to implement such functionality on your own.

According to Alex Kuznetsov (MVP):
In SQL Server 2005 you can use OUTPUT clause of an UPDATE statement
in order to achieve that.

See for yourself:
declare @updated_ids table(id int)

update permanent set d=s.d, comment = 'Modified Row'
output inserted.id into @updated_ids
from permanent p, staging s
where p.id=s.id

insert into permanent
select id, d, 'New Row' from staging where id not in(select id from @updated_ids)
go
Pretty neat!

You can find Alex's whole post here: Mimicking MERGE Statement in SQL 2005.

Tuesday, September 18, 2007

Parsing Xml with a Default Namespace Defined


What's that namespace doing there?


I recently answered a question on MS newsgroups regarding navigating an XML document whose fragment is below:
<?xml version="1.0" encoding="UTF-8"?>
<JMF xmlns="http://www.CIP4.org/JDFSchema_1_1" Version="1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Response ID="RDFS1934507113653e9" ReturnCode="0" Type="QueueStatus"
refID="132413dj323s223" xsi:type="ResponseQueueStatus">
<Queue DeviceID="Dev01" QueueSize="8" Status="Waiting">
<QueueEntry DeviceID="vl-5001" JobID="1048655" JobPartID="79">



The author of the question wanted to get to the QueueEntry using the following XPath expression:
JMF/Response/Queue/QueueEntry
Unfortunately - it returned no XmlNodes (when used in xmlDoc.SelectNodes(...)).

And here's why:
<JMF xmlns="http://www.CIP4.org/JDFSchema_1_1" ...
This means that all children of the <JMF> are in the "http://www.CIP4.org/JDFSchema_1_1" namespace.
So if you try to reference them without specifying that namespace, you are actually trying to reference some nodes that do not exist in the document!

Here's what you have to do to be able to select those nodes:
XmlDocument xmlDoc = new XmlDocument(); 
xmlDoc.LoadXml(doc);

XmlNamespaceManager nm = new
XmlNamespaceManager(xmlDoc.NameTable);
nm.AddNamespace("my", "http://www.CIP4.org/JDFSchema_1_1");

XmlNodeList list =
xmlDoc.SelectNodes("/my:JMF/my:Response/my:Queue/my:QueueEntry", nm);
foreach (XmlNode node in list)
{
string sID = node.Attributes["DeviceID"].Value;
}
Yeah, it looks a little bit cumbersome, but it works! :)

Monday, September 17, 2007

How to Execute a SQL Script from .NET Code?


The old days of splitting scripts by the GO statement or using osql are over! SMO is here!


It happens from time to time that you need to execute a T-SQL script - e.g. you are writing a database update manager or whatever.

And it's always the old problem of the 'GO' separator, isn't it? ADO.NET just won't swallow it!

The solution was always to either use some external tool (like osql or sqlcmd) to execute the script or to parse the script for all the 'GO'-es. But both those methods were kind of... not perfect.

The good news is - you don't have to worry about it anymore!

If you have a closer look at SQL Server Management Objects (SMO), you will find the following method:
Server.ConnectionContext.ExecuteNonQuery()
which can actually do all the job for you!


If course it is only available in SQL Server 2005. But isn't that what progress is all about - new things have more features than old ones? ;)


You can find the SMO assemblies here:
C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies
if you have SQL Server 2005 installed on your machine.

Friday, September 14, 2007

overloading operator==


Beware of infinite recursion when overloading the == operator!


When you overload the equality operator (==) in C#, you should of course remember that:

  • you should also overload operator !=

  • the operator's implementation should not throw exceptions


  • Having to overload the unequality operator (!=) as well is rather obvious. Saying:
    a != b
    is another way of saying:
    !(a == b)

    The second point is not unusual, either. No one would rather expect an operator to throw an exception - otherwise, you'd have to put every other if statement in a try-catch block.

    And what would you actually do with a "CouldNotCheckForEqualityException" anyway? It's not a kind of error you'd expect at runtime, is it? If you can't check two objects for equality - you would rather like to know that at compile time!


    Now - in order to protect yourself from throwing an exception in the implementation of the == operator, you should check its arguments for being null, so that you can safely access their fields for comparison purposes.

    Oh, that's simple, isn't it?
    if (x == null)
    return y == null;
    else
    return x.Equals(y);

    Pretty clever, eh? But this will NOT work, unfortunately!
    Note that the two comparisons to null will actually call the overloaded == operator, causing an infinite recursion!

    There are actually two ways of fixing that:

  • you could call x.ReferenceEquals(null) - not too elegant for me, or
  • you could cast the arguments to objects:
    if ((object) x == null)
    return (object) y == null;
    else
    return x.Equals(y);

    And that's what I call a really clever and slick piece of code :)
    Thanks go to the author of this post for sharing it:
    Re: Overloading operator== and comparing to null

    Oh, and of course the implementation of the != operator would be:
    return !(x == y)
  • Wednesday, September 12, 2007

    Access Denied on a Directory


    I don't know why that is, but when trying to access a directory instead of a file with a StreamReader (or anything else that uses it), you will get an Access Denied error...


    Try to run the following code:

    StreamReader sr = new StreamReader(@"c:\");
    Actually - before you run it - try to guess what error it might generate...
    Ok - now, make a note of your answer, and run to code.

    Well? Was your guess right?

    I know, I know - it doesn't seem to make much sense, but the actual error message is:
    Access Denied
    Apparently, you are not allowed to read from a directory. But the error message is at least misleading. It mislead me for some time when I started looking for the cause of this error.

    Of course the code I was debugging was not that simple as the one above - and that's why it took me quite a long time to notice, that I'm pointing to a directory, while wanting to load a file into an XmlDocument.


    Anyway - access denied does not always stem from insufficient user rights ;)

    Monday, September 10, 2007

    DialogResult - Remember to Keep It None


    If you don't want to look for the place in code where your modal dialog gets unexpectedly closed before you want it to - remember to set all buttons' DialogResult property to None!


    Everyone applies the 'copy-paste' rule from time to time - especially when creating Windows forms. You need another button of the same size - you just copy it and paste it, don't you?

    Well - you have to watch out! As you might expect - most of the values of the new button's properties are the same as those of the old one.
    That's good as long as you don't copy an OK button and use it as an ordinary button.

    An OK button has its DialogResult property set to "OK" (oh, really?).
    Now, according to http://msdn2.microsoft.com/en-us/library/system.windows.forms.button.dialogresult.aspx:

    If the DialogResult for this property is set to anything other than None, and if the parent form was displayed through the ShowDialog method, clicking the button closes the parent form without your having to hook up any events. The form's DialogResult property is then set to the DialogResult of the button when the button is clicked.

    So - if you copied the OK button and pasted it as some other button, say, "Refresh", that's fine.
    Then you write some code handling that button's Click event, e.g. you want it to reload contents of some list on the form.
    Now look what happens when you actually click the "Refresh" button: the Click event handler does get executed (the list gets refreshed), but right after that the form is closed!

    If you were debugging the Click event handler, you finally go out of that method's scope and all of a sudden - you find yourself on the form.ShowDialog() statement.

    It tooks me some time to finally find out what was going on! :)