A few years ago I was speaking at a conference in Raleigh, NC where Ted Neward and I found a fun way to promote a Java vs. .NET debate that was planned one evening. We stood in the middle of a crowd during one of the breaks and starting “arguing” about Java vs. .NET with one another. Our voice levels quickly raised and we ended it by slapping each other across the face with a glove to request a challenge. It was a great way to segue to our announcing of the actual debate planned later that evening. As you can imagine, it is a lively topic, with many folks taking extreme positions.

And so I found out about another highly debatable topic that is happening soon. Red Gate is sponsoring a live debate entitled “Oracle Heap Tables or SQL Server Clustered Indexes?”. Jonathan Lewis and Grant Fritchey will be “duking it out” on Thursday 7th June 2012 at 11:00 AM EDT (17:00 CEST, 16:00 BST, 10:00 CDT, 08:00 PDT).

I have been very remiss in posting lately. Unfortunately, much of what I do now involves client work that I cannot post. Fortunately, someone asked me how he could get a formatted list (e.g. tab-delimited) of files with GPS data from those files. He also added the constraint that this could not be a new piece of software (company security) and had to be scriptable.

I did some searching around, and found some techniques for extracting GPS data, but was unable to find a complete solution. So, I did a little PowerShell programming this weekend and came up with this script (below). It is not the most elegant of solutions, but it worked as expected.

Something of note: I was first lead down the path of searching for GPS data in the EXIF by looking for the property with an ID of 34853, but none of my files that appeared to have GPS data (according to file properties in Windows 7) contained an EXIF property with this ID. So I did a little spelunking and found that the following IDs are also used for GPS data:

I found out that the Property IDs that are being used are not the EXIF ones, but the ones from the System.Drawing.Imaging.PropertyItem that encapsulate the metadata from an image file (System.Drawing.Bitmap in this case). You can find a reference for these IDs at PropertyItem.Id Property (System.Drawing.Imaging).

Added in the ability to read the altitude data

Added some additional exception handling for those files that don’t play nice and an error log file for record of those image files

Once I sorted this out, it was time to start writing some PowerShell script. Once I got the core functionality working, I added some additional features (recurse folders, include/exclude files with no GPS data, and so on) to the script and cleaned up the code and comments. Enjoy…

SQLblog found a new home this past weekend and was moved onto a much needed, much better server infrastructure. SQLblog continues using MaximumASP (now CBeyond Cloud Services but still found at www.maximumasp.com). We have been very happy with our hosting and support and MaximumASP worked with us ensure that are hardware and disaster recovery options were optimal for our budget. It obvious why we are now in our fourth year being hosted at MaximumASP.

Not sure if you’ve attempted to convert a table with HIERARCHYID to an XML representation, and if you have, I’m sure you’ve experienced the same woes as me. Sure, I could have taken the route of using C# to create the XML, and it very well may be a better way to make such a conversion; instead I decided that I had to be able to do this in T-SQL, and so began the journey to find such a solution...

Since the XML modify method can only insert into a single node in an XML document, I had to either attempt to generate a string representation of the xml form the data (no simple task) or I could cursor through the data one row at a time (yes, cursor) and insert each node. For this implementation, I choose the cursor method simply because it would be easier to do than to recreate the XML document abilities akin to what is done in the .NET framework.

When using XQuery to insert nodes, you must use a static value (in other words, you cannot use a composed string variable for the XQuery in the XML modify method). At first, this made it difficult to figure out how to insert a node into another node since there was no point of reference.

And so thought that I could use the HIERARCHID’s ToString() method to figure out node positions in the XML, but that quickly was discarded after realizing that the path representation is guaranteed to be neither consecutive nor integers, and it would require using sp_executesql, which I also wanted to avoid.

Then I thought that the data would probably be uniquely identifiable, and so I could use that “id” to add an attribute to every node that I constructed and then cursor through and insert into the node that matched the parent id of the node I was inserting, which removed the need to use sp_executesql. In other words, I would create a cursor that contained the parent node ID and concatenated values from the row of data to create the node with an “id” attribute.

I would then iterate through the cursor and insert the node as follows:

SET @XR.modify('insert sql:variable("@xcol") into (//*[@id=sql:variable("@hparentid")])[1]')

This uses the sql:variable extension to find a node via a relative reference. It looks for the node, regardless of location in the XML, with the attribute" “id” equal to the “id” of the parent which is contained in the @hparent variable.

Alas, this could become more problematic if the unique key contained multiple fields. One also might not want to include an extra “id” attribute in the results. Because of these and other things that I realized could go wrong with this implementation, I decided to scrap it and moved on. And although the version I am about to present has its own potential for issues, I felt it was more flexible and cleaner in its approach. Essentially what I decided to do is to use a temp table that contained the generated the XML node, the original HIERARCHYID value, a row number, generated with ROW_NUMBER() ordered by the hierarchy order, and a parent row number, which would initially set to 0 then updated using a self join on the temp table.

Then since the XML nodes position will match the generated row number based on the HIERARCHYID position, we can simply insert the new node into the parent node based on its position.

