Category Archives: Imported from BeyondRelational

These posts are imported from my old blog page: http://beyondrelational.com/modules/2/blogs/77/nakuls-blog.aspx

#0178-SQL Server-CLOSE and DEALLOCATE cursor-A lesson for sustenance teams-Peer Reviews help resolve problems


I have worked for a product sustenance team for the better part of my 7+ years of experience and hence, I can closely relate to the problem at hand today. The challenges with sustenance teams are many, and one of them is understanding and building upon code written by other teams – developers of whom may not even be around in the organization. In addition to this the code that needs to be extended may be legacy code which was written years ago and may not confirm to the newer coding standards. To reduce the impact, most sustenance teams therefore do not spend the time to re-engineer or re-write a piece of code just because it is not up to standards, but instead work on extending the functionality. They leave it to the new development teams to re-write the code as part of the system refresh cycles.

The Scenario

This took the better of one of the engineers recently. An existing procedure (written in the old-fashioned way to use CURSORs) had to be extended. It was upto this poor developer to extend the stored procedure to incorporate some additional logic and release it out to QA within a considerably tight time-frame.

This stored procedure used the slower approach of using CURSORs to iterate through the list of employees. This cursor was to be re-used after another set of business operators to achieve the new requirement. The engineer therefore decided to add the additional piece of code after the old code ended – which is where he made a fatal mistake that cost him a couple of hours to notice.

The Example

To establish a parallel with the requirement, let us assume that a procedure exists to increment the VacationHours of all employees of a certain grade with AdventureWorks Cycles by 1 hour. We will extend this stored procedure by attempting to revert back the change to VacationHours, leaving a small error in doing so.

USE AdventureWorks2012
GO
IF OBJECT_ID('HumanResources.proc_DemoDellocateCursorIssues') IS NOT NULL
BEGIN
    DROP PROCEDURE HumanResources.proc_DemoDellocateCursorIssues
END
GO
CREATE PROCEDURE HumanResources.proc_DemoDellocateCursorIssues
    @orgLevel INT
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @businessEntityId INT 

    DECLARE employeeCursor INSENSITIVE CURSOR
    FOR (SELECT e.BusinessEntityID
         FROM HumanResources.Employee AS e
         WHERE e.OrganizationLevel = @orgLevel
        )

    OPEN employeeCursor 

    BEGIN TRANSACTION outerTran
        BEGIN TRY
            FETCH NEXT FROM employeeTran INTO @businessEntityId

            WHILE (@@FETCH_STATUS <> -1)
            BEGIN
                WHILE (@@FETCH_STATUS <> -2)
                BEGIN
                    UPDATE e
                    SET VacationHours += 1
                    FROM HumanResources.Employee AS e
                    WHERE BusinessEntityID = @businessEntityId
                END
                FETCH NEXT FROM employeeTran INTO @businessEntityId
            END

            --IF @@TRANCOUNT > 0
            --    COMMIT TRANSACTION outerTran
        END TRY
        BEGIN CATCH
            IF @@TRANCOUNT > 0
                ROLLBACK TRANSACTION outerTran
        END CATCH

        CLOSE employeeCursor
        DEALLOCATE employeeCursor

        --OLD CODE ENDS HERE!!! ADDITIONAL CODE BEGINS

        --This part did not exist before
        --Some more business logic added here

        --Re-open the cursor and begin processing
        OPEN employeeCursor
        BEGIN TRY
            FETCH NEXT FROM employeeTran INTO @businessEntityId

            WHILE (@@FETCH_STATUS <> -1)
            BEGIN
                WHILE (@@FETCH_STATUS <> -2)
                BEGIN
                    UPDATE e
                    SET VacationHours -= 1
                    FROM HumanResources.Employee AS e
                    WHERE BusinessEntityID = @businessEntityId
                END
                FETCH NEXT FROM employeeTran INTO @businessEntityId
            END
            IF @@TRANCOUNT > 0
                COMMIT TRANSACTION outerTran
        END TRY
        BEGIN CATCH
            IF @@TRANCOUNT > 0
                ROLLBACK TRANSACTION outerTran
        END CATCH

        CLOSE employeeCursor
        DEALLOCATE employeeCursor
END
GO

The Error

All those who have noticed the error, no further reading of this post is necessary. All those who have not found the error yet, please read on…

Executing the procedure raises the following error:

