Interview Question of the Week #043 – What is the Difference Between EXCEPT operator vs. NOT IN

Question: What is the Difference Between EXCEPT operator vs. NOT IN

Answer: The EXCEPT operator returns all of the distinct rows from the query to the left of the EXCEPT operator when there are no matching rows in the right query. The EXCEPT operator is equivalent of the Left Anti Semi Join. EXCEPT operator works the same way NOT IN. EXCEPTS returns any distinct values from the query to the left of the EXCEPT operand that do not also return from the right query.

Example of EXCEPT operator is displayed along Example of NOT IN. If you run both of the Query and compare the Execution plan it is exactly the same. EXCEPT and NOT IN does same functions and have the same execution plan, but EXCEPT has much simpler syntax. The row-by-row comparison provided by EXCEPT, combined with the number of rows being returned remaining consistent, provides compelling evidence that re-factored query is correct. EXCEPT works with * as well as aliases.

Example: (Both of the scripts returns the same number of rows)
-- SQL SERVER 2005 Method
USE AdventureWorks;
GO
SELECT ProductID
FROM Production.Product
EXCEPT
SELECT
ProductID
FROM Production.WorkOrder ;
GO

-- SQL SERVER 2000 Method which works IN SQL SERVER 2005
USE AdventureWorks;
GO
SELECT ProductID
FROM Production.Product
WHERE ProductID
NOT IN (
SELECT ProductID
FROM Production.WorkOrder);
GO

Following the execution plan which indicates EXCEPT and NOT IN uses same execution plan. Click on below image to see larger image.
SQLSERVER EXCEPT ExecPlan small SQL SERVER 2005 Comparison EXCEPT operator vs. NOT IN

