Showing posts with label tsql. Show all posts
Showing posts with label tsql. Show all posts

Sunday, 30 August 2015

Table scan or Unexpected output due to improper where condition applied for DateTime filter by serverku

When you are using date filter with queries, like to find the records from tables for the particular year/month, we are approaching some extra data type conversion on fields. Below are some examples.

Should not use,
 
-- #1. : This cause to table scan
SELECT *
FROM OrderDetails
WHERE datepart(month,Orderdate) =?
AND datepart(year,Orderdate) = ?

-- #2. : This cause to unexpected output
SELECT *
FROM OrderDetails
WHERE convert(varchar,OrderDate,112) between
'20110101' and '20110131'
Should use,
 
-- #1.
SELECT *
FROM OrderDetails
WHERE convert(varchar,Orderdate,112) >= '20110101'
AND convert(varchar,Orderdate,112) < '20110201' -- EndDate + 1

-- #2.
SELECT *
FROM OrderDetails
WHERE cast (OrderDate as datetime) BETWEEN
'2011-01-01 00:00:00.000' AND '2011-01-31 23:59:59.900'
Create table with sample records or use existing table which has datetime data type field, Run query above and check the results with execution plan. Hope you like it.

Sunday, 23 August 2015

COUNT(*) with a LEFT JOIN may produce unexpected results - SQL Server by serverku

We should not use count (*) for left outer join two or more tables with group by. Instead we can use right table's column for count, otherwise it will come up with unexpected output.

Way cause for unexpected output
 
SELECT
a.id,
COUNT(*)
FROM table1 a
LEFT JOIN table2 b ON (a.id=b.id)
GROUP BY a.id
GO
Right way
 
SELECT
a.id,
COUNT(b.id)
FROM table1 a
LEFT JOIN table2 b ON (a.id=b.id)
GROUP BY a.id
GO
Create tables with sample records, run above queries for them and see difference. Did you know this earlier?

Sunday, 9 August 2015

Linked Server and OPENQUERY with Execl Source - SQL Server by serverku


As I have written posts for the linked servers with SQL Server option in previous post, the same way we have another option is available and these are with other data sources as a linked server. With SQL Server as a linked server, we can communicate with two SQL servers. But we can also communicate other data sources like excel, csv and others as well. Let us link our SQL server with Excel and get the data from excel. For that we need to go through the same way as I did for SQL Server linked server, but here we need to choose option of other data sources.


How can we do it using TSQL?
 
USE [master]
GO

EXEC [master].[dbo].[sp_addlinkedserver]
@server='ExcelImport',
@srvproduct='Excel',
@provider='Microsoft.Jet.OLEDB.4.0',
@datasrc='D:\Import\Import_1.xls',
@provstr= 'Excel 8.0'

GO

EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'collation compatible', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'data access', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'dist', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'pub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'rpc', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'rpc out', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'sub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'connect timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'collation name', @optvalue=null
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'lazy schema validation', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'query timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'use remote collation', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'EXCELIMPORT', @optname=N'remote proc transaction promotion', @optvalue=N'true'
GO
USE [master]
GO
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname = N'EXCELIMPORT', @locallogin = NULL , @useself = N'False'
GO
It has other data source also like as following,

Now, after creating excel as linked server, we have time to communicate with it and fetch data through linked server & OPENQUERY
 
SELECT
*
FROM ExcelImport...[Sheet1$]

-- OR --

SELECT
*
FROM OPENQUERY(ExcelImport, 'SELECT * FROM [Sheet1$]')
Your comments are appreciated.

Sunday, 2 August 2015

Working with Linked Servers in SQL Server by serverku

We may have a need for some logic to move the data from one server database to another server database on production environment. Also for the distributed transactions or for the cross server database queries we require it.

How to do it? With Linked servers we can perform any distributed transactions between servers. Also, we can execute remote servers stored procedure with linked server communication.

How to create it?
Herewith we have small demo which I have captured during setup it.
  1. Go to the Server Objects --> Linked Servers, Right click on it and click on New Linked Servers.
  2. Specify Server name which you want to linked to current server.
  3. Go to Security tab and select the option for link.
The option details as msdn and book online are following,

Local login : Specify the local login that can connect to the linked server. The local login can be either a login using SQL Server Authentication or a Windows Authentication login. Use this list to restrict the connection to specific logins, or to allow some logins to connect as a different login.

Impersonate : Pass the username and password from the local login to the linked server. For SQL Server Authentication, a login with the exact same name and password must exist on the remote server. For Windows logins, the login must be a valid login on the linked server.

Remote User : Use the remote user to map users not defined in Local login. The Remote User must be a SQL Server Authentication login on the remote server.

Remote Password : Specify the password of the Remote User.

Not be made : Specify that a connection will not be made for logins not defined in the list.

Be made without using a security context : Specify that a connection will be made without using a security context for logins not defined in the list.

Be made using the logins current security context : Specify that a connection will be made using the current security context of the login for logins not defined in the list. If connected to the local server using Windows Authentication, your windows credentials will be used to connect to the remote server. If connected to the local server using SQL Server Authentication, login name and password will be used to connect to the remote server. In this case a login with the exact same name and password must exist on the remote server.

Be made using this security context : Specify that a connection will be made using the login and password specified in the Remote login and With password boxes for logins not defined in the list. The remote login must be a SQL Server Authentication login on the remote server.

We have a some server options like RPC, RPC out as follows


We have seen as how can we create linked server from SSMS. Now we will have script to create linked servers as well.
 USE [master]
GO

EXEC master.dbo.sp_addlinkedserver @server = N'PARESH-PC1', @srvproduct=N'SQL Server'
GO

EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'collation compatible', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'data access', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'dist', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'pub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'rpc', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'rpc out', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'sub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'connect timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'collation name', @optvalue=null
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'lazy schema validation', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'query timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'PARESH-PC1', @optname=N'use remote collation', @optvalue=N'true'
GO

