Saturday, August 10, 2013

Table Variable in SQL Server

How to TABLE or TABLE variable to SP



I want to pass a TABLE or TABLE variable to my stored procedure?  Can I do it? NO, SQL server does not support to pass a TABLE or TABLE variable as parameters of stored procedure. So how can I do it?

In this article I am trying to demonstrate the above scenario by using XML as the format to pass a table to a stored procedure.

We are going to transform the table or query result to an XML variable and pass to the stored procedure. We can either convert the XML parameters back to the TABLE variable or directly use the XQuery on the XML variable.

Let’s table a simple example to understand it.

“I have 2 table objects. One is Inventory table and it contains the item code and stock quantity. Another is Order details. When the order came in order details and when we run the stored procedure it always decrease the stock from inventory table by item wise.”
Please note that SQL Server 2008 is used for this example.


No first we create the Table definition

n  Inventory Table

CREATE TABLE [dbo].[tran_Inventory]
    (ITEMCD VARCHAR(20) NOT NULL PRIMARY KEY,
     STOCK   INT         NULL)
GO

INSERT INTO [dbo].[tran_Inventory]
            (ITEMCD, STOCK)
VALUES('A001', 1000), ('A002', 1000), ('A003', 1000)

GO

n  Order Table

CREATE TABLE [dbo].[Ord_Details]
      (
        ORDID    INT IDENTITY(1,1) NOT NULL,
        ITEMCD   VARCHAR(20)       NOT NULL,
        QTY      INT               NULL
      )

GO

INSERT INTO [dbo].[Ord_Details]
            (ITEMCD, QTY)
VALUES      ('A001', 20), ('A002', 30), ('A003', 40)

GO

Now Create the Stored procedure

CREATE PROCEDURE [dbo].[my_Proc]
             (
                   @param_x XML
             )
 AS
    SET NOCOUNT ON

    UPDATE tran_Inventory SET
           stock = stock - v.Qty
    FROM   tran_Inventory inv
           INNER JOIN (SELECT x.item.value('@ITEMCD[1]', 'VARCHAR(20)')AS ItemNumber,
                              x.item.value('@QTY[1]', 'INT') AS Qty
                       FROM   @param_x.nodes('//items/item') AS x(item)
                      ) v ON (v.ItemNumber = inv.ITEMCD)
   
    RETURN


Execution the Stored Procedure

DECLARE @x XML
SET @x = (SELECT ITEMCD, QTY FROM Ord_Details FOR XML RAW('item'),ROOT('items'), TYPE)
EXECUTE my_Proc @x

Observation the Result

SELECT * FROM tran_Inventory

Hope you like it.

Table Variable with Dynamic SQL

Introductions
The table variable gives us the high degree of flexibility to work with T-SQL construct. Most of the developer uses the table variable to find the desired solutions due to its simplicities. Here in this article I am not discussing related to table variable. I already discuss it in my previous article (Please refer to the related tropics portions of the article). 
Here I am trying to discuss about the Table variable with dynamic SQL constructs and how to use the table variable in the dynamic SQL.
Before writing this article, I saw several article related to it, but they are discussing about a specific example in mind. Here in this article I am trying to cover all the aspects related to Table variable and dynamic SQL.
Although I do not prefer any dynamic SQL constructs but depending on the criteria and implementations in mind we have to go to choose these options. With the dynamic SQL and Table variable, developers are facing lot of common problem. In this article I am trying to figure out those problems and try to explain the solution of it.
Focused area
The area that we focused in this article is mentioned bellow.
1.    Table variable and Performance aspects
2.    The Common problem that we faced
3.    An Example of the Problem
4.    How we can solve it

Table variable and Performance aspects
When we are making a SQL constructs we must think about the performance aspects of the query. So we must understand that the using Table variable is good for performance boost up or not. Before using Table variable, we must know some facts related to it.
ü  MS SQL Server will not use parallelism when modifying a table variable. 
    The query used to insert rows into the table variable will not use parallelism.
ü  MS SQL Server does not create any statistics, like histogram, for table variables, so 
    the estimates on your second query will be a rough guess.

ü  Use table variables for really low number of rows, and if possible use OPTION 
    (RECOMPILE) in our query so the query optimizer could know the number of rows in 
    the table variable by the time it executes the query.

ü  If the number of rows is high than using temporary table is a good idea for 
    performance boost up as the temporary table use the Parallelism.
What is Parallelism?
MS SQL server can performs a query or index operations in parallel by using several operating system threats, the operation can be completed quickly and efficiently.
For more information related to Parallelism, follow the MSDN link.

