2015-08-13

Fixing SQL Server Login SID by mapping it to Database User

When you restore a database from another server, especially for setting Availability Group, Database Mirroring, and Log Shipping, the client application may not able to use database and getting login failed for user after failover. It's due to a well known problem called "SID mapping" between the SQL Server Login and the Database User. If you recreate a SQL Server login (ie: not a Windows one), by default you get a new security ID (SID), even though you have the same user name and password. But the SID in the Database User is brought from the original (primary) database, which only linked to the SID of the corresponding SQL Server Login in the original (primary) server.

In order to fix this problem permanently, you should create the SQL Server Logins in secondary server by explicitly specifying the SID, e.g.

1. In primary database, get the SID of the database user:
USE LoginTest;
GO
SELECT sid FROM sysusers WHERE name = 'GregTest';

2. In the secondary server, after you restored the database into it, you can take a look in the user mapping and see that the Database User is NOT linked to any Login:
USE LoginTest
GO
EXEC sp_change_users_login 'Report';

3. You should fix it by creating a new SQL Server Login in the secondary server with the same SID:
CREATE LOGIN GregTest WITH PASSWORD = 'P@ssw0rd', SID = 0x2261C43EFD53F240AA989A8FB9E084DC

4. Check again the SID mapping, you should see no missing anymore:

2015-08-11

Checking Stored Procedure Performance Statistics

We can use the sys.dm_exec_procedure_stats DMV to check the last execution time, elapsed time, and other performance figures of stored procedures exist in the plan cache. Below is the SQL statement:

SELECT
DB_NAME(database_id) AS DB,
OBJECT_NAME(object_id, database_id)AS [OBJECT],
*
FROM sys.dm_exec_procedure_stats

2015-07-26

@@TRANCOUNT = 2 During DML statement Executing

In SQL Server BOL, it said @@TRANCOUNT returns the number of BEGIN TRANSACTION statements that have occurred on the current connection. Also, it said the open_tran column of sys.sysprocesses returns the number of open transactions for the process. So you may intuitively think that if that transaction count value of a process is greater than 1, it should be an explicit nested transaction.
However, explicit nested transactions are not the only reason why the transaction count can be greater than 1 during execution of a DML statement. Consider the following example:

IF EXISTS (SELECT 1 FROM sys.objects WHERE [type] = 'U' AND name = 'T1') DROP TABLE T1;
GO
CREATE TABLE T1
(
Col1 int
);
GO

-- INSERT
INSERT INTO T1
--SELECT @@TRANCOUNT;
SELECT open_tran FROM sys.sysprocesses WHERE spid = @@SPID;
-- returns 2
SELECT Col1 FROM T1;

-- UPDATE
UPDATE T1 SET
Col1 = --@@TRANCOUNT
(SELECT open_tran FROM sys.sysprocesses WHERE spid = @@SPID)
WHERE Col1 = 2;
-- returns 2
SELECT Col1 FROM T1;

-- DELETE
DELETE
FROM T1
WHERE Col1 = --@@TRANCOUNT
(SELECT open_tran FROM sys.sysprocesses WHERE spid = @@SPID)
-- returns nothing (as @@TRANCOUNT / sys.sysprocesses open_tran = 2)
SELECT Col1 FROM T1;

-- INSERT within Explicit Transaction
BEGIN TRAN
INSERT INTO T1
--SELECT @@TRANCOUNT;
SELECT open_tran FROM sys.sysprocesses WHERE spid = @@SPID;
COMMIT
-- Still returns 2
SELECT Col1 FROM T1;

-- UPDATE within Explicit Transaction
BEGIN TRAN
UPDATE T1 SET
Col1 = --@@TRANCOUNT
(SELECT open_tran FROM sys.sysprocesses WHERE spid = @@SPID)
WHERE Col1 = 2;
COMMIT
-- Still returns 2
SELECT Col1 FROM T1;

-- DELETE within Explicit Transaction
BEGIN TRAN
DELETE
FROM T1
WHERE Col1 = --@@TRANCOUNT
(SELECT open_tran FROM sys.sysprocesses WHERE spid = @@SPID)
COMMIT
-- returns nothing (as @@TRANCOUNT / sys.sysprocesses open_tran = 2)
SELECT Col1 FROM T1;

This shows that during execution of a DML statement, there will be more than one opened transactions reported. The results are the same for @@TRANCOUNT, open_tran column from sys.sysprocesses, or the open_transaction_count column from the sys.dm_exec_requests.
Effectively, in addition to the one transaction always associated with any DML statement, there is another nested transaction opened internally by SQL Server, lasting for the duration of DML statement’s execution. To be clear, the second transaction is open only while a DML statement is executing. Such situation can be easily observed especially during Process Blocking.

2015-07-15

CHECKDB on an Explicit Snapshot

Beginning with SQL 2005, DBCC CHECKDB creates a hidden snapshot on the same volume as the database – you have no control over where it’s placed. If you’re running CHECKDB at the same time that your server has a heavy workload the snapshot can run out of space and you’ll get an error showing that CHECKDB didn’t complete. In order to overcome this problem, you can create your own database snapshot on a drive that has enough space and run CHECKDB against that snapshot. CHECKDB will know that its running against a snapshot and won’t create another one. Below is an example:

CREATE DATABASE TestDB_Snapshot ON
(
    NAME = TestDB,
    FILENAME = 'C:\TestDB.ss'
) AS SNAPSHOT OF TestDB;
GO
DBCC CHECKDB (TestDB_Snapshot) WITH NO_INFOMSGS;
GO