USE AdventureWorks2012
GO
EXEC HumanResources.proc_DemoDellocateCursorIssues @orgLevel = 4
GO

Msg 16916, Level 16, State 1, Procedure proc_DemoDellocateCursorIssues, Line 40
A cursor with the name ’employeeCursor’ does not exist.
Msg 16916, Level 16, State 1, Procedure proc_DemoDellocateCursorIssues, Line 67
A cursor with the name ’employeeCursor’ does not exist.
Msg 16916, Level 16, State 1, Procedure proc_DemoDellocateCursorIssues, Line 68
A cursor with the name ’employeeCursor’ does not exist.

The error comes simply because the CURSOR – employeeCursor has been DEALLOCATEd before being used again. Let’s focus on the part where the old code ends and new extension was added:

        CLOSE employeeCursor
        --Notice the DEALLOCATE from the older implementation here. The solution is to comment this DEALLOCATE statement
        --DEALLOCATE employeeCursor

        --OLD CODE ENDS HERE!!! ADDITIONAL CODE BEGINS

        --This part did not exist before
        --Some more business logic added here

        --Re-open the cursor and begin processing
        OPEN employeeCursor

It was a simple typographical error on the part of the developer, but resulted in his spending an hour or two in debugging the issue and ultimately approaching me for help. A 10-minute study of the code from a different pair of eyes (mine) helped him resolve realize the miss and resolve the issue.

Moral of the story

The moral of the story is not whether we should use Cursors or not, but there is a more important behavioural pattern that I would like to highlight.

After being sure that we have done our level best in writing a piece of code, most of us tend to keep troubleshooting the code for hours at an end if it happens to develop an error. All I urge is that if you are sure that you have done due diligence from your end, do not be afraid to ask for peer reviews – they help because they involve a different pair of eyes on the same problem. The difficult part is not making it a habit by having others resolve all your problems for you.

Until we meet next time,

Be courteous. Drive responsibly.

#0177 – SQL Server – Control the trigger execution order


It is generally a bad practice to have execution sequence dependent business logic inside of triggers. They should be inside stored procedures, and not triggers. But, a lot of us maintain databases designed & developed by external teams – members of which may not be with the organization anymore. What I am sharing today comes from a Sunday afternoon long time ago from when I was on-call during a customer on-premise deployment.

Building the Scenario

The application I was working on used triggers which had custom logic embedded into them. Apparently, one of the areas required that a record in another table be updated by an update trigger before another trigger to send out a notification of the change to an external system is executed – and that was the source of our problems.

It is difficult to recreate the scenario because the trigger order is by default, controlled by the SQL Server database engine. However, here goes – the script below creates a table with two very simple insert triggers on it.

USE tempdb
GO

--Safety Check
IF OBJECT_ID('triggerSequenceTest') IS NOT NULL
BEGIN
    DROP TABLE triggerSequenceTest
END
GO

CREATE TABLE triggerSequenceTest (RowId INT IDENTITY(1,1),
                                   RowValue NVARCHAR(20)
                                  )
GO

CREATE TRIGGER dbo.trig_UpdateTrig01
ON dbo.triggerSequenceTest
FOR INSERT
AS 
BEGIN
    DECLARE @insertedValue NVARCHAR(20)
    
    SELECT @insertedValue = inserted.RowValue from inserted
    
    PRINT 'Trigger 01: ' + @insertedValue
END
GO

CREATE TRIGGER dbo.trig_UpdateTrig02
ON dbo.triggerSequenceTest
FOR INSERT
AS 
BEGIN
    DECLARE @insertedValue NVARCHAR(20)
    
    SELECT @insertedValue = inserted.RowValue from inserted
    
    PRINT 'Trigger 02: ' + @insertedValue
END
GO

--Perform the INSERT
INSERT INTO triggerSequenceTest (RowValue) VALUES ('Microsoft')

The output that we (normally) receive is:

Trigger 01: Microsoft

Trigger 02: Microsoft

In my case, the trigger 02 was the replication trigger and was designed such that it was expecting a change made by trigger 01 to be in place. However, the trigger 02 was executed before trigger 01 causing all sorts of weird behavioural issues within the application and failures being logged into integration logs.

The Solution

In the very rare case when SQL Server fails to set the correct trigger order, one can control the execution to a certain extent. We can control which trigger needs to be executed first, and which one the last through a system stored procedure – sp_settriggerorder.