Reference: Pinal Dave (http://blog.sqlauthority.com)

Interview Question of the Week #041 – What is a Merge Join?

Question: What is a Merge Join?

Answer: The Merge Join transformation provides an output that is generated by joining two sorted data sets using a FULL, LEFT, or INNER join. The Merge Join transformation requires that both inputs be sorted and that the joined columns have matching meta-data. The user cannot join a column that has a numeric data type with a column that has a character data type. If the data have a string data type, the length of the column in the second input must be less than or equal to the length of the column in the first input with which it is merged.

USE pubs
GO
SELECT a.au_id
FROM authors a
JOIN titleauthor b ON a.au_id = b.au_id
OPTION (MERGE JOIN)
GO

Merge join itself is very fast, but it can be an expensive choice if sort operations are required. However, if the data volume is large and the desired data can be obtained presorted from existing B-tree indexes, merge join is often the fastest available join algorithm. Because the query optimizer usually selects the best execution plan for a given select statement, it is not necessary to enforce the desirable join type, but sometimes it can be useful. User can enforce the desirable join type by using the OPTION clause.

Following query will be benefited by MERGE JOIN because authors and titleauthor both has a primary key index on au_id. Due to Primary Key on au_id it is physically sorted by au_id.

Reference: Pinal Dave (http://blog.sqlauthority.com)

SQL SERVER – Why Should You Not to Use Old Style JOIN?

If you want to JOIN two tables you can do it in two ways. One using ANSI compliant INNER JOIN and another with old style join

But it is advisable not to use old style join. Here is the reason.

Let us create the following data sets

CREATE TABLE item_master(item_id INT, item_description VARCHAR(100));
CREATE TABLE item_sales(item_id INT, sales_date DATETIME, qty INT);
INSERT INTO item_master
SELECT 1,'Samsung' UNION ALL
SELECT 2,'LG';
INSERT INTO item_sales
SELECT 1,'2015-04-03 12:10:10',2 UNION ALL
SELECT 2,'2015-06-11 07:22:00',3 UNION ALL
SELECT 2,'2015-06-12 11:00:48',22;

If you want to show each item_item description with total quantity that are sold, you can use this code

Method 1 : INNER JOIN

SELECT item.item_description,SUM(details.qty) AS qty FROM item_master AS item
INNER JOIN item_sales AS details
ON item.item_id=details.item_id
GROUP BY item.item_description;

Method2 : Old style join with WHERE clause

SELECT item.item_description,SUM(details.qty) AS qty FROM item_master AS item
,item_sales AS details
WHERE item.item_id=details.item_id
GROUP BY item.item_description;

Both returns the following result

item_description qty
 ------------------- ----------
 LG 25
 Samsung 2

But what happens if you omit the WHERE condtion in method 2 by mistake

SELECT item.item_description,SUM(details.qty) AS qty FROM item_master AS item
,item_sales AS details
GROUP BY item.item_description;

The result is

item_description qty
 ------------------- ----------
 LG 27
 Samsung 27

Which is totally wrong as it leads to cross join

But method 1 will throw error if no JOIN is specified

SELECT item.item_description,SUM(details.qty) AS qty FROM item_master AS item
INNER JOIN item_sales AS details
GROUP BY item.item_description;

The error is

Incorrect syntax near the keyword 'group'.

In summary, always use an ANSI compliant INNER JOIN.

Reference: Pinal Dave (http://blog.sqlauthority.com)

Interview Question of the Week #038 – What is Left Semi Join Showplan Operator?

It is very common of interviewers to ask questions which are a bit off and sometimes not used in daily life.  Here is such question I heard the other dya.

Question: What is Left Semi Join Showplan Operator?

Answer: 

There are few interesting kinds of joint operations exists when execution plan is displayed in text format.

Left Semi Join Showplan Operator
The Left Semi Join operator returns each row from the first (top) input when there is a matching row in the second (bottom) input. If no join predicate exists in the Argument column, each row is a matching row.

Left Anti Semi Join Showplan Operator
The Left Anti Semi Join operator returns each row from the first (top) input when there is no matching row in the second (bottom) input. If no join predicate exists in the Argument column, each row is a matching row.

Right Anti Semi Join Showplan Operator

The Right Anti Semi Join operator outputs each row from the second (bottom) input when a matching row in the first (top) input does not exist. A matching row is defined as a row that satisfies the predicate in the Argument column (if no predicate exists, each row is a matching row).

Right Semi Join Showplan Operator
The Right Semi Join operator returns each row from the second (bottom) input when there is a matching row in the first (top) input. If no join predicate exists in the Argument column, each row is a matching row.

Following script will display Left Anti Semi Join Showplan Operator in the result pane.
USE AdventureWorks;
GO
SET SHOWPLAN_TEXT ON
GO
SELECT ProductID
FROM Production.Product
WHERE ProductID
NOT IN (
SELECT ProductID
FROM Production.WorkOrder);
GO
SET SHOWPLAN_TEXT OFF
GO

leftsemijoin Interview Question of the Week #038   What is Left Semi Join Showplan Operator?

Reference: Pinal Dave (http://blog.sqlauthority.com)

SQL SERVER – What is the Difference Between An INNER JOIN and WHERE Clause

Just received a question in email:

“What is the Difference Between An INNER JOIN and WHERE Clause?”

Well, the answer can be quite long but I will try to answer in few words and a very simple one.

If you are talking about resultset – there will be not much different. If you are talking about performance – SQL Server engine is much smarter that it will automatically re-write your quest in most of the cases so you will have no difference in performance. With that said, I still prefer that you use INNER JOIN when a query involves more than one table as that is the ANSI valid syntax.

If you are ever going to use OUTER JOIN, the question of JOIN vs WHERE does not make sense at all, as the answer may be different in most of the cases. Here is the quick example of the same.

Let us create two tables and select the sample data from the table.
USE TempDB
GO
-- Creating Sales Rep Table
CREATE TABLE SalesRep (ID INT, SalesRep VARCHAR(10))
GO
INSERT INTO SalesRep (ID, SalesRep)
SELECT 1, 'Pinal'
UNION ALL
SELECT 2, 'Vinod'
UNION ALL
SELECT 3, 'Balmukund'
UNION ALL
SELECT 4, 'Chandra'
UNION ALL
SELECT 5, 'DJ'
GO
-- Creating Sales Orders Table
CREATE TABLE Orders (ID INT, Amount INT)
GO
INSERT INTO Orders (ID, Amount)
SELECT 1, 100
UNION ALL
SELECT 1, 200
UNION ALL
SELECT 2, 500
UNION ALL
SELECT 2, 300
UNION ALL
SELECT 3, 600
GO
-- Select Data
SELECT *
FROM SalesRep
GO
SELECT *
FROM Orders
GO

Here is the resultset of the data selected from above two tables.

innerwhere1 SQL SERVER   What is the Difference Between An INNER JOIN and WHERE Clause

Now let us see a quick example of the INNER JOIN and WHERE condition.

-- JOIN
SELECT s.SalesRep, o.Amount
FROM SalesRep s
INNER JOIN Orders o ON s.id = o.id
GO
-- WHERE
SELECT s.SalesRep, o.Amount
FROM SalesRep s, Orders o
WHERE s.id = o.id
GO

innerwhere2 SQL SERVER   What is the Difference Between An INNER JOIN and WHERE Clause

You will notice from the resultset both of the query returns exactly same result. As I mentioned earlier when we are using INNER JOIN and WHERE clause, there is no impact of the resultset if the JOIN condition and WHERE clause have almost same condition.

Let us see a quick example where Outer Join gives absolutely different results compared to where as there is totally different business logic when you have to use outer join. As I mentioned above, it is like comparing apples and oranges if you compare outer join and where clause. They are not the same logically.

-- JOIN
SELECT s.SalesRep, o.Amount
FROM SalesRep s
LEFT OUTER JOIN Orders o ON s.id = o.id
GO
-- WHERE
SELECT s.SalesRep, o.Amount
FROM SalesRep s, Orders o
WHERE s.id = o.id
GO

innerwhere3 SQL SERVER   What is the Difference Between An INNER JOIN and WHERE Clause

Here is the script to clean up the tables created.

-- Clean up
DROP TABLE SalesRep
DROP TABLE Orders
GO

Reference: Pinal Dave (http://blog.sqlauthority.com)

SQL SERVER – Does Use of CTE Change the Order of Join in Inner Join

I just had an interesting conversation the day before when I was discussing about Join Order in one of my recent presentations.

The comment which triggered all the conversation was “If I want to change the order of how tables are joined in SQL Server, I prefer to use CTE instead of Join Orders”. 

During the conversation user was suggesting that he wanted his tables to be joined in certain ways in SQL Server but SQL Server Engine Optimizer was re-organizing everything to give the most optimal performance. His needs were to join tables in certain ways and did not care about the performance. To join tables as per his need he had to use a FORCE ORDER hint of the table. However, he recently learned about CTE and believed that CTE will help him to achieve his tasks without the help of FORCE ORDER hint.

He believed that as CTE syntax is constructed before the SELECT statement SQL Server will build a result set instead first and will use the same resultset to join the SELECT statement following the CTE. Well, the casual conversation converted to debate and it was getting very difficult as everybody started to express their opinion with very loud voice. Finally, we decided to run an example on my machine.

NOTE: The example uses INNER JOIN only and the conclusion applies to only INNER JOIN. In the case of OUTER JOIN there is an entire different story, we will cover that in future blog posts.

We created two examples and executed them one by one. Once we executed them, we compared their resultant – they were identical. Right after that we compared the order of the join for both the example.

Example 1: Without CTE

USE AdventureWorks2012
GO
SELECT c.CustomerID, p.BusinessEntityID
FROM Person.Person p
INNER JOIN Person.EmailAddress ea ON ea.BusinessEntityID = p.BusinessEntityID
INNER JOIN Person.PersonPhone ph ON ph.BusinessEntityID = p.BusinessEntityID
INNER JOIN Sales.PersonCreditCard pc ON pc.BusinessEntityID = p.BusinessEntityID
INNER JOIN Sales.Customer c ON c.CustomerID = p.BusinessEntityID;

Execution Plan:

wtihoutcte SQL SERVER   Does Use of CTE Change the Order of Join in Inner Join

Example 2: With CTE

USE AdventureWorks2012
GO
WITH MyCTE AS
(SELECT c.CustomerID
FROM Sales.PersonCreditCard pc
INNER JOIN Sales.Customer c ON c.CustomerID = pc.BusinessEntityID)
SELECT cte.CustomerID, p.BusinessEntityID
FROM Person.Person p
INNER JOIN Person.EmailAddress ea ON ea.BusinessEntityID = p.BusinessEntityID
INNER JOIN MyCTE cte ON cte.CustomerID = p.BusinessEntityID
INNER JOIN Person.PersonPhone ph ON ph.BusinessEntityID = p.BusinessEntityID;

Execution Plan:

wtihoutcte1 SQL SERVER   Does Use of CTE Change the Order of Join in Inner Join

When we compare both the execution plan, it is very clear that there is no change in the order of the table orders. Both the statements have a very similar execution plan and tables were joined in exactly the same order. The matter of the fact in the case of CTE, it was interesting to see that SQL Server started to join very first one table from CTE and another from SELECT statements and continued to build the entire execution plan.

The reason for this is very simple – CTE is not temporary table kind of object or feature. It is just an expression to represent your SELECT statement in such a way that it increases your readability and usability. They do not execute before the regular SELECT statement or build a result set before hand. It actually executes just like regular SELECT statement all together.

In summary: CTE does not impact Table Join Order when all Joins in the query are INNER JOIN. 

Here is one of the my favorite videos on this subject:

http://www.youtube.com/watch?v=Z7VuYBXX2dg

Reference: Pinal Dave (http://blog.sqlauthority.com)

MySQL – How to do Natural Join in MySQL? – A Key Difference Between Inner Join and Natural Join

As you know MySQL supports ANSI JOINs like INNER JOIN, OUTER JOIN, CROSS JOIN etc. Do you know that it supports NATURAL JOIN?. A NATURAL JOIN is a type of JOIN which automatically maps the similar columns from both the tables.

Let us create the following tables
CREATE TABLE items(item_id INT, item_description VARCHAR(100));
CREATE TABLE sales(sales_id INT auto_increment KEY,item_id INT, sales_date DATETIME, sales_amount DECIMAL(12,2));
INSERT INTO items VALUES (1,'Television');
INSERT INTO items VALUES (2,'Mobile');
INSERT INTO items VALUES (3,'laptop');
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (1,'2014-01-01',1200);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (2,'2014-01-02',200);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (3,'2014-01-09',1700);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (3,'2014-01-29',1700);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (3,'2014-02-11',1700);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (1,'2014-02-16',1200);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (2,'2014-02-16',200);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (2,'2014-02-20',200);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (2,'2014-02-20',200);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (2,'2014-02-22',200);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (3,'2014-02-24',1700);
INSERT INTO sales(item_id,sales_date,sales_amount) VALUES (1,'2014-02-24',1200);

By using the NATURAL JOIN, we can select only distinct columns by using * in the SELECT statement.
SELECT * FROM items NATURAL JOIN sales;

The result is

item_id Item_description sales_id sales_date sales_amount
 1 Television 1 2014-01-01 00:00:00 1200.00
 2 Mobile 2 2014-01-02 00:00:00 200.00
 3 laptop 3 2014-01-09 00:00:00 1700.00
 3 laptop 4 2014-01-29 00:00:00 1700.00
 3 laptop 5 2014-02-11 00:00:00 1700.00
 1 Television 6 2014-02-16 00:00:00 1200.00
 2 Mobile 7 2014-02-16 00:00:00 200.00
 2 Mobile 8 2014-02-20 00:00:00 200.00
 2 Mobile 9 2014-02-20 00:00:00 200.00
 2 Mobile 10 2014-02-22 00:00:00 200.00
 3 laptop 11 2014-02-24 00:00:00 1700.00
 1 Television 12 2014-02-24 00:00:00 1200.00

As you have noticed, * does not return all the columns from both the tables. The common column item_id is chosen first, followed by other columns in the tables items and sales. But if you use INNER JOIN with SELECT * all the columns will be selected which will result to duplicate columns in the resultset.

naturaljoin MySQL   How to do Natural Join in MySQL?   A Key Difference Between Inner Join and Natural Join

You can also use the USING clause with INNER JOIN to produce the same result.

SELECT * FROM items INNER JOIN sales USING (item_id);

In the above statement item_id is used to map both the tables.

Reference: Pinal Dave (http://blog.sqlauthority.com)

SQL SERVER – Example of Performance Tuning for Advanced Users with DB Optimizer

Performance tuning is such a subject that everyone wants to master it. In beginning everybody is at a novice level and spend lots of time learning how to master the art of performance tuning. However, as we progress further the tuning of the system keeps on getting very difficult. I have understood in my early career there should be no need of ego in the technology field. There are always better solutions and better ideas out there and we should not resist them. Instead of resisting the change and new wave I personally adopt it.

Here is a similar example, as I personally progress to the master level of performance tuning, I face that it is getting harder to come up with optimal solutions. In such scenarios I rely on various tools to teach me how I can do things better. Once I learn about tools, I am often able to come up with better solutions when I face the similar situation next time.

A few days ago I had received a query where the user wanted to tune it further to get the maximum out of the performance. I have re-written the similar query with the help of AdventureWorks sample database.

SELECT *
FROM HumanResources.Employee e
INNER JOIN HumanResources.EmployeeDepartmentHistory edh ON e.BusinessEntityID = edh.BusinessEntityID
INNER JOIN HumanResources.Shift s ON edh.ShiftID = s.ShiftID;

User had similar query to above query was used in very critical report and wanted to get best out of the query. When I looked at the query – here were my initial thoughts

  • Use only column in the select statements as much as you want in the application
  • Let us look at the query pattern and data workload and find out the optimal index for it

Before I give further solutions I was told by the user that they need all the columns from all the tables and creating index was not allowed in their system. He can only re-write queries or use hints to further tune this query.

Now I was in the constraint box – I believe * was not a great idea but if they wanted all the columns, I believe we can’t do much besides using *. Additionally, if I cannot create a further index, I must come up with some creative way to write this query. I personally do not like to use hints in my application but there are cases when hints work out magically and gives optimal solutions.

Finally, I decided to use Embarcadero’s DB Optimizer. It is a fantastic tool and very helpful when it is about performance tuning.

I have previously explained how it works over here.

First open DBOptimizer and open Tuning Job from File >> New >> Tuning Job.

start dboptimizer SQL SERVER   Example of Performance Tuning for Advanced Users with DB Optimizer

Once you open DBOptimizer Tuning Job follow the various steps indicates in the following diagram.

dboptimizer1 SQL SERVER   Example of Performance Tuning for Advanced Users with DB Optimizer

Essentially we will take our original script and will paste that into Step 1: New SQL Text and right after that we will enable Step 2 for Generating Various cases, Step 3 for Detailed Analysis and Step 4 for Executing each generated case. Finally we will click on Analysis in Step 5 which will generate the report detailed analysis in the result pan.

The detailed pan looks like. It generates various cases of T-SQL based on the original query. It applies various hints and available hints to the query and generate various execution plans of the query and displays them in the resultant. You can clearly notice that original query had a cost of 0.0841 and logical reads about 607 pages. Whereas various options which are just following it has different execution cost as well logical read. There are few cases where we have higher logical read and there are few cases where as we have very low logical read.

If we pay attention the very next row to original query have Merge_Join_Query in description and have lowest execution cost value of 0.044 and have lowest Logical Reads of 29. This row contains the query which is the most optimal re-write of the original query. Let us double click over it.

opt embc SQL SERVER   Example of Performance Tuning for Advanced Users with DB Optimizer

Here is the query:

SELECT *
FROM HumanResources.Employee e
INNER JOIN HumanResources.EmployeeDepartmentHistory edh ON e.BusinessEntityID = edh.BusinessEntityID
INNER JOIN HumanResources.Shift s ON edh.ShiftID = s.ShiftID
OPTION (MERGE JOIN)

If you notice above query have additional hint of Merge Join. With the help of this Merge Join query hint this query is now performing much better than before.

The entire process takes less than 60 seconds.

Please note that it the join hint Merge Join was optimal for this query but it is not necessary that the same hint will be helpful in all the queries. Additionally, if the workload or data pattern changes the query hint of merge join may be no more optimal join. In that case, we will have to redo the entire exercise once again. This is the reason I do not like to use hints in my queries and I discourage all of my users to use the same.

However, if you look at this example, this is a great case where hints are optimizing the performance of the query. It is humanly not possible to test out various query hints and index options with the query to figure out which is the most optimal solution. Sometimes, we need to depend on the efficiency tools like DB Optimizer to guide us the way and select the best option from the suggestion provided.

Let me know what you think of this article as well your experience with DB Optimizer. Please leave a comment.

Click to Download Scripts

Reference: Pinal Dave (http://blog.sqlauthority.com)

SQL – Difference Between INNER JOIN and JOIN

Here is the follow up question to my earlier question SQL – Difference between != and Operator <> used for NOT EQUAL TO Operation. There was a pretty good discussion about this subject earlier and lots of people participated with their opinion. Though the answer was very simple but the conversation was indeed delightful and was indeed very informative.

In this blog post I have another following up question to all of you.

What is the difference between INNER JOIN and JOIN?

joinvsinnerjoin SQL   Difference Between INNER JOIN and JOIN

If you are working with database you will find developers use above both the kinds of the joins in their SQL Queries. Here is the quick example of the same.

Query using INNER JOIN

SELECT *
 FROM Table1
 INNER JOIN  Table2 ON Table1.Col1 = Table2.Col1

Query using JOIN

SELECT *
 FROM Table1
 JOIN  Table2 ON Table1.Col1 = Table2.Col1

The question is what is the difference between above two syntax.

Here is the answer – They are equal to each other. There is absolutely no difference between them. They are equal in performance as well as implementation. JOIN is actually shorter version of INNER JOIN.

Personally I prefer to write INNER JOIN because it is much cleaner to read and it avoids any confusion if there is related to JOIN. For example if users had written INNER JOIN instead of JOIN there would have been no confusion in mind and hence there was no need to have original question.

Here is the question back to you –

Which one of the following syntax do you use when you are inner joining two tables – INNER JOIN or JOIN? and Why?

Reference: Pinal Dave (http://blog.sqlauthority.com)

SQL SERVER – Script to Update a Specific Column in Entire Database

Last week, I have received a very interesting question and I find in email and I really liked the question as I had to play around with SQL Script for a while to come up with the answer he was looking for. Please read the question and I believe that all of us face this kind of situation.

“Pinal,

In our database we have recently introduced ModifiedDate column in all of the tables. Now onwards any update happens in the row, we are updating current date and time to that field.

Now here is the issue, when we added that field we did not update it with a default value because we were not sure when we will go live with the system so we let it be NULL. Now modification to the application went live yesterday and we are now updating this field.

Here is where I need your help. We need to update all the tables in our database where we have column created ModifiedDate and now want to update with current datetime. As our system is already live since yesterday there are several thousands of the rows which are already updated with real world value so we do not want to update those values. Essentially, in our entire database where ever there is a ModifiedDate column and if it is NULL we want to update that with current date time? 

Do you have a script for it?”

Honestly I did not have such a script. This is very specific required but I was able to come up with two different methods how he can use this method.

Method 1 : Using INFORMATION_SCHEMA

SELECT 'UPDATE ' + T.TABLE_SCHEMA + '.' + T.TABLE_NAME + ' SET ModifiedDate = GETDATE() WHERE ModifiedDate IS NULL;'
FROM INFORMATION_SCHEMA.TABLES T
INNER JOIN INFORMATION_SCHEMA.COLUMNS C
ON T.TABLE_NAME = C.TABLE_NAME
AND c.COLUMN_NAME ='ModifiedDate'
WHERE T.TABLE_TYPE = 'BASE TABLE'
ORDER BY T.TABLE_SCHEMA, T.TABLE_NAME;

Method 2: Using DMV

SELECT 'UPDATE ' + SCHEMA_NAME(t.schema_id) + '.' + t.name + ' SET ModifiedDate = GETDATE() WHERE ModifiedDate IS NULL;'
FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE c.name ='ModifiedDate'
ORDER BY SCHEMA_NAME(t.schema_id), t.name;

Above scripts will create an UPDATE script which will do the task which is asked.

updatescriptgen SQL SERVER   Script to Update a Specific Column in Entire Database

We can pretty much the update script to any other SELECT statement and retrieve any other data as well.

Click to Download Scripts

Reference: Pinal Dave (http://blog.sqlauthority.com