Thursday, 12 June 2014

Types of Functions

SQL Server 2008 supports user-defined functions and built-in, system, functions.

Scalar Functions

User-defined scalar functions return a single data value of the type defined in the RETURNS clause. For an inline scalar function, there is no function body; the scalar value is the result of a single statement. For a multistatement scalar function, the function body, defined in a BEGIN...END block, contains a series of Transact-SQL statements that return the single value. The return type can be any data type except textntextimagecursor, and timestamp.
The following examples creates a multistatement scalar function. The function takes one input value, a ProductID, and returns a single data value, the aggregated quantity of the specified product in inventory.
USE AdventureWorks2008R2;
GO
IF OBJECT_ID (N'dbo.ufnGetInventoryStock', N'FN') IS NOT NULL
    DROP FUNCTION ufnGetInventoryStock;
GO
CREATE FUNCTION dbo.ufnGetInventoryStock(@ProductID int)
RETURNS int 
AS 
-- Returns the stock level for the product.
BEGIN
    DECLARE @ret int;
    SELECT @ret = SUM(p.Quantity) 
    FROM Production.ProductInventory p 
    WHERE p.ProductID = @ProductID 
        AND p.LocationID = '6';
     IF (@ret IS NULL) 
        SET @ret = 0;
    RETURN @ret;
END;
GO


The following example uses the ufnGetInventoryStock function to return the current inventory quantity for products that have a ProductModelID between 75 and 80.
USE AdventureWorks2008R2;
GO
SELECT ProductModelID, Name, dbo.ufnGetInventoryStock(ProductID)AS CurrentSupply
FROM Production.Product
WHERE ProductModelID BETWEEN 75 and 80;
GO


Table-Valued Functions

User-defined table-valued functions return a table data type. For an inline table-valued function, there is no function body; the table is the result set of a single SELECT statement.
The following example creates an inline table-valued function. The function takes one input parameter, a customer (store) ID, and returns the columns ProductIDName, and the aggregate of year-to-date sales as YTD Total for each product sold to the store.
USE AdventureWorks2008R2;
GO
IF OBJECT_ID (N'Sales.ufn_SalesByStore', N'IF') IS NOT NULL
    DROP FUNCTION Sales.ufn_SalesByStore;
GO
CREATE FUNCTION Sales.ufn_SalesByStore (@storeid int)
RETURNS TABLE
AS
RETURN 
(
    SELECT P.ProductID, P.Name, SUM(SD.LineTotal) AS 'Total'
    FROM Production.Product AS P 
    JOIN Sales.SalesOrderDetail AS SD ON SD.ProductID = P.ProductID
    JOIN Sales.SalesOrderHeader AS SH ON SH.SalesOrderID = SD.SalesOrderID
    JOIN Sales.Customer AS C ON SH.CustomerID = C.CustomerID
    WHERE C.StoreID = @storeid
    GROUP BY P.ProductID, P.Name
);
GO


The following example invokes the function and specifies customer ID 602.
SELECT * FROM Sales.ufn_SalesByStore (602);


For a multistatement table-valued function, the function body, defined in a BEGIN...END block, contains a series of Transact-SQL statements that build and insert rows into the table that will be returned.
The following example creates a table-valued function. The function takes a single input parameter, an EmployeeID and returns a list of all the employees who report to the specified employee directly or indirectly. The function is then invoked specifying employee ID 109.
USE AdventureWorks2008R2;
GO
IF OBJECT_ID (N'dbo.ufn_FindReports', N'TF') IS NOT NULL
    DROP FUNCTION dbo.ufn_FindReports;
GO
CREATE FUNCTION dbo.ufn_FindReports (@InEmpID INTEGER)
RETURNS @retFindReports TABLE 
(
    EmployeeID int primary key NOT NULL,
    FirstName nvarchar(255) NOT NULL,
    LastName nvarchar(255) NOT NULL,
    JobTitle nvarchar(50) NOT NULL,
    RecursionLevel int NOT NULL
)
--Returns a result set that lists all the employees who report to the 
--specific employee directly or indirectly.*/
AS
BEGIN
WITH EMP_cte(EmployeeID, OrganizationNode, FirstName, LastName, JobTitle, RecursionLevel) -- CTE name and columns
    AS (
        SELECT e.BusinessEntityID, e.OrganizationNode, p.FirstName, p.LastName, e.JobTitle, 0 -- Get the initial list of Employees for Manager n
        FROM HumanResources.Employee e 
   INNER JOIN Person.Person p 
   ON p.BusinessEntityID = e.BusinessEntityID
        WHERE e.BusinessEntityID = @InEmpID
        UNION ALL
        SELECT e.BusinessEntityID, e.OrganizationNode, p.FirstName, p.LastName, e.JobTitle, RecursionLevel + 1 -- Join recursive member to anchor
        FROM HumanResources.Employee e 
            INNER JOIN EMP_cte
            ON e.OrganizationNode.GetAncestor(1) = EMP_cte.OrganizationNode
   INNER JOIN Person.Person p 
   ON p.BusinessEntityID = e.BusinessEntityID
        )