The Common problem that we faced
Here I am mentioning an error message that most of the developer found if they made mistake when working with Table variable and dynamic SQL constructs.
Msg 137, Level 16, State 1, Procedure my_proc, Line 13
Must declare the scalar variable "@TBLVAR".
OR
Msg 1087, Level 15, State 2, Line 1
Must declare the table variable "@TBLVAR" 

An Example of the Problem
Here is the example from where the Error message is generated.
Here is a simple SQL example where the Error occurred.
Example-1
DECLARE @my_TABLE VARCHAR(50)='my_STUDENT'   
EXEC('SELECT * FROM  @my_TABLE')
-- Error Messages
Msg 1087, Level 15, State 2, Line 1
Must declare the table variable "@my_TABLE".
Example-2
In this example, the error message is generated when the stored procedure is compiled.
-- Base Table
IF OBJECT_ID('my_STUDENT') IS NOT NULL
   BEGIN
      DROP TABLE my_STUDENT;
   END
GO  
CREATE TABLE my_STUDENT
       (ROLL  INT          NOT NULL IDENTITY(1,1) PRIMARY KEY,
        SNAME VARCHAR(50)  NOT NULL,
        CLASS INT          NOT NULL);
      
GO
-- Insert Records
INSERT INTO my_STUDENT
       (SNAME, CLASS)
VALUES ('Joydeep', 1),
       ('Sukamal', 2),
       ('Sandip', 3),
       ('Samgram jit', 4)
GO 
-- SP Example
IF OBJECT_ID('my_proc') IS NOT NULL
   BEGIN
      DROP PROCEDURE my_proc;
   END                     
GO  
CREATE PROCEDURE my_proc
   (
      @p_OpeningMood   INT = 4
   )
AS
DECLARE @SQLSTRING VARCHAR(100)
DECLARE @TBLVAR TABLE
        (ROLL   INT,
         SNAME  VARCHAR(50))
BEGIN
    IF @p_OpeningMood = 4
       BEGIN
          SET @SQLSTRING = 'INSERT INTO '+ @TBLVAR
                           +' SELECT * FROM my_STUDENT'
                          
         EXEC (@SQLSTRING);                           
       END
    SELECT * FROM @TBLVAR;  
END     
GO
-- Error Messages
Msg 137, Level 16, State 1, Procedure my_proc, Line 13
Must declare the scalar variable "@TBLVAR".
In this example, the Error message is generated when the Stored Procedure is executed.
 -- SP Example [Another Variations]
IF OBJECT_ID('my_proc') IS NOT NULL
   BEGIN
      DROP PROCEDURE my_proc;
   END                     
GO  
CREATE PROCEDURE my_proc
   (
      @p_OpeningMood   INT = 4
   )
AS
DECLARE @SQLSTRING VARCHAR(2000)
DECLARE @TBLVAR TABLE
        (ROLL   INT,
         SNAME  VARCHAR(50))
BEGIN
    IF @p_OpeningMood = 4
       BEGIN
          SET @SQLSTRING = 'INSERT INTO  @TBLVAR
                           SELECT * FROM my_STUDENT'
                          
         EXEC (@SQLSTRING);                           
       END
     SELECT * FROM @TBLVAR;  
END     
GO
-- Messages
Command(s) completed successfully.

-- Executing the Stored Proc
EXEC my_proc
     @p_OpeningMood=
    
-- Error Messages    
Msg 1087, Level 15, State 2, Line 1
Must declare the table variable "@TBLVAR"

How we can solve it
We have to know, how to write the Dynamic SQL with Table variable to solve the problem.
First we look at the Example-1 and solve it (Passing table variable into dynamic SQL).
Please remember that the solutions of Example-1 is worked on MS SQL Server 2008 only
IF TYPE_ID('my_STUDENT') IS NOT NULL
   BEGIN
     DROP TYPE my_STUDENT
   END
GO  
CREATE TYPE my_STUDENT AS TABLE
(ROLL  INT    NOT NULL IDENTITY(1,1) PRIMARY KEY,
 SNAME VARCHAR(50)  NOT NULL,
 CLASS INT    NOT NULL);
GO

BEGIN
      DECLARE @my_TABLE my_STUDENT
      INSERT @my_TABLE VALUES ('Joydeep', 1),
                              ('Sukamal', 2),
                              ('Sandip', 3),
                              ('Samgram jit', 4)

      EXECUTE sp_executesql N'SELECT * FROM @my_TABLE',
                            N'@my_TABLE my_STUDENT READONLY',
                            @my_TABLE
    