USE [master]
GO
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname = N'PARESH-PC1', @locallogin = NULL , @useself = N'False', @rmtuser = N'dba', @rmtpassword = N'dba@test'
GO

How to get list of linked servers?

SQL Server provide system stored procedure as well system tables , from we can have the details for the same.
  • sp_linkedservers
  • sys.servers
 
-- 1. Fetch the data using tsql

SELECT
*
FROM paresh-pc.demo.dbo.dempteable

-- 2. Running remote stored procedures

EXECUTE paresh-pc.demo.dbo.DemoSP 'test_param'
I hope you enjoyed linked servers. Please share your comments what you are performing with linked servers. 

Sunday, 21 June 2015

PERSISTED Columns with HierarchyId Datatype - SQL Server 2008 by serverku

I think you have already read all the articles related to HierarchyId in the past And hope you understood the concept and usage of it. You also experienced with performance by using HierarchyId datatype as I have posted the performance review in earlier posts as well.

With earlier posts you got some basic methods or functions as how we get the hierarchy data levels, root node, string path. Also, some other methods like as how can we get up-line & down-line modes. Here I am going to present the same, but as Persisted column. So we do not need to write those functions in query level every time. Let us see the workaround for that.

How can ?
We need to create one table and define those columns as a function call as a PERSISTED. We will look the methods to get the hierarchical data without defining columns as Persisted columns and we will use those function calls at the query level. The scripts for the same are as follows.
-- Creating objects
IF (OBJECT_ID('TblHierarchyStructure','U') > 0)
DROP TABLE TblHierarchyStructure
GO

CREATE TABLE TblHierarchyStructure
(
ItemId INT,
ParentItemId INT,
ItemOrder INT,
ItemName VARCHAR(100),
HierarchyNode HIERARCHYID
)

GO

-- Inseting records in tables for the demo
INSERT INTO TblHierarchyStructure
(ItemId,
ParentItemId,
ItemOrder,
ItemName,
HierarchyNode)
SELECT 1,
NULL,
1,
'RootItem',
HierarchyId::Parse('/')
UNION ALL
SELECT 2,
1,
1,
'FirstItem',
HierarchyId::Parse('/1/')
UNION ALL
SELECT 3,
1,
2,
'SecondItem',
HierarchyId::Parse('/2/')
UNION ALL
SELECT 4,
1,
3,
'ThirdItem',
HierarchyId::Parse('/3/')
UNION ALL
SELECT 5,
2,
1,
'FourthItem',
HierarchyId::Parse('/1/1/')
UNION ALL
SELECT 6,
4,
1,
'FifthItem',
HierarchyId::Parse('/3/1/')
UNION ALL
SELECT 7,
5,
1,
'SixthItem',
HierarchyId::Parse('/1/1/1/')
UNION ALL
SELECT 8,
5,
2,
'SeventhItem',
HierarchyId::Parse('/1/1/2/')
UNION ALL
SELECT 9,
5,
3,
'NinthItem',
HierarchyId::Parse('/1/1/3/')
UNION ALL
SELECT 10,
8,
1,
'TenthItem',
HierarchyId::Parse('/1/1/2/1/')

GO

-- Usinf HierarchyId functions at query level and see output.
SELECT *,
HierarchyNode.ToString() AS ItemNodeString,
HierarchyNode.GetLevel() AS ItemNodeLevel,
HierarchyNode.GetAncestor(1) AS ParentNode,
HierarchyNode.GetAncestor(1).ToString() AS ParentNodeString
FROM TblHierarchyStructure
GO

Now we will look the methods to get the hierarchical data with defining columns as Persisted columns and we will use those functions call at the column level. The scripts for the same are as follows.
-- Creating objects
IF (OBJECT_ID('TblHierarchyStructure','U') > 0)
DROP TABLE TblHierarchyStructure
GO

CREATE TABLE TblHierarchyStructure
(
ItemId INT,
ParentItemId INT,
ItemOrder INT,
ItemName VARCHAR(100),
HierarchyNode HIERARCHYID NOT NULL PRIMARY KEY,
ItemNodeString AS HierarchyNode.ToString() PERSISTED,
ItemNodeLevel AS HierarchyNode.GetLevel() PERSISTED,
ParentNode AS HierarchyNode.GetAncestor(1) PERSISTED,
ParentNodeString AS HierarchyNode.GetAncestor(1).ToString()
)

GO

-- Inserting sample records here
INSERT INTO TblHierarchyStructure
(ItemId,
ParentItemId,
ItemOrder,
ItemName,
HierarchyNode)
SELECT 1,
NULL,
1,
'RootItem',
HierarchyId::Parse('/')
UNION ALL
SELECT 2,
1,
1,
'FirstItem',
HierarchyId::Parse('/1/')
UNION ALL
SELECT 3,
1,
2,
'SecondItem',
HierarchyId::Parse('/2/')
UNION ALL
SELECT 4,
1,
3,
'ThirdItem',
HierarchyId::Parse('/3/')
UNION ALL
SELECT 5,
2,
1,
'FourthItem',
HierarchyId::Parse('/1/1/')
UNION ALL
SELECT 6,
4,
1,
'FifthItem',
HierarchyId::Parse('/3/1/')
UNION ALL
SELECT 7,
5,
1,
'SixthItem',
HierarchyId::Parse('/1/1/1/')
UNION ALL
SELECT 8,
5,
2,
'SeventhItem',
HierarchyId::Parse('/1/1/2/')
UNION ALL
SELECT 9,
5,
3,
'NinthItem',
HierarchyId::Parse('/1/1/3/')
UNION ALL
SELECT 10,
8,
1,
'TenthItem',
HierarchyId::Parse('/1/1/2/1/')

GO

-- We have not using HierarchyId functions at query level
-- and using them at columns level as Persisted
SELECT
*
FROM TblHierarchyStructure

