Pages

Tuesday, October 4, 2011

Microsoft SQL Server 2008 Advanced SQL Best Practices


How to calculate last (previous) date periods?
Execute the following Microsoft SQL Server T-SQL script in SSMS Query Editor to demonstrate the creation and test of a stored procedure to calculate last week, last month, last quarter and last year date limits using datetime variable types.

use AdventureWorks2008;
go
create proc sprocLastPeriodRange
      @Period varchar(8),
      @PeriodStart datetime output,
      @PeriodEnd datetime output,
      @PeriodEndPlusOneDay datetime output
as
begin
declare @FirstDayOfCurrentPeriod datetime = getdate(),
        @FirstDayOfLastPeriod datetime = getdate()

if @Period in ('Week','wk','ww')
begin
      set @FirstDayOfCurrentPeriod = DATEADD(DD, 1 - DATEPART(DW, CONVERT(VARCHAR(10),
                      getdate(), 111)), CONVERT(VARCHAR(10), getdate(), 111))
      set @FirstDayOfLastPeriod = dateadd(dd, -7, @FirstDayOfCurrentPeriod)
end

else if @Period in ('Month', 'mm', 'm')
begin
      set @FirstDayOfCurrentPeriod = convert(datetime,
                               left(convert(varchar, getdate(), 111),8) + '01')
      set @FirstDayOfLastPeriod = dateadd(mm, -1, @FirstDayOfCurrentPeriod)
end
else if @Period in ('Quarter','qq', 'q')
begin
      set @FirstDayOfCurrentPeriod = CONVERT(CHAR(4), YEAR(getdate())) +
                       CASE WHEN MONTH(getdate()) between 1 and 3 THEN '/01/01'
                            WHEN MONTH(getdate()) between 4 and 6 THEN '/04/01'
                            WHEN MONTH(getdate()) between 7 and 9 THEN '/07/01'
                            ELSE '/10/01'
                       END

      set @FirstDayOfLastPeriod = dateadd(mm, -3, @FirstDayOfCurrentPeriod)
end
else if @Period in ('Year', 'yyyy', 'yy')
begin

      set @FirstDayOfCurrentPeriod = convert(datetime,
                               left(convert(varchar, getdate(), 111),5) + '01/01')
      set @FirstDayOfLastPeriod = dateadd(yy, -1, @FirstDayOfCurrentPeriod)
end
set @PeriodStart = @FirstDayOfLastPeriod
set @PeriodEndPlusOneDay = @FirstDayOfCurrentPeriod
set @PeriodEnd = DATEADD (dd,-1, @FirstDayOfCurrentPeriod)
end
go

-- Test
declare @LastPeriodBegin datetime, @LastPeriodEnd datetime
declare @ThisPeriodBegin datetime

exec sprocLastPeriodRange 'qq',
                          @LastPeriodBegin output,
                          @LastPeriodEnd output,
                          @ThisPeriodBegin output
select Period='qq',
       LastBegin=@LastPeriodBegin,
       LastEnd=@LastPeriodEnd,
       ThisBegin=@ThisPeriodBegin                         

-- To use it for WHERE range filtering
-- Note that < is used at period upper limit
select * from Purchasing.PurchaseOrderHeader
where OrderDate >= @LastPeriodBegin
and   OrderDate <  @ThisPeriodBegin
go

Partial results:
Period
LastBegin
LastEnd
ThisBegin
qq
7/1/08 0:00
9/30/08 0:00
10/1/08 0:00

How to upsert with the MERGE statement?
Execute the following Microsoft SQL Server T-SQL scripts in SSMS Query Editor to demonstrate UPSERT, update or insert, operation using the MERGE statement. If the row exists as identified by the 3 key columns, UPDATE is performed on OrderQuantity and SalesAmount, otherwise INSERT action is carried out. MERGE can do INSERT, UPDATE and DELETE in a single statement.

use tempdb;

select top (5000) ResellerKey, OrderDateKey, ProductKey, OrderQuantity, SalesAmount
into FactResellerSales
from AdventureWorksDW2008.dbo.FactResellerSales
go