END
GO

To passing the Table variable to a dynamic SQL please follow the gideline mentioned bellow.

[A] Defining the Table Type:

In my example, first I check that the type name exists or not. If exists than drop the type name.

IF TYPE_ID('my_STUDENT') IS NOT NULL
   BEGIN
     DROP TYPE my_STUDENT
   END
GO  

CREATE TYPE my_STUDENT AS TABLE
      (ROLL  INT    NOT NULL IDENTITY(1,1) PRIMARY KEY,
       SNAME VARCHAR(50)  NOT NULL,
       CLASS INT    NOT NULL);
GO

[B] Now declare a Variable by this Type name:

Here in this example, we are also filling some records in it.

DECLARE @my_TABLE my_STUDENT
INSERT @my_TABLE VALUES ('Joydeep', 1),
                        ('Sukamal', 2),
                        ('Sandip', 3),
                        ('Samgram jit', 4)

In case of stored procedure
CREATE PROCEDURE my_proc
           (
              @my_TABLE my_STUDENT READONLY
           )
AS
BEGIN

END
GO
[C] Passing the variable into Dynamic statement:
In my example
EXECUTE sp_executesql N'SELECT * FROM @my_TABLE',
                      N'@my_TABLE my_STUDENT READONLY',
                        @my_TABLE

For the stored procedure we can
DECLARE @DataTable AS my_STUDENT
INSERT INTO @DataTable SELECT * FROM (Some data)

-- Call the stored proc
EXECUTE my_proc @my_TABLE = @DataTable
Now we are going to solve the Example-2
We can solve it by
IF OBJECT_ID('my_proc') IS NOT NULL
   BEGIN
      DROP PROCEDURE my_proc;
   END                     
GO  
CREATE PROCEDURE my_proc
   (
      @p_OpeningMood   INT = 4
   )
AS
DECLARE @SQLSTRING VARCHAR(2000)

BEGIN
    IF @p_OpeningMood = 4
       BEGIN
         
         SET @SQLSTRING = 'DECLARE @TBLVAR TABLE
                                 (ROLL   INT,
                                  SNAME  VARCHAR(50))
                           INSERT INTO @TBLVAR
                           SELECT ROLL, SNAME FROM my_STUDENT'
                          
         EXEC (@SQLSTRING);                           
       END
