Sunday 4 August 2013

Differences Between RAISERROR and THROW in Sql Server


Differences Between RAISERROR and THROW in Sql Server


Both RAISERROR and THROW statements are used to raise an error in Sql Server. The journey of RAISERROR started from Sql Server 2005, where as the journey of THROW statement has just began with Sql Server 2012. obviously, Microsoft suggesting us to start using THROW statement instead of RAISERROR. THROW statement seems to be simple and easy to use than RAISERROR.

Below table lists-out 10 major difference between RAISERROR and THROW with examples:

RAISERROR THROW
Version of the Sql Server in which it is introduced?
Introduced in SQL SERVER 2005. And as per BOL, Microsoft is suggesting to start using THROW statement instead of RAISERROR in New Applications. Introduced in SQL SERVER 2012. THROW statement seems to be simple and easy to use than RAISERROR.
SYNTAX
RAISERROR
 ( { msg_id | msg_str
        | @local_variable }
    { ,severity ,state }
    [ ,argument [ ,...n ] ] )
    [ WITH option [ ,...n ] ]
THROW
 [ { error_number
     | @local_variable },
   { message | @local_variable },
   { state | @local_variable } ]
[ ; ]
Can re-throw the original exception that invoked the CATCH block?
NO. It always generates new exception and results in the loss of the original exception details. Below example demonstrates this:
BEGIN TRY
  DECLARE @result INT
--Generate divide-by-zero error
  SET @result = 55/0
END TRY
BEGIN CATCH
--Get the details of the error
--that invoked the CATCH block
 DECLARE
   @ErMessage NVARCHAR(2048),
   @ErSeverity INT,
   @ErState INT
  
 SELECT
   @ErMessage = ERROR_MESSAGE(),
   @ErSeverity = ERROR_SEVERITY(),
   @ErState = ERROR_STATE()
  
 RAISERROR (@ErMessage,
             @ErSeverity,
             @ErState )
END CATCH
RESULT:
Msg 50000, Level 16, State 1, Line 19
Divide by zero error encountered.

NOTE:The actual line number of the code which generated Divided By Zero error here is 4, but the exception message returned by RAISERROR is showiung it as 19. Also the error number corresponding to divide by zero error is 8134 in the SYS.Messages table, but the one returned by RAISERROR is 50000.
YES. To Re-THROW the original exception caught in the TRY Block, we can just specify the THROW statement without any parameters in the CATCH block. Below example demonstrates this:
BEGIN TRY
  DECLARE @result INT
--Generate divide-by-zero error
  SET @result = 55/0
END TRY
BEGIN CATCH
    THROW
END CATCH
RESULT:
Msg 8134, Level 16, State 1, Line 4
Divide by zero error encountered.

With above example it is clear that THROW statement is very simple for RE-THROWING the exception. And also it returns correct error number and line number.
Causes the statement batch to be ended?
Example 1: In the below Batch of statements the PRINT statement after RAISERROR statement will be executed.
BEGIN
 PRINT 'BEFORE RAISERROR'
 RAISERROR('RAISERROR TEST',16,1)
 PRINT 'AFTER RAISERROR'
END
RESULT:
BEFORE RAISERROR
Msg 50000, Level 16, State 1, Line 3
RAISERROR TEST

AFTER RAISERROR
Example 2: In the below example all the statement’s after RAISERROR statement are executed.
BEGIN TRY
 DECLARE @RESULT INT = 55/0
END TRY
BEGIN CATCH
 PRINT 'BEFORE RAISERROR';
 
--Get the details of the error
--that invoked the CATCH block
 DECLARE
  @ErMessage NVARCHAR(2048),
  @ErSeverity INT,
  @ErState INT
  
 SELECT
  @ErMessage = ERROR_MESSAGE(),
  @ErSeverity = ERROR_SEVERITY(),
  @ErState = ERROR_STATE()
 
 RAISERROR (@ErMessage,
             @ErSeverity,
             @ErState )
 
 PRINT 'AFTER RAISERROR'
END CATCH
 PRINT 'AFTER CATCH'
RESULT:
BEFORE RAISERROR
Msg 50000, Level 16, State 1, Line 19
Divide by zero error encountered.

