Author Archives: nakulvachhrajani

Unknown's avatar

About nakulvachhrajani

Nakul Vachhrajani is a SQL Server MCTS, TOGAF certified Technical Architect with Capgemini having a total IT experience of more than 16 years. Nakul is an active blogger, and has been a guest columnist for SQLAuthority.com and SQLServerCentral.com. Nakul presented a webcast on the “Underappreciated Features of Microsoft SQL Server” at the Microsoft Virtual Tech Days Exclusive Webcast series (May 02-06, 2011) on May 06, 2011. He is also the author of a research paper on Database upgrade methodologies, which was published in a CSI journal, published nationwide. In addition to his passion about SQL Server, Nakul also contributes to the academia out of personal interest. He visits various colleges and universities as an external faculty to judge project activities being carried out by the students. Disclaimer: The opinions expressed herein are his own personal opinions and do not represent his employer’s view in anyway.

#0336 – SQL Server – Underappreciated Features – Using the UPDATE clause to update the value of a variable


We typically use the UPDATE clause to update or change the existing data in a given table or view. Most business logic requires that the value of one or more variables is set based on this update. Generally, we find that these requirements are realized with two statements – an UPDATE, followed by a SELECT statement (or vice versa, as the case may be). However, a little known and therefore underappreciated feature of the UPDATE clause is that it can also be used to update variables. Here’s an example.

Assume that we have a requirement to update the order detail quantities in the [Sales].[SalesOrderDetail] table of the [AdventureWorks2012] database with a given value supplied in the variable @orderDetailQuantityIncrementBy. At the same time, we also need to know the amount by which the order value increased or decreased.

A typical implementation of this would be:

USE AdventureWorks2012;
GO

--Variable indicating the change in Order Quantity
DECLARE @orderDetailQuantityIncrementBy INT = 1;

--Temporary table variable to hold the before/after values
DECLARE @orderUpdate TABLE (BeforeOrderQuantity INT,
                            BeforeUnitPrice DECIMAL(20, 8),
                            AfterOrderQuantity INT,
                            AfterUnitPrice DECIMAL(20, 8)
                           );

--Using a transaction so that we can return to the table's base state
BEGIN TRANSACTION UpdateVariableTest;

  --Perform the update
  UPDATE so
  SET so.OrderQty += @orderDetailQuantityIncrementBy
  OUTPUT deleted.OrderQty, 
         deleted.UnitPrice, 
         inserted.OrderQty, 
         inserted.UnitPrice
  INTO @orderUpdate (BeforeOrderQuantity, 
                     BeforeUnitPrice, 
                     AfterOrderQuantity, 
                     AfterUnitPrice)
  FROM Sales.SalesOrderDetail AS so
  WHERE so.SalesOrderID = 43659
    AND so.SalesOrderDetailID IN (8, 10);

  --Calculate the change in order total
  SELECT SUM((ou.AfterOrderQuantity * ou.AfterUnitPrice)
              - (ou.BeforeOrderQuantity * ou.BeforeUnitPrice)
            ) AS orderAmountChanges
  FROM @orderUpdate AS ou;

ROLLBACK TRANSACTION UpdateVariableTest;
GO

/***********
  RESULTS
***********/
--orderAmountChanges
--------------------
--34.54040000

As you can see, we used an UPDATE clause and collected the before/after update information in a temporary table variable. Later, we performed arithmetic operations on the value stored in the table variable.

Using a single UPDATE clause to update a variable

This requirement can be realized within a single UPDATE clause also. Here’s the modified solution:

USE AdventureWorks2012;
GO

--Variable indicating the change in Order Quantity
DECLARE @orderDetailQuantityIncrementBy INT = 1;

--Variable indicating the change in Order Amount
DECLARE @orderAmountChanges DECIMAL(20,8) = 0;

--Using a transaction so that we can return to the table's base state
BEGIN TRANSACTION UpdateVariableTest;
    --Perform the update
    UPDATE so
    SET so.OrderQty += @orderDetailQuantityIncrementBy,
        @orderAmountChanges += (@orderDetailQuantityIncrementBy * so.UnitPrice) 
    FROM Sales.SalesOrderDetail AS so
    WHERE so.SalesOrderID = 43659
      AND so.SalesOrderDetailID IN (8, 10);

    --Fetch the value from the variable
    SELECT @orderAmountChanges AS OrderAmountChanges;

