Included Column Index with non clustered index in SQL Server 2005/2008/Denali

My previous article was based on “Nonclustered Index” and this article focuses on “Included Column” feature ofIndex which was introduced in SQL Server 2005 and still there in newer version.
As long as architecture of “Included Column” concerns,whatever columns you have defined under “Include” clause under the index, thosewill be stored on the Leaf pages, it won’t get stored on the Root page orIntermediate page of the index.
Now, let us talk little bit about the benefit we are goingto get out of this feature.
The main feature is that, the columns you have under “Include”clause of “Create Index” statement would not affect the size of the index.Index has limitation that you can have it on maximum of 16 column / 900 bytes. Sono matter how big columns you are going to use in your “Include”, you will getbenefit for sure.
You should keep those columns in “Include” clause whichgenerally comes under “SELECT” clause and not being used much in “WHERE”, “GROUPBY” or “ON” clause of “JOIN”.
We are going to create one table with big column size andwill try to create Non Clustered Index on that.
–create dummytable and see whether we are able to create index on that or not.
create table NonClustCheck
(
Col1 char(500),
col2 char(400),
col3 char(10)
)
GO
–if you willtry following idex, you will greeted with error message as follows:
–Msg 1944, Level 16, State 1, Line 1
–Index ‘idx_NonClustCheck’ was not created. This index has akey length of at least 910 bytes.
–The maximum permissible key length is 900 bytes.
create nonclustered indexidx_NonClustCheck on NonClustCheck(col1,col2,col3)
go
–you can createfollowing index on the same table
create nonclustered indexidx_NonClustCheck1 on NonClustCheck(col1)
INCLUDE(col2,col3)
go
we finally we have created non clustered index with one keycolumn (Col1) and two included column (Col2 and Col3). Let us check whetherthis index gets scan or seek or optimizer decides not to use this.
–well there isno data in this table yet,
–even justwanted to see whether Non clustered index is having any effect or not.
–run followingquery with execution plan and you can see Index Seek
— Ctrl + M andthan F5 to run query with execution plan.
select * from NonClustCheck where Col1=‘SQLHub.Com’
GO
–you can seeCol2 is not as the Index Key,
–even you cansee that our non clustered index is getting SCAN.
select * from NonClustCheck where Col2=‘hello’
GO
Hope this will be helpful to you. Do drop comments; it willencourage me for sure.

if you want to refer all other articles related to index, click here.
Reference: Ritesh Shah
http://www.sqlhub.com
Note: Microsoft Books online is adefault reference of all articles but examples and explanations prepared byRitesh Shah, founder of
http://www.SQLHub.com
Ask me any SQL Server related question at my “ASK Profile
Advertisements

Included Column Index with non clustered index in SQL Server 2005/2008/Denali

My previous article was based on “Nonclustered Index” and this article focuses on “Included Column” feature ofIndex which was introduced in SQL Server 2005 and still there in newer version.
As long as architecture of “Included Column” concerns,whatever columns you have defined under “Include” clause under the index, thosewill be stored on the Leaf pages, it won’t get stored on the Root page orIntermediate page of the index.
Now, let us talk little bit about the benefit we are goingto get out of this feature.
The main feature is that, the columns you have under “Include”clause of “Create Index” statement would not affect the size of the index.Index has limitation that you can have it on maximum of 16 column / 900 bytes. Sono matter how big columns you are going to use in your “Include”, you will getbenefit for sure.
You should keep those columns in “Include” clause whichgenerally comes under “SELECT” clause and not being used much in “WHERE”, “GROUPBY” or “ON” clause of “JOIN”.
We are going to create one table with big column size andwill try to create Non Clustered Index on that.
–create dummytable and see whether we are able to create index on that or not.
create table NonClustCheck
(
Col1 char(500),
col2 char(400),
col3 char(10)
)
GO
–if you willtry following idex, you will greeted with error message as follows:
–Msg 1944, Level 16, State 1, Line 1
–Index ‘idx_NonClustCheck’ was not created. This index has akey length of at least 910 bytes.
–The maximum permissible key length is 900 bytes.
create nonclustered indexidx_NonClustCheck on NonClustCheck(col1,col2,col3)
go
–you can createfollowing index on the same table
create nonclustered indexidx_NonClustCheck1 on NonClustCheck(col1)
INCLUDE(col2,col3)
go
we finally we have created non clustered index with one keycolumn (Col1) and two included column (Col2 and Col3). Let us check whetherthis index gets scan or seek or optimizer decides not to use this.
–well there isno data in this table yet,
–even justwanted to see whether Non clustered index is having any effect or not.
–run followingquery with execution plan and you can see Index Seek
— Ctrl + M andthan F5 to run query with execution plan.
select * from NonClustCheck where Col1=‘SQLHub.Com’
GO
–you can seeCol2 is not as the Index Key,
–even you cansee that our non clustered index is getting SCAN.
select * from NonClustCheck where Col2=‘hello’
GO
Hope this will be helpful to you. Do drop comments; it willencourage me for sure.

