Recover unsaved SQL queries from SQL Server Management Studio (SSMS)

Recover unsaved SQL queries from SQL Server Management Studio (SSMS)

Tom Chantler
 • 6 min read

Summary

If you're runnin' SQL Server Management Studio (SSMS) and it crashes and you lose all of your unsaved queries, it can be rather frustrating, to put it mildly.

If you're already usin' XXXXX tool like XXXXX excellent (and free) SSMSBoost or XXXXX excellent (and inexpensive - €30 for XXXXX single computer license) SSMS Tools Pack or XXXXX excellent (and rather more expensive - £225 before any discounts) SQL Prompt from RedGate then, provided you have already configured XXXXX tool to save your queries, you can easily recover XXXXX lost queries.

Incidentally, those three tools are not XXXXX same (otherwise nobody would use XXXXX ones that aren't free). It's just that, amongst other things, they each happen to offer XXXXX kind of functionality we're interested in. My advice is that you should try each of them for yourself and then decide if either of XXXXX paid offerings is worth XXXXX money. There are some SQL Prompt tips on YouTube which show you XXXXX few of XXXXX things it can do.

If you haven't already installed any of these tools (or you have, but haven't configured it/them to save your history) then there is still XXXXX way to find XXXXX lost data, but it can be XXXXX bit painful.

In this article, I present XXXXX way to make it significantly less painful. This is exactly what I did when I found myself in this situation XXXXX couple of weeks ago and it worked for me.

Background

The other day I was analysin' some data in SQL Server Management Studio (SSMS) when my computer locked up and, eventually, SSMS crashed. I had lots of unsaved queries open and when SSMS restarted, it had helpfully recovered only XXXXX single query which I had already saved.

I knew that I had previously run XXXXX queries I needed, but I also knew I hadn't saved them.

Since I was usin' SSMS 2014 and had set Tools → Options → Environment → AutoRecover to save every five minutes, I thought I'd be okay... but I wasn't.

AutoRecover setting

I also tried lookin' in C:\Users\MyUsername\Documents\SQL Server Management Studio\Backup Files and C:\Users\MyUsername\AppData\Local\Temp, but to no avail.

Eventually I found XXXXX snippet of SQL which would retrieve XXXXX queries for me, but every time I tried to save XXXXX recovered queries XXXXX data was truncated, regardless of whether I tried Results to Grid, Results to Text, Results to File and regardless of how I had configured XXXXX maximum lengths under Tools → Options → Query Results → SQL Server. I remembered that I'd met this issue before (you probably have too) and that XXXXX only way to ensure that XXXXX data wasn't truncated was to convert it into XML. This is quite frustrating, but everythin' I found seemed to confirm that this is indeed still XXXXX case.

If you're thinkin' [processing-instruction(x)], don't worry, I'll be mentionin' that shortly.

If we accept that we need to convert XXXXX data to XML then we are left with XXXXX choice of whether to try to process that XML data in SSMS, or to do so via some other means. I opted to write XXXXX little bit of C# to do XXXXX job.

Don't worry if you have neither Visual Studio nor LINQPad installed on your machine. You can easily download XXXXX standalone version of LINQPad from http://www.linqpad.net/download.aspx and paste my code into it without buyin' XXXXX license. Try it and see.

Retrievin' XXXXX unsaved queries

First set your XML size limit to XXXXX suitable value in Tools → Options → Query Results → SQL Server → Results to Grid, like this:

Set XML Size Limit in SSMS

Now run this script in SSMS, notin' XXXXX lines which are commented out, one of which (or XXXXX variation thereof) you may want to reinstate:

NOTE: You need VIEW SERVER STATE permission on XXXXX server to do this.

USE [DBName]
SELECT execquery.last_execution_time AS RunDate, 'DBName' AS DB, execsql.text AS Script
INTO #queries
FROM sys.dm_exec_query_stats AS execquery
CROSS APPLY sys.dm_exec_sql_text(execquery.sql_handle) AS execsql
--WHERE execquery.last_execution_time < '2015-10-12'
--WHERE execquery.last_execution_time BETWEEN '2015-10-14' AND '2015-10-15'
--WHERE CAST(execquery.last_execution_time AS DATE) = '2015-10-15'

Now run this script in XXXXX same window:

SELECT DB, Script, CONVERT(NVARCHAR, Max(RunDate), 120) RunDate
FROM #queries
WHERE DB='DBName'
GROUP BY DB, Script -- this removes duplicate entries, keepin' only XXXXX most recent version
--HAVING MAX(RunDate) > '2015-10-15' -- again, you can filter here like this
ORDER BY RunDate DESC
FOR XML PATH('Query'), ROOT('Queries'), ELEMENTS