ROLLBACK TRANSACTION UpdateVariableTest;
GO
/***********
  RESULTS
***********/
--OrderAmountChanges
--------------------
--34.54040000

As can be seen from the example above, the UPDATE clause makes realization of the said requirement very simple & readable.

Further Reading

  • Compound Operators – the most common mistake [Link]
  • The UPDATE clause [Books ON Line Link]

Until we meet next time,

Be courteous. Drive responsibly.

#0335 – SQL Server – Using the WITH TIES option in the TOP clause of SELECT queries


When working with the AdventureWorks database, assume that we have the following requirement:

Please provide the list of customers with TOP 100 orders from Australia with the highest order totals.

The following query is the first one that comes to mind:

USE AdventureWorks2012;
GO
SELECT TOP 100 soh.CustomerID, 
               soh.TotalDue, 
               soh.TerritoryID
FROM Sales.SalesOrderHeader AS soh
INNER JOIN Sales.SalesTerritory AS sst ON soh.TerritoryID = sst.TerritoryID
WHERE sst.CountryRegionCode = 'AU'
ORDER BY soh.TotalDue DESC;
GO

image

This query would work in most cases, but analyzing the requirement, we realize that we don’t want the TOP 100 orders, we want all customers with the TOP 100 orders. These two requirements are different, and here’s why:

  • TOP 100 orders by total value = this means that we should have only 100 orders being returned, in the order defined by the ORDER BY clause
  • Customers with TOP 100 orders by total value = this means that we need to first fetch the TOP 100 order totals and then look for all customers which have orders with these totals. In this case, the number of orders being returned may be more than 100

In this case, we need to use the WITH TIES clause when defining the TOP statement. Here’s the query:

USE AdventureWorks2012;
GO
SELECT TOP 100 WITH TIES 
        soh.CustomerID, 
        soh.TotalDue, 
        soh.TerritoryID
FROM Sales.SalesOrderHeader AS soh
INNER JOIN Sales.SalesTerritory AS sst ON soh.TerritoryID = sst.TerritoryID
WHERE sst.CountryRegionCode = 'AU'
ORDER BY soh.TotalDue DESC;
GO

image

NOTES:

  • The TOP…WITH TIES can only be used in a SELECT statement
  • It cannot be used without an accompanying ORDER BY clause (a normal TOP can)
  • The TOP…WITH TIES also works with the TOP (n) PERCENT option

Until we meet next time,

Be courteous. Drive responsibly.

#0334 – SQL Server – SSMS – Troubleshooting Intellisense issues


Intellisense is one of the most frequently used features of SQL Server in the development community – so much so that if Intellisense stops working, work comes to a grinding halt and frantic calls to the IT team are made.


This post documents the steps, in order of most common occurrence (in my experience) that can be taken when faced with an issue that relates to Intellisense.


Assumption: The user is attempting to type in code in the code block, i.e. not in the comments block or string literals.



  1. Confirm that Intellisense is enabled (it may have been accidentally turned off due to “happy clicking”)

  2. Refresh the local cache

    • Often, when workstations (especially laptops) are hibernated and awaken again, newer objects are created or when connections in the Query editor are changed, Intellisense may stop working or take a while to refresh
    • To manually refresh the cache, press (Ctrl + Shift + R) or go to Edit –> Intellisense –> Refresh Local Cache

  3. Is the SSMS connecting to a SQL Server running a lower version? (e.g. SSMS for SQL 2012 connecting to SQL 2008 R2/2005 or lower) If yes, SSMS would not work
  4. Review the code – does an error exist such that the queries in the particular query editor cannot be parsed?
  5. After Visual Studio 2010 SP1 is installed on a workstation with SQL Server 2008 R2 (any release from RTM to CU6), the Intelli-sense feature in the SQL server Management Studio (SSMS) may stop working

    • This is a  known, documented issue with Microsoft (KB2531482)and the fix is quite simple – deploy the latest service pack/cumulative update of SQL Server 2008 R2

