Category Archives: #SQLServer

All about Microsoft SQL Server

#0159-SQL Server-Row Constructors-Triggers process the entire batch at once


Almost every product comes with a set of predefined system default data, which may or may not be editable by the user. When writing about returning result sets from  triggers, I realized that there are very few product teams who use row constructors (introduced in SQL Server 2008) to populate their default data.


What these teams fail to realize is that many a times, the conventional approach may have a negative impact on the time it takes to insert the data and also reduces the overall efficiency of the entire data load operation. Let’s see a demo.


The Test Setup


To demonstrate the point, I will create a small table in the tempdb database with an INSERT trigger on it that would return us the inserted values from the INSERTED view.

USE tempdb
GO
CREATE TABLE dbo.RowConstructorDemo (RowId INT IDENTITY(1,1),
                                     RowValue VARCHAR(30),
                                     RowDescription VARCHAR(100)
                                    )
GO

CREATE TRIGGER dbo.trig_RowConstructorDemo_Insert
ON dbo.RowConstructorDemo
FOR INSERT
AS
BEGIN
  --Some business logic here
  SELECT ROW_NUMBER() OVER (ORDER BY inserted.RowId) AS [RowNumber],
         inserted.RowId,
         inserted.RowValue,
         inserted.RowDescription
  FROM inserted
END
GO

The Conventional Method


Now, let us try to insert a couple of rows using the conventional style of data insertion, i.e. row-by-row approach.

#0158-SQL Server-Returning result sets from triggers


This week, I am taking a brief break from my series on deprecated features of SQL Server 2012 to share with you two experiences I had recently. A developer had suspected that one of the triggers in the database had an issue. To debug this, the developer placed a simple SELECT statement within the trigger.

Below is an example reproducing the scenario for demonstration purposes.

--Use tempdb for demonstration purposes
USE tempdb
GO

--Create a demo table
CREATE TABLE dbo.TriggerResultSets (IdCol INT IDENTITY(1,1),
                                    IdName VARCHAR(50)
                                   )
GO

--Create a trigger that returns a result set
CREATE TRIGGER dbo.trig_TriggerResultSets
ON dbo.TriggerResultSets
FOR INSERT
AS
BEGIN
    /*Some business logic here*/
    SELECT INSERTED.IdCol, INSERTED.IdName
    FROM [INSERTED]
    ORDER BY INSERTED.IdCol
END
GO

Upon inserting some data into our demo table, TriggerResultSets, the developer could see the data that was inserted.

--Insert some test data
INSERT INTO dbo.TriggerResultSets (IdName)
VALUES ('Microsoft'),
       ('SQLServer')
GO

image

The Problem

After resolving the issue that the developer had encountered, the team proceeded to unit test the application, which failed. This was because the SELECT statement was left behind, and the application was not written to handle result sets from triggers.

While the ultimate solution to prevent such a situation in the future is to have proper code review checklists, techniques and methods that would help ensure such code never gets “deployed”, there are workarounds for which developers can be educated for, and a check that all database administrators can use to trap any fall-through occurrences.

The Workaround

The workaround is actually quite simple. Developers can be educated to use PRINT statements instead of the SELECT statements. Because PRINT does not return result sets, the application will remain “happy” and at work as expected. Although, please note that I am not very much in favour of this because data is being exposed here unnecessarily.

--CREATE TABLE statement removed for sake of brevity.

--Create a trigger that returns a result set
CREATE TRIGGER dbo.trig_TriggerResultSets
ON dbo.TriggerResultSets
FOR INSERT
AS
BEGIN
    /*Some business logic here*/
    DECLARE @InsertedId INT
    DECLARE @InsertedName VARCHAR(50)

    SELECT @InsertedId = inserted.IdCol, @InsertedName = inserted.IdName
    FROM [INSERTED]
    ORDER BY INSERTED.IdCol
     
    PRINT 'Id = ' + CAST(@InsertedId AS VARCHAR(10)) + ', Name = ' + ISNULL(@InsertedName,'')