if you want to refer all other articles related to index, click here.
Reference: Ritesh Shah
http://www.sqlhub.com
Note: Microsoft Books online is adefault reference of all articles but examples and explanations prepared byRitesh Shah, founder of
http://www.SQLHub.com
Ask me any SQL Server related question at my “ASK Profile

Nonclustered Index in SQL Server

After finishing two articles on Index “Somebasics about Index in SQL Server” and “ClusteredIndex in SQL Server”, this is now time to explore nonclustered index in SQLServer. There are only two major difference between Clustered and NonclusteredIndex are as follows:
1.)    Youcan have only one Clustered index per table and 249 (till SQL Server 2005) and999 (after SQL Server 2005) Nonclustered Index per table
2.)    ClusteredIndex stores actual row data in the leaf level and nonclustered index wouldstores only those columns which are included in nonclustered index and pointerto the actual row which may be in clustered index or in heap (know more aboutleaf, heap, RID etc. from here).
Like clustered index, you can have one or multiple columnsdefined in Nonclustered Index too. Order of the column defined in Nonclusteredplays an important role to meet Index seek, as I told you in previous articletoo that Index seek is good than Index Scan, so one should try to meet up Indexseek as long as possible.
We are going to use same database and “Orders” table definedin previous article “ClusteredIndex in SQL Server”.
–creatingnonclustered index,
CREATE NONCLUSTERED INDEXidx_orderdate on Orders(orderdate,orderid)
–run followingquery with execution plan and see the results in execution plan
–you can seeexecution plan with the following steps
–first selectbelow given query
–Press Ctrl+M
–press F5
SELECT OrderDate,orderid from orders where OrderDate = ‘2010-02-01 00:04:00.000’
You will see your nonClustered Index Seek in your executionplan.
 
There are few more indexes under the same category likeINCLUDE columns, Filtered Index, Covering Index which we will be exploring verysoon.

if you want to refer all other articles related to index, click here.
Reference: Ritesh Shah
http://www.sqlhub.com
Note: Microsoft Books online is a defaultreference of all articles but examples and explanations prepared by RiteshShah, founder of http://www.SQLHub.com
Ask me any SQL Server related question at my “ASK Profile

Clustered Index in SQL Server