-- copy the required columns to the result of the function 
   INSERT @retFindReports
   SELECT EmployeeID, FirstName, LastName, JobTitle, RecursionLevel
   FROM EMP_cte 
   RETURN
END;
GO
-- Example invocation
SELECT EmployeeID, FirstName, LastName, JobTitle, RecursionLevel
FROM dbo.ufn_FindReports(1); 

GO


Built-in Functions

Built-in functions are provided by SQL Server to help you perform a variety of operations. They cannot be modified. You can use built-in functions in Transact-SQL statements to:
Built-in functions return either scalar or table data types. For example, @@ERROR returns 0 if the last Transact-SQL statement executed successfully. If the statement generated an error, @@ERROR returns the error number. And the function SUM(parameter) returns the sum of all the values for the parameter.

Monday, 9 June 2014

Difference Between Heap table and Clustered Table

Problem
One very important design aspect when creating a new table is the decision to create or not create a clustered index.  A table that does not have a clustered index is referred to as a HEAP and a table that has a clustered index is referred to as a clustered table.  A clustered table provides a few benefits over a heap such as physically storing the data based on the clustered index, the ability to use the index to find the rows quickly and the ability to reorganize the data by rebuilding the clustered index.  Depending on the INSERT, UPDATE and DELETE activity against your tables your physical data can become very fragmented.  This fragmentation can lead to wasted space in your database, because of partly full pages as well as the need to read several more pages in order to satisfy the query.  So what can be done?
SolutionThe primary issue that we want to address is the fragmentation that occurs with normal database activity.  Depending on whether your table has a clustered index or not will determine if you can easily address the fragmentation problem down to the physical data level.  Because a heap or a clustered index determines the physical storage of your table data, there can only be one of these per table.  So a table can either have one heap or one clustered index.
Let's take a look at the differences between a heap and clustered table.
HEAP
  • Data is not stored in any particular order
  • Specific data can not be retrieved quickly, unless there are also non-clustered indexes
  • Data pages are not linked, so sequential access needs to refer back to the index allocation map (IAM) pages
  • Since there is no clustered index, additional time is not needed to maintain the index
  • Since there is no clustered index, there is not the need for additional space to store the clustered index tree
  • These tables have a index_id value of 0 in the sys.indexes catalog view
IAM pages retrieve data in a single partition heap
source: SQL Server 2005 books online
Clustered Table
  • Data is stored in order based on the clustered index key
  • Data can be retrieved quickly based on the clustered index key, if the query uses the indexed columns
  • Data pages are linked for faster sequential access
  • Additional time is needed to maintain clustered index based on INSERTS, UPDATES and DELETES
  • Additional space is needed to store clustered index tree
  • These tables have a index_id value of 1 in the sys.indexes catalog view
Levels of a clustered index
source: SQL Server 2005 books online
So based on the above you can see there are a few fundamental differences on whether a table has a clustered index or not.
Fragmentation A problem that occurs on all tables is the issue of becoming fragmented.  Depending on the activity performed such as DELETES, INSERTS and UPDATES, your heap tables and clustered tables can become fragmented.  A lot of this depends on the activity as well as the key values that are used for your clustered index. 
  • If your heap table only has INSERTS occurring, your table will not become fragmented, since only new data is written.
  • If your clustered index key is sequential, such as an identity value, and you only have INSERTS, again this will not become fragmented since the new data is always written at the end of the clustered index.
  • But if your table is either a heap or a clustered table and there are a lot of INSERTS, UPDATES and DELETES the data pages can become very fragmented.  This results in wasted space as well as additional data pages to read to satisfy the queries. 
    • When a table is created as a heap, SQL Server does not force where the new data pages are written.  Whenever new data is written this data is always written at the end of the table or on the next available page that is assigned to this table.  When data is deleted the space becomes free in the data pages, but it is not reused because new data is always written to the next available page.
    • With a clustered index, depending on the index key, new records may be written to existing pages where free space exists or there may be need to split a page into multiple pages in order to insert the new data.  When deletes occur the same issue occurs as with a heap, but this free space may be used again if data needs to be inserted into one of the existing pages that has free space.
    • So based on this, your heap table could become more fragmented then your clustered table.
