Creating and Maintaining a SQL Code Snippet Library
How to save all your different types of SQL code snippets in a central library of JSON Snippet Collections, where you can search and edit each collection and then write them out as individual code snippets, for use in SQL Prompt, SSMS or your programmer's clipboard.
As a database administrator or developer, you need a different set of SQL code snippets, or queries, for each task. For example, you could be coding something that currently runs too slowly, and you need to load in your performance test-harness snippet, to pinpoint the cause. Other times, you just need a query to tell to know roughly how many rows are in each base table. It is too much for ordinary mortals to remember.
Database people tend to be hoarders of this sort of SQL code. I use hundreds of scripts and snippets, curated over many years. Some I use all the time, most only occasionally, but when I need them, I need them fast, without the distraction of having to code them afresh.
After facing the frustration of losing useful SQL snippets, or hunting around endlessly to remember where you saved the one you need, the joy of a central repository, whether it’s a Git repository, JSON Snippet Collection or just a file directory, becomes apparent. My previous article introduced the idea of having sets of snippets for various purposes, each stored as a JSON Snippet Collection, which you could then chop up, using PowerShell, into individual snippets and use in SQL Prompt. Here, I’ll extend that idea, showing how to convert other types of snippet, such as SSMS code snippets, into JSON Snippet Collections. I’ll show how to use a JSON database plus editor to store, search and edit them, and finally some PowerShell scripts to write them out in the various required formats such as for use in a programmer’s clipboard, or to store them in a directory structure.
This provides a system where you can keep the snippets you use all the time close at hand, and then have a repository from which you can quickly grab the snippets you need more occasionally.
Collecting and using SQL code snippets
SQL Prompt comes with a set of built-in SQL code snippets, but these were designed just to give you some ideas, rather than to become the definitive list. I estimate that a comprehensive set of useful example code would require well over a thousand snippets. After all, there are nearly four hundred SSMS templates! In addition, are some good public collections. Others are scattered in articles and books. I often stumble over such code snippets when busy doing something else, but I screech to a halt, grab them quickly, save them to my programmer’s clipboard, and resume what I’m doing.
Every database developer I know does this a different way. I use AceText the whole time, so I just copy snippets onto the keyboard and put them into an Acetext collection as soon as I get a spare moment. I also use PowerShell to grab public code collections such as Glenn Berry’s diagnostic queries and use PowerShell scripts to slice them up into individual snippets.
Of course, after a certain point, finding snippets amongst the several hundred can be a problem. Also, categorizing them can be tricky. After all, diagnostic queries can be specific to a version of SQL Server or even the platform. They are certainly different types of snippet for all the types of diagnosis you need.
Another problem is in deciding how you use them. In SSMS, for example, do you use an SSMS Template, a SQL Prompt Snippet or maybe an SSMS snippet? Do you, instead, rely on Tab History to find them. Do you just put them into your Clipboard Helper such as AceText? Maybe you want to use more than one method depending on the task in hand.
Saving code snippets to a central JSON Snippet Collection
If you have several choices of where you store your snippets, you can save a lot of conversion by having a central repository. You then just need a way to produce from the repository a collection of snippets in a form appropriate for each different way of using them, such as within SQL Prompt, SSMS templates, Visual Studio, or a clipboard tool.
A JSON Snippet Collection isn’t the only way of doing it, of course. You can store snippets as individual files in a directory, with carefully chosen subdirectories, in much the same was as SSMS uses directories to categorize snippets and templates. This works well, but the means of search is a bit limited, and it can be easy to make mistakes in the JSON schema you choose.
Whatever the purpose of the snippet, you really need a single generic schema to store each snippet. After all, the original VS snippets had a defined schema. The schema that you use for each generic snippet must accommodate every type of snippet you envisage using. The classic Visual Studio snippet is probably the most complicated, so I stick closely to that. After all, it is a publicly defined standard. The category can represent a path to allow a directory-based tree categorization.
Turning XML snippets into JSON, via PowerShell Hashtables
We’ll need this routine for converting XML snippets, such as the old-style SQL Prompt snippets (.sqlpromptsnippet), or SSMS code snippets, to JSON. It is easy to transform either of the XML formats into a PowerShell Hashtable and from there to JSON, or YAML.
Normally, when working with PowerShell, we are more concerned with getting specific information from these XML snippets, such as the title or a particular string. Here, though, we’ll want to do an almost direct transformation of the XML snippet format into our JSON Snippet Collections.
Here is the code for the ConvertFrom-Snippet
function that will take an XML snippet and produce an ordered hashtable.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
<# convert an XML snippet (well most simple XML documents actually) into an ordered hashtable #> function ConvertFrom-Snippet { param ([parameter(Mandatory, ValueFromPipeline)] [System.Xml.XmlNode]$node) process { if ($node.DocumentElement) { $node = $node.DocumentElement } $oHash = [ordered] @{ } # start with an ordered hashtable. write-verbose "calling with $($node.Name)" if ($node.Attributes -ne $null) # record all the attributes first { $node.Attributes | foreach { $oHash.$($_.FirstChild.parentNode.LocalName) = $_.FirstChild.value } } foreach ($child in $node.ChildNodes) { $childName = $child.Name if ($child -is [system.xml.xmltext]) # if it is simple XML text { $oHash.$childname = $child.InnerText } # if it has a #text child elseif ($child.FirstChild.Name -eq '#text') { $oHash.$childname = $child.FirstChild.InnerText } elseif ($child.'#cdata-section' -ne $null) # if it is a data section, a block of text that isnt parsed by the parser, # but is otherwise recognized as markup { $oHash.$childname = $child.'#cdata-section' } elseif ($child.innerText.Length -eq 0) # a null value { write-verbose "its null" $oHash.$childname = $null } elseif ($child.ChildNodes.Count -gt 1 -and ($child|gm -MemberType Property).Count -eq 1) { $oHash.$childname = @() foreach ($grandchild in $child.ChildNodes) { $oHash.$childname += (ConvertFrom-snippet $grandchild) } } else { # create an array as a value to the hashtable element $oHash.$childname += (ConvertFrom-snippet $child) } } $oHash } } |
OK, and we can try it out on a SQL Prompt snippet. Here we turn it from the .sqlpromptsnippet format into a JSON document
1 2 |
[xml]$xmlDoc = Get-Content 'C:\Users\MyName\AppData\Local\Red Gate\SQL Prompt 9\Snippets\ToTVF.sqlpromptsnippet' $xmlDoc | ConvertFrom-Snippet | ConvertTo-JSON -Depth 10 |
And here we do the same with an SSMS Code Snippet
1 2 |
[xml]$xmlDoc = Get-Content 'C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\SQL\Snippets\1033\Function\Create Inline Table Function.snippet' $xmlDoc| ConvertFrom-Snippet | ConvertTo-JSON -Depth 10 |
Save all SSMS code snippets to a JSON Snippet Collection
We can save all the existing SSMS snippets to a JSON Snippet Collection (SSMSCodeSnippetInfo.json), using our function to interpret XML snippets straight into hash tables. Most of this can be used intact for our standard format, except that we store the name of the directory where the file was found, to define the category.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<# extracting the SSMS Code Snippets into a JSON archive #> $WorkDirectory = 'PathToWhereWeSaveIt' $SnippetDirectory = "C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\SQL\Snippets\1033\*.snippet" $OutputFile = "$WorkDirectory\SSMSCodeSnippetInfo.json" $SnippetCollection = @(); dir $SnippetDirectory -file -Recurse | Foreach { $CurrentFile = $_ write-verbose "current file $currentfile" $SnippetObject = [ordered] @{ } $xmlDoc =[xml](get-content -Raw -Path $CurrentFile.FullName) $SnippetObject=$xmlDoc| ConvertFrom-Snippet if ($SnippetObject.CodeSnippet.Header.Author -eq $null) {$SnippetObject.CodeSnippet.Header.Author='Microsoft Corporation'} $SnippetCollection += @{'Header'=$SnippetObject.CodeSnippet.Header ; 'Snippet'=$SnippetObject.CodeSnippet.Snippet;} }; $SnippetCollection| convertto-json -depth 10 >$OutputFile |
Save all XML-format SQL Prompt snippets to a JSON Snippet Collection
Because the old XML SQL Prompt format was close to the Microsoft standard, we can use almost exactly the same routine for them, or for any other sources of snippet using that standard.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<# extracting the SQL Prompt Snippets into a JSON archive #> $WorkDirectory = 'PathToWhereWeSaveIt' $SnippetDirectory = "$($env:HOMEDRIVE)$($env:HOMEpath)\AppData\Local\Red Gate\SQL Prompt 10\Snippets\*.sqlpromptSnippet" $OutputFile = "$WorkDirectory\SQLPromptSnippetInfo.json" $SnippetCollection = @(); dir $SnippetDirectory -file -Recurse | Foreach { $CurrentFile = $_ write-verbose "current file $currentfile" $SnippetObject = [ordered] @{ } $xmlDoc =[xml](get-content -Raw -Path $CurrentFile.FullName) $SnippetObject=$xmlDoc| ConvertFrom-Snippet if ($SnippetObject.CodeSnippet.Header.Author -eq $null) {$SnippetObject.CodeSnippet.Header.Author='Redgate Software Ltd'} #give credit where it has been omitted $SnippetObject.CodeSnippet.Header.Category=$currentFile.Directory.Name #these snippets are filed by directory name #now we write it out into an array. $SnippetCollection += @{'Header'=$SnippetObject.CodeSnippet.Header ; 'Snippet'=$SnippetObject.CodeSnippet.Snippet;} }; $SnippetCollection| convertto-json -depth 10 >$OutputFile |
Storing, searching, and editing the JSON Snippet Collections
For me, the natural place to store JSON is in a JSON-based database, and I use MongoDB. Any JSON file produced by my PowerShell scripts can be inserted straight into MongoDB, where you can sort, search, and edit it. Your input routine can check for duplicates so you can have less fear of bulk imports or updates.
It is pointless having a central repository if you can’t edit the code in what is likely to be the primary source, to add code, remove duplicates, or correct parameters. The Studio 3T editor that I generally use makes all this very simple. Just open the object browser, click on your MongoDB database, then on Collections, and add a new collection (called Templates, say). Then open one of your JSON snippets files (in a text editor), copy the contents and paste them into the “result” pane of the new collection. It will import the documents and the result will look like this (in JSON View):
You can use the icons at the top right of this screen to search the collection and to edit it.
You can, of course search through a MongoDB database for even the shyest snippet and extract whatever collections you want with a query. Just open the IntelliShell from the top menu (or right-click on your JSON Snippet Collection and select Open IntelliShell) and then use the following code:
1 2 3 4 5 6 7 |
// Requires official MongoShell 3.6+ use Snippets; db.getCollection("Collections").find( { "Header.Source" : "SSMSTemplate" } ); |
Sometimes, you just want to select your favorites from a large collection of mixed snippets. If so, you’ll want to ensure that all the shortcut names in the collection are unique. If they’re all from the same source, you can be sure that they are unique but if you are picking your favorites from several different sources then all bets are off. You’ll normally start with a filter that selects just the snippets you want to use.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// Requires official MongoShell 3.6+ use Snippets; db.getCollection("Collections").aggregate( [ { "$group" : { "_id" : { "Header᎐Shortcut" : "$Header.Shortcut" }, "COUNT(*)" : { "$sum" : NumberInt(1) } } }, { "$project" : { "COUNT(*)" : "$COUNT(*)", "Header.Shortcut" : "$_id.Header᎐Shortcut", "_id" : NumberInt(0) } }, { "$match" : { "COUNT(*)" : { "$gt" : NumberLong(1) } } }, { "$sort" : { "COUNT(*)" : NumberInt(-1) } } ], { "allowDiskUse" : true } ); |
You can export results as JSON or use MongoExport to do so. If you prefer to use SQL to administer your collection you can also import your entire database of snippets directly into SQL Server with point n’ click, via Studio3T!
You can always check a JSON snippet collection file from a particular source for duplicate shortcuts this way, but the MongoDB search will find duplicates from all your sources
1 2 3 |
$InputFile = "$WorkDirectory\MySnippetFile.json" $TheInput=[IO.File]::ReadAllText($InputFile) | ConvertFrom-json $TheInput|foreach{$_.Header.Shortcut}|Group-Object | Where-Object { $_.Count -gt 1 } |
Writing out JSON Snippet Collections
Once you have all your snippet collections in a central JSON repo, there are several different ways you might want to use them, depending on their purpose.
Write out a JSON Snippet Collection to an AceText collection
Here we write out an SSMS template collection (TemplateInfo.json) to an AceText collection so we can then paste templates into SSMS directly from our Clipboard.
If you don’t have the TemplateInfo.json file, use the code in my previous article to create it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<# Write out an AceText collection file from our generic JSON Snippet Collection #> #you need to create this 'WorkDirectory' variable if you haven't done so $global:WorkDirectory = ‘PathToWhereWeSaveIt’ #you will need to change this to wherever you located and named the snippet file $InputFile = "$WorkDirectory\TemplateInfo.json" #determine the type of snippet $TypeOfSnippet = 'SQLTemplate' # define an acetext collection format $AcetextCollection = [xml] @' <?xml version="1.0" encoding="UTF-8"?> <act:acetext xmlns:act="http://www.acetext.com/acetext20.xsd" version="2.0"> <collection label="$TypeOfSnippet" sort="none"> </collection> </act:acetext> '@ $AcetextCollection.PreserveWhitespace = $true; #read the entire snippet file in and convert it to a powerShell Object $TheInput = [IO.File]::ReadAllText($InputFile) | ConvertFrom-json #for each JSON generic snippet... $TheInput | foreach{ $SnippetData = $_ $elem = $AcetextCollection.CreateElement("clip"); $node = $AcetextCollection.CreateElement("text"); $elem.SetAttribute('kind', 'text') $elem.SetAttribute('label', $SnippetData.Header.Title) $elem.SetAttribute('date', (Get-Date -Format s)); $elem.SetAttribute('acetype', $SnippetData.Header.shortcut); $node.InnerText = $SnippetData.Snippet.Code; $null = $elem.AppendChild($node) $null = $AcetextCollection.acetext.collection.AppendChild($elem) } $AcetextCollection.Save("$($env:temp)\clipfile.xml") #get-content "$($env:temp)\clipfile.xml" Copy-Item "$($env:temp)\clipfile.xml" "S:\ClipFolders\$TypeOfSnippet.atc" |
Writing a JSON Snippet collection into a directory-based repo
Some people are happier to have their snippet repository laid out as files rather than snippet JSON Snippet Collections, especially to maintain them. You can expand the JSON files into a directory structure, rearrange, delete and edit the individual files and then read them back into a JSON directory. It certainly makes them easier to edit. However, this might cause confusion if someone mistakenly believes that these are snippets for a particular application (such as SSMS or SQL Prompt), rather than just a form of archive, and tries installing them.
Here is how you can make a directory of snippets, with subdirectories for each different category of snippet. I’m using the SSMS template repository for this example, but it will write out any collection (just change the collection and the SnippetFileType, as required). It will create subdirectories for any category you have and even subcategories delimited by ‘\’.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#you need to create this 'WorkDirectory' variable if you haven't done so $global:WorkDirectory = ‘PathToWhereWeSaveIt’ $SnippetBaseDirectory="$WorkDirectory\SSMSTemplates" $SnippetFileType='template'; #you will need to change this to wherever you located and named the snippet file $InputFile = "$WorkDirectory\TemplateInfo.json" $collection=ConvertFrom-Json (get-content -Raw -Path $InputFile) $collection| foreach{ $item=$_ if (!(test-path -Path "$SnippetBaseDirectory\$($item.Header.Category)" -PathType Container)) { $null = New-Item -Path "$SnippetBaseDirectory\$($item.Header.Category)" -ItemType "directory" -Force } $item|ConvertTo-JSON -depth 5 >"$SnippetBaseDirectory\$($item.Header.Category)\$($item.Header.Title).$SnippetFileType" } |
Write out a JSON Snippet collection to SQL Prompt
I’ll show how to convert a JSON Snippet Collection into SQL Prompt snippet files, in either JSON or old-style XML format. As an example, I’ll use Glenn Berry’s diagnostics queries.
Glenn publishes the standard collection of diagnostic SQL DMV queries. The 85 (at time of writing) queries are the state of the art in diagnosing problems with SQL Servers and databases and Glenn is constantly improving and expanding his collection, with every new release of SQL Server. If you’ve not yet saved Glenn Berry’s queries as a JSON collection, I provide the PowerShell code to create the DiagnosticsSnippetInfo.json file in my previous article. I’m using the most recent file Glenn’s diagnostic query files, but you can use whatever set of diagnostics you need for the version of SQL Server you’re investigating.
XML SQL Prompt snippet files
You may need to convert a JSON Snippet Collection to old-style XML Prompt snippets, for users who are stuck on older versions of SQL Prompt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<# Here we create a whole lot of SQL Prompt snippet files, one for each document in the array in a single JSON Snippet Collection file. The SQL Prompt file ignores the description and author and inserts the shortcut in the description, so we don't transfer these. You need to specify the JSON file (I put them all in the base of the $workdirectory path) #> #you need to create this 'WorkDirectory' variable if you haven't done so $global:WorkDirectory = ‘PathToWhereWeSaveIt’ #you will need to change this to wherever you located and named the snippet file #$InputFile = "$WorkDirectory\TemplateInfo.json" $InputFile = "$WorkDirectory\<a id="post-4885352-_Hlk72246301"></a>DiagnosticsSnippetInfo.json" #$TypeOfSnippet='Templates' $TypeOfSnippet = 'PromptDiagnostics' $DestinationPath = "$WorkDirectory\$TypeOfSnippet" if (!(Test-Path -Path $DestinationPath)) #does it already exist? { New-Item -Path $DestinationPath -ItemType directory } #if not, create it. #read the entire file in and convert it to a powerShell Object $TheInput = [IO.File]::ReadAllText($InputFile) | ConvertFrom-json $TheInput | foreach{ $SnippetData = $_ #$DeclarationsNode=$null #$xmlnode=$null #We create a blank file of the correct form and fill in the parameters #here is just the blank format $xmlDoc = [System.Xml.XmlDocument]'<?xml version="1.0" encoding="utf-8"?> <CodeSnippets> <CodeSnippet Format="1.0.0"> <Header> <Title/> <Shortcut/> <Description/> <Author/> <SnippetTypes> <SnippetType/> </SnippetTypes> </Header> <Snippet> <Declarations /> <Code Language="SQL"><![CDATA[ ]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets> '; # we'll fill in the header first $Header = $xmldoc.CodeSnippets.CodeSnippet.Header # sql prompt doesn't use a separate title, just the shortcut # $Header.Title = $SnippetData.Header.Title $Header.Title = $SnippetData.Header.Shortcut #we create a suitable snippet file $WhereToStoreIt = "$DestinationPath\$($SnippetData.Header.Title -replace '\s', '').sqlpromptsnippet" $Header.Shortcut = $SnippetData.Header.Shortcut $Header.Description = $SnippetData.Header.Description #$Header.Author = $SnippetData.Header.Author $Header.SnippetTypes.SnippetType = $SnippetData.Header.SnippetTypes[0].SnippetType $Snippet = $xmldoc.CodeSnippets.CodeSnippet.Snippet if ($SnippetData.Snippet.Declarations -ne $null -and $SnippetData.Snippet.Declarations.Literal -ne $null) { #we have to do this slightly awkwardly in order to get the format exactly right $DeclarationsNode = $Snippet.AppendChild($xmldoc.CreateElement('Declarations')) $SnippetData.Snippet.Declarations.Literal | foreach { $Literal = $DeclarationsNode.AppendChild($xmldoc.CreateElement('Literal')); $TheLiteral = $xmldoc.CreateDocumentFragment(); $TheLiteral.InnerXml = "<ID>$($_.ID)</ID><ToolTip>$($_.Tooltip)</ToolTip><Default>$($_.Default)</Default>"; $null = $Literal.AppendChild($TheLiteral); } } # now we add the code $Snippet.Code.InnerText = $SnippetData.Snippet.Code # and we save the file $xmlDoc.Save("$WhereToStoreIt"); #save it to the workDirectory } |
JSON SQL Prompt snippet files
With the following PowerShell script, you can convert any collection to new-style (v10.6 and later) JSON SQL Prompt snippet files. Again, I’m using JSON collection of Glenn Berry’s DMV scripts as the example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#you need to create this 'WorkDirectory' variable if you haven't done so $global:WorkDirectory = ‘PathToWhereWeSaveIt’ #you will need to change this to wherever you located and named the snippet file #$InputFile = "$WorkDirectory\TemplateInfo.json" $InputFile = "$WorkDirectory\DiagnosticsSnippetInfo.json" #$TypeOfSnippet='Templates' $TypeOfSnippet = 'PromptNewFormatDiagnostics' $DestinationPath = "$WorkDirectory\$TypeOfSnippet" if (!(Test-Path -Path $DestinationPath)) #does it already exist? { $null = New-Item -Path $DestinationPath -ItemType directory } #if not, create it. #read the entire file in and convert it to a powerShell Object $TheInput = [IO.File]::ReadAllText($InputFile) | ConvertFrom-json $TheInput | foreach{ $SnippetData = $_ $GUID = new-GUID;<#GUID#> $WhereToStoreIt = "$DestinationPath\$($SnippetData.Header.Shortcut -replace '\s', '')-$GUID.json" @{ 'id' = $GUID;<#GUID#> 'prefix' = $SnippetData.Header.Shortcut; 'Description' = $SnippetData.Header.Description; 'body' = $SnippetData.Snippet.Code } | ConvertTo-JSON > "$WhereToStoreIt" } |
Conclusions
It is useful to have a system that allows you to hoard clever or useful SQL code snippets that you come across, or to use public domain code libraries. Not only does it allow you to stop re-inventing ways of doing things, but it also is a great way of learning and improving skills. SQL Prompt provides an excellent way of using snippets, but it is up to you to make the best possible use of this feature to provide the snippets that are useful for you to prevent repetitive typing or, in my case, a memory that has a too-efficient garbage collection.