As per the promise I have made in “Somebasics about Index in SQL Server” article, I am coming up with each Indexesof SQL Server and first and most important is “Clustered Index”. 
Understanding ofClustered Index:
As I told in previous article too that Index is the key ofperformance, good and managed Index could boost up your speed of retrieving ofdata from table.
Clustered Index contains actual data of the table in leaflevel pages in logically sorted order (to understand root and leaf level page, clickhere). Since it is logically sorted, it doesn’t need to go for all datapresent there in Index. 
For example if you are looking for the phone number of “RiteshShah” in telephone directory, you can move to a page which has phone number of theperson whose first name starts with “RI” and once all instance of “RI” over indirectory, you don’t need to move on as there is NO CHANCE, you will get “Ritesh”anywhere in directory except those particular pages.
BTW, if you don’t have clustered index on your table, thanyour table would be called “HEAP”,which wouldn’t not have logically sorted data so if you are searching for “RiteshShah”, you can to check complete table as you never know, where you will find “RiteshShah”, just one method, go each and every row of table and check for matchingcriteria.
Like any other columns, you can define clustered index inmore than one field too and all the columns covered up under the index, calledkey column.
While choosing a prime candidate for Clustered Index columnin your table, you have to select the columns which meet few of the generalcriteria defined below. (you can say following criteria as a best practicewhile choosing index candidate)
–> You keycolumn or combination of key columns should be unique and not null. If your Youkey column or combination of key columns are not unique than SQL Server has toadd one more hidden column of 4-byte INT to make it unique. However, you can’tsee that hidden column neither can query it directly; it would be purely forSQL Server’s internal use.
–> It shouldbe short as wide key value would increase the depth of Clustered Index and willreduce the performance a bit and also increase the size of non-clustered indexas it is being there as a reference in all non-clustered index.
–> Selectless changing or no changing fields for you clustered index as Key valueindicates the location of page where actual data resides, if you change thiskey value, row has to be deleted from that page and has to move to anotherappropriate page which reduces the performance and increase unnecessaryoverhead to IO.
Generally whenever you make Primary Key in any of yourtable, SQL Server itself create clustered index on it but if you want to keepclustered index on any other column(s) due to high selectivity on thosecolumn(s), you can do it.
Have you got bored of so long theory? Let us do some practicaland check it out?
–create onedatabase which you can delete after running this example
create database SQLHub
GO
USE SQLHub
GO
–if orderstable is already there. you can delete it than create new one with name”Orders”
IF OBJECT_ID(‘orders’, ‘U’) IS NOT NULL BEGIN
      DROP TABLEorders
END
GO
–creating table
CREATE TABLE orders (OrderID INT IDENTITY, OrderDate DATETIME, Amount MONEY, Refno INT)
GO
–inserting100000 fack rows into table (BTW, thank to Jacob Sebastian, copy in INSERTscript from one of his demo to generate big table)
INSERT INTO orders (OrderDate, Amount, Refno)
SELECT TOP 100000
      DATEADD(minute, ABS(a.object_id % 50000 ), CAST(‘2010-02-01’ AS DATETIME)),
      ABS(a.object_id % 10),
      CAST(ABS(a.object_id) AS VARCHAR)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
GO
–creatingclustered index, however it is not meeting the criteria I have
–given inarticle, but this is ok for example
CREATE CLUSTERED INDEXidx_refno ON orders(refno)
GO
–run both ofthe following query with execution plan and see the results in execution plan
–you can seeexecution plan with the following steps
–first selectboth of the below given query
–Press Ctrl+M
–press F5
SELECT TOP 10 * from orders whererefno=4
SELECT TOP 10 * from orders whereOrderDate = ‘2010-02-0100:04:00.000’
GO
–if you wish,you can uncomment below code and delete SQLHub database
–use master
–go
–drop databasesqlhub
If you execute both the query with “Actual Execution Plan”,you will see first query is having Index Seek and second query is having IndexScan. 
Seek and Scan is really interesting topic which I will coverlater but just keep in mind that, Seek is good, Scan is bad as it will checkall records of the index.
if you want to refer all other articles related to index, click here.

Reference: Ritesh Shah
http://www.sqlhub.com
Note: Microsoft Books online is a defaultreference of all articles but examples and explanations prepared by RiteshShah, founder of http://www.SQLHub.com
Ask me any SQL Server related question at my “ASK Profile

Clustered Index in SQL Server