Identifying FragmentationTo identify whether your clustered table or heap table is fragmented you need to either run DBCC SHOWCONTIG (2000 or 2005) or use the new DMV sys.dm_db_index_physical_stats (2005).  These commands will give you insight into the fragmentation problems that may exist in your table.  For further information on this take a look at this past tip: SQL Server 2000 to 2005 Crosswalk - Database Fragmentation.
Resolving FragmentationClustered Tables
Resolving the fragmentation for a clustered table can be done easily by rebuilding or reorganizing your clustered index.  This was shown in this previous tip: SQL Server 2000 to 2005 Crosswalk - Index Rebuilds.
Heap Tables
For heap tables this is not as easy.  The following are different options you can take to resolve the fragmentation:
  1. Create a clustered index
  2. Create a new table and insert data from the heap table into the new table based on some sort order
  3. Export the data, truncate the table and import the data back into the table
Additional Info
When creating a new table via Enterprise Manager or Management Studio when you specify a primary key for the table, the management tools automatically make this a clustered index, but this can be overridden.  When creating a new table via scripts you need to identify that the table be created with a clustered index.   So based on this most of your tables are going to have a clustered index, because of the primary key, but if you do not specify a primary key or build a clustered index the data will be stored as a heap.
Next Steps
  • Keeping table and index fragmentation under control is a key process to maintain optimum performance out of your database.  Now that you can see how a heap vs a clustered table differs and what needs to be done to address the fragmentation, take a look at your table structures to see if you need to address these issues.
  • Even if you are doing a complete index rebuild on all of your tables once a week or whenever, your heap tables will never be de-fragmented, so you will need to come up with another strategy to handle fragmentation issues with these tables.
  • Take a look at these other related tips:
  • Based on the above it seems that all tables should have a clustered index. For the most part this is the case, but there may be some reason that you do not want to have a clustered index.  One reason could be a table that only has INSERTS, such as a log file.  But if in doubt, it would be better to have a clustered index then to not have one.
Ref: http://www.mssqltips.com/sqlservertip/1254/clustered-tables-vs-heap-tables/

Friday, 29 November 2013

Wednesday, 27 November 2013

Cursors & Different Types of Cursors in SQL Server And How to avoid cursors using cursor's alternatives

A Cursor allow us to retrieve data from a result set in singleton fashion means row by row. Cursor are required when we need to update records in a database table one row at a time
A Cursor impacts the performance of the SQL Server since it uses the SQL Server instances' memory, reduce concurrency, decrease network bandwidth and lock resources. Hence it is mandatory to understand the cursor types and its functions so that you can use suitable cursor according to your needs.
You should avoid the use of cursor. Basically you should use cursor alternatives like as WHILE loop, sub queries, Temporary tables and Table variables. We should use cursor in that case when there is no option except cursor.
Types of Cursors
1.                 Static Cursors
A static cursor populates the result set at the time of cursor creation and query result is cached for the lifetime of the cursor. A static cursor can move forward and backward direction. A static cursor is slower and use more memory in comparison to other cursor. Hence you should use it only if scrolling is required and other types of cursors are not suitable.
You can't update, delete data using static cursor. It is not sensitive to any changes to the original data source. By default static cursors are scrollable.
2.                Dynamic Cursors
A dynamic cursor allows you to see the data updation, deletion and insertion in the data source while the cursor is open. Hence a dynamic cursor is sensitive to any changes to the data source and supports update, delete operations. By default dynamic cursors are scrollable.
3.                Forward Only Cursors
A forward only cursor is the fastest cursor among the all cursors but it doesn't support backward scrolling. You can update, delete data using Forward Only cursor. It is sensitive to any changes to the original data source.
There are three more types of Forward Only Cursors.Forward_Only KEYSET, FORWARD_ONLY STATIC and FAST_FORWARD.
A FORWARD_ONLY STATIC Cursor is populated at the time of creation and cached the data to the cursor lifetime. It is not sensitive to any changes to the data source.
A FAST_FORWARD Cursor is the fastest cursor and it is not sensitive to any changes to the data source.
4.                Keyset Driven Cursors
A keyset driven cursor is controlled by a set of unique identifiers as the keys in the keyset. The keyset depends on all the rows that qualified the SELECT statement at the time of cursor was opened. A keyset driven cursor is sensitive to any changes to the data source and supports update, delete operations. By default keyset driven cursors are scrollable.
SQL SERVER – Examples of Cursors
1.   CREATE TABLE Employee
2.  (
3.   EmpID int PRIMARY KEY,
4.   EmpName varchar (50) NOT NULL,
5.   Salary int NOT NULL,
6.   Address varchar (200) NOT NULL,
7.  )
8.  GO
9.  INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(1,'Mohan',12000,'Noida')
10.INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(2,'Pavan',25000,'Delhi')
11.INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(3,'Amit',22000,'Dehradun')
12.INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(4,'Sonu',22000,'Noida')
13.INSERT INTO Employee(EmpID,EmpName,Salary,Address) VALUES(5,'Deepak',28000,'Gurgaon')
14.GO
15.SELECT * FROM Employee