END
GO

INSERT INTO dbo.TriggerResultSets (IdName)
VALUES ('SQLServer')
GO

/*
Result Set:
Id = 1, Name = SQLServer
*/

image

The Check

Despite the proper developer education, multiple checklists and code reviews, it is quite possible that sometimes things may slip through. In such cases (or until the developer education does not complete), the DBA may need to take extreme steps such as completely disabling functionalities that may cause problems. In the case I am referring to, I turned ON a configuration option – disallow results from triggers. Please note that this is an advanced option.

sp_configure 'show advanced options',1
RECONFIGURE
GO

sp_configure 'disallow results from triggers',1
RECONFIGURE
GO

Once turned ON, the original code with the SELECT statement would fail.

INSERT INTO dbo.TriggerResultSets (IdName)
VALUES ('Microsoft'),
       ('SQLServer')
GO

Msg 524, Level 16, State 1, Procedure trig_TriggerResultSets, Line 9

A trigger returned a resultset and the server option ‘disallow results from triggers’ is true.

PLEASE NOTE

Please note that the “disallow results from triggers” option has been marked as deprecated from SQL Server 2008 onwards and now that SQL Server 2012 is out, it will be removed from the next version of SQL Server. Future versions of SQL Server will not support returning result sets from triggers and hence the switch is no longer required.

Per Microsoft’s recommendation, I have turned the switch to 1 in my environments.

Read more about this announcement here: http://msdn.microsoft.com/en-us/library/ms186337(v=sql.110).aspx

Until we meet next time,

Be courteous. Drive responsibly.

#0157–SQL Server 2012–Deprecated features-DATABASEPROPERTY replaced by DATABASEPROPERTYEX


As I document the the database engine features that have been deprecated  in SQL Server 2012, one of the most important ones has to be the deprecation of DATABASEPROPERTY. I am yet to see a production code that does not use this function.

For those who came in late, DATABASEPROPERTY is a function that returns the named database property value for the specified database and property name. Here’s an example that uses the DATABASEPROPERTY function to see if the ANSI NULLS property is ON or OFF by default (runs on SQL Server 2008/R2).

SELECT DATABASEPROPERTY('AdventureWorks2008','IsAnsiNullDefault') AS IsAnsiNullDefault

/* Result Set
IsAnsiNullDefault
-----------------
0
*/

This function was quite old and did not cover some of the newly introduced properties like the comparison style for a collation. Also, the return type of the current function – DATABASEPROPERTY was an integer, which means that the function could not return a character or other data types.

Microsoft SQL Server 2005 therefore shipped with a new, replacement function – DATABASEPROPERTYEX. While the syntax is the same as the function it replaces, the return types are different. The extended, DATABASEPROPERTYEX now returns a sql_variant and therefore has the capability to return a string. We can therefore do the following which was not possible with the previous function.

SELECT DATABASEPROPERTYEX('AdventureWorks2012','Collation') AS Collation,
       DATABASEPROPERTYEX('AdventureWorks2012','Status') AS Status

/* Result Set
Collation                       Status
------------------------------  -------
SQL_Latin1_General_CP1_CI_AS    ONLINE
*/

I have already started replacing instances of the old DATABASEPROPERTY with DATABASEPROPERTYEX. Have you?

Reference:

Until we meet next time,

Be courteous. Drive responsibly.

#0157–SQL Server 2012–Deprecated features-DATABASEPROPERTY replaced by DATABASEPROPERTYEX


As I document the the database engine features that have been deprecated  in SQL Server 2012, one of the most important ones has to be the deprecation of DATABASEPROPERTY. I am yet to see a production code that does not use this function.

For those who came in late, DATABASEPROPERTY is a function that returns the named database property value for the specified database and property name. Here’s an example that uses the DATABASEPROPERTY function to see if the ANSI NULLS property is ON or OFF by default (runs on SQL Server 2008/R2).