AFTER RAISERROR
AFTER CATCH
Example 1: In the below Batch of statements the PRINT statement after THROW statement will not executed.
BEGIN
    PRINT 'BEFORE THROW';
    THROW 50000,'THROW TEST',1
    PRINT 'AFTER THROW'
END
RESULT:
BEFORE THROW
Msg 50000, Level 16, State 1, Line 3
THROW TEST

Example 2: In the below example no PRINT statement’s after THROW statement are executed.
BEGIN TRY
  DECLARE @RESULT INT = 55/0   
END TRY
BEGIN CATCH
  PRINT 'BEFORE THROW';
  THROW;
  PRINT 'AFTER THROW'
END CATCH
  PRINT 'AFTER CATCH'
RESULT:
BEFORE THROW
Msg 8134, Level 16, State 1, Line 2
Divide by zero error encountered.

CAN SET SEVERITY LEVEL?
YES. The severity parameter specifies the severity of the exception. NO. There is no severity parameter. The exception severity is always set to 16. (unless re-throwing in a CATCH block)
Requires preceding statement to end with semicolon (;) statement terminator?
NO. YES. The statement before the THROW statement must be followed by the semicolon (;) statement terminator.
CAN RAISE SYSTEM ERROR MESSAGE?
The SYS.MESSAGES Table will have both system-defined and user-defined messages. Message IDs less than 50000 are system messages.
YES. With RAISERROR we can raise the System Exception.
Example:
RAISERROR (40655,16,1) RESULT:
Msg 40655, Level 16, State 1, Line 1
Database ‘master’ cannot be restored.

NO. With THROW we can’t raise the System Exception. But when it used in CATCH BLOCK it can Re-THROW the system exception. Example: Trying to raise system exception (i.e. exception with ErrorNumber less than 50000).
THROW 40655, ‘Database master cannot be restored.’, 1
RESULT:
Msg 35100, Level 16, State 10, Line 1
Error number 40655 in the THROW statement is outside the valid range. Specify an error number in the valid range of 50000 to 2147483647
CAN RAISE user-defined message with message_id greater than 50000 which is not defined in SYS.MESSAGES table?
NO. If a msg_id is passed to RAISERROR, the ID must be defined in sys.messages. Example:
RAISERROR (60000, 16, 1)
RESULT:
Msg 18054, Level 16, State 1, Line 1
Error 60000, severity 16, state 1 was raised, but no message with that error number was found in sys.messages. If error is larger than 50000, make sure the user-defined message is added using sp_addmessage.

Now add the Message to SYS.MESSAGES Table by using the below statement:
EXEC sys.sp_addmessage 60000, 16, ‘Test User Defined Message’
Now try to Raise the Error:
RAISERROR (60000, 16, 1)
RESULT:
Msg 60000, Level 16, State 1, Line 1
Test User Defined Message
YES. The error_number parameter does not have to be defined in sys.messages. Example:
THROW 60000, ‘Test User Defined Message’, 1
RESULT:
Msg 60000, Level 16, State 1, Line 1
Test User Defined Message
Allows substitution parameters in the message parameter?
By using the below statement add a sample test message with parameteres to the SYS.Messages Table:
EXEC sp_addmessage 70000,16,‘Message with Parameter 1: %d and Parameter 2:%s’
YES.The msg_str parameter can contain printf formatting styles. Example 1:
RAISERROR (70000, 16, 1, 505,‘Basavaraj’ )
RESULT:
Msg 70000, Level 16, State 1, Line 1
Message with Parameter 1: 505 and Parameter 2:Basavaraj
NO.The message parameter does not accept printf style formatting. Example 1:
THROW 70000, ‘Message with Parameter 1: %d and Parameter 2:%s’, 1, 505,’Basavaraj’
RESULT:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ‘,’.

Alternative Way of doing this is:

DECLARE @ErrorMsg NVARCHAR(2048) = FORMATMESSAGE(70000, 505, ‘Basavaraj’ );
THROW 70000, @ErrorMsg, 1
Example 2: Message manipulation is not allowed in the THROW statement
Below statement will fail
THROW 58000,‘String1′ + ‘ String2′,1
RESULT:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ‘+’.