select top (8000) ResellerKey, OrderDateKey, ProductKey, OrderQuantity, SalesAmount
into ResellerSalesTransaction
from AdventureWorksDW2008.dbo.FactResellerSales
go

delete rsc
from  ResellerSalesTransaction rsc
join (select top 1000 * from ResellerSalesTransaction order by ResellerKey desc) x
on x.ResellerKey=rsc.ResellerKey
go


update top (6000) ResellerSalesTransaction
set SalesAmount = SalesAmount * 1.1
go

select top (10) * from FactResellerSales
order by ResellerKey, OrderDateKey, ProductKey
go

select BeforeFactCount=COUNT(*) from FactResellerSales
go

-------------------------------------------------------------------- 
-- Test data sets created, ready for the MERGE (update or insert)
-------------------------------------------------------------------- 
MERGE FactResellerSales AS fact
USING (
  SELECT *  FROM ResellerSalesTransaction
) AS feed
ON (    fact.ProductKey = feed.ProductKey
    AND fact.ResellerKey = feed.ResellerKey
    AND fact.OrderDateKey = feed.OrderDateKey )
WHEN MATCHED THEN
    UPDATE SET
        fact.OrderQuantity = fact.OrderQuantity + feed.OrderQuantity
        ,fact.SalesAmount = fact.SalesAmount + feed.SalesAmount 
WHEN NOT MATCHED THEN
    INSERT (ResellerKey, OrderDateKey, ProductKey, OrderQuantity, SalesAmount)
    VALUES (feed.ResellerKey, feed.OrderDateKey, feed.ProductKey,
            feed.OrderQuantity, feed.SalesAmount);
--------------------------------------------------------------------  
go

select top (10) * from FactResellerSales
order by ResellerKey, OrderDateKey, ProductKey
go

select AfterFactCount=COUNT(*) from FactResellerSales
go



-- Cleanup
use tempdb;
drop table ResellerSalesTransaction
go
drop table FactResellerSales
go

How to create date series with multiple-value assignment?
Execute the following Microsoft SQL Server Transact-SQL script in Management Studio Query Editor to demonstrate the creation of integer and date series with the use of the multiple-value assignment operator within an UPDATE statement. The construct avoids the use of cursor WHILE loop, therefore it is a scalable solution.


use tempdb;

select ID = CONVERT(int,SalesOrderID),
TestDate=convert(date,ModifiedDate) into DateSeries
from AdventureWorks2008.Sales.SalesOrderHeader
go

select top 100 * from DateSeries
go

-- Multi-value assignment UPDATE
declare @Date  date = dateadd(day,1,getdate()), @id int = 0
update DateSeries
set @id = ID = @id + 1,
    @Date = TestDate = dateadd (Day, -1, @Date)
   
go

select top 100 * from DateSeries
go

use tempdb;
drop table DateSeries
go

First and second partial results:
ID
TestDate
43659
7/8/2001
43660
7/8/2001
43661
7/8/2001
43662
7/8/2001
43663
7/8/2001
ID
TestDate
1
11/26/2008
2
11/25/2008
3
11/24/2008
4
11/23/2008
5
11/22/2008
6
11/21/2008
7
11/20/2008
8
11/19/2008

How to design for stored procedure missing (NULL) parameter?
Execute the following Microsoft SQL Server T-SQL script in Query Editor to demonstrate the design for missing filter parameter(s). In case of all 3 parameters missing, the entire JOINed Production.Product table content is returned without the WHERE filter.
USE AdventureWorks2008;
GO

CREATE PROCEDURE sprocProductFilter
                @LowPrice    SMALLMONEY  = NULL,
                @SubCategory VARCHAR(32)  = NULL,
                @Color       VARCHAR(16)  = NULL
AS
  SET nocount  ON;
  
  SELECT p.*
  FROM   Production.Product p
         INNER JOIN Production.ProductSubcategory sc
           ON p.ProductSubcategoryID = sc.ProductSubcategoryID
  WHERE  1 = 1
         AND (@LowPrice <= ListPrice
               OR @LowPrice IS NULL)
         AND (@SubCategory = sc.Name
               OR @SubCategory IS NULL)
         AND (@Color = Color
               OR @Color IS NULL)

GO