Static Cursor - Example
1.   SET NOCOUNT ON
2.  DECLARE @Id int
3.  DECLARE @name varchar(50)
4.  DECLARE @salary int
5.   DECLARE cur_emp CURSOR
6.  STATIC FOR
7.  SELECT EmpID,EmpName,Salary from Employee
8.  OPEN cur_emp
9.  IF @@CURSOR_ROWS > 0
10. BEGIN
11. FETCH NEXT FROM cur_emp INTO @Id,@name,@salary
12. WHILE @@Fetch_status = 0
13. BEGIN
14. PRINT 'ID : '+ convert(varchar(20),@Id)+', Name : '+@name+ ', Salary : '+convert(varchar(20),@salary)
15. FETCH NEXT FROM cur_emp INTO @Id,@name,@salary
16. END
17.END
18.CLOSE cur_emp
19.DEALLOCATE cur_emp
20.SET NOCOUNT OFF

Dynamic Cursor - Example
1.   --Dynamic Cursor for Update
2.  SET NOCOUNT ON
3.  DECLARE @Id int
4.  DECLARE @name varchar(50)
5.   DECLARE Dynamic_cur_empupdate CURSOR
6.  DYNAMIC
7.  FOR
8.  SELECT EmpID,EmpName from Employee ORDER BY EmpName
9.  OPEN Dynamic_cur_empupdate
10.IF @@CURSOR_ROWS > 0
11. BEGIN
12. FETCH NEXT FROM Dynamic_cur_empupdate INTO @Id,@name
13. WHILE @@Fetch_status = 0
14. BEGIN
15. IF @name='Mohan'
16. Update Employee SET Salary=15000 WHERE CURRENT OF Dynamic_cur_empupdate
17. FETCH NEXT FROM Dynamic_cur_empupdate INTO @Id,@name
18. END
19.END
20.CLOSE Dynamic_cur_empupdate
21.DEALLOCATE Dynamic_cur_empupdate
22.SET NOCOUNT OFF
23. Go
24.Select * from Employee

1.   -- Dynamic Cursor for DELETE
2.  SET NOCOUNT ON
3.  DECLARE @Id int
4.  DECLARE @name varchar(50)
5.   DECLARE Dynamic_cur_empdelete CURSOR
6.  DYNAMIC
7.  FOR
8.  SELECT EmpID,EmpName from Employee ORDER BY EmpName
9.  OPEN Dynamic_cur_empdelete
10.IF @@CURSOR_ROWS > 0
11. BEGIN
12. FETCH NEXT FROM Dynamic_cur_empdelete INTO @Id,@name
13. WHILE @@Fetch_status = 0
14. BEGIN
15. IF @name='Deepak'
16. DELETE Employee WHERE CURRENT OF Dynamic_cur_empdelete
17. FETCH NEXT FROM Dynamic_cur_empdelete INTO @Id,@name
18. END
19.END
20.CLOSE Dynamic_cur_empdelete
21.DEALLOCATE Dynamic_cur_empdelete
22.SET NOCOUNT OFF


