{"id":2194,"date":"2016-03-31T00:00:00","date_gmt":"2016-03-31T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/sql-server-system-functions-the-basics\/"},"modified":"2021-09-29T16:21:21","modified_gmt":"2021-09-29T16:21:21","slug":"sql-server-system-functions-the-basics","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/sql-server\/t-sql-programming-sql-server\/sql-server-system-functions-the-basics\/","title":{"rendered":"SQL Server System Functions: The Basics"},"content":{"rendered":"<div class=\"article-content\">\n<p class=\"start\">SQL Server provides a set of system functions for working with database objects, settings, and values. You can use the functions to carry out a variety of tasks, such as generating sequential GUIDs, calculating checksums, or returning identity values. You can also perform such operations as viewing connection properties, retrieving information about your transactions, or returning error messages.<\/p>\n<p>In this article, I walk you through a number of examples that demonstrate how to use system functions. Some of the examples rely on the <b><code>AdventureWorks2014<\/code><\/b> database, but you can use a different database to run most of the statements, modifying the T-SQL where necessary. I don&#8217;t cover every system function, but I touch upon the majority of them. You can find information about all system functions in the MSDN topic <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ms187786.aspx\">System Functions (Transact-SQL)<\/a>.<\/p>\n<h1>Identifier functions<\/h1>\n<p>The system functions include several for identifying the connecting environment or the data itself. For example, SQL Server provides the <b><code>NEWSEQUENTIALID<\/code><\/b> function for generating sequential GUIDs, the <b><code>HOST_ID<\/code><\/b> function for returning the identifier of the connecting computer, and the <b> <code>HOST<\/code><\/b>_<b><code>NAME<\/code><\/b> function for returning the computer&#8217;s name. The following <b><code>CREATE<\/code><\/b> <b> <code>TABLE<\/code><\/b> statement uses all three functions to provide default values for columns in the <b> <code>dbo.Products<\/code><\/b> table:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.Products', N'U') IS NOT NULL\r\nDROP TABLE dbo.Products;\r\nGO\r\n\u00a0\r\nCREATE TABLE dbo.Products\r\n(\r\n\u00a0 ProdID INT PRIMARY KEY IDENTITY(101, 1),\r\n\u00a0 ProdAltID UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID(),\r\n\u00a0 ProdName NVARCHAR(50) NOT NULL,\r\n\u00a0 AppPID CHAR(10) NOT NULL DEFAULT HOST_ID(),\r\n\u00a0 Workstation NVARCHAR(128) NOT NULL DEFAULT HOST_NAME()\r\n);\r\n<\/pre>\n<p>Each time the <b> <code>NEWSEQUENTIALID<\/code><\/b> function runs, it creates a GUID greater than any previously created since Windows first started. Sequential GUIDs can result in faster operations than those generated with the <b> <code>NEWID<\/code><\/b> function. If you restart the server, the <b> <code>NEWSEQUENTIALID<\/code><\/b> function will start with a lower value, but still create unique GUIDs in sequential order.<\/p>\n<p>The workstation identifier returned by the <b> <code>HOST_ID<\/code><\/b> function is actually the process ID (PID) of the application on the client computer that is connecting to SQL Server. The <b> <code>HOST_NAME<\/code><\/b> function returns the workstation name provided by the client application, which means the name is not necessarily reliable.<\/p>\n<p>In practice, you probably won&#8217;t want to include information such as the host ID and name in the same table as the product information, choosing instead to log this data elsewhere. For example, you could create an <b><code>INSERT<\/code><\/b> trigger that sends the data to an audit table. For now, however, the example shown here provides a simply way to demonstrate how easy it is to capture this information.<b><\/b><\/p>\n<p>Another system function that falls into the identifier category is <b><code>@@IDENTITY<\/code><\/b>, which returns the last identity value inserted into a table. For example, suppose we create a stored procedure for adding data to the <b> <code>Products<\/code><\/b> table:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.InsertProduct', N'P') IS NOT NULL\r\nDROP PROCEDURE dbo.InsertProduct;\r\nGO\r\n\u00a0\r\nCREATE PROCEDURE dbo.InsertProduct\r\n\u00a0 (@ProdName VARCHAR(50) = NULL)\r\nAS\r\nIF @ProdName IS NOT NULL\r\nBEGIN\r\n\u00a0 INSERT INTO dbo.Products(ProdName)\r\n\u00a0 VALUES(@ProdName);\r\n\u00a0 SELECT @@IDENTITY;\r\nEND\r\nGO\r\n<\/pre>\n<p>The last step within the procedure is to return the identity value of the inserted row. As a result, when we run the following statement for the first time, it returns the value <b> <code>101<\/code><\/b>:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC InsertProduct 'widget1';\r\n<\/pre>\n<p>We can use the returned value to log our insert operation or perform other tasks that rely on the identity value. In the meantime, let&#8217;s retrieve the row of data we just inserted:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT * FROM dbo.Products;\r\n<\/pre>\n<p>As expected, we find that out <b> <code>ProdID<\/code><\/b> value matches the value returned by the <b> <code>EXEC<\/code><\/b> statement:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> ProdID<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> ProdAltID<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> ProdName<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> AppPID<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> Workstation<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>101<\/p>\n<\/td>\n<td valign=\"top\">\n<p>E0AAAB89-79E8-E511-B4C5-001C429524EA<\/p>\n<\/td>\n<td valign=\"top\">\n<p>widget1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>5764\u00a0\u00a0\u00a0\u00a0\u00a0<\/p>\n<\/td>\n<td valign=\"top\">\n<p>WKSTN9263VM<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Notice that the row also includes the data generated by the <b><code>NEWSEQUENTIALID<\/code><\/b>, <b> <code>HOST_ID<\/code><\/b>, and <b><code>HOST<\/code><\/b>_<b><code>NAME<\/code><\/b> functions, as well as the product name itself.<\/p>\n<p>As well as <code>@@identity<\/code> that provides the last identity generated in the current session, we should just mention that there are two other functions; <code>SCOPE_IDENTITY()<\/code> that provides the last identity value generated in the current session for just the current scope and <code>IDENT_CURRENT(Tablename)<\/code> that returns the last identity value generated for a particular table .<\/p>\n<h1>Data-specific functions<\/h1>\n<p>SQL Server&#8217;s system functions also include functions for working with specific types of data. For example, you can use the <b> <code>ISNUMERIC<\/code><\/b> function to determine whether an expression is a valid numeric type, and you can use the <b> <code>ISNULL<\/code><\/b> function to replace <b><code>NULL<\/code><\/b> with a specified value.<\/p>\n<p>The following example creates the <b> <code>Orders<\/code><\/b> table and the <b> <code>InsertOrder<\/code><\/b> stored procedure, which incorporates both functions into its definition:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.Orders', N'U') IS NOT NULL\r\nDROP TABLE dbo.Orders;\r\nGO\r\n\u00a0\r\nCREATE TABLE dbo.Orders\r\n(\r\n\u00a0 OrderID INT PRIMARY KEY IDENTITY(101, 1),\r\n\u00a0 OrderDate DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),\r\n\u00a0 ShipToZip INT NOT NULL,\r\n\u00a0 SalesPerson NVARCHAR(25) \r\n);\r\n\u00a0\r\n\u00a0\r\nIF OBJECT_ID (N'dbo.InsertOrder', N'P') IS NOT NULL\r\nDROP PROCEDURE dbo.InsertOrder;\r\nGO\r\n\u00a0\r\nCREATE PROCEDURE dbo.InsertOrder\r\n\u00a0 (@ShipToZip VARCHAR(15) = NULL, @SalesPerson VARCHAR(25) = NULL)\r\nAS\r\nIF @ShipToZip IS NOT NULL\r\nBEGIN\r\n\u00a0 IF ISNUMERIC(@ShipToZip) = 1\r\n\u00a0\u00a0\u00a0 INSERT INTO dbo.Orders(ShipToZip, SalesPerson)\r\n\u00a0\u00a0\u00a0 VALUES(@ShipToZip, ISNULL(@SalesPerson, 'online'));\r\n\u00a0 ELSE\r\n\u00a0\u00a0\u00a0 PRINT N'You must provide a valid five-digit US zip code.';\r\nEND\r\nGO\r\n<\/pre>\n<p>The <b> <code>ISNUMERIC<\/code><\/b> function tests whether the <b><code>@ShipToZip<\/code><\/b> input parameter is a valid numeric value. The function returns <b> <code>1<\/code><\/b> if the value is valid, otherwise returns <b> <code>0<\/code><\/b>. Chances are, you&#8217;ll want to use a regular expression or some other device to verify the input parameter, in addition to the application verification, but this example is enough to demonstrate the <b> <code>ISNUMERIC<\/code><\/b> function. If the input value is numeric, the <b> <code>INSERT<\/code><\/b> statement will run.<\/p>\n<p>If the statement does run, it also uses the <b> <code>ISNULL<\/code><\/b> function to replace the value of the <b> <code>@SalesPerson<\/code><\/b> input parameter if that value is <b> <code>NULL<\/code><\/b>. For example, suppose we run the following <b> <code>EXEC<\/code><\/b> and <b> <code>SELECT<\/code><\/b> statements:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC InsertOrder '90210';\r\nSELECT OrderID, ShipToZip, SalesPerson FROM dbo.Orders;\r\n<\/pre>\n<p>Because we do not pass in a value for the <b> <code>@SalesPerson<\/code><\/b> parameter, the <b> <code>SELECT<\/code><\/b> statement returns the following results:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> OrderID<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> ShipToZip<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> SalesPerson<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>101<\/p>\n<\/td>\n<td valign=\"top\">\n<p>90210<\/p>\n<\/td>\n<td valign=\"top\">\n<p>online<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If we do provide a value for the <b> <code>@SalesPerson<\/code><\/b> parameter:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC InsertOrder '90210', 'Jeff';\r\nSELECT OrderID, ShipToZip, SalesPerson FROM dbo.Orders;\r\n<\/pre>\n<p>We get the results shown in the second row of the following table:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> OrderID<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> ShipToZip<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> SalesPerson<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>101<\/p>\n<\/td>\n<td valign=\"top\">\n<p>90210<\/p>\n<\/td>\n<td valign=\"top\">\n<p>online<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>102<\/p>\n<\/td>\n<td valign=\"top\">\n<p>90210<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Jeff<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Now suppose we pass in a postal code from a district in London, England:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC InsertOrder 'SW6 SBY';\r\nSELECT OrderID, ShipToZip, SalesPerson FROM dbo.Orders;\r\n<\/pre>\n<p>This time the <b> <code>ISNUMERIC<\/code><\/b> function returns a <b> <code>0<\/code><\/b>, which results in the procedure&#8217;s <b><code>ELSE<\/code><\/b> statement being executed and returning the following results:<\/p>\n<pre>You must provide a valid five-digit US zip code.\r\n<\/pre>\n<p>As the example demonstrates, the <b> <code>ISNUMERIC<\/code><\/b> function is very simple to use, but know that the function is anything but simple and can return unexpected results. For a discussion of the <b><code>ISNUMERIC<\/code><\/b> pitfalls, see the Simple-Talk article <a href=\"https:\/\/www.simple-talk.com\/sql\/t-sql-programming\/how-to-get-sql-server-data-conversion-horribly-wrong\"> How to Get SQL Server Data-Conversion Horribly Wrong<\/a>. The gist of this discussion is that you&#8217;re usually better off using the <b><code>TRY_CONVERT<\/code><\/b> function if you&#8217;re running SQL Server 2012 or later:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">ALTER PROCEDURE dbo.InsertOrder\r\n\u00a0 (@ShipToZip VARCHAR(15) = NULL, @SalesPerson VARCHAR(25) = NULL)\r\nAS\r\nIF @ShipToZip IS NOT NULL\r\nBEGIN\r\n\u00a0 IF TRY_CONVERT(INT, @ShipToZip) IS NOT NULL\r\n\u00a0\u00a0\u00a0 INSERT INTO dbo.Orders(ShipToZip, SalesPerson)\r\n\u00a0\u00a0\u00a0 VALUES(@ShipToZip, ISNULL(@SalesPerson, 'online'));\r\n\u00a0 ELSE\r\n\u00a0\u00a0\u00a0 PRINT N'You must provide a valid five-digit US zip code.';\r\nEND\r\nGO\r\n<\/pre>\n<p>Another system function you might find useful (or at least entertaining) is <b> <code>GETANSINULL<\/code><\/b>, which returns the database&#8217;s default nullability for the current session. For example, the following <b> <code>SELECT<\/code><\/b> statement returns the default nullability for the <b> <code>AdventureWorks2014<\/code><\/b> database:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT GETANSINULL('AdventureWorks2014');\r\n<\/pre>\n<p>In this case, the statement returns <b> <code>1<\/code><\/b>, which means the database allows <b> <code>NULL<\/code><\/b> values in the current session, unless a column is specifically defined as <b> <code>NOT<\/code><\/b> <b> <code>NULL<\/code><\/b>. As interesting as this might appear, the <b> <code>GETANSINULL<\/code><\/b> function does not seem to be one that is commonly implemented, but I suppose you can use it to check a database&#8217;s nullability before carrying out an operation.<\/p>\n<p>SQL Server also provides the <b> <code>CHECKSUM<\/code><\/b> and <b> <code>BINARY_CHECKSUM<\/code><\/b> system functions for returning the checksum value over a row or list of expressions. This can be handy when you want to create a hash index based on one or more columns in a table. Some developers also use the functions to compare tables to determine whether values have changed, but the results are unreliable enough that even the SQL Server documentation warns against using <b> <code>CHECKSUM<\/code><\/b> in this way:<\/p>\n<blockquote>\n<p>&#8216;However, there is a small chance that the checksum will not change. For this reason, we do not recommend using <b> <code>CHECKSUM<\/code><\/b> to detect whether values have changed, unless your application can tolerate occasionally missing a change. Consider using <b> <code>HashBytes<\/code><\/b> instead. When an MD5 hash algorithm is specified, the probability of <b> <code>HashBytes<\/code><\/b> returning the same result for two different inputs is much lower than that of <b> <code>CHECKSUM<\/code><\/b>.&#8217;<\/p>\n<\/blockquote>\n<p>Microsoft is not as clear about the <b> <code>BINARY_CHECKSUM<\/code><\/b> function in this regard, so let&#8217;s dig into both of the functions a bit more.<\/p>\n<p>To begin with, the order of expressions can affect the <b><code>CHECKSUM<\/code><\/b> outcome, when comparing data values, as can different collations. There is also the issue of the same checksum being returned for different values, as the Microsoft documentation alludes to. Phil Factor provides a great overview of the <b> <code>CHECKSUM<\/code><\/b> dilemma in his article <a href=\"https:\/\/www.simple-talk.com\/sql\/t-sql-programming\/on-comparing-tables-in-sql-server\/\"> On Comparing Tables in SQL Server<\/a>. Borrowing from his examples, I created the following T-SQL code to sum up his points:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">DECLARE @str TABLE(StrValue VARCHAR(25));\r\n\u00a0\r\nINSERT INTO @str VALUES\r\n\u00a0 ('accosted guards'),\r\n\u00a0 ('accorded feasts'),\r\n\u00a0 ('Accorded Feasts');\r\n\u00a0\r\nSELECT StrValue, CHECKSUM(StrValue) Chksum,\r\n\u00a0 BINARY_CHECKSUM(StrValue) BiChksum\r\nFROM @str;\r\nGO\r\n<\/pre>\n<p>The example creates a table variable to hold three values and then runs the <b> <code>CHECKSUM<\/code><\/b> and <b> <code>BINARY_CHECKSUM<\/code><\/b> functions against each one, giving us the following results:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> StrValue<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> Chksum<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> BiChksum<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>accosted guards<\/p>\n<\/td>\n<td valign=\"top\">\n<p>-1099752389<\/p>\n<\/td>\n<td valign=\"top\">\n<p>22470672<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>accorded feasts<\/p>\n<\/td>\n<td valign=\"top\">\n<p>-1103946693<\/p>\n<\/td>\n<td valign=\"top\">\n<p>22470672<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Accorded Feasts<\/p>\n<\/td>\n<td valign=\"top\">\n<p>-1103946693<\/p>\n<\/td>\n<td valign=\"top\">\n<p>592896016<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Notice that <b> <code>CHECKSUM<\/code><\/b> is case insensitive so treats the second and third values the same. On the other hand, <b> <code>BINARY_CHECKSUM<\/code><\/b> is case sensitive, but treats the first two values the same, even though they are clearly different. This appears to have something to do with values that share the same type and byte representation. Unfortunately, SQL Server documentation provides few specifics about the distinction between the two functions, made worse by the fact that their results are difficult to predict.<\/p>\n<p>To further complicate matters, the SQL Server documentation states that <b> <code>CHECKSUM<\/code><\/b> returns &#8220;the checksum value&#8221; and &#8220;is intended for use in building hash indexes,&#8221; whereas <b> <code>BINARY_CHECKSUM<\/code><\/b> returns &#8220;the binary checksum value&#8221; and &#8220;can be used to detect changes to a row of a table.&#8221; The latter assertion is particularly surprising given that <b><code>BINARY_CHECKSUM<\/code><\/b> appears less reliable than <b> <code>CHECKSUM<\/code><\/b>, unless case sensitivity is an overriding concern.<\/p>\n<p>Another interesting fact about the SQL Server documentation is that it states that <b> <code>CHECKSUM<\/code><\/b> returns an <b> <code>INT<\/code><\/b> value but fails to state the return type for <b> <code>BINARY_CHECKSUM<\/code><\/b>. Luckily, we can find that out for ourselves:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">DECLARE @str VARCHAR(25) = 'Accorded Feasts';\r\n\u00a0\r\nSELECT @str StrValue,\r\n\u00a0 SQL_VARIANT_PROPERTY(CHECKSUM(@str), 'basetype') ChkType,\r\n\u00a0 SQL_VARIANT_PROPERTY(BINARY_CHECKSUM(@str), 'basetype') BiChkType;\r\nGO\r\n<\/pre>\n<p>As the following results show, both functions return an <b><code>INT<\/code><\/b> value, although we might have expected a more binary-like result for <b> <code>BINARY_CHECKSUM<\/code><\/b>:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> StrValue<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> ChkType<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> BiChkType<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>Accorded Feasts<\/p>\n<\/td>\n<td valign=\"top\">\n<p>int<\/p>\n<\/td>\n<td valign=\"top\">\n<p>int<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If you plan to use either of the checksum functions to compare rows, then do so with caution and be sure your application can tolerate a few anomalies; otherwise, go with another solution.<\/p>\n<p>In the meantime, let&#8217;s look at how we can use <b> <code>CHECKSUM<\/code><\/b> to create a hash index. In the following T-SQL code, I use the function in the table definition to create a hash on the <b> <code>ProdName<\/code><\/b> column and then create an index on the <b> <code>ProdHash<\/code><\/b> column:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.Products', N'U') IS NOT NULL\r\nDROP TABLE dbo.Products;\r\nGO\r\n\u00a0\r\nCREATE TABLE dbo.Products\r\n(\r\n\u00a0 ProdID INT PRIMARY KEY IDENTITY(101, 1),\r\n\u00a0 ProdName NVARCHAR(50) NOT NULL,\r\n\u00a0 ProdHash AS CHECKSUM(ProdName)\r\n);\r\nGO\r\n\u00a0\r\nCREATE INDEX chksum_products ON dbo.Products(ProdHash);\r\nGO\r\n\u00a0\r\nINSERT INTO dbo.Products(ProdName)\r\nVALUES('Widget Enterprise 1');\r\n<\/pre>\n<p>After I create the index, I insert a row into the <b> <code>Product<\/code><\/b> table. I can then use the <b> <code>CHECKSUM<\/code><\/b> function in my <b> <code>WHERE<\/code><\/b> clause to compare a value to the <b><code>ProdHash<\/code><\/b> column:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT * FROM dbo.Products\r\nWHERE CHECKSUM(N'Widget Enterprise 1') = ProdHash;\r\n<\/pre>\n<p>As expected, the <b> <code>SELECT<\/code><\/b> statement returns the row I just created, as shown in the following results:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> ProdID<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> ProdName<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> ProdHash<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>101<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Widget Enterprise 1<\/p>\n<\/td>\n<td valign=\"top\">\n<p>422011215<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You probably noticed that I preceded my string with <b> <code>N'<\/code><\/b> to pass it in as Unicode. Had I instead run the following <b> <code>SELECT<\/code><\/b> statement, without the <b> <code>N'<\/code><\/b> prefix, the query would have returned no results:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT * FROM dbo.Products\r\nWHERE CHECKSUM('Widget Enterprise 1') = ProdHash;\r\n<\/pre>\n<p>When working with the checksum functions, you want to be sure you take into account all the possible differences and peculiarities. And be sure to check out the <b> <code>HashBytes<\/code><\/b> function. It gives you more control over the algorithm used to hash the values and provides more reliable results, although it is not as fast as the checksum alternatives.<\/p>\n<h1>Session-related functions<\/h1>\n<p>Several of the system functions available in SQL Server provide session-related information. One of these, <b> <code>CONNECTIONPROPERTY<\/code><\/b>, returns information about the properties associated with the current connection. When you call the function, you must specify one of several acceptable string values. For example, the following <b> <code>SELECT<\/code><\/b> statement uses the function to call the <b> <code>net_transport<\/code><\/b> property:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT CONNECTIONPROPERTY('net_transport');\r\n<\/pre>\n<p>The <b> <code>net_transport<\/code><\/b> property refers to the physical transport protocol used by the connection. The value returned will be one of the following types: <b> <code>HTTP<\/code><\/b>, <b><code>Named<\/code><\/b> <b> <code>pipe<\/code><\/b>, <b> <code>Session<\/code><\/b>, <b> <code>Shared<\/code><\/b> <b> <code>memory<\/code><\/b>, <b> <code>SSL<\/code><\/b>, <b> <code>TCP<\/code><\/b>, or <b> <code>VIA<\/code><\/b>.<\/p>\n<p>You can instead call the <b> <code>protocol_type<\/code><\/b> property, which returns either <b> <code>TSQL<\/code><\/b> or <b> <code>SOAP<\/code><\/b>:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT CONNECTIONPROPERTY('protocol_type');\r\n<\/pre>\n<p>The function supports other properties, but you get to the point. Refer to SQL Server online help if you want to see the full list. Note, however, you can retrieve the same information through the <b> <code>sys.dm_exec_connections<\/code><\/b> system view, along with other properties not available through the <b> <code>CONNECTIONPROPERTY<\/code><\/b> function:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT session_id, net_transport, protocol_type, num_reads\r\nFROM sys.dm_exec_connections\r\nWHERE session_id = @@SPID;\r\n<\/pre>\n<p>In addition to the <b> <code>net_transport<\/code><\/b> and <b><code>protocol_type<\/code><\/b> values, the <b> <code>SELECT<\/code><\/b> clause includes the <b><code>session_id<\/code><\/b> and <b> <code>num_reads<\/code><\/b> values, neither of which are available to the <b> <code>CONNECTIONPROPERTY<\/code><\/b> function. The <b> <code>WHERE<\/code><\/b> clause uses the <b> <code>@@SPID<\/code><\/b> built-in function to return the session ID of the current user process, giving us the following results:<\/p>\n<table class=\"MsoTableGrid\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p><b> session_id<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> net_transport<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> protocol_type<\/b><\/p>\n<\/td>\n<td valign=\"top\">\n<p><b> num_reads<\/b><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>52<\/p>\n<\/td>\n<td valign=\"top\">\n<p>Shared memory<\/p>\n<\/td>\n<td valign=\"top\">\n<p>TSQL<\/p>\n<\/td>\n<td valign=\"top\">\n<p>17<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Another session-related system function is <b> <code>CURRENT_REQUEST_ID<\/code><\/b>, which returns the ID of the current request in the current session. On the surface, there might seem to be few uses for this function, and SQL Server documentation offers even fewer clues, other than to suggest using the function in conjunction with <b> <code>@@SPID<\/code><\/b>.<\/p>\n<p>One possible use of the <b> <code>CURRENT_REQUEST_ID<\/code><\/b> function is for checking your code&#8217;s performance from within the code. For example, you can use the function along with <b> <code>@@SPID<\/code><\/b> to query the <b> <code>sys.dm_exec_requests<\/code><\/b> system view multiple times in a single batch:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">DECLARE @start DATETIME, @end DATETIME, @duration INT;\r\n\u00a0\r\nSELECT @start = start_time\r\nFROM sys.dm_exec_requests\r\nWHERE session_id = @@SPID\r\n\u00a0 AND request_id = CURRENT_REQUEST_ID(); \r\n\u00a0\r\nWAITFOR DELAY '00:00:02';\r\n\u00a0\r\nSELECT @end = DATEADD(ms, total_elapsed_time, start_time),\r\n\u00a0 @duration = DATEDIFF(ms, @start, @end)\r\nFROM sys.dm_exec_requests\r\nWHERE session_id = @@SPID\r\n\u00a0 AND request_id = CURRENT_REQUEST_ID();\r\n\u00a0\r\nSELECT CAST(@duration AS VARCHAR(10)) + ' milliseconds';\r\n<\/pre>\n<p>In this case, we&#8217;re using a <b> <code>WAITFOR<\/code><\/b> statement to simulate our primary operation. By querying the <b> <code>sys.dm_exec_requests<\/code><\/b> view multiple times, we can measure the operation&#8217;s performance.<\/p>\n<p>SQL Server also supports the <b> <code>CONTEXT_INFO<\/code><\/b> function to return the <b> <code>context_info<\/code><\/b> value that was explicitly set for the current session or batch through the use of a <b><code>SET CONTEXT_INFO<\/code><\/b> statement, as shown in the following example:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SET CONTEXT_INFO 0x1234567890\r\nGO\r\nSELECT CONTEXT_INFO()\r\nGO\r\n<\/pre>\n<p>The <b> <code>SET CONTEXT_INFO<\/code><\/b> statement associates a binary data value (up to 128 bytes) with the current session or connection. In SQL Server Management Studio, the <b> <code>SELECT<\/code><\/b> statement returns the following results:<\/p>\n<pre>0x1234567890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\r\n000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\r\n000000000000000000000000000000000000000000000000000000\r\n<\/pre>\n<p>However, you can also retrieve the <b> <code>context_info<\/code><\/b> value by querying the <b> <code>sys.dm_exec_session<\/code><\/b>s system view:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT session_id, context_info \r\nFROM sys.dm_exec_sessions\r\nWHERE session_id = @@SPID;\r\n<\/pre>\n<p>Not surprisingly, the view will also provide us with an assortment of other session-related information, such as the login time, host name, CPU time, memory usage, and much more. But if you need only the <b> <code>context_info<\/code><\/b> value, the <b> <code>CONTEXT_INFO<\/code><\/b> function is the way to go.<\/p>\n<h1>Transactions and statement execution<\/h1>\n<p>SQL Server also provides a number of system functions related to transactions and statement execution. For example, you can use the <b> <code>@@ROWCOUNT<\/code><\/b> function to return the number of rows affected by the last statement, whether the statement modified data or simply read that data. The following example shows how you might use the function in a batch to return a message based on the row count:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">DECLARE @prod TABLE(ProdID INT, ProdName VARCHAR(25));\r\n\u00a0\r\nINSERT INTO @prod VALUES\r\n\u00a0 (101, 'widget1'),\r\n\u00a0 (102, 'widget2'),\r\n\u00a0 (103, 'widgetX');\r\n\u00a0\r\nUPDATE @prod\r\nSET ProdName = 'widget3'\r\nWHERE ProdID = 103;\r\nGO\r\n\u00a0\r\nDECLARE @rowcount INT = @@ROWCOUNT;\r\n\u00a0\r\nIF @rowcount &gt; 1\r\n\u00a0 PRINT 'More than one row has been updated.';\r\nELSE IF @rowcount = 0\r\n\u00a0 PRINT 'No rows have been updated.';\r\nELSE\r\n\u00a0 PRINT 'One row has been updated.';\r\n<\/pre>\n<p>The T-SQL code updates a single row and then captures the row count. In this case, the batch returns the following message:<\/p>\n<pre>One row has been updated.\r\n<\/pre>\n<p>Given the we&#8217;ve defined the table without a primary key or unique index, any number of rows could have potentially been updated, but this was meant only as a simple example to demonstrate how the <b> <code>@@ROWCOUNT<\/code><\/b> function can be used, not only to return a message, but also for logging and auditing purposes.<\/p>\n<p>Note that, if you plan to carry out operations that exceed 2 billion rows, you should instead use the <b> <code>ROWCOUNT_BIG<\/code><\/b> function.<\/p>\n<p>Another count-like function is <b> <code>@@TRANCOUNT<\/code><\/b>, which returns the number of <b> <code>BEGIN<\/code><\/b> <b> <code>TRANSACTION<\/code><\/b> statements that have run in the current sessions. Let&#8217;s take a look at how this works by embedding one transaction in another and returning counts along the way:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">DECLARE @trancounts VARCHAR(20) = NULL;\r\nSET @trancounts = CAST(@@TRANCOUNT AS VARCHAR(10));\r\n\u00a0\r\nBEGIN TRAN\r\n\u00a0 SET @trancounts += '; ' + CAST(@@TRANCOUNT AS VARCHAR(10));\r\n\u00a0 BEGIN TRAN\r\n\u00a0\u00a0\u00a0 SET @trancounts += '; ' + CAST(@@TRANCOUNT AS VARCHAR(10));\r\n\u00a0 COMMIT\r\n\u00a0 SET @trancounts += '; ' + CAST(@@TRANCOUNT AS VARCHAR(10)); \r\nCOMMIT\r\n\u00a0\r\nSET @trancounts += '; ' + CAST(@@TRANCOUNT AS VARCHAR(10));\r\nSELECT @trancounts;\r\n<\/pre>\n<p>We start by retrieving the first transaction count. Then we begin the first transaction and get another count. Next we begin a second transaction and get that count. We commit the second transaction and then the first, getting counts every step of the way. The final <b> <code>SELECT<\/code><\/b> statement returns the following results:<\/p>\n<pre>0; 1; 2; 1; 0\r\n<\/pre>\n<p>As you can see, the first and last calls to <b> <code>@@TRANCOUNT<\/code><\/b> result in a <b> <code>0<\/code><\/b> value, the second and fourth calls result in <b> <code>1<\/code><\/b>, and the third call results in <b><code>2<\/code><\/b>, which is the call embedded in both active transactions. It turns out that the <b> <code>COMMIT<\/code><\/b> statement decrements the count by one, as would the <b> <code>ROLLBACK<\/code><\/b> statement, had we included one.<\/p>\n<p>Now let&#8217;s look at the <b> <code>XACT_STATE<\/code><\/b> function, which returns the transaction state of a current request. We&#8217;ll start by creating the <b> <code>Products<\/code><\/b> table:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.Products', N'U') IS NOT NULL\r\nDROP TABLE dbo.Products;\r\nGO\r\n\u00a0\r\nCREATE TABLE dbo.Products\r\n(\r\n\u00a0 ProdID INT PRIMARY KEY IDENTITY(101, 1),\r\n\u00a0 ProdAltID INT NULL REFERENCES Production.Product(ProductID)\r\n);\r\n<\/pre>\n<p>Notice that the table includes a foreign key that references the <b><code>Production.Product<\/code><\/b> table in the <b> <code>AdventureWorks2014<\/code><\/b> database.<\/p>\n<p>If we were to now call the <b> <code>XACT_STATE<\/code><\/b> function:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT XACT_STATE();\r\n<\/pre>\n<p>We would receive a value of <b> <code>0<\/code><\/b>, indicating that there are no active user transactions for the current request.<\/p>\n<p>However, let&#8217;s now use the function within a transaction. But first, we&#8217;ll run a <b> <code>SET<\/code><\/b> <b> <code>XACT_ABORT<\/code><\/b> statement that sets the <b> <code>XACT_ABORT<\/code><\/b> property to <b> <code>ON<\/code><\/b>. Because we set the property to <b><code>ON<\/code><\/b>, if our statement raises an error, the entire transaction is terminated and rolled back. This also lets us use the <b> <code>XACT_STATE<\/code><\/b> function to capture the transaction state, as shown in the following example:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SET XACT_ABORT ON;\r\nBEGIN TRY\r\n\u00a0 BEGIN TRANSACTION;\r\n\u00a0\u00a0\u00a0 INSERT INTO dbo.Products(ProdAltID) VALUES(1);\r\n\u00a0\u00a0\u00a0 SELECT XACT_STATE();\r\n\u00a0 COMMIT TRANSACTION;\r\nEND TRY\r\nBEGIN CATCH\r\n\u00a0 IF XACT_STATE() = -1\r\n\u00a0\u00a0\u00a0 SELECT XACT_STATE();\r\n\u00a0\u00a0\u00a0 ROLLBACK TRANSACTION;\r\nEND CATCH\r\n<\/pre>\n<p>When we run the batch, the <b> <code>INSERT<\/code><\/b> statement adds the row of data, and the <b> <code>XACT_STATE<\/code><\/b> function returns a value of <b> <code>1<\/code><\/b>, indicating that the current request has an active transaction. The transaction will then commit.<\/p>\n<p>If we instead pass the value <b> <code>5<\/code><\/b> into our <b><code>INSERT<\/code><\/b> statement, the statement will result in a foreign key constraint violation and the <b> <code>CATCH<\/code><\/b> block will run. This time, the <b> <code>XACT_STATE<\/code><\/b> function will return a value of <b> <code>-1<\/code><\/b>, indicating that the current request has an active transaction but that an error has occurred.<\/p>\n<p>Now let&#8217;s look at a much different system function: <b> <code>@@PACK_RECEIVED<\/code><\/b>. The function returns the number of input packets that SQL Server has read from the network since it was last started. For example, when I ran the following <b> <code>SELECT<\/code><\/b> statement, the function returned a value of <b> <code>2638<\/code><\/b>:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT @@PACK_RECEIVED;\r\n<\/pre>\n<p>Note that you can also retrieve this same information, and much more, by using the <b> <code>sp_monitor<\/code><\/b> system stored procedure.<\/p>\n<p>There&#8217;s one other interesting function I wanted to point out in this category, <b> <code>MIN_ACTIVE_ROWVERSION<\/code><\/b>, which returns the lowest active <b> <code>rowversion<\/code><\/b> value in the current database. The <b> <code>rowversion<\/code><\/b> data type is often used for version-stamping rows in a table, as shown in the following example:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.Products', N'U') IS NOT NULL\r\nDROP TABLE dbo.Products;\r\nGO\r\n\u00a0\r\nCREATE TABLE dbo.Products\r\n(\r\n\u00a0 ProdID INT PRIMARY KEY IDENTITY(101, 1),\r\n\u00a0 ProdName NVARCHAR(25) NOT NULL,\r\n\u00a0 ProdRowVersion rowversion\r\n);\r\nGO\r\n<\/pre>\n<p>When the <b> <code>rowversion<\/code><\/b> data type is used, SQL Server automatically assigns a binary number to that row that is unique across the entire database. In conjunction with this, we can use the <b> <code>MIN_ACTIVE_ROWVERSION<\/code><\/b> function to retrieve the next available (active) <b><code>rowversion<\/code><\/b> value:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">SELECT MIN_ACTIVE_ROWVERSION();\r\n<\/pre>\n<p>When I tried this on my system, I received the following value:<\/p>\n<pre>0x00000000000B6220\r\n<\/pre>\n<p>I then verified that this was the next available value by running the following <b> <code>INSERT<\/code><\/b> and <b> <code>SELECT<\/code><\/b> statements:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">INSERT INTO dbo.Products(ProdName) VALUES('widget1');\r\nSELECT ProdRowVersion FROM dbo.Products;\r\n<\/pre>\n<p>As expected, this <b> <code>SELECT<\/code><\/b> statement returned the same <b> <code>rowversion<\/code><\/b> value that was returned by the preceding <b> <code>SELECT<\/code><\/b> statement. Because that was the lowest active value, that was the value inserted into my table.<\/p>\n<h1>Error-related functions<\/h1>\n<p>The final category of system functions that we&#8217;ll look at are related to capturing error information. To demonstrate how they work, we can start by once again creating a version of our <b> <code>Products<\/code><\/b> table:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.Products', N'U') IS NOT NULL\r\nDROP TABLE dbo.Products;\r\nGO\r\n\u00a0\r\nCREATE TABLE dbo.Products\r\n(\r\n\u00a0 ProdID INT PRIMARY KEY IDENTITY(101, 1),\r\n\u00a0 ProdName NVARCHAR(25) NOT NULL,\r\n\u00a0 ProdAltID INT NULL REFERENCES Production.Product(ProductID)\r\n);\r\n<\/pre>\n<p>As in an earlier example, we are including a foreign key constraint that references the <b> <code>Production.Product<\/code><\/b> table in the <b><code>AdventureWorks2014<\/code><\/b> database. Now let&#8217;s re-create our <b> <code>InsertProduct<\/code><\/b> stored procedure, using several error-related system functions:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">IF OBJECT_ID (N'dbo.InsertProduct', N'P') IS NOT NULL\r\nDROP PROCEDURE dbo.InsertProduct;\r\nGO\r\n\u00a0\r\nCREATE PROCEDURE dbo.InsertProduct\r\n\u00a0 (@ProdName VARCHAR(50) = NULL, @AltID INT = NULL)\r\nAS\r\nIF @ProdName IS NOT NULL AND @AltID IS NOT NULL\r\nBEGIN\r\n\u00a0 BEGIN TRY\r\n\u00a0\u00a0\u00a0 INSERT INTO dbo.Products(ProdName, ProdAltID)\r\n\u00a0\u00a0\u00a0 VALUES(@ProdName, @AltID);\r\n\u00a0 END TRY\r\n\u00a0 BEGIN CATCH\r\n\u00a0\u00a0\u00a0 PRINT 'Error number: ' + CAST(ERROR_NUMBER() AS VARCHAR(10));\r\n\u00a0\u00a0\u00a0 PRINT 'Error severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR(10));\r\n\u00a0\u00a0\u00a0 PRINT 'Error state: ' + CAST(ERROR_STATE() AS VARCHAR(10));\r\n\u00a0\u00a0\u00a0 PRINT 'Error procedure: ' + ERROR_PROCEDURE();\r\n\u00a0\u00a0\u00a0 PRINT 'Error line: ' + CAST(ERROR_LINE() AS VARCHAR(10));\r\n\u00a0\u00a0\u00a0 PRINT 'Error message: ' + ERROR_MESSAGE();\r\n\u00a0 END CATCH\r\nEND\r\nGO\r\n<\/pre>\n<p>In this case, we&#8217;ve included six error-related functions, with names that should make their purpose self-evident. For example, the <b> <code>ERROR_NUMBER<\/code><\/b> function returns the SQL Server error number, and the <b><code>ERROR_MESSAGE<\/code><\/b> function returns the error text. Now let&#8217;s call the stored procedure, passing in an ID value that we know will raise a foreign key violation error:<\/p>\n<pre class=\"theme:ssms2012 lang:tsql\">EXEC InsertProduct 'widget1', 5;\r\n<\/pre>\n<p>Because of the foreign key violation, the <b> <code>CATCH<\/code><\/b> block in our stored procedure runs and returns the following error-related information:<\/p>\n<pre>Error number: 547\r\nError severity: 16\r\nError state: 0\r\nError procedure: InsertProduct\r\nError line: 7\r\nError message: The INSERT statement conflicted with the FOREIGN KEY constraint \"FK__Products__ProdAl__13A7DD28\". The conflict occurred in database \"AdventureWorks2014\", table \"Production.Product\", column 'ProductID'.\r\n<\/pre>\n<p>As you can see, the functions make it relatively simple to return the details we want. One important point to note, however, is that each function returns a <b> <code>NULL<\/code><\/b> value if called outside the scope of the <b> <code>CATCH<\/code><\/b> block. Clearly, these functions serve a very specific purpose. For more details about them and other error-related functions, refer to the SQL Server documentation.<\/p>\n<h1>SQL Server system functions<\/h1>\n<p>Although we have not covered all the system functions here, we&#8217;ve looked at most of them, so you should have a good sense of their scope and the types of tasks you can accomplish by using them. Because we covered so many of the functions, we could not go into each one in the detail that might have been warranted, so I again refer you to the SQL Server documentation to help you fill in the blanks.<\/p>\n<p>Systems functions represent only a subset of the built-in functions available in SQL Server. In addition to a wide range of scalar functions, SQL Server also provides rowset, aggregate, and ranking function. System functions represent only the tip of the iceberg, but they can serve as a good starting point for familiarizing yourself with the many types of built-in functions that Microsoft offers.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Every SQL Server Database programmer needs to be familiar with the System Functions. These range from the sublime (such as @@rowcount or @@identity) to the ridiculous (IsNumeric())  Robert Sheldon provides an overview of the most commonly used of them.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143531],"tags":[4242,4168,4150,4151,4252],"coauthors":[6779],"class_list":["post-2194","post","type-post","status-publish","format-standard","hentry","category-t-sql-programming-sql-server","tag-basics","tag-database","tag-sql","tag-sql-server","tag-t-sql-programming"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2194","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=2194"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2194\/revisions"}],"predecessor-version":[{"id":92509,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/2194\/revisions\/92509"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=2194"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=2194"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=2194"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=2194"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}