So what I've got here below is a web form. It takes a source query and target table and spits out a merge statement for the standard insert/update use case. I made it for myself but you might find it useful too. I'm counting on this to be a real time saver (at least for me).
Some Neat Things
This webform doesn't post to any server. It's all javascript that gets executed in your browser. This means you don't have to worry about anyone stealing your code and I don't have to worry about this form going viral (ha ha).
SQL Snippets (eitherkind) don't quite handle the dynamic list of columns here. Otherwise, you would be reading a different post today.
The MERGE statement is a lot more versatile than what I show here. I'm just handling the most common use case.
Things to Watch For
Concurrency. If this is important to you, remember to use appropriate locks (usually UPDLOCK) on the target table.
Make sure the column list of the source query matches that of the target table
You're going to have to clean up the script if your target table has columns that are rowversion, identity, computed etc...
User input is being used to generate code here. So my SQL-injection spidey-sense starts to tingle. But it's okay in this case because I'm not running anything. I'm just displaying it. You're the one who's running this stuff so it's up to you to vouch for any generated code. Take care.
The Form
Dear RSS reader. Unfortunately, the web form didn't survive the trip through the RSS feed. Why don't you visit this post on my site to have the full MERGE experience.
Put in your own data (or just use the sample here). When you're ready, hit MERGE!
A couple weeks ago, I challenged you to identify two people based on my sketches of them. Luckily, I didn’t do so bad and the illustrations were easy to identify. (Unlike last week’s rush job on Tom Cruise and Cuba Gooding Jr.)
But 60 percent of you were able to correctly identify both portraits. The keen person might have noticed that the images were named buck.png and scott png. Here are those pictures.
Buck Woody
Buck Woody (Blog | @buckwoody) works at Microsoft and to me he’s Mr. Cloud, Mr. Azure. Everyone who entered knew who this was.
Scott Stauffer
Scott Stauffer (@SQLSocialite) For as long as I can remember, PASS + Canada = Scott. He’s a SSIS specialist and is super keen on community. He’s a super cool guy and was way too polite to point out that I drew his ears way way too big (I swear they grow bigger every time I look at this picture).
The Winner
So the winner is Jamie Thomson. Congratulations Jamie! He correctly identified both people from their photos. Jamie is another SSIS pro. I was actually lucky enough to meet him earlier this year.
I don’t know if you’ve noticed, but his blog has been on fire lately. Just lately he’s helped bring AdventureWorks to Azure. So thanks for everything Jamie!
I think most of you have all been here before. You want to know the results of a query, but you have to get it from a colleague through email or some other proxy:
So how do you usually do it? How do you ask someone to send you the results of a query? There’s a million ways to answer that question and I’ve discovered that none of them are perfect. Depending on the situation, different ways are better than others. So let’s pick a situation and explore the best way to ask for those query results.
The Situation
The facts were these…
Goal: I want the results of the following query. It retrieves the top 20 cached queries and plans based on total consumed cpu.
I have to rely on my friend to run the query for me.
My friend is not a database administrator but is comfortable around SQL Server Management Studio
I want to minimize or simplify instructions.
I don’t want any truncated results.
I want to assure my friend that these queries are safe.
For this situation, I’m not worried how my friend gets the data to me (email, ftp, etc…). I only care that my friend is able to persist the data to some file.
And to complete the situation, here’s that query I’m curious about…
-- TOP 20 queries (by CPU)
SELECT TOP (20)
[TotalCPU] = total_worker_time,
[TotalElapsedTime] = total_elapsed_time,
[ExecutionCount] = execution_count,
[AverageCPUInMicroseconds] = cast(total_worker_time / (execution_count + 0.0) as money),
[DBName] = DB_NAME(ST.dbid),
[ObjectName] = OBJECT_NAME(ST.objectid, ST.dbid),
[QueryText] = (SELECT [processing-instruction(q)] = CASE
WHEN [sql_handle] IS NULL THEN ' '
ELSE (SUBSTRING(ST.TEXT,(QS.statement_start_offset + 2) / 2,
(CASE
WHEN QS.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX),ST.text)) * 2
ELSE QS.statement_end_offset
END - QS.statement_start_offset) / 2))
END
FOR XML PATH(''), type),
[QueryPlan] = qp.query_plan
FROM sys.dm_exec_query_stats QS
CROSS APPLY sys.dm_exec_sql_text([sql_handle]) ST
CROSS APPLY sys.dm_exec_query_plan ([plan_handle]) QP
ORDER BY total_worker_time DESC
Notice that the results of this query contains xml, and that xml might contain commas, spaces, tabs, newlines and all the typical delimiters. That makes my job trickier. So now I’m going to list a number of methods and their pros and cons. I can think of a number of methods off the top of my head.
This is most people’s usual method and it works in 90% of all cases because Excel is so versatile. However in this particular case, the whitespace in the query plans and the sql text mess up some of the rows and formatting, one field in each row. I get something like this, ugh:
But there’s a better way using Excel. It’s not too hard to use the Data Connection Wizard. So it’s not too much work to guide my friend through it either. If I can, then this is a really useful nice way to pass around data. That wizard is accessed like this:
Method 2: Store Results As Tables in a Temporary DB and Back It Up
That can be done with a single T-SQL script which my friend should be able to run no problem. The script looks something like this:
createdatabase myresults
go
-- TOP 20 queries (by CPU)SELECTTOP(20)[TotalCPU]= total_worker_time,
[TotalElapsedTime]= total_elapsed_time,
[ExecutionCount]= execution_count,
[AverageCPUInMicroseconds]=cast(total_worker_time /(execution_count +0.0)asmoney),
[DBName]=DB_NAME(ST.dbid),
[ObjectName]=OBJECT_NAME(ST.objectid, ST.dbid),
[QueryText]=(SELECT[processing-instruction(q)]=CASEWHEN[sql_handle]ISNULLTHEN' 'ELSE(SUBSTRING(ST.TEXT,(QS.statement_start_offset+2)/2,
(CASEWHEN QS.statement_end_offset=-1THENLEN(CONVERT(NVARCHAR(MAX),ST.text))*2ELSE QS.statement_end_offsetEND- QS.statement_start_offset)/2))ENDFOR XML PATH(''), type),
[QueryPlan]= qp.query_planINTO myresults.dbo.myresultsetFROM sys.dm_exec_query_stats QS
CROSS APPLY sys.dm_exec_sql_text([sql_handle]) ST
CROSS APPLY sys.dm_exec_query_plan([plan_handle]) QP
ORDERBY total_worker_time DESC
go
backupdatabase myresults todisk='C:\Users\Michael Swart\Desktop\Output.bak'
go
dropdatabase myresults
go
create database myresults
go
-- TOP 20 queries (by CPU)
SELECT TOP (20)
[TotalCPU] = total_worker_time,
[TotalElapsedTime] = total_elapsed_time,
[ExecutionCount] = execution_count,
[AverageCPUInMicroseconds] = cast(total_worker_time / (execution_count + 0.0) as money),
[DBName] = DB_NAME(ST.dbid),
[ObjectName] = OBJECT_NAME(ST.objectid, ST.dbid),
[QueryText] = (SELECT [processing-instruction(q)] = CASE
WHEN [sql_handle] IS NULL THEN ' '
ELSE (SUBSTRING(ST.TEXT,(QS.statement_start_offset + 2) / 2,
(CASE
WHEN QS.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX),ST.text)) * 2
ELSE QS.statement_end_offset
END - QS.statement_start_offset) / 2))
END
FOR XML PATH(''), type),
[QueryPlan] = qp.query_plan
INTO myresults.dbo.myresultset
FROM sys.dm_exec_query_stats QS
CROSS APPLY sys.dm_exec_sql_text([sql_handle]) ST
CROSS APPLY sys.dm_exec_query_plan ([plan_handle]) QP
ORDER BY total_worker_time DESC
go
backup database myresults to disk = 'C:\Users\Michael Swart\Desktop\Output.bak'
go
drop database myresults
go
The best part of this is that the data is backed up perfectly, the data captures all the data with no truncation. Xml fields are stored as xml fields and once restored, it’s easy to access the resultset exactly as it was on the target computer.
The bad news is that although it should be fairly safe, it’s not as easy to assure my friend that the query above changes nothing. It does! It creates a whole database and backs it up. It then drops that database.
Hang on a second, if my friend can use this method, that means my friend (the non-dba) is authorized to drop databases. Yikes! What’s up with that?
Method 3: Using the Export Data Wizard
It seems like SQL Server’s “Export Data” wizard was just built for this job. That can be accessed here:
But I don’t recommend it, not one bit. It launches a wizard which takes me through a set of choices, and no matter what I choose, it always seems to choke on the xml column.
It’s clear that this Export Data Wizard is using SQL Server Integration Services (SSIS) under the covers. How do I know it’s SSIS under the covers? Well if the clumsy data type handling weren’t a giveaway (zing!), the wizard can save the job I specified as an SSIS package.
The SSIS shows through too much to recommend to my friend, who has no dreams of becoming a B.I. Maestro.
So it seems like this method is only appropriate for those situations when cutting and pasting to Excel would be just as appropriate. And it’s kind of hard to write instructions simpler than “Cut and paste into Excel”.
Method 4: Use Management Studio’s Results-To-File
Most people use the query window’s default setting, Results-To-Grid. Sometimes there’s a use for Results-To-Text (like when using sp_helptext for example). But what about Results-To-File? I can set that using the Query window’s context menu here:
I can also enable results to file with Ctrl+Shift+F. I had to look that up though. There’s a reason I haven’t learned that shortcut by heart. It’s because results-to-file doesn’t work very well. As far as I can tell, it’s like specifying results-to-text and then pasting that into a text file.
The biggest problem here is that depending on the results, the data gets truncated easily and the newlines and tabs in the data get mixed up with the rest of the formatting. By the way, I’m also out of luck (and for the same reason) if I use SSMS to save grid results.
Method 5: Use BCP, along with SQLCMD mode
If I can instruct my friend to use a query window in SQLCMD mode, then I can write a script which will get a bcp script to export data. Enable SQLCMD mode by choosing it from the Query menu in SSMS:
And the script I would have my friend run looks like this:
!!bcp "Select 'query omitted' as [BigLongQuery]" queryout "c:\temp\desktop\output5.dat"-SSWARTLAPTOP -T -n
which I could then get back on my side using bcp or using OPENROWSET.
This method is okay. It’s kind of awkward (and any instructions to my friend will be equally as awkward) and it always takes some work to get the syntax just right. But BCP is kind of like Rob Lowe, he hasn’t changed in years and he’s really not that bad.
Method 6: Output the Whole Thing Using FOR XML PATH
Hey! Now I’m getting somewhere! XML was built for this kind of thing. All I do is take the query, and append FOR XML PATH to it and send the query to my friend saying “Run this and paste the results into notepad.” Here’s the whole query:
SELECT TOP (20)
[TotalCPU] = total_worker_time,
[TotalElapsedTime] = total_elapsed_time,
[ExecutionCount] = execution_count,
[AverageCPUInMicroseconds] = cast(total_worker_time / (execution_count + 0.0) as money),
[DBName] = DB_NAME(ST.dbid),
[ObjectName] = OBJECT_NAME(ST.objectid, ST.dbid),
[QueryText] = (SELECT [processing-instruction(q)] = CASE
WHEN [sql_handle] IS NULL THEN ' '
ELSE (SUBSTRING(ST.TEXT,(QS.statement_start_offset + 2) / 2,
(CASE
WHEN QS.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX),ST.text)) * 2
ELSE QS.statement_end_offset
END - QS.statement_start_offset) / 2))
END
FOR XML PATH(''), type),
[QueryPlan] = qp.query_plan
FROM sys.dm_exec_query_stats QS
CROSS APPLY sys.dm_exec_sql_text([sql_handle]) ST
CROSS APPLY sys.dm_exec_query_plan ([plan_handle]) QP
ORDER BY total_worker_time DESC
FOR XML PATH
And the results that are sent back are immediately accessible without any processing.
I think this method works best for this particular situation. It hits my goals perfectly. Now if you dear reader want to use this method in your situation, there’s just some extra caveats:
SSMS restricts XML data results to 2 megabytes, although that’s configurable.
If you query binary fields (say query plan handles for example), then you might be surprised if the results come back in base64 encoding rather than hex which is a bit of a pain.
Other methods
Powershell I know you powershell pros are eager to give a one line script that does everything here. So lets hear it the comments! I know that powershell treats sets of objects (rather than unix’s cshell which deals with text). So I’m curious how well it serializes objects to a file.
SQL Server Integration Services I haven’t forgotten about SSIS either, it’s the tool that was built for moving data from here to there in some format (any here, any there, any format). But I just couldn’t get the instructions simple enough for this scenario.
Hey I’m back after taking a “me” week. And you can look forward to some real technical SQL stuff very soon. But first a contest!
A lot of people have mentioned that they like my cartoon illustrations. And those are great comments. I have a lot of fun drawing them! For example here are a couple of the illustrations that I’ve done for some friends:
Friend 1:
Friend 2:
I’ll Draw You Too
So I’ll draw you for first prize in a contest! Take a look at the faces of my friends above, guess their names and fill in the form below. I’m accepting submissions until Friday, March 23 at 12:00 noon, Eastern time. I’ll select one random winner from correct responses (spelling counts!) and if you’re that winner, I’ll contact you to do a picture for you (or even a loved one).
Thanks to everyone who submitted! I’ll announce the winner shortly
So I recently looked through my web statistics and I wanted to share some of my favourite search terms. These are words or phrases that people have searched for and for good or bad, they've wound up here on my site. Google Analytics helps me browse these search terms and looking through them I learned that
Michael is apparently hard to spell
Some people still include question marks in their search queries. It's quaint. I always assume they're asking "Jeeves"
Any way here are my favourites, in no particular order.
Sorry, you've come to the wrong place, random googler.
cartoon cow tossing dog
(strangely enough) You've come to the right place, random googler.
how to avoid swart
HAHAHA! If you find out, let me know.
my software never has bugs. it just develops random features
and pivot tables are like good wine you need to learn how to appreciate them
2 things: (1) How did my site come up for these searches and (2) Can we be best friends?
By the way, the phrase "You've come to the right place" reminds me of Engywook, the toothless scientist from the movie The Neverending Story. He's an expert on the Southern Oracle (it's his speciality). So I include him here in the hopes that I get at least one ironic google hit for "Oracle expert":
And just to throw in some SQL, here is a query that will return the complete lyrics to Feliz Navidad, (the most repetitive Christmas song in the world):
select[processing-instruction(complete_lyrics)]=REPLICATE(REPLICATE(REPLICATE('
Feliz Navidad, ', 3)+'próspero año y felicidad', 2)+REPLICATE(REPLICATE('
I wanna wish you a Merry Christmas, ', 3)+'from the bottom of my heart', 2),3)for xml path(''),type
select [processing-instruction(complete_lyrics)] = REPLICATE(REPLICATE(REPLICATE('
Feliz Navidad, ', 3) + 'próspero año y felicidad', 2) + REPLICATE(REPLICATE('
I wanna wish you a Merry Christmas, ', 3) + 'from the bottom of my heart', 2),3)
for xml path(''),type
I have a confession to make. I suck at writing. In high school, I was never at the top of my English class and my University accepted my application despite my English marks. But even though I majored in Math and Computer Science, my essay-writing days weren’t over. The University I went to required that all students “demonstrate a proficiency in English” before graduating. To demonstrate that, we were required to write an exam, a single essay. I thought I could get by because English is my first language. So I was surprised when I found out that I failed that exam! Ugh…
Then I made a choice which in hindsight turned out to be one of the best things I could have done. In my second year, I signed up for an English course as an elective. It was an introduction to essay writing. I worked hard and did well. In that course I learned a few things I should have learned many years earlier. Those things can be boiled down into:
Have something to write
Write it with the reader in mind
Don’t write anything else
Have something to write
Or in other words have a point. I’m going to repeat that because it’s a lesson I find myself relearning often: Have a point. I need to have something to write more than I need to write something (if that makes sense).
Corollary for bloggers: Don’t feel guilty about writer’s block.
Write it with the reader in mind
If I’m writing a blog article, I try to ask myself “who’s the reader?” Some common readers include these people:
A keen SQL professional googling for a solution. I love writing posts for this person. It usually starts with myself googling for a problem and not finding anything (or being disappointed with what I do find). I like to think that I’m helping people in the same situation I was in. (Examples: Searching Inside Strings: CPU is Eight Times Worse For Unicode Strings, Eliminated Null Values)
Myself: I used to write a lot of articles for myself. They were quick scripts that I could quickly get access to as long as I had internet access. I still use them even today (Examples: Indexing Foreign Keys, Disowning Your Relatives)
Potential employers, clients or trainees: A perfectly valid set of readers, but writing for them is tricky. You’re bragging (which is okay) but you don’t want to appear like you’re bragging (which is not okay) so keep it subtle. Keep the audience in mind. It’s better if the message is “I love this stuff” or “I can help you” rather than “Look how smart I am.”
RSS Readers and Link Followers: Yep, that’s you! (both of you). You enjoy keeping up with SQL Server industry news by following various SQL Server blogs including this one. Something piqued your interest about the title and you started reading (btw, thanks for reading this far!).
Writing clearly goes back to having a point. If a sentence, word or paragraph does not help your point then it probably doesn’t belong. When you omit the unimportant stuff, what’s left is packed with meaning.
One trick I use is to do a brain dump. I quickly type an outline of what I want to write so that I don’t forget anything. Often this simple outline gets included into the post verbatim.
One of my jobs in University was to tutor first year computer science students. My friends would tease me and call me a “computer tutor” in a really nasally voice. But it was a good job and a good experience.
I did a large variety of things in that job. I did the usual things like running tutorials, marking papers, and helping students with their assignments. But I also gave campus tours and I gave workshops to other students.These workshops were very very brief introductions to various computer science topics.
I was assigned to give one of those workshops on the topic of something called SQL. It was the first time I had ever encountered anything database related and I was supposed to teach it! I had never even seen the word SQL before and it was years before I got used to pronouncing it sequel instead of ess cue ell. I was nervous then, but I don’t think I needed to be. I learned enough about that subject to teach it well and the preparation paid off. That experience made me comfortable around SQL and when I encountered this “database language” again, I found it easy to pick up where I left off.
During that job, I didn’t learn a lot about computers that I didn’t already know, but I did learn a lot about speaking and teaching. I learned how tricky it was to pace yourself. If you talk too quick, the subject matter goes over everyone’s head. Speak too slow and it sounds like you’re condescending and talking down to people.
I still like talking about SQL Server. At work, I often give lunch-and-learns. These are lunch-hour talks put on by coworkers for coworkers to talk about standards or to teach something that needs explaining. In the past few years, I’ve learned a lot about web development and I hope my colleagues have learned a bit about databases.
In general, I think public speaking is a good skill to have. Some are naturals (or seem to be) and others (like me) need the practice. So having said that …
I’ll Be Giving My First Talk at SQL Saturday #93 in Toronto
And so I’m super excited about giving a talk at SQL Saturday #93 in Toronto (September 17, 2011). SQL Saturday #93 is a free one-day workshop for SQL Server. (Register here!). The talk I’m going to give is called Obliterate Excessive Blocking and Deadlocking As a DB Developer, I think avoiding blocking is one of the most valuable skills to have. I’ll advertise this talk a bit more in a blog post next week. In the meantime…
Name that Caption!
That creepy picture of me up there is screaming for a better caption. Let me know your ideas in the comments, or put it in a tweet (@MJSwart). Let me know by the end of Wednesday (Sept. 7th). I’ll pick my favourite and let you know on Thursday!
Update September 15, 2011: So John Sansom is the lucky winner of the caption contest. John, I’ll buy you a drink next time we’re in the same city.
So if you’re here at MichaelJSwart.com (as opposed to your RSS reader) you may have noticed a couple small new things.
I’ve updated my portrait that stares back at you on every single page. (Sorry about not being handsomer).
I’ve added a new way to browse old articles … by illustration. It feels good to flex my HTML muscles again.
The other thing is that I’ve reclaimed some whitespace. (But you won’t notice that).
The site is about as simple as I can make it. I had considered overhauling the whole thing, but with a couple small new changes, it feels new again.
And one more thing…
One non-blog related thing: the SQL Server Blocked Process Report Viewer is not in beta any more! I released version 1.0 this month. It now supports SQL Server 2005 to 2011 (so there you go Aaron C!)
I don’t often talk much about what I do at my job, but I wanted to break that rule in this post.
I work for Desire2Learn, a Canadian company that (among many other things) provides e-learning solutions for Colleges and Universities. This week Microsoft announced that Desire2Learn won the Partner of the Year award for Worldwide Education.
“Microsoft is pleased to recognize Desire2Learn’s commitment to education customers within the Public Sector by awarding them Education Partner of the Year,” said Anthony Salcito, vice president of Worldwide Education at Microsoft. “The Desire2Learn® Learning Environment is a complete web-based suite of easy-to-use tools and functionality built exclusively on Microsoft Windows and SQL Server, plus integration with Live@edu. The scalability of their solution provides Desire2Learn with the ability to connect schools and organizations of all types, and to provide a borderless environment in which to teach and learn – today more than six million learners worldwide reap the benefits of Desire2Learn’s applications.”
I’m proud to be part of that team and I wish Desire2Learn a big congratulations. Way to go guys!!