23.Go
24.Select * from Employee
Forward Only Cursor - Example
1.   --Forward Only Cursor for Update
2.  SET NOCOUNT ON
3.  DECLARE @Id int
4.  DECLARE @name varchar(50)
5.   DECLARE Forward_cur_empupdate CURSOR
6.  FORWARD_ONLY
7.  FOR
8.  SELECT EmpID,EmpName from Employee ORDER BY EmpName
9.  OPEN Forward_cur_empupdate
10.IF @@CURSOR_ROWS > 0
11. BEGIN
12. FETCH NEXT FROM Forward_cur_empupdate INTO @Id,@name
13. WHILE @@Fetch_status = 0
14. BEGIN
15. IF @name='Amit'
16. Update Employee SET Salary=24000 WHERE CURRENT OF Forward_cur_empupdate
17. FETCH NEXT FROM Forward_cur_empupdate INTO @Id,@name
18. END
19.END
20.CLOSE Forward_cur_empupdate
21.DEALLOCATE Forward_cur_empupdate
22.SET NOCOUNT OFF
23. Go
24.Select * from Employee

1.   -- Forward Only Cursor for Delete
2.  SET NOCOUNT ON
3.  DECLARE @Id int
4.  DECLARE @name varchar(50)
5.   DECLARE Forward_cur_empdelete CURSOR
6.  FORWARD_ONLY
7.  FOR
8.  SELECT EmpID,EmpName from Employee ORDER BY EmpName
9.  OPEN Forward_cur_empdelete
10.IF @@CURSOR_ROWS > 0
11. BEGIN
12. FETCH NEXT FROM Forward_cur_empdelete INTO @Id,@name
13. WHILE @@Fetch_status = 0
14. BEGIN
15. IF @name='Sonu'
16. DELETE Employee WHERE CURRENT OF Forward_cur_empdelete
17. FETCH NEXT FROM Forward_cur_empdelete INTO @Id,@name
18. END
19.END
20.CLOSE Forward_cur_empdelete
21.DEALLOCATE Forward_cur_empdelete
22.SET NOCOUNT OFF
23. Go
24.Select * from Employee

Keyset Driven Cursor - Example
1.   -- Keyset driven Cursor for Update
2.  SET NOCOUNT ON
3.  DECLARE @Id int
4.  DECLARE @name varchar(50)
5.   DECLARE Keyset_cur_empupdate CURSOR
6.  KEYSET
7.  FOR
8.  SELECT EmpID,EmpName from Employee ORDER BY EmpName
9.  OPEN Keyset_cur_empupdate
10.IF @@CURSOR_ROWS > 0
11. BEGIN
12. FETCH NEXT FROM Keyset_cur_empupdate INTO @Id,@name
13. WHILE @@Fetch_status = 0
14. BEGIN
15. IF @name='Pavan'
16. Update Employee SET Salary=27000 WHERE CURRENT OF Keyset_cur_empupdate
17. FETCH NEXT FROM Keyset_cur_empupdate INTO @Id,@name
18. END
19.END
20.CLOSE Keyset_cur_empupdate
21.DEALLOCATE Keyset_cur_empupdate
22.SET NOCOUNT OFF
23. Go
24.Select * from Employee

1.   -- Keyse Driven Cursor for Delete
2.  SET NOCOUNT ON
3.  DECLARE @Id int
4.  DECLARE @name varchar(50)
5.   DECLARE Keyset_cur_empdelete CURSOR
6.  KEYSET
7.  FOR
8.  SELECT EmpID,EmpName from Employee ORDER BY EmpName
9.  OPEN Keyset_cur_empdelete
10.IF @@CURSOR_ROWS > 0
11. BEGIN
12. FETCH NEXT FROM Keyset_cur_empdelete INTO @Id,@name
13. WHILE @@Fetch_status = 0
14. BEGIN
15. IF @name='Amit'
16. DELETE Employee WHERE CURRENT OF Keyset_cur_empdelete
17. FETCH NEXT FROM Keyset_cur_empdelete INTO @Id,@name
18. END
19.END
20.CLOSE Keyset_cur_empdelete
21.DEALLOCATE Keyset_cur_empdelete
22.SET NOCOUNT OFF
23. Go Select * from Employee



How to avoid cursors using cursor's alternatives
The following articles shows how to avoid to use cursors by it’s alternatives