~~~SELECT DATABASEPROPERTY(‘AdventureWorks2008′,’IsAnsiNullDefault’) AS IsAnsiNullDefault

/* Result Set
IsAnsiNullDefault
—————–
0
*/~~~

This function was quite old and did not cover some of the newly introduced properties like the comparison style for a collation. Also, the return type of the current function – DATABASEPROPERTY was an integer, which means that the function could not return a character or other data types.

Microsoft SQL Server 2005 therefore shipped with a new, replacement function – DATABASEPROPERTYEX. While the syntax is the same as the function it replaces, the return types are different. The extended, DATABASEPROPERTYEX now returns a sql_variant and therefore has the capability to return a string. We can therefore do the following which was not possible with the previous function.

~~~SELECT DATABASEPROPERTYEX(‘AdventureWorks2012′,’Collation’) AS Collation,
DATABASEPROPERTYEX(‘AdventureWorks2012′,’Status’) AS Status

/* Result Set
Collation Status
—————————— ——-
SQL_Latin1_General_CP1_CI_AS ONLINE
*/~~~

I have already started replacing instances of the old DATABASEPROPERTY with DATABASEPROPERTYEX. Have you?

Reference:

Until we meet next time,

Be courteous. Drive responsibly.

#0156–SQL Server 2012–Deprecated features-Valid compatibility levels–COMPATIBILITY_LEVEL 80 support-Msg 15048


SQL Server 2012 (“Denali”) introduces many new features directed towards improving server efficiency and availability and developer/administrator productivity. At the same time, it needs to do away with the past “luggage”. These are features supported by the older versions of SQL Server, that are either no longer in popular use or violate the business rules as T-SQL enhancements made in the newer releases.

Compatibility levels

Every database within Microsoft SQL Server is characterized by something called as the “Compatibility Level”. The compatibility level is primarily used to allow for backward compatibility as it determines the database behaviour with respect to a specific SQL Server version. For example, if you would like to use a SQL Server 2005 specific database behaviour on a SQL Server 2008 R2 instance, you would need to set the compatibility level to “90”.

Trivia: The compatibility level is essentially the major build# of the product. Because SQL Server 2000 was build #80, the compatibility level for SQL Server 2000 databases is 80. Similarly, for SQL Server 2008, the build# is 10.0, and therefore the compatibility level is 100 (compatibility level of a newer release cannot be less than that of the previous releases). For SQL Server 2012 databases, the compatibility level is 110.

Removal of support for COMPATIBILITY_LEVEL 80

SQL Server 2008 and SQL Server 2008R2 were the last versions to support a database compatibility level of 80, i.e. a SQL Server 2000 database. Those who came in late would recall that SQL Server 2005 was a major departure from the architecture of the older SQL Server 2000. This meant that in order to provide backward compatibility support, the SQL Server code would need to carry a lot of additional code which hardly anyone would ever use (in order to leverage the latest features introduced in higher releases, the compatibility level would need to be changed).

In-line with the product’s policy to support 2 prior releases, starting SQL Server 2012, the compatibility level of 80 is no longer supported.

image
SQL Server 2008 – supported compatibility levels
image
SQL Server 2012 – supported compatibility levels

Attempting to use T-SQL to change the Compatibility level results in the following error:

~~~ALTER DATABASE AdventureWorks2008R2
SET COMPATIBILITY_LEVEL=80~~~

Msg 15048, Level 16, State 1, Line 1

Valid values of the database compatibility level are 90, 100, or 110.

So, if you are still using a SQL Server 2000 database, please upgrade!

More about the compatibility levels supported by SQL Server 2012 (“Denali”)

To know more about the compatibility levels supported by SQL Server 2012, please visit: http://msdn.microsoft.com/en-us/library/bb510680.aspx

Until we meet next time,

Be courteous. Drive responsibly.