While the long term solution was to re-engineer the application to move the required logic into a stored procedure, the solution that we ended up using that day was forcibly setting the trigger order for the integration related trigger to last. As a demo, let’s move the trigger 01 to the last in the execution sequence:

--Change the trigger order
USE tempdb;
GO
sp_settriggerorder @triggername= 'dbo.trig_UpdateTrig01', @order='Last', @stmttype = 'INSERT';
GO

An attempt to insert some data now yields:

--Perform the INSERT again
INSERT INTO triggerSequenceTest (RowValue) VALUES ('SQL Server')

Trigger 02: Microsoft

Trigger 01: Microsoft

IMPORTANT: One of the key points to remember is that server scoped triggers are always executed first.

Reference: http://msdn.microsoft.com/en-us/library/ms186762.aspx

Until we meet next time,

Be courteous. Drive responsibly.

#0176 – SQL Server – Creating a database without the log backup file – Error Msg. 5120


Recently, one of my friends (new to SQL Server) was attempting to the deploy the AdventureWorks2012 versions of the sample databases available for download from the CodePlex community on his laptop for study purposes, but was facing an error and hence called me up.

When I reached to his place, he told me that he had downloaded the AdventureWorks2012 database from the following website: http://msftdbprodsamples.codeplex.com/releases/view/55330#DownloadId=165399

When attempting to restore the database, he was facing the following error:

Attach Database Wizard showing an error arising out of a missing log file

Error message #5120

A quick glance at the screenshot shows us the error –

Unable to open the physical file “E:SQL DatabasesAdventureWorksAdventureWorks_Log.ldf”. Operating system error 2: “2(The system cannot find the file specified.)”. (Microsoft SQL Server, Error: 5120)

Clearly the error is because the sample databases available on the CodePlex website are just the data files. A transaction log file is not supplied by CodePlex. To restore a database that comes without a data file, one can use the option to rebuild the transaction log when running the CREATE DATABASE command. For example:

USE [master]
GO

CREATE DATABASE [AdventureWorks] 
ON  PRIMARY ( NAME = N'AdventureWorks_Data', 
              FILENAME = N'E:SQL DatabasesAdventureWorksAdventureWorks_Data.mdf', 
              SIZE = 3072KB,
              MAXSIZE = UNLIMITED,
              FILEGROWTH = 1024KB )
FOR ATTACH_REBUILD_LOG
GO

Some finer points about ATTACH_REBUILD_LOG:

  • When rebuilding the transaction log, a collation name cannot be specified to change the collation of the database
  • This option is only available for read/write databases
  • Because the transaction log file is being rebuilt, we do not have control over the location, initial size and file growth increment values. The new log file will be created at the default log path location and will have a size of 1MB
  • Naturally, this operation breaks the log chain

You can also find this script with a lot of other SQL Server, C# and XML scripts at the Scripts Module on BeyondRelational.com: http://beyondrelational.com/modules/30/scripts.aspx?s=stream&tab=scripts

Until we meet next time,

Be courteous. Drive responsibly.

#0175-BeyondRelational.com-Introducing the new Scripts module! Share and find code snippets and scripts


Don’t reinvent the wheel. Period.

The evolution of the human race is based on the fact that humans have had intelligence to reuse an already available solution to a problem to achieve solutions to even bigger problems. In software engineering, we know this as design patterns, which are general reusable solutions to commonly occurring problems. In the world of T-SQL, these are downloadable scripts, which, with a finite amount of customization can be reused within any administrator’s environment or any developer’s T-SQL code. In the world of C# or other programming languages & frameworks, these are blocks or snippets of code that help realize a business requirement.

I have often seen developers and administrators searching for hours on search engines to download scripts or code snippets which they can use for a variety of purposes:

  1. To learn about a particular feature
  2. To implement an industry-standard practice in their environments
  3. To compare a pre-existing solution with industry-standard best practices
  4. To use as reference in order to get out of “writers block”
  5. And many more..

