SQLUSA

SQL Server Training Scripts

Dynamic SQL & Stored Procedure Usage in T-SQL

Important security article related to dynamic SQL: How To: Protect From SQL Injection in ASP.NET

 

------------

-- Dynamic SQL QUICK SYNTAX

------------

USE AdventureWorks2008;

EXEC ('SELECT * FROM Sales.SalesOrderHeader')

 

DECLARE @DynamicSQL varchar(256); SET @DynamicSQL='SELECT * FROM Sales.SalesOrderHeader'

EXEC (@DynamicSQL)

GO

DECLARE @DynamicSQL varchar(256), @Table sysname;

SET @DynamicSQL='SELECT * FROM'; SET @Table = 'Sales.SalesOrderHeader'

SET @DynamicSQL = @DynamicSQL+' '+@Table

PRINT @DynamicSQL  -- for testing & debugging

EXEC (@DynamicSQL)

GO

-- Dynamic SQL for rowcount in all tables

DECLARE @DynamicSQL nvarchar(max), @Schema sysname, @Table sysname;

SET @DynamicSQL = ''

SELECT @DynamicSQL = @DynamicSQL + 'SELECT '''+QUOTENAME(TABLE_SCHEMA)+'.'+

  QUOTENAME(TABLE_NAME)+''''+

  '= COUNT(*) FROM '+ QUOTENAME(TABLE_SCHEMA)+'.'+QUOTENAME(TABLE_NAME) +';'

FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'

PRINT @DynamicSQL -- test & debug

EXEC sp_executesql @DynamicSQL

 

-- Equivalent code using the undocumented sp_MSforeachtable

EXEC sp_MSforeachtable 'select ''?'', count(*) from ?'

------------

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following dynamic SQL scripts demonstrate:

   1. dynamic SQL stored procedure

   2. dynamic SQL with OUTPUT parameter

   3. stored procedure with dynamic SQL WHILE loop

   4. dynamic SQL with using parent's #temptable

   5. dynamic SQL for dynamic PIVOT query

   6. dynamic stored procedure with output parameter

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

-- FIRST EXAMPLE - dynamic stored procedure for customer list

USE Northwind;

GO

 

-- DROP stored procedure if exists to make CREATE work

IF EXISTS ( SELECT *

                  FROM sys.objects

                  WHERE object_id = OBJECT_ID(N'[dbo].[CustomerListByState]')

                              AND TYPE IN (N'P',N'PC'))

DROP PROCEDURE [dbo].[CustomerListByState]

GO

 

-- Sproc (stored procedure) with dynamic SQL

CREATE PROCEDURE CustomerListByState

@States VARCHAR(128)

AS

BEGIN

      SET NOCOUNT ON

      DECLARE @SQL NVARCHAR(MAX) -- alternate nvarchar(1024)

      -- Dynamic query assembly with string concatenation

      SET @SQL = 'select Region, CustomerID, CompanyName, ContactName, Phone

      from Customers where Region IN (' + @States + ')' +

      ' order by Region, CompanyName'

      PRINT @SQL -- for testing & debugging

/* Assembled code

select Region, CustomerID, CompanyName, ContactName, Phone

from Customers where Region IN ('WA', 'OR', 'ID', 'CA')

order by Region, CompanyName

*/

 

      EXEC sp_executeSQL @SQL

END

GO

 

-- Execute dynamic stored procedure script

DECLARE @States VARCHAR(100)

SET @States = '''WA'', ''OR'', ''ID'', ''CA'''

EXEC CustomerListByState @States

GO

/* Results

 

Region CustomerID CompanyName ContactName Phone
CA LETSS Let's Stop N Shop Jaime Yorres (415) 555-5938
ID SAVEA Save-a-lot Markets Jose Pavarotti (208) 555-8097
OR GREAL Great Lakes Food Market Howard Snyder (503) 555-7555
OR HUNGC Hungry Coyote Import Store Yoshi Latimer (503) 555-6874
OR LONEP Lonesome Pine Restaurant Fran Wilson (503) 555-9573
OR THEBI The Big Cheese Liz Nixon (503) 555-3612
WA LAZYK Lazy K Kountry Store John Steel (509) 555-7969
WA TRAIH Trail's Head Gourmet Provisioners Helvetius Nagy (206) 555-8257
WA WHITC White Clover Markets Karl Jablonski (206) 555-4112

 

*/

 

-- SECOND EXAMPLE - search names in Person.Person table

 

-- Dynamic SQL with input and output parameters

USE AdventureWorks2008;

 

DECLARE  @ParmDefinition NVARCHAR(1024) = N'@FirstLetterOfLastName char(1),

      @LastFirstNameOUT nvarchar(50) OUTPUT'

DECLARE @FirstLetter CHAR(1) = 'P', @LastFirstName NVARCHAR(50)

DECLARE @SQL NVARCHAR(MAX) = N'SELECT @LastFirstNameOUT = max(FirstName)

      FROM Person.Person'+CHAR(13)+

      'WHERE left(LastName,1) = @FirstLetterOfLastName'

PRINT @SQL+CHAR(13)     -- test & debug

PRINT @ParmDefinition    -- test & debug

EXECUTE sp_executeSQL

      @SQL,

      @ParmDefinition,

      @FirstLetterOfLastName = @FirstLetter,

      @LastFirstNameOUT=@LastFirstName OUTPUT

 

SELECT

  [Last First Name] = @LastFirstName,

  Legend='of last names starting with',

  Letter=@FirstLetter

GO

/* Results

 

Last First Name   Legend                        Letter

Zoe               of last names starting with   P

*/

 

-- THIRD EXAMPLE - SPROC to enumerate all objects in databases


-- Return objects count in all databases on the server

-- Dynamic SQL stored procedure with cursor loop

-- QUOTENAME function is used to build valid identifiers

USE AdventureWorks;

GO

IF EXISTS (SELECT *

           FROM   sys.objects

           WHERE  object_id = OBJECT_ID(N'[dbo].[sprocObjectCountsInAllDBs]')

                  AND TYPE IN (N'P',N'PC'))

  DROP PROCEDURE [dbo].[sprocObjectCountsInAllDBs]

GO

 

CREATE PROC sprocObjectCountsInAllDBs

AS

  BEGIN

    DECLARE  @dbName      SYSNAME,

             @ObjectCount INT

    

    DECLARE  @SQL NVARCHAR(MAX)

    DECLARE  @DBObjectStats  TABLE(

                                   DBName    SYSNAME,

                                   DBObjects INT

                                   )

    DECLARE curAllDBs CURSOR  FOR

    SELECT   name

    FROM     MASTER.dbo.sysdatabases

    WHERE    name NOT IN ('master','tempdb','model','msdb')

    ORDER BY name

    OPEN curAllDBs

    FETCH  curAllDBs

    INTO @dbName

    

    WHILE (@@FETCH_STATUS = 0) -- Loop through all db-s

      BEGIN

        -- Build valid yet hard-wired SQL statement

        SET @SQL = 'select @dbObjects = count(*)' + char(13) + 'from ' +

                    QuoteName(@dbName) + '.dbo.sysobjects'

        PRINT @SQL -- Use it for debugging

        /*

                  select @dbObjects = count(*)

                  from [AdventureWorks].dbo.sysobjects

        */

        -- Dynamic call for query execution with output parameter(s)

        EXEC sp_executesql @SQL, N'@dbObjects int output', 

             @dbObjects = @ObjectCount output

        INSERT @DBObjectStats SELECT @dbName, @ObjectCount

        FETCH  curAllDBs

        INTO @dbName

      END -- while

    CLOSE curAllDBs

    DEALLOCATE curAllDBs

  -- Return results 

  SELECT * FROM @DBObjectStats ORDER BY DBName

  END

GO

 

-- Execute stored procedure

EXEC sprocObjectCountsInAllDBs

GO

/* Partial results

 

DBName                  DBObjects

AdventureWorks          604

AdventureWorks2008      646

AdventureWorksDW        151

AdventureWorksDW2008    164

AdventureWorksLT        158

AdventureWorksLT2008    158

*/

 

/* FOURTH EXAMPLE - automatic T-SQL code generation
        for datetime conversion from style 0 to 14
*/


USE
AdventureWorks2008;

DECLARE  @I INT = -1

DECLARE  @SQLDynamic NVARCHAR(1024)

-- Temporary table is used for data sharing between parent & child processes

-- This is the parent process; the child process is the dynamic SQL execution

CREATE TABLE #SQL (

  STYLE  INT,

  [SQL]  VARCHAR(256),

  Result VARCHAR(32))

-- Loop on @I from 0 to 13

WHILE (@I < 14)

  BEGIN

    SET @I += 1

    -- Store query and dynamic results in temporary table

    INSERT #SQL (STYLE, [SQL])

    SELECT @I,

           'SELECT ' + 'CONVERT(VARCHAR, GETDATE(), ' +

                        CONVERT(VARCHAR,@I) + ')'

    -- Build dynamic sql statement

    SET @SQLDynamic = 'UPDATE #SQL SET Result=(SELECT  CONVERT(VARCHAR,

        GETDATE(), ' + CONVERT(VARCHAR,@I) + ')) WHERE STYLE=' +

                       CONVERT(VARCHAR,@I)

    PRINT @SQLDynamic

    /*

    UPDATE #SQL SET Result=(SELECT  CONVERT(VARCHAR,

        GETDATE(), 0)) WHERE STYLE=0

    */

    EXEC sp_executeSQL  @SQLDynamic

  END

-- Return results from temporary table

SELECT * FROM   #SQL

DROP TABLE #SQL

GO

/* Partial results

 

STYLE SQL                                       Result

0     SELECT CONVERT(VARCHAR, GETDATE(), 0)     Mar 14 2009  6:10AM

1     SELECT CONVERT(VARCHAR, GETDATE(), 1)     03/14/09

2     SELECT CONVERT(VARCHAR, GETDATE(), 2)     09.03.14

*/

-- FIFTH EXAMPLE - dynamic pivot crosstab query

------------

-- T-SQL Dynamic Pivot Crosstab Report - Column header YYYY is dynamically assembled

------------

USE AdventureWorks

GO

DECLARE  @YearList  AS  TABLE(

                               YYYY INT    NOT NULL    PRIMARY KEY

                               )

DECLARE  @DynamicSQL  AS NVARCHAR(MAX)

INSERT INTO @YearList

SELECT DISTINCT YEAR(OrderDate)

FROM   Sales.SalesOrderHeader

 

DECLARE  @ReportColumnNames  AS NVARCHAR(MAX),

         @IterationYear      AS INT

 

SET @IterationYear = (SELECT MIN(YYYY)

                      FROM   @YearList)

 

SET @ReportColumnNames = N''

 

-- Assemble pivot list dynamically

WHILE (@IterationYear IS NOT NULL)

  BEGIN

    SET @ReportColumnNames = @ReportColumnNames + N',' + QUOTENAME(CAST(@IterationYear AS NVARCHAR(10)))

    

    SET @IterationYear = (SELECT MIN(YYYY)

                          FROM   @YearList

                          WHERE  YYYY > @IterationYear)

  END

 

SET @ReportColumnNames = SUBSTRING(@ReportColumnNames,2,LEN(@ReportColumnNames))

 

PRINT @ReportColumnNames

 

-- [2001],[2002],[2003],[2004]

SET @DynamicSQL = N'SELECT * FROM (SELECT [Store (Freight Summary)]=s.Name,    

YEAR(OrderDate) AS OrderYear, 

Freight = convert(money,convert(varchar, Freight))    

FROM Sales.SalesOrderHeader soh    

INNER JOIN Sales.Store s    

ON soh.CustomerID = s.CustomerID) as Header    

PIVOT (SUM(Freight)    FOR OrderYear

IN(' + @ReportColumnNames + N'))  AS Pvt

ORDER BY 1'

 

PRINT @DynamicSQL -- Testing & debugging 

 

/* 

 

SELECT * FROM (SELECT [Store (Freight Summary)]=s.Name,    

YEAR(OrderDate) AS OrderYear, 

Freight = convert(money,convert(varchar, Freight))    

FROM Sales.SalesOrderHeader soh    

INNER JOIN Sales.Store s    

ON soh.CustomerID = s.CustomerID) as Header    

PIVOT (SUM(Freight)    FOR OrderYear

IN([2001],[2002],[2003],[2004]))  AS Pvt

ORDER BY 1

 

*/

-- Execute dynamic sql

EXEC sp_executesql   @DynamicSQL

GO
-- Partial results

Store (Freight Summary) 2001 2002 2003 2004
A Bike Store 921.55 1637.24 NULL NULL
A Great Bicycle Company 142.08 114.34 15.24 NULL
A Typical Bike Shop 976.61 1529.08 NULL NULL
Acceptable Sales & Service 12.58 25.17 NULL NULL
Accessories Network NULL NULL 24.72 43.06
Acclaimed Bicycle Company NULL NULL 190.01 53.8
Ace Bicycle Supply NULL 21.46 21.67 69.46

------------

-- SIXTH EXAMPLE - dynamic stored procedure with output


-- SQL Server dynamic SQL stored procedure to find size for all databases

CREATE PROC sprocSizeForAllDBs

AS

BEGIN

    DECLARE  @dbName      SYSNAME,

             @ObjectSize INT

    DECLARE  @SQL NVARCHAR(MAX)

    DECLARE  @DBSizes  TABLE(

                                   DBName             SYSNAME,

                                   DBSizeinMB       MONEY

                                   )

    DECLARE curAllDBs CURSOR  FOR

    SELECT   name

    FROM     MASTER.dbo.sysdatabases

    WHERE    name NOT IN ('master','tempdb','model','msdb')

    ORDER BY name

    OPEN curAllDBs

    FETCH  curAllDBs

    INTO @dbName

    WHILE (@@FETCH_STATUS = 0) -- Loop through all db-s 

      BEGIN

        -- Build valid yet hard-wired SQL statement

        SET @SQL = 'select @DBSize = 0.0078125 * sum(size) ' + char(13) +

                   'from ' + QuoteName(@dbName) + '.dbo.sysfiles'

        PRINT @SQL -- test & debug 

/* 

select @DBSize = 0.0078125 * sum(size)

from [AdventureWorks].dbo.sysfiles

*/

 

-- Dynamic call for query execution with output parameter(s)

        EXEC sp_executesql  @SQL ,

                                          N'@DBSize Money output' ,

                                          @DBSize = @ObjectSize OUTPUT

        INSERT @DBSizes

        SELECT @dbName,  @ObjectSize

        

        FETCH  curAllDBs

        INTO @dbName

      END -- while 

    CLOSE curAllDBs

    DEALLOCATE curAllDBs

    INSERT @DBSizes  -- total size

    SELECT 'Total Space Used', SUM(DBSizeinMB) FROM @DBSizes   

    -- Return results

    SELECT *

    FROM     @DBSizes

    ORDER BY DBSizeinMB DESC

END -- sproc

GO

 

EXEC sprocSizeForAllDBs

/*

DBName                    DBSizeinMB

....

AdventureWorks            172.00

AdventureWorks2008        182.00

AdventureWorksDW          69.00

AdventureWorksDW2008      87.00

.....

*/

------------

 

The Best SQL Server Training in the World