END     
GO
The Example works fine but the table variable is not accessible from outside the dynamic SQL. The reason is the dynamic SQL is not part of the procedure, but constitutes a scope of its own. So if we go to the simple solutions we must use the temporary table (#, ##) to solve this problem.
Or if we stick to the table variable we must go to the hybrid solutions for it.
IF OBJECT_ID('my_proc') IS NOT NULL
   BEGIN
      DROP PROCEDURE my_proc;
   END                     
GO  
CREATE PROCEDURE my_proc
   (
      @p_OpeningMood   INT = 4
   )
AS
DECLARE @SQLSTRING VARCHAR(2000)
DECLARE @TBLVAR TABLE
        (ROLL   INT,
         SNAME  VARCHAR(50))
BEGIN
    IF @p_OpeningMood = 4
       BEGIN
         
         SET @SQLSTRING = 'SELECT ROLL, SNAME FROM my_STUDENT'
                          
         INSERT INTO @TBLVAR                 
            EXEC (@SQLSTRING);                           
       END
    SELECT * FROM @TBLVAR;  
END     
GO
-- Executing the Stored Proc
EXEC my_proc
     @p_OpeningMood=4

-- Output

ROLL       SNAME
1              Joydeep
2              Sukamal
3              Sandip
4              Samgram jit    

The above example works fine and we can get the desired result.
Related tropics
1.    Table-Valued Parameters
2.    Temporary table and Table variable
3.    How to TABLE or TABLE variable to SP
4.    Dynamic SQL With sp_executesql()


Hope you like it.



Is Table-Valued UDF is Performance Killer


One my previous article "Can view takes input parameters" I got some response from various sources like "Linked In" etc.
Here some of my readers give me some comments like
"A view might be more efficient than a table valued functions"
I searched different blogs and article related to it and find that there are a common misconnect is the Table value function is a poor performer.
So I decide to wire this article related to the performance factor of Table Valued function by collecting some notes from different sources.
How many type of Table valued function does MS SQL Server supports
There are 2 types of table valued function
1.    Single-statement table valued user defined functions, also called in-line
2.     Multi-statement table valued user defined functions
The simple example of both is mentioned bellow
Single Statement Table Valued function
CREATE FUNCTION dbo.fn_TableVal
                (@p_sal DECIMAL(20,2))
RETURNS TABLE
AS RETURN
(
      SELECT *
      FROM   emp_matsre
      WHERE  empSal>@p_sal
)
GO
Multi-statement table valued Function
CREATE FUNCTION dbo.fn_TableVal
                (@p_sal DECIMAL(20,2))
RETURNS @Ret_Table TABLE
        (idno    INT,
         empName VARCHAR(50),
         empSal  DECIMAL(20,2))
AS
BEGIN
    INSERT INTO @Ret_Table
      SELECT *
      FROM   emp_matsre
      WHERE  empSal>@p_sal
     
      RETURN

END
GO

The single-statement table valued function sometimes called the views with parameters.
Execution of these UDF's will be incorporated directly into the execution plan with the rest of the associated query. Indexes may be used if the query within the UDF is well constructed. It is generally safe constructs to use within your queries.
Multi-statement UDF's are built upon the structure of table variables.
These are not incorporated within the rest of the execution plan and usually show up as a table scan against a table with one row. The problem is your query may have more than one row. When the multi-statement UDF's are used on large sets of data they cause very serious performance problems. They won't scale well, and the performance issues are masked by the apparent low cost of the operations involved. We need to be extremely careful when using the multi-statement table valued UDF's. The problem is that the system sees the BEGIN/END and runs the PROCEDURE in a different context. Without it, it takes the subquery in the return() call and can simplify it out.
Non-inline functions do not contribute towards the cost reported by an execution plan. You need to use Profiler to see their effect.

Hope you like it.



Table-Valued Parameters

In my previous article I am trying to demonstrate, "How to pass a table or table variable in a stored procedure via xml string".

Some of my reader, asking about Table-valued parameters, the new feature of SQL Server 2008.

In this article I am trying to demonstrate related to Table-Valued parameters in SQL server 2008.

Hope it will be helpful for all of my readers.

The table-valued parameters are the new feature of SQL Server 2008. By this we can send the multiple rows of data in a stored procedure or a function without creating any temporary table. Before SQL server 2008 it is not possible to send the table or table variable in the stored procedure or function.

For the Table-value parameters we must follow the following tasks mentioned bellow.

1.    Create the table structure
2.    Create stored procedure that has Table type parameter.
3.    Create the table type variable and reference the table type.
4.    Using the INSERT statement and occupy the variable.
5.    Passes the variable to the procedure.


The Table Definition

CREATE TABLE emp
            (
                  empID   INT         NOT NULL PRIMARY KEY,
                  empName VARCHAR(30) NULL
            )
GO

Step-1 [ Create the table structure ]

CREATE TYPE empType AS TABLE
            (
                  empID    INT,
                  empName  VARCHAR(30)
            );
GO



Step-2 [ Create stored procedure that has Table type parameter ]


CREATE PROCEDURE my_Employee
      @Iemp empType READONLY
AS
INSERT INTO emp(empID,empName)
SELECT * FROM @Iemp;

GO

Please note the table-value parameter must be passes as readonly parameters. We can only perform DML operation in the body of the routine.

Step-3 [  Create the table type variable and reference the table type ]

DECLARE @Iemp AS empType;


Step-4 [ Using the INSERT statement and occupy the variable ]


INSERT INTO @Iemp(empID, empName)
VALUES (1,'Joydeep Das'),
       (2,'Sudip Das'),
       (3,'Tuhin Shina'),
       (4,'Palash Paul')

Step-5 [ Passes the variable to the procedure ]

EXEC my_Employee @Iemp;
GO      
      
SELECT * FROM  emp


The complete code set to execute


      CREATE TABLE emp
                  (
                        empID   INT         NOT NULL PRIMARY KEY,
                        empName VARCHAR(30) NULL
                  )
    GO
      CREATE TYPE empType AS TABLE
                  (
                        empID    INT,
                        empName  VARCHAR(30)
                  )

    GO
      CREATE PROCEDURE my_Employee
              @Iemp empType READONLY
      AS
      INSERT INTO emp(empID,empName)
      SELECT * FROM @Iemp;
    GO
   
      BEGIN
            DECLARE @Iemp AS empType;


            INSERT INTO @Iemp(empID, empName)
            VALUES (1,'Joydeep Das'),
                     (2,'Sudip Das'),
                     (3,'Tuhin Shina'),
                     (4,'Palash Paul')
                  
                  
            EXEC my_Employee @Iemp
      END   
   
    GO  
      SELECT * FROM  emp   



Hope you like it.