As per the promise I have made in “Somebasics about Index in SQL Server” article, I am coming up with each Indexesof SQL Server and first and most important is “Clustered Index”. 
Understanding ofClustered Index:
As I told in previous article too that Index is the key ofperformance, good and managed Index could boost up your speed of retrieving ofdata from table.
Clustered Index contains actual data of the table in leaflevel pages in logically sorted order (to understand root and leaf level page, clickhere). Since it is logically sorted, it doesn’t need to go for all datapresent there in Index. 
For example if you are looking for the phone number of “RiteshShah” in telephone directory, you can move to a page which has phone number of theperson whose first name starts with “RI” and once all instance of “RI” over indirectory, you don’t need to move on as there is NO CHANCE, you will get “Ritesh”anywhere in directory except those particular pages.
BTW, if you don’t have clustered index on your table, thanyour table would be called “HEAP”,which wouldn’t not have logically sorted data so if you are searching for “RiteshShah”, you can to check complete table as you never know, where you will find “RiteshShah”, just one method, go each and every row of table and check for matchingcriteria.
Like any other columns, you can define clustered index inmore than one field too and all the columns covered up under the index, calledkey column.
While choosing a prime candidate for Clustered Index columnin your table, you have to select the columns which meet few of the generalcriteria defined below. (you can say following criteria as a best practicewhile choosing index candidate)
–> You keycolumn or combination of key columns should be unique and not null. If your Youkey column or combination of key columns are not unique than SQL Server has toadd one more hidden column of 4-byte INT to make it unique. However, you can’tsee that hidden column neither can query it directly; it would be purely forSQL Server’s internal use.
–> It shouldbe short as wide key value would increase the depth of Clustered Index and willreduce the performance a bit and also increase the size of non-clustered indexas it is being there as a reference in all non-clustered index.
–> Selectless changing or no changing fields for you clustered index as Key valueindicates the location of page where actual data resides, if you change thiskey value, row has to be deleted from that page and has to move to anotherappropriate page which reduces the performance and increase unnecessaryoverhead to IO.
Generally whenever you make Primary Key in any of yourtable, SQL Server itself create clustered index on it but if you want to keepclustered index on any other column(s) due to high selectivity on thosecolumn(s), you can do it.
Have you got bored of so long theory? Let us do some practicaland check it out?
–create onedatabase which you can delete after running this example
create database SQLHub
GO
USE SQLHub
GO
–if orderstable is already there. you can delete it than create new one with name”Orders”
IF OBJECT_ID(‘orders’, ‘U’) IS NOT NULL BEGIN
      DROP TABLEorders
END
GO
–creating table
CREATE TABLE orders (OrderID INT IDENTITY, OrderDate DATETIME, Amount MONEY, Refno INT)
GO
–inserting100000 fack rows into table (BTW, thank to Jacob Sebastian, copy in INSERTscript from one of his demo to generate big table)
INSERT INTO orders (OrderDate, Amount, Refno)
SELECT TOP 100000
      DATEADD(minute, ABS(a.object_id % 50000 ), CAST(‘2010-02-01’ AS DATETIME)),
      ABS(a.object_id % 10),
      CAST(ABS(a.object_id) AS VARCHAR)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
GO
–creatingclustered index, however it is not meeting the criteria I have
–given inarticle, but this is ok for example
CREATE CLUSTERED INDEXidx_refno ON orders(refno)
GO
–run both ofthe following query with execution plan and see the results in execution plan
–you can seeexecution plan with the following steps
–first selectboth of the below given query
–Press Ctrl+M
–press F5
SELECT TOP 10 * from orders whererefno=4
SELECT TOP 10 * from orders whereOrderDate = ‘2010-02-0100:04:00.000’
GO
–if you wish,you can uncomment below code and delete SQLHub database
–use master
–go
–drop databasesqlhub
If you execute both the query with “Actual Execution Plan”,you will see first query is having Index Seek and second query is having IndexScan. 
Seek and Scan is really interesting topic which I will coverlater but just keep in mind that, Seek is good, Scan is bad as it will checkall records of the index.
if you want to refer all other articles related to index, click here.

Reference: Ritesh Shah
http://www.sqlhub.com
Note: Microsoft Books online is a defaultreference of all articles but examples and explanations prepared by RiteshShah, founder of http://www.SQLHub.com
Ask me any SQL Server related question at my “ASK Profile