-- UPDATE the parent row number using the HIERARCHYID method GetAncestor in the self join -- If the amount of data is great, an index could be created on the temp table prior to the updateUPDATE T1 SET T1.ParentRowNum = T2.RowNum FROM #T AS T1 INNER JOIN #T AS T2 ON T2.HierarchyNode = T1.HierarchyNode.GetAncestor(1)

DECLARE @xcol XML, @parentrownum INT, @flag BIT = 0, @XR XML = ''

-- We actually only need the generated XML and the parent row number to do the rest of this workDECLARE crH CURSOR READ_ONLY FOR SELECT XmlToInsert, ParentRowNum FROM #T ORDER BY RowNum

OPEN crH

FETCH NEXT FROM crH INTO @xcol, @parentrownum WHILE(@@FETCH_STATUS = 0) BEGIN -- First time through, we add a root node IF @flag = 0 SET @XR.modify('insert sql:variable("@xcol") into (/)[1]') ELSE -- Subsequent passes we find the parent node by position SET @XR.modify('insert sql:variable("@xcol") into (//*)[sql:variable("@parentrownum")][1]')

Instead of inserting the initial values into the temp table #T, this generates the completed set of data and then inserts it into #T. However, regardless of what variation I tried (e.g. add indexes), the initial method I show that inserts then updates #T was always faster.

Please let me know if you have any ideas that might optimize this, and if you have an implementation (T-SQL or .NET), please share.

In a previous blog post, I had discussed a method of shredding XML to a table with HIERARCHYID, and realized that it had a dependency that I was not too keen about: The XML data required an “id” attribute in order to create the hierarchy. I had sorted out a way to inject a unique attribute ID into all the nodes (I’ll discuss this in a follow up post), but having to modify the original XML didn’t have much appeal. But, upon reading another post by my fellow blogger, Adam Machanic, I realized it could be done without this requirement. Using the technique that Adam presented, I can generate unique paths to be parsed into a HIERARCHYID column.

Of course, these values could also be shredded into the hierarchy. One way of doing this would be to add an additional column to the results that represents the type of entry in the hierarchy (node versus attribute). My challenge to you is to create that solution.