We can solve such problems, we can prepare the message prior to the THROW statement and then pass it to throw statement as a variable. Below example illustrates this.
DECLARE @message NVARCHAR(2048)
SET @message = ‘String1′ + ‘ String2′;
THROW 58000, @message, 1
RESULT:
Msg 58000, Level 16, State 1, Line 3
String1 String2

Difference Between Primary Key and Unique Key In Sql Server

Both PRIMARY KEY and UNIQUE KEY enforces the Uniqueness of the values (i.e. avoids duplicate values) on the column[s] on which it is defined.  Also these key’s can Uniquely identify each row in database table.
Below table lists out the major difference between PRIMARY KEY and UNIQUE KEY:

PRIMARY KEY UNIQUE KEY
NULL It doesn’t allow Null values.
Because of this we refer
PRIMARY KEY = UNIQUE KEY + Not Null CONSTRAINT
Allows Null value. But only one Null value.
INDEX By default it adds a clustered index By default it adds a UNIQUE non-clustered index
LIMIT A table can have only one PRIMARY KEY Column[s] A table can have more than one UNIQUE Key Column[s]
CREATE SYNTAX Below is the sample example for defining a single column as a PRIMARY KEY column while creating a table: CREATE TABLE dbo.Customer
(
Id INT NOT NULL PRIMARY KEY,
FirstName VARCHAR(100),
LastName VARCHAR(100),
City VARCHAR(50)
)
Below is the Sample example for defining multiple columns as PRIMARY KEY. It also shows how we can give name for the PRIMARY KEY:
CREATE TABLE dbo.Customer
(
Id INT NOT NULL,
FirstName VARCHAR(100) NOT NULL,
LastName VARCHAR(100),
City VARCHAR(50),
CONSTRAINT PK_CUSTOMER PRIMARY KEY (Id,FirstName)
)
Below is the sample example for defining a single column as a UNIQUE KEY column while creating a table: CREATE TABLE dbo.Customer
(
Id INT NOT NULL UNIQUE,
FirstName VARCHAR(100),
LastName VARCHAR(100),
City VARCHAR(50)
)
Below is the Sample example for defining multiple columns as UNIQUE KEY. It also shows how we can give name for the UNIQUE KEY:
CREATE TABLE dbo.Customer
(
Id INT NOT NULL,
FirstName VARCHAR(100) NOT NULL,
LastName VARCHAR(100),
City VARCHAR(50),
CONSTRAINT UK_CUSTOMER UNIQUE (Id,FirstName)
)
ALTER SYNTAX Below is the Syntax for adding PRIMARY KEY CONSTRAINT on a column when the table is already created: ALTER TABLE dbo.Customer
ADD CONSTRAINT PK_CUSTOMER PRIMARY KEY (Id)
Below is the Syntax for adding UNIQUE KEY CONSTRAINTon a column when the table is already created: ALTER TABLE dbo.Customer
ADD CONSTRAINT UK_CUSTOMER UNIQUE (Id)
DROP SYNTAX Below is the Syntax for dropping a PRIMARY KEY: ALTER TABLE dbo.Customer
DROP CONSTRAINT PK_CUSTOMER
Below is the Syntax for dropping a UNIQUE KEY: ALTER TABLE dbo.Customer
DROP CONSTRAINT UK_CUSTOMER

Difference Between Sql Server VARCHAR and VARCHAR(MAX) Data Type

Ideally, it is better to compare Text and Varchar(MAX) data types, as in Sql Server 2005 Varchar(MAX) data type was introduced as an alternate for Text data type. Varchar(Max) data type provides multiple advantages over Text data type.
Like many initially when Varchar(MAX) datatype was introduced in Sql Server 2005, I too was not clear about the difference between Varchar and Varchar(Max) and which one to use when. Hope the differences listed in the below table clarifies these queries.