2015-07-14

Check Dependent Objects

In order to check the dependent objects being referenced by a specified object, e.g. to find the referenced tables of a stored procedure, previously we can used the sp_depends system builtin stored procedure. However, if the dependent objects (e.g. table) are created after the referencing object (e.g. stored procedure), sp_depends cannot find out such dependency. Started from SQL2008, there are two new DMFs (sys.dm_sql_referenced_entities and sys.dm_sql_referencing_entities) which overcome such problem. Let's have a try in the following example:

USE TempDB
GO

DROP TABLE TestTable
GO
CREATE TABLE dbo.TestTable
( ID INT,
Name VARCHAR(100))
GO

-- referencing usp1 NOT created yet
DROP PROC usp2
GO
CREATE PROCEDURE dbo.usp2
AS
EXEC dbo.usp1
GO

DROP PROC usp1
GO
CREATE PROCEDURE dbo.usp1
AS
SELECT ID, Name
FROM TestTable
GO

SELECT * FROM sys.dm_sql_referencing_entities ('dbo.usp1', 'OBJECT');
SELECT * FROM sys.dm_sql_referenced_entities ('dbo.usp1', 'OBJECT');
SELECT * FROM sys.dm_sql_referenced_entities ('dbo.usp2', 'OBJECT');

2015-07-03

Get current stored proc name and params list

Inside a stored procedure, it can get the current stored procedure name and its parameters list by the follow statements:
DECLARE @names varchar(MAX) = OBJECT_NAME(@@PROCID) + ' ';
SELECT @names += name + ',' FROM sys.parameters WHERE [object_id] = @@PROCID ORDER BY parameter_id;

2015-06-02

Stored Procedure Error Handling Pattern

Thanks to SQL Server MVP Erland Sommarskog, we got a unified and reliable error and transaction handling in stored procedures. Here I briefly demonstrate how to do.

1. Create the error handler sp:
-- =============================================
-- Author: Erland Sommarskog
-- Description: Error Handler SP
-- Usage: To be called inside CATCH block of a stored proc to reraise error. Error Line Number can be precisely checked by using [sp_helptext] system stored proc., e.g. sp_helptext '[dbo].[the_sp_name]'
-- Reference: General Pattern for Error Handling (http://www.sommarskog.se/error_handling/Part1.html)
-- =============================================
CREATE PROCEDURE error_handler_sp AS
BEGIN
   DECLARE @errmsg   nvarchar(2048),
           @severity tinyint,
           @state    tinyint,
           @errno    int,
           @proc     sysname,
           @lineno   int
         
   SELECT @errmsg = error_message(), @severity = error_severity(),
          @state  = error_state(), @errno = error_number(),
          @proc   = error_procedure(), @lineno = error_line()
     
   IF @errmsg NOT LIKE '***%'
   BEGIN
      SELECT @errmsg = '*** ' + coalesce(quotename(@proc), '<dynamic SQL>') +
                       ', Line ' + ltrim(str(@lineno)) + '. Errno ' +
                       ltrim(str(@errno)) + ': ' + @errmsg
   END
   RAISERROR('%s', @severity, @state, @errmsg)
END
GO

2. Employ the error handling pattern in your stored procs:
/* TESTING TABLE */
CREATE TABLE sometable(a int NOT NULL,
                       b int NOT NULL,
                       CONSTRAINT pk_sometable PRIMARY KEY(a, b))
GO

/* ERROR HANDLING PATTERN */
CREATE PROCEDURE insert_data @a int, @b int AS
BEGIN
   SET XACT_ABORT, NOCOUNT ON
   /*
    * The first line in the procedure turns on XACT_ABORT and NOCOUNT in single statement. This line is the only line to come before BEGIN TRY.
* Everything else in the procedure should come after BEGIN TRY: variable declarations, creation of temp tables, table variables, everything.
* Even if you have other SET commands in the procedure (there is rarely a reason for this, though), they should come after BEGIN TRY.
*/
   BEGIN TRY
     /* Non-transactional statements, e.g. variable declarations */
      BEGIN TRANSACTION /* If your procedure does not perform any updates or only has a single INSERT/UPDATE/DELETE/MERGE statement, you typically don't have an explicit transaction at all. */
 /* Transactional statements */
      INSERT sometable(a, b) VALUES (@a, @b) -- sample
      INSERT sometable(a, b) VALUES (@b, @a) -- sample
      COMMIT TRANSACTION
 /* Non-transactional statements, e.g. final SELECT to return data or assign values to output parameters */
   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION -- Rolls back any open transaction
      EXEC error_handler_sp -- Reraises the error
      RETURN 55555 -- any error return code you defined on your application (Non-zero, zero is usually understood as success)
   END CATCH /* NEVER have any code after END CATCH for the outermost TRY-CATCH */
END
GO

/* TEST CASES */
-- Outer SP
CREATE PROCEDURE outer_sp @a int, @b int AS
   SET XACT_ABORT, NOCOUNT ON
   BEGIN TRY
      EXEC insert_data @a, @b
   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION
      EXEC error_handler_sp
      RETURN 55555
   END CATCH
GO
EXEC insert_data 9, NULL
EXEC insert_data 8, 8
EXEC outer_sp 8, 8
EXEC outer_sp null, null
EXEC sp_helptext '[insert_data]' -- check error line number of the error throwing stored proc.