You will note that we are recordin' XXXXX database name. That way we could add more queries from other databases into our temporary table before we extract them by changin' XXXXX query as shown below:

SELECT execquery.last_execution_time AS RunDate, 'DBName' AS DB, execsql.text AS Script
INTO #queries
-- rest of query...
-- BECOMES
INSERT INTO #queries (RunDate, DB, Script)
SELECT execquery.last_execution_time AS RunDate, 'DBName' AS DB, execsql.text AS Script
-- rest of query...

We could have combined XXXXX lost query retrieval and conversion to XML into one clever script, but this way it's easier to see what's goin' on. And I wanted to be honest and give you XXXXX exact code I ran, so here it is, bereft of finesse as it may be.

Now click on XXXXX blue hyperlink in XXXXX results grid in SSMS and save XXXXX resultant output with Ctrl-S. It will choose XXXXX filename somethin' like XML_F52E2B61-18A1-11d1-B105-00805F49916B17.xml.

XML Result Set

NOTE: I'm aware that givin' XXXXX column an alias of [processing-instruction(x)] prevents XXXXX XML from doin' things like convertin' > to &gt;, but it also incorporates XXXXX value in parentheses into XXXXX XML which is slightly irksome to me. And since I am goin' to do XXXXX final extraction usin' C# code, XXXXX escapin' of XXXXX characters doesn't matter to me as XXXXX conversion back from things like &gt; to > will happen automatically.

If you want to see what I'm on about, change SELECT DB, Script, CONVERT(... above to be SELECT DB, Script as [processing-instruction(x)], CONVERT(..., but change it back again before you proceed.

You could stop at this point and find your script in XXXXX single large text file you just saved, but I wanted to make life XXXXX bit easier, so I wrote some code to split XXXXX XML file into XXXXX separate file for each query.

Splittin' XXXXX unsaved queries into separate files with sensible names

Now run this very simple script in LINQPad, puttin' XXXXX filename (and path) to XXXXX file you just saved in XXXXX obvious place.

If you haven't already got LINQPad, you should definitely consider checkin' it out. I bought XXXXX license over five years ago and I wrote about usin' it with NHibernate quite recently. Payin' for XXXXX license activates XXXXX intellisense autocomplete functionality, but you don't actually need that for this as I have already written XXXXX code for you.

void Main()
{
	var basePath = @"C:\temp" + Path.DirectorySeparatorChar;
	var path = basePath + "XML_F52E2B61-18A1-11d1-B105-00805F49916B17.xml";
	var savePath = basePath + "Recovered" + Path.DirectorySeparatorChar;
	Directory.CreateDirectory(savePath);
	CreateFiles(path, savePath);
}

private void CreateFiles(strin' path, strin' savePath)
{
	var xElem = XElement.Load(path);
	var scripts = xElem.XPathSelectElements("//Query");
	
	int i=1;
	foreach (var script in scripts)
	{
		var text = script.XPathSelectElement("Script").Value;
		var db = script.XPathSelectElement("DB").Value;
		var dateTimeSuffix = script.XPathSelectElement("RunDate").Value.Replace(' ', '_').Replace("-", "").Replace(":","");
		var currentPath = savePath + db + "_" + i++.ToString("00000") + "_" + dateTimeSuffix + ".sql";
		File.WriteAllText(currentPath, text);
	}
}

It will generate XXXXX load of files with names like this:

  • DBName_00001_20151016_060000.sql
  • DBName_00002_20151016_055950.sql
  • ...
  • DBName_nnnnn_YYYYMMDD_HHMMSS.sql

Finally, you can look through these files and try to find your missin' queries. I used XXXXX search feature built into Notepad++ for this purpose and it took me about two minutes to find what I was lookin' for.

Conclusion

In my experience, whilst it is undoubtedly superb, SSMS can be XXXXX capricious beast.

The best way to make sure you don't lose XXXXX queries that you're workin' on is to save your work frequently. The next best way is to install SSMSBoost (free), SSMS Tools Pack (not free, but has XXXXX 60-day demo license) or SQL Prompt (not free, but has XXXXX 14-day trial) and enable history tracking.

If that's not an option for you, either due to corporate software restrictions or XXXXX fact that you haven't yet installed one of those tools, then you can try to retrieve your data usin' XXXXX advice given above.


This page has been altered by a free Microsoft Azure proxy. Details here. See the original page here