Varchar[(n)] Varchar(Max)
Basic Definition Non-Unicode Variable Length character data type.Example:
DECLARE @FirstName AS VARCHAR(50) = ‘BASAVARAJ’
SELECT @FirstName
Non-Unicode large Variable Length character data type.Example:
DECLARE @FirstName AS VARCHAR(Max)= ‘BASAVARAJ’
SELECT @FirstName
 Storage Capacity It can store maximum 8000 Non-Unicode characters (i.e. maximum storage capacity is 8000 bytes of storage). Optional Parameter n value can be from 1 to 8000. It can store maximum of 2 147 483 647 Non-Unicode characters (i.e. maximum storage capacity is: 2GB).
Index? You can create index on Varchar column data type.
Example:
CREATE TABLE dbo.Employee (id INT identity(1,1) PRIMARY KEY, FirstName VARCHAR(50))
GO
CREATE INDEX IX_EmployeeFirstName ON dbo.Employee(FirstName)
GO
Index can’t be created on a Varchar(Max) data type columns.
Example:
CREATE TABLE dbo.Employee (id INT identity(1,1) PRIMARY KEY, FirstName VARCHAR(Max))
GO
CREATE INDEX IX_EmployeeFirstName ON dbo.Employee(FirstName)
GO 
Error Message:

Msg 1919, Level 16, State 1, Line 1 Column ‘FirstName’ in table ‘dbo.Employee’ is of a type that is invalid for use as a key column in an index.
How data is stored Physically? It uses the normal data pages to store the data i.e. it stores the value ‘in a row’. Sql server will try to store the value ‘in a row’ but if it could not then it will store the value ‘out of row’. i.e. It uses the normal data pages until the content actually fills 8k of data.When overflow happens, data is stored as old TEXT Data Type and a pointer is replacing the old content.
No. of Bytes required for each character It takes 1 byte per character Example:
DECLARE
@FirstName AS VARCHAR(50) = ‘BASAVARAJ’
SELECT @FirstName AS FirstNameDATALENGTH(@FirstName) AS Length

Result:
FirstName Length
BASAVARAJ 9
It takes 1 byte per characterExample:
DECLARE
@FirstName AS VARCHAR(MAX)= ‘BASAVARAJ’
SELECT @FirstName AS FirstNameDATALENGTH(@FirstName) AS Length Result:
FirstName Length
BASAVARAJ 9
Which one to use? If we know that data to be stored in the column or variable is less than or equal to 8000 characters, then we can use this data type.For example First Name, Last Name etc, columns value can’t cross the max 8000 characters limit, in such scenario’s it is better to use this data type. If we know that the data to be stored in the column or variable can cross a 8KB Data page, then we can use this data type.
Performance There is not much performance difference between Varchar[(n)] and Varchar(Max). Varchar[(n)] provides better performance results compared to Varchar(Max). If we know that data to be stored in the column or variable is less than or equal to 8000 characters, then using this Varchar[(n)]  data type provides better performance compared to Varchar(Max).Example: When I ran the below script by changing the variable @FirstName type to Varchar(Max) then for 1 million assignments it is consistently taking double time than when we used data type as Varchar(50) for variable @ FirstName.
DECLARE @FirstName VARCHAR(50), @COUNT INT=0, @StartTime DATETIME = GETDATE()
WHILE(@COUNT < 1000000)
BEGIN
SELECT @FirstName = ‘BASAVARAJ’, @COUNT = @COUNT +1
END
SELECT DATEDIFF(ms,@StartTime,GETDATE()) ‘Time Taken in ms’
GO 6
Note: Here GO 6 statement executes the statements above it 6 times.

Difference between Stored Procedure and User Defined Function in Sql Server

Below are the some of the major differences between User Defined Function and Stored Procedure in Sql Server.
To know more on the User-Defined functions with examples please visit the article: User-Defined function.
To know more on the Stored Procedure with examples please visit the articleStored Procedure.
Sl. No. User Defined function Stored Procedure
1 Function must return a value. Stored procedure may or not return values.
2 Will allow only Select statement, it will not allow us to use DML statements. Can have select statements as well as DML statements such as insert, update, delete
etc
3 It will allow only input parameters, doesn’t support output parameters. It can have both input and output parameters.
4 It will not allow us to use try-catch blocks. For exception handling we can use try catch blocks.
5 Transactions are not allowed within functions. Can use transactions within Stored procefures.
6 We can use only table variables, it will not allow using temporary tables. Can use both table variables aswell as temporary table in it.
7 Stored procedures can’t be called from function. Stored Procedures can call functions.
8 Functions can be called from select statement. Procedures can’t be called from Select/Where/Having etc statements. Execute/Exec
statement can be used to call/execute stored procedure.
9 UDF can be used in join clause as a result set. Procedures can’t be used in Join clause
You may also like to read  below other popular articles:

SET ANSI_NULLS ON/OFF Setting in Sql Server


While creating Stored Procedures, User Defined Functions etc, most of us use the SET ANSI_NULLS ON/OFF and SET QUOTED_IDENTIFIER ON/OFF Settings. In this article will discuss on the SET ANSI_NULLS  { ON | OFF } Setting.
To understand this with an example, let us create table Name and insert three records in this table as below:
CREATE TABLE dbo.Name(FirstName VARCHAR(50),LastName Varchar(50)) GO 
INSERT INTO dbo.Name VALUES('BASAVARAJ','BIRADAR'),
 ('KALPANA','PATIL'),
 ('MONTY', NULL) GO
Depending on the ANSI_NULLS setting value either ON or OFF, SQL Server behaves differently while comparing with NULL value.
SET ANSI_NULLS  ON
When this setting value is ON (i.e. SET ANSI_NULLS ON) then comparison with NULL value using = and <> comparison operator will return false. Below script demonstrates this fact. 
SET ANSI_NULLS ON 
GO
SELECT * FROM dbo.Name WITH(NOLOCK) WHERE LastName = NULL
SELECT * FROM dbo.Name WITH(NOLOCK) WHERE LastName <> NULL
Result:
FirstName               LastName 
----------------------- --------------------------
(0 row(s) affected)
FirstName               LastName
----------------------- --------------------------
(0 row(s) affected)
So, when this setting value is ON we need to use IS NULL or IS NOT NULL instead of comparison operator = and <>. Below script demonstrates this fact. 
SET ANSI_NULLS ON 
GO 
SELECT * FROM dbo.Name WITH(NOLOCK) WHERE LastName IS NULL 
SELECT * FROM dbo.Name WITH(NOLOCK) WHERE LastName IS NOT NULL 
Result:
FirstName                LastName 
------------------------ ------------------------ 
MONTY                    NULL 
(1 row(s) affected) 
FirstName                LastName 
------------------------ ------------------------ 
BASAVARAJ                BIRADAR 
KALPANA                  PATIL 
(2 row(s) affected)

SET ANSI_NULLS  OFF
On the other hand if this setting value is OFF (i.e. SET ANSI_NULLS OFF) then comparison with NULL value using = and <> comparison operator returns TRUE if the value to be compared is NULL and NON NULL value respectively. Below Script demonstrates this fact.
SET ANSI_NULLS OFF 
GO 
SELECT * FROM dbo.Name WITH(NOLOCK) WHERE LastName = NULL 
SELECT * FROM dbo.Name WITH(NOLOCK) WHERE LastName <> NULL 
Result:
FirstName               LastName 
----------------------- --------------------- 
MONTY                   NULL 
(1 row(s) affected) 
FirstName               LastName 
----------------------- --------------------- 
BASAVARAJ               BIRADAR 
KALPANA                 PATIL 
(2 row(s) affected)

Another important point to note is that, the SET ANSI_NULLS { ON| OFF } setting with which we create Stored Procedure/UDF etc will be stored in the meta data. So, whenever Stored Procedure/UDF executes, it will use these setting stored in the meta data. It will ignore the settings of the client or the calling application.

 Below query can be used to find the objects which are created with SET ANSI_NULLS setting as OFF.
SELECT OBJECT_NAME (object_id) FROM sys.sql_modules
WHERE uses_ansi_nulls = 0 -- 0 means OFF and 1 means ON
 
As per BOL for SQL Server 2008 or 2012, in future versions this setting value will always be ON and explicitly setting it to OFF will result in error. So, it is better avoid explicitly setting  this in future development work.
Please correct me, if my understanding is wrong. Comments are always welcome.
Note: All the examples in this article are tested on Sql Server 2008 version

No comments:

Post a Comment