GO

I hope you liked this post about Persisted columns with HierarchyID new datatype. Share your experience if you know this type of the usage.

Wednesday, 10 June 2015

Move Node to other place with HierarchyId Data Type - SQL Server 2008 by serverku

After writing some of the posts related HierachyId data type, Finally moving in last topics of HierachyId functions, we will see here the movement of the hierarchy nodes. Let's start from the script to create data for the demo.
-- Create database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
The logical image of hierarchy data are as following,


(Click on image to enlarge)
Now we will move hierarchy nodes and it's down-line from one place to another place.

But how to move?


GetReparentedValue(OldNode, NewNode) : It will move all nodes, including itself and down-line as well to another place. Let's see  what should be new place of the hierarchy id "1" and it's down-line nodes after moving to another place.
-- GetReparentedValue()
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeCurrentPath,
NodePath.GetReparentedValue(HIERARCHYID::Parse('/1/'), HIERARCHYID::Parse('/4/1/')).ToString()
AS NewNodePath,
-- Above line will give new node path of id 1 and it's downline where it will be placed.
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab
WHERE NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) = 1

GO

(Click on image to enlarge)


Now run the below query and move the id 1 and down-line of hierarchy id and then see a logical image from data
UPDATE HierarchyTab
SET NodePath = NodePath.GetReparentedValue(HIERARCHYID::Parse('/1/'), HIERARCHYID::Parse('/4/1/')),
NodeDepth = '4.' + NodeDepth
WHERE NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) = 1

GO

(Click on image to enlarge)

Hope you understood well and get used this feature for the hierarchical data. Please comments how you are using HierarchyId Data Type and their functions.

Tuesday, 9 June 2015

Is Child Node? - With HierarchyId Data Type in SQL Server 2008 by serverku

Various method I have introduced in my earlier posts, like How to get levels of hierarchy nodes, get up-line and down-line of nodes, get string paths of nodes and get next available nodes.

I hope you have read all of them and you liked too. In this post I am presenting how can we know the node is child of particular node or not? Before going ahead to run the script and see the output of hierarchy structure.
-- Create database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
Here is the logical image of the above data are as follows,


(Click on image to enlarge)

So our topic here, Is the node Child ?

IsDescendantOf() : This functions will return 1 if the node is child of given node, return 0 if the node is not child.

Let's run the script with example as who are child of hierarchy node "1".
SELECT 
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetAncestor(1).ToString() AS ParentNode,
NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) IsParent,
-- Above line will return 1 or 0
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab

GO

You can see the above image and check parent node for appropriate child nodes. Hope you like this. Stay tuned for more.


Monday, 8 June 2015

Next Available Node with HierarchyId Data Type - SQL Server 2008 by serverku


I have posted for the some of the functions with examples and demonstrate them in earlier posts. I have written articles of HierarchyId data type overview, some basic functions, even demonstrate for the up-line & down-line of hierarchy nodes which you can read from below links.

Here I will present how can we get the next available node to be planed with HierarchyID data type function. Let's first create hierarchy data structure with the script.
-- Create a database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
Hierarchy data structure as imaged as below.


(Click on image to enlarge)

How to we find it?

GetDescendant() : This function will give next available node where we can place new node.

1. GetDescendant(NULL,NULL) : will return default next left node.
2. GetDescendant(LeftNode,NULL) : will return right node next to left node.
3. GetDescendant(NULL,RighNode) : will return left node previous to left node.

Run the following script and see the output.
-- GetDescendant()
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetDescendant(NULL,NULL).ToString() AS NextDefaultNode,
-- Above line will get default node.
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab

GO


Looking for one hierarchy node id 13,
SELECT 
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetDescendant(NULL,NULL).ToString() AS NextDefaultNode,
NodePath.GetDescendant(HIERARCHYID::Parse('/3/2/1/'),NULL).ToString() AS NextRightNode,
NodePath.GetDescendant(NULL,HIERARCHYID::Parse('/3/2/2/')).ToString() AS NextLeftNode,
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab
WHERE NodeId = 13

GO

(Click on image to enlarge)

This is what I want to share here and hope you like it.


Sunday, 7 June 2015

Up-Line and Down-Line with HierarchyId Data type - SQL Server 2008 by serverku

I have already given HierarchyId datatype overview in my earlier post. Also explained some of HierarchyId functions in post with details as well. Please go through to the overview and some function details which I explained in my previous posts. In this article I am going to demonstrate following items.

1. How to get the up-line nodes?
2. How to get down-line nodes?

Hierarchies functions will give you the answer to all above questions. Let's demonstrate the answers with examples. Before going ahead, I would like to create a hierarchy data structure by following a script.
-- Create database and table
CREATE DATABASE HierarchyDB

GO

USE HierarchyDB

GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab

GO

CREATE TABLE HierarchyTab
(
NodeId INT IDENTITY(1, 1)
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NOT NULL
,NodeDesc VARCHAR(100)
)

GO

-- Creating constraint on hierarchy data type.
ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)