exec sprocProductFilter 700.0, 'Road Frames', 'Red'
go

exec sprocProductFilter NULL, 'Road Frames', 'Red'
go

exec sprocProductFilter NULL, NULL, 'Red'
go

exec sprocProductFilter 700.0, 'Road Frames'
go

exec sprocProductFilter 700.0
go

exec sprocProductFilter
go

How to measure stored procedure execution time?
Execute the following Microsoft SQL Server T-SQL script in SSMS Query Editor take execution time measurement for a stored procedure. The timing is the average of 10 measurements.
DECLARE @i int = 0, @TestLimit int = 10
DECLARE @Timing TABLE ( Timing int)

WHILE (@i < @TestLimit)
BEGIN
      DECLARE @RC int, @ManagerID int =1
      DBCC DROPCLEANBUFFERS
      DECLARE @Start datetime = getdate()
      EXECUTE @RC = [AdventureWorks2008].[dbo].[uspGetManagerEmployees] @ManagerID
      INSERT @Timing
      SELECT ExecutionMsec = datediff (millisecond, @Start, getdate())
      SET @i=@i+1
END
SELECT ExecutionMsec = avg(Timing) FROM @Timing
Go

How to combine detail data with GROUP BY aggregate?
Execute the following Microsoft SQL Server T-SQL script in SSMS Query Editor demonstrate the usage of CTE GROUP BY aggregate to JOIN to detail data lines.
Use AdventureWorks2008;

WITH ctePOTotal AS
 (
        SELECT
            PurchaseOrderID,
            Sum(OrderQty) as [TotalQty]
        FROM Purchasing.PurchaseOrderDetail T2
        GROUP BY PurchaseOrderID
    )
SELECT
    PO = pod.PurchaseOrderID,
    LineItem=ROW_NUMBER()OVER (PARTITION BY pod.PurchaseOrderID ORDER BY ProductId),
    OrderDate = convert(varchar,OrderDate,110),
    ProductId,
    UnitPrice,
    OrderQty,
    TotalQty=ctePOTotal.[TotalQty]
FROM Purchasing.PurchaseOrderHeader poh
    INNER JOIN Purchasing.PurchaseOrderDetail pod
        ON poh.PurchaseOrderId = pod.PurchaseOrderId
    INNER JOIN  ctePOTotal
        ON pod.PurchaseOrderID = ctePOTotal.PurchaseOrderID
WHERE Year(OrderDate)=2004
ORDER BY PO, LineItem

How to list all columns with extended properties?
Execute the following Microsoft SQL Server Transact-SQL (T-SQL) script in Management Studio (SSMS) Query Editor to list all tables column information with extended properties.
USE AdventureWorks2008;

SELECT   [Schema] = s.name,
         [Table] = object_name(major_id),
         [Column] = c.name,
         [Type] = t.name,
         [Extended] = p.VALUE
FROM     sys.extended_properties p
         JOIN sys.columns c
           ON c.column_id = p.minor_id
              AND c.object_id = p.major_id
         JOIN sys.objects o
           ON o.object_id = p.major_id
              AND o.object_id = c.object_id
         JOIN sys.schemas s
           ON s.schema_id = o.schema_id
         JOIN sys.types t
           ON c.system_type_id = t.user_type_id
WHERE    o.TYPE = 'U'
         AND class_desc = 'OBJECT_OR_COLUMN'
ORDER BY [Schema],
         [Table],
         c.column_id

How to calculate running total fast?
Execute the following Microsoft SQL Server Transact-SQL (T-SQL) script in Management Studio (SSMS) Query Editor to calculate running total without using a loop. In the update statement double assignment is applied.
USE tempdb;

SELECT * INTO POH from AdventureWorks2008.Purchasing.PurchaseOrderHeader
ORDER by PurchaseOrderID

-- select * from POH

ALTER TABLE POH ADD RunningTotal money
GO

SET NOCOUNT ON
GO

DECLARE @RunningTotal MONEY

SET @RunningTotal=0

UPDATE POH

SET @RunningTotal = RunningTotal = @RunningTotal+ISNULL(TotalDue, 0)
GO

SELECT * FROM POH
GO

No comments:

Post a Comment