The BeyondRelational.com platform has come up with a revolutionary new concept of the “Scripts” module (http://beyondrelational.com/modules/30/scripts.aspx?s=stream&tab=scripts), where one can share scripts and code snippets which demonstrate solutions to a given engineering problem, examples include:

So, if you are a developer or administrator or an enthusiast and use a piece of code frequently to address a business problem, the “Scripts” module is the right platform for you to share your solution with the community.

So, head over to the Scripts module today – share your scripts while learning something new!

BeyondRelational.com will be celebrating a “scripts week” from July 09, 2012 – July 15, 2012. Do not forget to visit the Scripts module during this time for fresh, new and extremely useful scripts on each day of the week!

Until we meet next time,

Be courteous. Drive responsibly.

#0174 – SQL Server – sys.dm_exec_sessions – Identify a session based on the Windows Process Id


Recently at the office an automated performance testing team came up to me and asked me a very interesting question – Based on the Windows Process Id, how can one determine the number sessions that have been initiated by a process? The question is interesting because their application is a client-server based application, capable of running on a terminal server. Multiple instances of the same application may be running from the same machine, and hence, simply tracing based on the host name would not be sufficient.

Depending upon the requirement, there are two methods to answer this question:

  1. If a T-SQL based method is required for logging and other purposes, the DMV: sys.dm_exec_sessions can be used
  2. If the intention is to troubleshoot or monitor a process (i.e. a one-time activity), the SQL Server Profiler can be used

Using the DMV: sys.dm_exec_sessions

The sys.dm_exec_sessions DMV returns one row per authenticated session on SQL Server. Therefore, the view will return us one record for each session that an application establishes with the SQL Server.

Because the sys.dm_exec_sessions DMV maps to the sys.processes system table (it’s a best practice not to query the system tables directly), the columns returned contain information about the client/host process including (but not limited to):

  1. Host Name
  2. User context used to establish the session
  3. Program name, and
  4. Host Process Id

The column of interest for us is the “host_process_id”. This columns holds the (Windows) process ID of the client program that initiated the session. Please note that this column will show a value of NULL for internal sessions (established by the SQL Server itself).

Using the SQL Server Profiler

The SQL Server profiler, being a dedicated debugging and performance analysis tool, has to have a mechanism to track the processes based on the windows process Id.

Editing a profiler trace allows us to select data columns of our choice (If you are new to Profiler, or need instructions on how to customize a Profiler/SQL Trace, please follow my series of tutorials on the subject at: http://beyondrelational.com/modules/12/tutorials/631/getting-started-with-sql-server-profiler.aspx?tab=tutorials&ts=46&bs=57). The column – “ClientProcessId” captures the (windows) process ID of the application calling SQL Server.

Filtering on the required ClientProcessId column will give us a way to monitor the required session.

Example

Let’s take an example – assume we want to monitor the sessions initiated by the SQL Server Management Studio on my machine.

To begin, get the Process ID (PID) of the required process.

  1. An application might use system functions to obtain this information
  2. If you wish to manually get this information, you can use the Task Manager (Press: Ctrl + Alt + Del, launch the Task Manager)

Based on the screenshot below, you can see that the SSMS process on my test workstation is running under a PID = 2152

image
Armed with this information, let us go to SQL Server Management Studio, and use the following query to know the open sessions initiated by SQL Server:

SELECT *
FROM sys.dm_exec_sessions
WHERE host_process_id = 2152

Running this query yields 2 records as shown below:

image
(Remember that the SSMS can open multiple connections to the SQL Server: http://beyondrelational.com/modules/2/blogs/77/posts/11275/the-multiple-connections-of-ssms.aspx)
Now, let us launch the SQL Server profiler. Choose a template of your choice to create the SQL trace.

(If you are new to Profiler, or need instructions on how to customize a Profiler/SQL Trace, please follow my series of tutorials on the subject at: http://beyondrelational.com/modules/12/tutorials/631/getting-started-with-sql-server-profiler.aspx?tab=tutorials&ts=46&bs=57)

As part of the customization, choose to display all columns for editing and choose to display the “ClientProcessID” in the trace output.
image
Click on “Column Filters” and filter on the required host process Id (i.e. PID = 2152).

Once done, click “OK” to start the trace.
image
Navigate to the SSMS, create a new window and run any test query. For example,

USE AdventureWorks2012
GO
SELECT * FROM HumanResources.Employee
GO

Look at the output of the sys.dm_exec_sessions (query above) and the Profiler. Note that both show the presence of a new user session. The Profiler also shows the associated activity.

image
image
image

References:

Until we meet next time,

Be courteous. Drive responsibly.