sys.dm_db_index_usage_stats and sys.dm_db_index_operational_stats in SQL Server 2005

sys.dm_db_index_usage_stats  and  sys.dm_db_index_operational_stats in SQL Server 2005

Today I am happy to introduced one more article written by Mr. Mark Will regarding two very useful data management views  (DMV) in SQL Server 2005. I am sure every reader of my blog will be happy to read it as the information provided in the article could become very useful asset for everybody who are using SQL Server 2005.

Introducing SQL 2005 Two Data Management Views

by Mark Wills

In SQL 2005, new Data Management Views were introduced known as DMV’s.

I have recently been involved in some discussions as to what these views really do, and thought I might share some relatively light hearted discussion.

At first glance they give information which doesn’t reconcile. And that is why they are different, it is more the differences that become important rather than they don’t match.

They actually tell us different pieces of the index puzzle, and collectively, are very telling about your index designs (which will be a different post).

The views are :

sys.dm_db_index_usage_stats  and  sys.dm_db_index_operational_stats

sys.dm_db_index_usage_stats only reports on indexes that have been used at least once since the server was last restarted and updated each time the plan is executed.

sys.dm_db_index_operational_stats reports on all indexes regardless of whether they have been used and records how many times the storage engine executes a specific operation on the index.

The way I remember how to use them is by name (duh), as in “is my index useful” then usage, “is my index operating efficiently” then operational.

Let’s create a couple of test tables for this purpose :

— first a ‘heap’ table ie one with no PK or clustered index

IF object_id(‘tst_tbl_heap’,‘U’) is not null drop table tst_tbl_heap

CREATE TABLE tst_tbl_heap (ID int not null, Firstname varchar(60), SecurityNumber varchar(20))

go

— now a ‘clustered’ table

IF object_id(‘tst_tbl_indexes’,‘U’) is not null drop table tst_tbl_indexes

CREATE TABLE tst_tbl_indexes (ID int not null, Firstname varchar(60), SecurityNumber varchar(20))

ALTER TABLE tst_tbl_indexes ADD CONSTRAINT PK_tst_tbl_id PRIMARY KEY CLUSTERED(ID)

CREATE UNIQUE INDEX idx_tst_tbl_SN ON tst_tbl_indexes(SecurityNumber)

go

–OK, now lets see what we have :

select object_name(s.object_id),i.name, *

from sys.dm_db_index_usage_stats s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

— we get nothing – and why not ? remember the name ? we haven’t used any indexes yet…

— but…

select object_name(s.object_id),i.name, *

from sys.dm_db_index_operational_stats (db_id(),0,NULL,0) s            — note : this has parameters

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

 

— does show us our two indexes, and even shows us a row for our table without an index.

— So, it is not just for indexes huh !

— now, remember our name ? operational – but are they working, no, the counts are zero.

— lets now add some data…

INSERT tst_tbl_heap(ID,FirstName,SecurityNumber) values (1,‘Mark’,‘1234567’)

INSERT tst_tbl_indexes(ID,FirstName,SecurityNumber) values (1,‘Mark’,‘1234567’)

— now lets look again at our DMV’s

 

select object_name(s.object_id),i.name, *

from sys.dm_db_index_usage_stats s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

— Now we get something ! despite being called indexes, not just for indexes.

— Similarly for below we also return information

select object_name(s.object_id),i.name, *

from sys.dm_db_index_operational_stats (db_id(),0,NULL,0) s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

— We wont bother anymore about the heap table, you can play with that

— the lesson was that even a table without indexes is getting in there.

— Now lets do an insert

 

INSERT tst_tbl_indexes(ID,FirstName,SecurityNumber)

SELECT 2 as id,‘Mark2’ as firstname,‘2234567’ as SecurityNumber union all

SELECT 3 as id,‘Mark3’ as firstname,‘3234567’ as SecurityNumber

 

— now lets look again at our DMV’s

select object_name(s.object_id),i.name, *

from sys.dm_db_index_usage_stats s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

select object_name(s.object_id),i.name, *