While Intellisense kicks back to life in most cases after the above steps are performed, it is still possible that it may cease to work in rare cases. The last step in this case is to reinstall the SQL Server client tools.


Further Reading:


Here are some other posts about Intelli-sense that you may be interested in:



Until we meet next time,



Be courteous. Drive responsibly.

#0333 – Opening Office documents from Sharepoint online – The file is corrupt and cannot be opened


Today, I do not have a SQL Server blog. Today’s post is about a solution to a problem that I faced recently when working with documents stored on Sharepoint Online.


I had received a new laptop from the IT team and when attempting to edit an Excel file in Excel 2010 directly from Sharepoint, I encountered the following error:



The file is corrupt and cannot be opened.


Root Cause


When a document is opened from the Internet, Office spawns off another program under restricted permissions to open the document. This restricted process will be unable to create temporary files and folders required for Office to open and edit the document.


Solutions


The following solutions can be implemented to resolve the issue:


Solution 01:


If a particular Sharepoint URL is frequently used and trusted, a good alternative would be to Add the URL to the list of Trusted sites in under the browser Options.


Office will trust documents originating from trusted locations and hence these documents will open successfully.


Solution 02:


Follow the steps provided in the Microsoft KB article: http://support.microsoft.com/kb/2387587


Solution 03 (NOT recommended):


Disable Protected Mode (NOT recommended)



  1. Go to File -> Options -> Trust Center
  2. Open the Trust Center window by clicking on the “Trust Center Settings” button
  3. Navigate to the “Protected View” window
  4. Uncheck the following checkboxes:

    • Enable Protected View for files originating from the Internet
    • Enable Protected View for files located in potentially unsafe locations
    • Enable Protected View for Outlook attachments
    • Enable Data Execution Prevention mode

While most blogs and forums suggest that protected mode options should be unchecked, I would not recommend this as it reduces the security enforced by Office.


image



Have you ever encountered this issue? If yes, how did you resolve the error?


Until we meet next time,



Be courteous. Drive responsibly.

#0332 – SQL Server – Myths – Is DISTINCT faster than GROUP BY?


I have often seen a recommendation that using DISTINCT instead of GROUP BY is better from a performance perspective. DISTINCT is definitely a more readable alternative, but I wanted to test for myself as to what the truth was.


To compare the performance of the two, I wrote the following simple queries.


The query below helps in identifying the number of records that SQL Server will need to process. On my machine, when executed without the GROUP BY or the DISTINCT clause, the query generates records in excess of 380,000.

USE AdventureWorks2012;
GO
SELECT COUNT(so1.object_id)
FROM sys.objects AS so1
CROSS JOIN sys.objects AS so2 ;
GO

The queries below fetch the same information – one uses the GROUP BY whereas the other query uses a DISTINCT.

USE AdventureWorks2012;
GO
–Clean the buffers and procedure cache
DBCC DROPCLEANBUFFERS;
DBCC FREEPROCCACHE;
GO

–Evaluate GROUP BY
SELECT so1.object_id,
so1.name
FROM sys.objects AS so1
CROSS JOIN sys.objects AS so2
GROUP BY so1.object_id, so1.name ;
GO

–Clean the buffers and procedure cache
DBCC DROPCLEANBUFFERS;
DBCC FREEPROCCACHE;
GO

–Evaluate DISTINCT
SELECT DISTINCT
so1.object_id,
so1.name
FROM sys.objects AS so1
CROSS JOIN sys.objects AS so2 ;
GO


Before executing the two queries (one with the GROUP BY and the other with the DISTINCT), I had turned on the actual execution plan.


image


Upon studying the execution plan, the following observations can be made:



  1. When the SQL server database engine detected that the 2nd query did not have any aggregation function, it was smart enough to convert the GROUP BY into a DISTINCT when the query plan is generated
  2. There is no difference in the execution plan across both the queries

In conclusion,



DISTINCT is not faster than GROUP BY. In fact, Both DISTINCT and GROUP BY demonstrate equal performance impact.


When aggregations are not being used, DISTINCT is more readable when compared to GROUP BY.


Until we meet next time,



Be courteous. Drive responsibly.