GO
-- Inserting data in above creatd table.
INSERT INTO HierarchyTab(NodeDepth,NodePath,NodeDesc)
VALUES
('1',HIERARCHYID::Parse('/'),'Node-1'),
('1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
('1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
('1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
('1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
('1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
('1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
('1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
('1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9'),
('1.2.2.1.2',HIERARCHYID::Parse('/2/2/1/2/'),'Node-10'),
('1.3',HIERARCHYID::Parse('/3/'),'Node-11'),
('1.3.1',HIERARCHYID::Parse('/3/1/'),'Node-12'),
('1.3.2',HIERARCHYID::Parse('/3/2/'),'Node-13'),
('1.4',HIERARCHYID::Parse('/4/'),'Node-14')

GO
The structure of HierarchyId data looks as follows,

(Clink on image to enlarge)

Now we have time to demonstrate the answers one by one here.

1. How to get the up-line nodes?

GetAncestor(n) : This function will help us to get up-line of the particular hierarchy node. Looking at the script, which will find first up-line node of all hierarchy nodes.
-- GetAncestor(n)
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodePath.GetAncestor(1).ToString() AS ParentNode,
-- Here 1 is used to get 1st u-line node
-- You can use n to get nth up-line node
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab

GO

Please change value to 2 as GetAncestor(n) in place of 1 in script and get the output, You will get more idea.

2. How to get down-line nodes?

IsDescendantOf() : This function will give us the down-line noes of a particular node. Let us run the script and clear our answer. We are viewing a downline node of hierarchy node 1 by script.

-- IsDescendantOf()
SELECT
NodePath.GetLevel() AS NodeLevel,
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeDepth,
NodePath,
NodeDesc
FROM HierarchyTab
WHERE NodePath.IsDescendantOf(HIERARCHYID::Parse('/1/')) = 1
-- Here we have filtered condition to get down-line of node 1.
GO


I hope you are very clear now after demonstrate to how get up-line and down-line of hierarchy node.

Saturday, 6 June 2015

Get Downline and Upline of hierarchical data and Performance review - CTE vs HierarchyId in SQL Server 2008 by serverku

I have already posted for the HierarchyId and CTE (Common Table Expression), also given the comparison review of them for the level and hierarchical order data. I am not saying that HierarchyId is better than CTE or CTE is better then HierarchyId. But it all depends on. You need to practically use them and review the performance of hierarchies and CTE. I am going to show one more demo to find the members with downline and upline.
-- creating database and objects
CREATE DATABASE HierarchyDB
GO

USE HierarchyDB
GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab
GO

CREATE TABLE HierarchyTab
(
NodeId INT NOT NULL
,NodeParent int
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NULL
,NodeLevel as (NodePath.[GetLevel]())
,NodeStringPath as (NodePath.ToString())
,NodeDesc VARCHAR(100)
)
GO

ALTER TABLE HierarchyTab ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)
GO

INSERT INTO HierarchyTab(NodeId,NodeParent,NodeDepth,NodePath,NodeDesc)
VALUES
(1,NULL,'1',HIERARCHYID::Parse('/'),'Node-1'),
(2,1,'1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
(3,2,'1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
(4,2,'1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
(5,1,'1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
(6,5,'1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
(7,5,'1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
(8,7,'1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
(9,8,'1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9')
GO

SELECT
*
FROM HierarchyTab
GO

1. Get down line data using CTE and HierarchyId and compare the execution plan.
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeParent as Perent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
FROM HierarchyTab
WHERE NodeId = 1

UNION ALL

SELECT
NodeId
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeParent = Node )
WHERE [Level] + 1 < =3
)

SELECT
*
FROM cteLevels
WHERE [Level] = 3
GO


-- With HierarchyId
DECLARE @DownlineNode HierarchyId =
(SELECT NodePath FROM HierarchyTab WHERE NodeId = 1)

SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
WHERE NodePath.IsDescendantOf(@DownlineNode) = 1
AND NodeLevel = @DownlineNode.GetLevel() + 3
GO
When you run above script you have data and execution plans as follow,



2. Get up line data using CTE and HierarchyId and compare the execution plan.
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeStringPath as StringPath
,NodeParent as Parent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
FROM HierarchyTab
WHERE NodeId = 9


UNION ALL

SELECT
NodeId
,NodeStringPath as StringPath
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeId = Parent )
WHERE [Level] + 1 < =3
)

SELECT
*
FROM cteLevels
WHERE [Level] = 3
GO


-- With HierarchyId
SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodePath.GetAncestor(3).ToString() as ParentOn4thPos,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
WHERE NodeId = 9
GO
The result set and execution plan when you run above script



Hope you like this and share your experience as comments.

Friday, 5 June 2015

Breath First and Depth First Strategy with Hierarchical Data and Performance review - HierarchyId Data Type vs CTE in SQL Server 2008 by serverku

It is very important to check the performance if we use the new features of alternative methods of SQL Server. I have written earlier posts of the HierarchyId and CTE (Common Table expression). Now in this post you can view the usage of HierarchyId and CTE, comparisons between them. Go ahead with the object creation.
  1. Introduction to HierarchyId data type - Amazing feature of SQL Server 2008
  2. HierarchyId DataType in SQL Server 2008 - Get Level and String Path
-- Creating databse and table
CREATE DATABASE HierarchyDB
GO

USE HierarchyDB
GO

IF ( Object_id('HierarchyTab') > 0 )
DROP TABLE HierarchyTab
GO

CREATE TABLE HierarchyTab
(
NodeId INT NOT NULL
,NodeParent int
,NodeDepth VARCHAR(100) NOT NULL
,NodePath HIERARCHYID NULL
,NodeLevel as (NodePath.[GetLevel]())
,NodeDesc VARCHAR(100)
)
GO

ALTER TABLE HierarchyTab
ADD CONSTRAINT U_NodePath UNIQUE CLUSTERED (NodePath)
GO
Inserting demo records in table created above.
INSERT INTO HierarchyTab(NodeId,NodeParent,NodeDepth,NodePath,NodeDesc)
VALUES
(1,NULL,'1',HIERARCHYID::Parse('/'),'Node-1'),
(2,1,'1.1',HIERARCHYID::Parse('/1/'),'Node-2'),
(3,2,'1.1.1',HIERARCHYID::Parse('/1/1/'),'Node-3'),
(4,2,'1.1.2',HIERARCHYID::Parse('/1/2/'),'Node-4'),
(5,1,'1.2',HIERARCHYID::Parse('/2/'),'Node-5'),
(6,5,'1.2.1',HIERARCHYID::Parse('/2/1/'),'Node-6'),
(7,5,'1.2.2',HIERARCHYID::Parse('/2/2/'),'Node-7'),
(8,7,'1.2.2.1',HIERARCHYID::Parse('/2/2/1/'),'Node-8'),
(9,8,'1.2.2.1.1',HIERARCHYID::Parse('/2/2/1/1/'),'Node-9')
GO
Now we will check the usage  and execution plan of the HierarchyId and CTE.

1. First, we will run the scripts for the to get data level by level order. Let’s start with CTE and the query without using a hierarchy node,
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeParent as Perent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
FROM HierarchyTab
WHERE NodeId = 1


UNION ALL

SELECT
NodeId
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeParent = Node )

)

select
*
from cteLevels
Order by [Level]
GO


Now running the script to get hierarchy data level by level order using HierarchyId,
-- With HierarchyId
SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
Order by NodeLevel

GO


Before going ahead with next script, we are checking the execution plan of both above script,

2. Getting records of hierarchy in hierarchical order with CTE and HierarchyId.
-- Using CTE (Not Using NodePath and NodeLevel)
;WITH cteLevels
AS
(
SELECT
NodeId as Node
,NodeParent as Perent
,NodeDepth as Depth
,0 AS [Level]
,NodeDesc as [Desc]
,CAST(NodeId AS VARCHAR(MAX)) AS [Order]
FROM HierarchyTab
WHERE NodeId = 1


UNION ALL

SELECT
NodeId
,NodeParent
,NodeDepth
,[Level] + 1 AS [Level]
,NodeDesc
,[Order] + '.' + CAST(NodeId AS VARCHAR(MAX)) AS [Order]
FROM HierarchyTab
INNER JOIN cteLevels
ON ( NodeParent = Node )

)

SELECT
*
FROM cteLevels
ORDER BY [Order]

GO

View the result of hierarchical order data with running below query with HierarhyId,
-- With HierarchyId
SELECT
NodePath.ToString() AS NodeStringPath,
NodeId,
NodeParent,
NodeDepth,
NodeLevel,
NodeDesc
FROM HierarchyTab
Order by NodePath

GO

Finally, we go through the performance and the execution plan review of both above scripts,


This is just the details of execution and performance review.

Thursday, 4 June 2015

Example of SET XACT_ABORT ON in SQL Server by serverku

We have seen one error The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION and seen workaround too. There is related to transaction mismatch and now I am writing further same with different error and an issue.

In that post, if I remove one column of used tables in those SPs. What will happen? It will raise an error and keep the transaction open. Let us check.
-- Creating table which will be used in SPs.
CREATE TABLE tbl_Tran
(
TranId INT NOT NULL PRIMARY KEY
,TranName VARCHAR(10)
)

GO

-- Altering first stored procedure here
CREATE PROCEDURE Firttranproc
AS
BEGIN
SET NOCOUNT ON

-- Here we have specified Tran1 as transaction name

BEGIN TRY
BEGIN TRANSACTION

INSERT INTO tbl_Tran
(TranId
,TranName)

SELECT
1
,'Tran-1'
UNION ALL
SELECT
1
,'Tran-1'

COMMIT TRANSACTION
END TRY

BEGIN CATCH
PRINT 'Rollback Tran1'

-- This statement first check open transaction for their session
-- If found then will rollback it.
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
END

GO

-- Altering second stored procedure here
CREATE PROCEDURE Secondtranproc
AS
BEGIN
SET NOCOUNT ON

BEGIN TRY
BEGIN TRANSACTION

-- Inserting records
INSERT INTO tbl_Tran
(TranId
,TranName)
SELECT
2
,'Tran-2'

-- Calling first stored procedure here
EXEC Firttranproc

COMMIT TRANSACTION
END TRY

BEGIN CATCH
PRINT 'Rollback Tran2'


-- This statement first check open transaction for their session
-- If found then will rollback it.
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
END

GO

-- Executing second stored procedure which will also call SP1
EXEC Secondtranproc
GO

-- Droping one column to raise an complite type error
ALTER TABLE tbl_Tran
DROP COLUMN TranName
GO

-- Executing second stored procedure which will also call SP1
EXEC Secondtranproc
GO
Msg 207, Level 16, State 1, Procedure Secondtranproc, Line 13
Invalid column name 'TranName'.
Msg 266, Level 16, State 2, Procedure Secondtranproc, Line 13
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
This will raise a compiled type error and keep transaction open which you can get details using the following script,
USE master
GO

SELECT
est.session_id as [Session ID],
est.transaction_id as [Transaction ID],
tas.name as [Transaction Name],
tds.database_id as [Database ID]
FROM sys.dm_tran_active_transactions tas
INNER JOIN sys.dm_tran_database_transactions tds
ON (tas.transaction_id = tds.transaction_id )
INNER JOIN sys.dm_tran_session_transactions est
ON (est.transaction_id=tas.transaction_id)
WHERE est.is_user_transaction = 1 -- user
AND tas.transaction_state = 2 -- active
AND tas.transaction_begin_time IS NOT NULL
GO
/*
Output :

Session ID Transaction ID Transaction Name Database ID
----------- ---------------- ----------------- -----------
54 176426 user_transaction 19

*/
You can see the open transaction details above. For a fox for such issue we should use SET XACT_ABORT ON in the beginning of the stored procedures. When SET XACT_ABORT is ON, if a Transact-SQL statement raises a run-time error, the entire transaction is terminated and rolled back. So let's change stored procedures,
-- Altering first stored procedure here
ALTER PROCEDURE Firttranproc
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
-- Here we have specified Tran1 as transaction name

BEGIN TRY
BEGIN TRANSACTION

INSERT INTO tbl_Tran
(TranId
,TranName)

SELECT
1
,'Tran-1'
UNION ALL
SELECT
1
,'Tran-1'

COMMIT TRANSACTION
END TRY

BEGIN CATCH
PRINT 'Rollback Tran1'

-- This statement first check open transaction for their session
-- If found then will rollback it.
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
END

GO

-- Altering second stored procedure here
ALTER PROCEDURE Secondtranproc
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON

BEGIN TRY
BEGIN TRANSACTION

-- Inserting records
INSERT INTO tbl_Tran
(TranId
,TranName)
SELECT
2
,'Tran-2'

-- Calling first stored procedure here
EXEC Firttranproc

COMMIT TRANSACTION
END TRY

BEGIN CATCH
PRINT 'Rollback Tran2'


-- This statement first check open transaction for their session
-- If found then will rollback it.
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
END

GO

-- Executing second stored procedure which will also call SP1
EXEC Secondtranproc
GO
It will raise a run-time error, but does not keep the transaction open. Please execute stored procedures and check for open transaction using query provided above. This is just what I want to share with you. I would like to put your comments.

Tuesday, 2 June 2015

script to Rebuild / Reorganize all index for all databases as per fragmentation ratio by serverku

AS a DBA activity, index maintenance is a main activity and here I am sharing a script to rebuild or reorganize indexes as periodically. The way is here to first collect index fragmentation statistics in one table and process further for rebuilding or reorganize. So follow the script below,

1. Create table to collect index fragmentation data
CREATE TABLE [dba].[IndexFragDetails](
[SID] [int] IDENTITY(1,1) NOT NULL,
[TableName] [varchar](500) NULL,
[IndexName] [varchar](500) NULL,
[IndexType] [varchar](100) NULL,
[FragPercent] [int] NULL,
[PageCount] [int] NULL,
[CheckedDate] [datetime] NULL,
[LastRebuiltDate] [datetime] NULL,
[TimeTaken_Min] [int] NULL,
[Result] [int] NOT NULL
) ON [PRIMARY]

GO

ALTER TABLE [dba].[IndexFragDetails] ADD DEFAULT (getdate()) FOR [CheckedDate]
ALTER TABLE [dba].[IndexFragDetails] ADD DEFAULT ((0)) FOR [Result]
GO
2. Capture Index fragmentation statistics
DECLARE @CurrentDate datetime
SET @CurrentDate = GETDATE()

-- Fetching top 100 indexes
;With IndexFragReBuild
As
(
SELECT TOP 100
[object_id] AS objectid,
index_id AS indexid,
partition_number AS partitionnum,
avg_fragmentation_in_percent AS frag,
page_count
FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, N'LIMITED')
WHERE avg_fragmentation_in_percent > 5.0 -- Allow limited fragmentation
AND index_id > 0 -- Ignore heaps
AND page_count > 1000 -- Ignore small tables
ORDER BY avg_fragmentation_in_percent desc
)

-- Inserting in detail table
INSERT INTO dba.IndexFragDetails
(
TableName,
IndexName,
IndexType,
FragPercent,
[PageCount],
CheckedDate
)
SELECT
QUOTENAME(Schema_name(t.schema_id))+'.'+QUOTENAME(object_name(IFD.objectid)) as TableName,
QUOTENAME(i.name) as IndexName,
I.Type_Desc,
IFD.Frag,
IFD.Page_Count,
@CurrentDate
FROM IndexFragReBuild IFD
INNER JOIN sys.indexes i
ON i.object_id = IFD.objectid AND i.index_id = IFD.indexid
INNER JOIN sys.tables t
ON t.object_id = IFD.objectid

-- Removing non-clustered indexes where clustered index exists for same table
DELETE a
FROM dba.IndexFragDetails a
WHERE a.CheckedDate = @CurrentDate
AND a.IndexType = 'NONCLUSTERED'
AND EXISTS
(
SELECT
b.tablename
FROM dba.IndexFragDetails b
WHERE b.CheckedDate = @CurrentDate
AND a.tablename = b.tablename
AND b.IndexType = 'CLUSTERED'
)
GO
3. Rebuilding or Reorganize indexes
SET QUOTED_IDENTIFIER ON

DECLARE @MaxCheckedDate datetime,
@ErrorMessage varchar(4000)

SET @MaxCheckedDate = (SELECT MAX(c.CheckedDate) FROM dba.IndexFragDetails c WITH (NOLOCK))

CREATE TABLE #IndexFragRebuild
(
Seq int identity (1,1),
TableName varchar(100),
IndexName varchar(100),
IndexType varchar(100),
FragPercent int,
PageCount bigint,
LastRebuiltDate varchar(20) default 'NA'
)

INSERT INTO #IndexFragRebuild
(
TableName,
IndexName,
IndexType,
FragPercent,
PageCount,
LastRebuiltDate
)

-- Fetching indexes to be rebuild or reorganize from details table
SELECT
a.TableName,
a.IndexName,
a.IndexType,
a.FragPercent,
a.PageCount,
ISNULL((select CONVERT(varchar(20),MAX(b.LastRebuiltDate),101) FROM dba.IndexFragDetails b WITH (NOLOCK)
WHERE b.TableName=a.TableName AND b.IndexName=a.IndexName ),'NA')
FROM dba.IndexFragDetails a WITH (NOLOCK)
WHERE
a.LastRebuiltDate IS NULL
AND a.CheckedDate = @MaxCheckedDate
AND a.Result = 0
Order by a.FragPercent DESC

DECLARE @Subject VARCHAR (1000)
DECLARE @Body VARCHAR(MAX)
DECLARE @hpart VARCHAR(400)
Declare @ini int,@cnt int,
@sql varchar(500),
@tableName varchar(500),
@indexName varchar(500),
@Startdate datetime,
@EndDate datetime,
@SDate datetime,
@EDate datetime,
@FragPercent int


SET @Subject = 'List of the indexes to be Rebuild\Reorganize'
SET @hpart='TableName

IndexName

IndexType

FragPercent

PageCount

LastRebuiltDate

'

SET @Body =
CAST ( ( SELECT
td = TableName, '',
td = IndexName, '',
td = IndexType, '',
td = FragPercent, '',
td = PageCount, '',
td = LastRebuiltDate, ''
from #IndexFragRebuild
FOR XML PATH('tr'), TYPE
) AS VARCHAR(MAX) ) +
N''

SET @Body = ''+@hpart+@Body+'

' + '
'

-- Sending an email of list of indexes to be rebuild\reorganize
EXEC msdb.dbo.sp_send_dbmail
@recipients = 'prajapatipareshm@gmail.com'
, @subject = @subject
, @body = @Body
, @profile_name = 'ProfileName'
, @body_format = 'HTML' ;


SET @ini = 1
SET @cnt = (select MAX(seq) from #IndexFragRebuild)
SET @SDate = GETDATE()

WHILE(@ini <= @cnt)
BEGIN
BEGIN TRY

SELECT @tableName = TableName,
@indexName = IndexName,
@FragPercent = FragPercent
FROM #IndexFragRebuild
WHERE seq = @ini

IF (@FragPercent > 30)
BEGIN
SET @sql = 'ALTER INDEX '+@indexName+' on ' +@tableName+ ' REBUILD WITH (ONLINE = Off);'
END
ELSE BEGIN
SET @sql = 'ALTER INDEX '+@indexName+' on ' +@tableName+ ' REORGANIZE ;'
END

EXEC (@sql)

Update dba.IndexFragDetails
SET LastRebuiltDate = GETDATE(),
TimeTaken_Min = DATEDIFF(MINUTE,@Startdate,@Enddate),
Result = 1 -- 1-Finish, 2-Failed, 0-Pending
WHERE
LastRebuiltDate IS NULL
AND TableName = @tableName
AND IndexName = @indexName
AND CheckedDate = @MaxCheckedDate


END TRY
BEGIN CATCH
SET @ErrorMessage = ERROR_MESSAGE()

SET @Enddate = GETDATE()

UPDATE dba.IndexFragDetails
SET LastRebuiltDate = NULL,
TimeTaken_Min = DATEDIFF(MINUTE,@Startdate,@Enddate),
Result = 2 -- 1-Finish, 2-Failed, 0-Pending
WHERE
LastRebuiltDate IS NULL
AND TableName = @tableName
AND IndexName = @indexName
AND CheckedDate = @MaxCheckedDate

SET @subject = 'Rebuild\Reorganize error occured : ' + @indexName + ' index on ' + @tableName + ' table '

-- Sending an email of failed operation
EXEC msdb.dbo.sp_send_dbmail
@recipients = 'prajapatipareshm@gmail.com'
, @subject = @subject
, @body = @ErrorMessage
, @profile_name = 'ProfileName'
, @body_format = 'HTML' ;

END CATCH

SET @ini = @ini + 1
WAITFOR DELAY '00:00:05'
END
SET @EDate = GETDATE()

SET @subject = 'All Indexes Rebuilt\Reorganized in '+ cast(DATEDIFF(MINUTE,@Sdate,@EDate) as varchar) + ' Minutes'

-- Sending an email of completion
EXEC msdb.dbo.sp_send_dbmail
@recipients = 'prajapatipareshm@gmail.com'
, @subject = @subject
, @body = @subject
, @profile_name = 'ProfileName'
, @body_format = 'HTML' ;

DROP TABLE #IndexFragRebuild
GO
This is just what i want to share. Please share your opinion.

Sunday, 24 May 2015

MERGE statement - a new tsql feature of SQL Server 2008 by serverku

It will a lengthy and complex coding if we need to perform insert, update and delete statement individually. Instead of writing separate statements for the insert, update and delete operation, we have one more option which can be very helpful in this matter.

Yes, that feature is "Merge" statement and supported in SQL server 2008 or later version. Merge is allow multiple DML operation to perform. That must be ended by semicolon. Let's see the example using Merge statement.
-- Creating Database

CREATE DATABASE MergeDatabase

GO

USE MergeDatabase

GO

-- Creating tables used for merged operation

IF ( Object_id('UsingTable') > 0 )
DROP TABLE UsingTable

GO

CREATE TABLE UsingTable
(
RefId INT IDENTITY(1, 1),
name VARCHAR(100)
)

GO

IF ( Object_id('TargetTable') > 0 )
DROP TABLE TargetTable

GO

CREATE TABLE TargetTable
(
ChildId INT,
val INT
)

GO

-- Inserting records in both tables

INSERT INTO UsingTable(name)
VALUES ('Target-1'),
('Target-2'),
('Target-3'),
('Target-4'),
('Target-5')

GO

INSERT INTO TargetTable(ChildId,val)
VALUES (1,1),
(2,2),
(3,3),
(6,6)

GO
Let us see how Merge statement works.

1. Merge statement with WHEN MATCHED clause and updating records,>
MERGE TargetTable 
USING UsingTable
ON (RefId = ChildId)

WHEN MATCHED THEN
UPDATE set val = val + 5 ;
2. Merge statement with WHEN MATCHED clause and deleting records,
MERGE TargetTable
USING UsingTable
ON (RefId = ChildId)

WHEN MATCHED AND ChildId = 3 THEN
DELETE ;
3. Merge statement with WHEN NOT MATCHED BY TARGET clause and inserting records,
MERGE TargetTable
USING UsingTable
ON (RefId = ChildId)

WHEN NOT MATCHED BY TARGET THEN
INSERT(childId,val)
VALUES(4,4)
;
4. Merge statement with WHEN NOT MATCHED BY SOURCE clause and deleting records,
MERGE TargetTable
USING UsingTable
ON (RefId = ChildId)

WHEN NOT MATCHED BY SOURCE
THEN DELETE;
5. All together at once,
MERGE TargetTable
USING UsingTable
ON (RefId = ChildId)


WHEN MATCHED AND ChildId = 3 THEN
DELETE

WHEN MATCHED THEN
UPDATE set val = val + 5

WHEN NOT MATCHED BY TARGET THEN
INSERT(childId,val)
VALUES(4,4)

WHEN NOT MATCHED BY SOURCE
THEN DELETE;
Let's see the result set of TargetTable before and after the Merge statement used.

Before Merge statement ran,


After Merge statement ran,


6. Using OUTPUT with Merge statement,
MERGE TargetTable
USING UsingTable
ON (RefId = ChildId)


WHEN MATCHED AND ChildId = 3 THEN
DELETE

WHEN MATCHED THEN
UPDATE set val = val + 5

WHEN NOT MATCHED BY TARGET THEN
INSERT(childId,val)
VALUES(4,4)

WHEN NOT MATCHED BY SOURCE
THEN DELETE

OUTPUT

$action,
INSERTED.ChildId,
INSERTED.Val,
DELETED.childId,
DELETED.val
;

Hope you have already started to use Merge statement.

Tuesday, 19 May 2015

Find active open transactions from a SQL Server Instance by serverku

I would like to share the script to find open active transactions and sessions at database level, here you can find the same which capture for active transactions initiated by users only. This will result for all the databases, but you can make a database filter as well. This result is most relatively as DBCC OPENTRAN check in a particular database.
USE master
GO

SELECT
est.session_id as [Session ID],
est.transaction_id as [Transaction ID],
tas.name as [Transaction Name],
tds.database_id as [Database ID]
FROM sys.dm_tran_active_transactions tas
INNER JOIN sys.dm_tran_database_transactions tds
ON (tas.transaction_id = tds.transaction_id )
INNER JOIN sys.dm_tran_session_transactions est
ON (est.transaction_id=tas.transaction_id)
WHERE est.is_user_transaction = 1 -- user
AND tas.transaction_state = 2 -- active
AND tas.transaction_begin_time IS NOT NULL
GO

You can see the conditions made in this script and the values for the same are as following,

is_user_transaction
  • 1 = The transaction was initiated by a user request.
  • 0 = System transaction
transaction_state
  • 0 = The transaction has not been completely initialized yet.
  • 1 = The transaction has been initialized but has not started.
  • 2 = The transaction is active.
  • 3 = The transaction has ended. This is used for read-only transactions.
  • 4 = The commit process has been initiated on the distributed transaction. This is for distributed transactions only. The distributed transaction is still active but further processing cannot take place.
  • 5 = The transaction is in a prepared state and waiting resolution.
  • 6 = The transaction has been committed.
  • 7 = The transaction is being rolled back.
  • 8 = The transaction has been rolled back.is_user_transaction

Tuesday, 12 May 2015

Download all files through FTP in SQL Server by serverku

We have completed the reading of earlier methods to receive single file and receive file dynamically through the FTP in SSIS. Hope you like those posts. Previously we have downloaded a file which has specific extension with format of current date. The same thing and criteria will be repeated again, but one thing is different and it's not downloading a single file and not the specific current date format. It's a download of multiple files which are having an extension with current year like you can see in below shot.

1. Here you can see the files from FTP, among them we will download files meet with the criteria. You can see target files.


2. Drag and drop FTP Task as shown on screen


3. Choose new FTP connection option for to connect FTP and apply FTP configuration as shown below.


4. Here we want to download files, so we need to go ahead with 'Receive' operation.


5. Configure Local Folder for file to be downloaded.


6. Create one variable for the Remote Path for the FTP and assign the value of the FTP directory from where the file will be received.


7. Here we haven't given static value of Remote Path and file name, but the qualified name will be created dynamically. So apply expression for the Remote Path option like following.


8. Finally, execute the created SSIS package and you will see the files received in the local folder. You can see the target files received.


What is the next? Stay tuned for the next posts.

Monday, 11 May 2015

Download multiple files through the FTP in SQL Server - SSIS by serverku

We have completed the reading of earlier methods to receive single file and receive file dynamically through the FTP in SSIS. Hope you like those posts. Previously we have downloaded a file which has specific extension with format of current date.

The same thing and criteria will be repeated again, but one thing is different and it's not downloading a single file and not the specific current date format. It's a download of multiple files which are having an extension with current year like you can see in below shot.

1. Here you can see the files from FTP, among them we will download files meet with the criteria. You can see target files.


2. Drag and drop FTP Task as shown on screen.


3. Choose new FTP connection option for to connect FTP and apply FTP configuration as shown below.


4. Here we want to download files, so we need to go ahead with 'Receive' operation.


5. Configure Local Folder for file to be downloaded.


6. Create one variable for the Remote Path for the FTP and assign the value of the FTP directory from where the file will be received.


7. Here we haven't given static value of Remote Path and file name, but the qualified name will be created dynamically. So apply expression for the Remote Path option like following.


8. Finally, execute the created ssis package and you will see the files received in the local folder. You can see the target files received.


What is the next? Stay tuned for the next posts.

Download file having specific extension in SQL Server - SSIS by serverku

Earlier we have seen some of the methods to download single file from FTP using tsql scripts and SSIS. So it's the same with single file, but a little bit different as we will download the file which have specific extension. Like we will have to receive the files which have extension of specific format of current date.

1. Here you can see the files from FTP, among them we will download one file. You can see target file.


2. Drag and drop FTP Task as shown in screen.


3. Choose new FTP connection option for to connect FTP and apply FTP configuration as shown below.


4. Here we want to download files, so we need to go ahead with 'Receive' operation.


5. Configure Local Folder for file to be downloaded.


6. Create one variable for the Remote Path for the FTP and assign the value of the FTP directory from where the file will be received.


7. Here we haven't given static value of Remote Path and file name, but the qualified name will be created dynamically. So apply expression for the Remote Path option like following.



8. Finally, execute the created ssis package and you will see the file received in the local folder. You can see the target file received in local directory.

Just it. Please share your ideas and comments here. Hope you enjoyed this post. Stay tuned for more posts.