A friend and colleague of mine, Caleb Jenkins, has started a fitness challenge (http://calebjenkins.wordpress.com/2009/01/29/official-rules/) and I decided that it was time for me to accelerate my fitness program that I had started in January. As of 9:00 AM on February 1, I weighed in at 201 pounds. My overall and ambitious goal is to lose at least 20 pounds. I hope to lose at least 10 pounds during the month of February. Even if I don’t win the fitness challenge, I will be much better off and will hopefully get to my ultimate goal of staying under 180 pounds.

I worked out on Saturday and Sunday. On Sunday I also helped my 5 year old son attempt to ride his bicycle without training wheels for the first time. My backs hurts from leaning over and catching Chris from falling. I am sore. I am achy. But I am determined. Wish me luck on my road to a healthier lifestyle.

Last year, my 4 year-old son and I walked (well, I carried him for 2 miles) in support of Juvenile Diabetes Research Foundation (JDRF). We raised a modest amount in support of this wonderful effort. This year, we want to help more than ever, and I thought I'd ask the SQLblog community for some additional support.

We are walking in support of our friend Marissa (Marissa's Mermaids team). Marissa was diagnosed in Sept. 2005 with Type 1 Diabetes, since then she has had 4620 finger pricks to draw blood and 1095 injections. Now, she is attached to an insulin pump 24 hours a day. Type 1 (or Juvenile) Diabetes is a chronic, debilitating disease affecting every organ system - it is an autoimmune disease where the body attacks its pancreas and permanently destroys the cells that make insulin. Once Type 1 Diabetes arrives, it becomes a life-long condition.

Like nearly 3 million Americans with Type 1 Diabetes, Marissa has lower projected lifespan by 14 years and is more likely to suffer blindness, amputation, heart disease, kidney disease, nerve damage, stroke, and heart attack. Every year, 13,000 children are diagnosed. INSULIN IS NOT A CURE - IT IS MERELY LIFE SUPPORT. At times when Marissa is most upset, with tears in her eyes, she says, "I wish I could take my diabetes and throw it in the trash can."

To manage Marissa's diabetes, her parents have the impossible struggle of balancing carbohydrates, insulin, and physical activity with uncontrollable variables such as illness, growth, exercise-level, excitement, and stress. Every day offers Marissa the risk of a drastic low blood sugar causing seizure or possibly death. It could only take one mistake on one morning or one afternoon or one night. Or it could simply involve a random event from her body.

Marissa is brave. Her parents poke her finger to draw blood 8-12 times each day including routine checks at midnight and 3am. Every carbohydrate at every meal or snack must be counted. The carbs must be perfectly balanced immediately through injections or an insulin pump to attempt to imitate the way yours and my pancreas works. There are no exceptions and there is never a day off!

Major research advances have occurred in the past few years! Diabetes has been cured in mice in at least 4 ways and various human trials are beginning. Scientists legitimately believe a cure is possible before Marissa loses this life-long battle. We ask you to share Marissa's story with your organization so that your entire company would consider sponsoring Marissa. Your contribution might make the difference in the one research laboratory where the cure will be achieved!

Dan Jones wrote a great post about Facets from the new Policy-Based Management feature of SQL Server 2008. At one point in the post, he listed all of the available facets and their supported evaluation modes. Since SQL Server 2008 is not RTM, and since facets can be added in the future, I thought I'd write a query that would list the facets and supported evaluation modes.

Note that the On Demand mode is always supported and has therefore been left out of the query.

After numerous questions and requests, we decided to create a forum for all SQL Server related questions. You can view the new forum at http://sqlblog.com/forums/57/ShowForum.aspx or use the Forums menu item on the site to browse there. We hope this new addition to the SQLblog community provides the members a valuable benefit and look forward to seeing your forum posts.

With some help from two esteemed colleagues, Greg Low and Mark Whitehorn, the Introducing SQL Server 2008 book is now content complete. Sure, there is still some tech and copy editing that needs to be done, but the core writing is complete. The book, based on the Feburary CTP6 release, will be available free from Microsoft and covers a slew of new features including:

Policy-Based Management and Auditing

Transparent Data Encryption and Data and Backup Compression

Resource Governor

Performance Data Collection

New data types including HierarchyID, Filestream, Spatial, Date and Time, and Enhancements to the XML data type

He then goes about dismissing most of the misinformation about why stored procedures are better than ad-hoc (parameterized) queries.

The first bit of misinformation he dispels is the now defunct argument that "Stored Procedures are faster than ad-hoc queries". He states that "Unless your ad-hoc queries are always significantly different from each other, their execution plans are cached right along side those of the SP's." I completely agree. We'll call that one a tie, so the score so far: SP 0, Ad-hoc 0.

Another myth he tries to debunk is that "Editing SP's is a breeze with Query Analyzer". Query Analyzer - that's so SQL Server 2000. Seriously, though, there are a number of fine code editors that allow you to edit SPs with ease. Query Analyzer is not at the top of that list, however. I will say that when you write T-SQL you should use a code editor that is meant for T-SQL, for the same reasons that when you write C#, you want to use a code editor meant for C#. Again, no winner here, so the score remains: SP 0, Ad-hoc 0.

He addresses another statement that is supposedly made in defense of SPs: "Ad-hoc queries are a nightmare to maintain, as they are spread all over your code". Again, either one is easy to maintain, with the right tools. We are still scoreless: SP 0, Ad-hoc 0.

It just so happens that I agree with many of his points. And there are other objective and subjective points on topics such as organization, maintenance, design, and so on, which one could argue for either SPs or ad-hoc queries equally so. Don't get me wrong, however, as I believe that using ad-hoc queries when you could have used stored procedures is simply wrong.

And so I will address Will's last point (actually, it was his second point) that is repeatedly misrepresented: "Stored Procedures are safe against SQL injection attacks; ad-hoc queries are not".

Ad-hoc queries prevent SQL Injection attacks as well as SPs do. Any claim otherwise would be wrong. But that's not the issue. The problem is that ad-hoc queries require that you expose the underlying objects of the database. In order to use ad-hoc queries, you must allow direct access for select, insert, update, and delete operations to the tables in the database. Although I know most experienced developers would only write ad-hoc/parameterized queries against the underlying data, at a later date, some disgruntled or inexperienced developer may write dynamic SQL instead (I have seen it happen), and expose the database to SQL injection attacks (which I have also seen in production systems), including exposure to such awful actions as...

-- NEVER EVER DO THIS, PLEASE -- This will execute a DELETE against all tables EXEC sp_MSforeachtable 'DELETE ?'

...or even, even worse (assuming the SQL login has elevated permissions - which many apps do)...

-- NEVER EVER DO THIS, PLEASE -- This will drop all tables from the database EXEC sp_MSforeachtable 'DROP TABLE ?'

...and so although your ad-hoc query code won't allow SQL injection, some other programmer's dynamic SQL will. Assuming you've correctly secured your database, this doesn't happen with stored procedures since you do not have to expose any of the underlying tables (because of a little something known as chain of ownership).

Of course, you could completely self-destruct any security benefits by creating a SP such as this one:

-- NEVER EVER DO THIS, PLEASE, I beg of you... CREATE PROC prExecuteSql (@sql VARCHAR(MAX)) WITH EXECUTE AS dbo AS EXEC (@sql) GO

As you can see, SPs aren't fool proof, but you can mitigate your risk by having an employee or a consultant who knows what they are doing in the database.

Yes, there are some applications will not require the extra security, or other factors may simply prevent you from using stored procedures, and so using ad-hoc SQL is a viable option in those cases. But I believe that security should be at the top of your important-things-for-your-application list, and alas, ad-hoc queries require you to unnecessarily expose your database objects, which will more than likely lead to problems down the road. You can argue any other point and there are no clear winners, but when it comes to security, ad-hoc loses. If you want to a more secure database, you need to be using SPs.

"We have recently discovered an issue with SQL Server 2008 CTPs that result in SQL Server 2008 not starting or installing on Feb 29 GMT only. We recommend that you do not run, install or upgrade this CTP on Feb 29 GMT to minimize any impact in your environment. You can install starting on March 1 GMT...."