from sys.dm_db_index_operational_stats (db_id(),0,NULL,0) s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

 

— So, what happens with a select ?

SELECT * from tst_tbl_indexes where ID = 2

— now lets look again at our DMV’s

select object_name(s.object_id),i.name, *

from sys.dm_db_index_usage_stats s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

— we get user_seeks=1

select object_name(s.object_id),i.name, *

from sys.dm_db_index_operational_stats (db_id(),0,NULL,0) s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

— singleton_lookups=1

— so let’s now try another query

 

SELECT * from tst_tbl_indexes where ID in (1,2,3)

go

 

select object_name(s.object_id),i.name, *

from sys.dm_db_index_usage_stats s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

— we get user_seeks=2

select object_name(s.object_id),i.name, *

from sys.dm_db_index_operational_stats (db_id(),0,NULL,0) s

inner join sys.indexes i on s.object_id=i.object_id and i.index_id=s.index_id

where object_name(s.object_id) in (‘tst_tbl_indexes’,‘tst_tbl_heap’)

go

— singleton_lookups=4 on our PK, nothing on the second (1 per selected rows above)

— and finally clean up those tables

IF object_id(‘tst_tbl_heap’,‘U’) is not null drop table tst_tbl_heap

IF object_id(‘tst_tbl_indexes’,‘U’) is not null drop table tst_tbl_indexes

 

Now I am not going to go into detail – there are plenty of other postings out there

but what I am going to do is to summarise how we can use these differences

sys.dm_db_index_usage_stats

Is cleared when service starts / reboots etc. So keep that firmly in mind

Good to help identify if an index is used – no entry unless it is

Good to help identify if a table is used – no entry unless it is

Has handy dates can help show when types of activity were last used

Has counts to help identify frequency of use

If machine has been up for the entire period that encapsulates all usage then can highlight unused indexes and consider their removal

 

sys.dm_db_index_operational_stats

Exists when table / indexes are created

Shows volumes of activity (plan, rows, pages), not just an instance

Far more detailed to help identify what type of activity

Shows row_lock_wait_count – indicating lock contention

Can measure the cost of having an index, or missing one

 

Lets look at the different types of activity that is going to be recorded:

 

SQL Statement   Read Write

Select          Yes  No

Insert          No   Yes on all indexes

Update          Yes  Yes if row affects the index

Delete          Yes  Yes

 

Armed with the above table, looking at DMV’s activity, you can soon work out where possible areas of further inspection are. Now go read books on line for a more detailed understanding of what each column is saying…

 

http://msdn.microsoft.com/en-us/library/ms188755.aspx

http://msdn.microsoft.com/en-us/library/ms174281.aspx

Reference: Ritesh Shah
http://www.sqlhub.com
Note: Microsoft Books online is a default reference of all articles but examples and explanations prepared by Ritesh Shah, founder of http://www.SQLHub.com

Where is my Open Package option of DTS in SSMS?

If you are working with SQL Server 7.0 or SQL Server 2000 since long and jump into SQL Server 2005 and/or 2008 suddenly you may be finding the option to open DTS package of SQL Server 2000 in SSMS. It was very easy from Enterprise manager of SQL Server2000. You can expand “Data Transformation Service” table under the “Database” tab, right click on that and click on “Open Package “ command. Have a look at screen capture.

But you will not be able to see “Data Transformation Service” command in SSMS directly. Isn’t it there? Answer is, it is there but at other location.

 Expand the Management object.

 Open the Legacy object.

 Right click on Data Transformation Services.

 Click on “Open Package File”

Have a look at screen capture:

However, you can open old .DTS packages in SSMS but it is better to upgrade it to .DTSX package. There is one nice article at simple-talk.com. Have a look.

http://www.simple-talk.com/sql/sql-server-2005/dts-to-ssismigration/

 

Reference: Ritesh Shah
http://www.sqlhub.com
Note: Microsoft Books online is a default reference of all articles but examples and explanations prepared by Ritesh Shah, founder of
http://www.SQLHub.com