Call now: (800) 766-1884  



 Home


 SQL Server Tips
 SQL Server Training

 SQL Server Consulting
 SQL Server Support
 SQL Server Remote DBA



 Articles
 Services
 SQL Server Scripts
 Scripts Menu



 

 

 

 
  SQL Server Tips by Gama and Naughter

 

Reuse code for different objects
 

If the same code is used very often for different objects and the only change is the objectís name then using dynamic SQL will certainly allow for consolidating this code in one object only.

 

Example: Create an SP that will return the mean from a selected column from a table. The column and table names are the input parameters.

 

CREATE PROCEDURE myDynamicMean (@Column NVARCHAR(128), @Table NVARCHAR(128))

--Calculates the mean of any column from any table dynamically

AS

DECLARE @SQL NVARCHAR(4000)

SET @SQL='SELECT AVG('+@Column+'*1.0) AS Mean FROM '+@Table

EXEC(@SQL)

 

The dynamic code has the usual code to return the mean value of a column from a table but both table and column names are variable.

 

Example: Create an SP that will return the mode from a selected column from a table. The column and table names are the input parameters.

 

CREATE PROCEDURE myDynamicMode (@Column NVARCHAR(128), @Table NVARCHAR(128))

--Calculates the modal of any column from any table dynamically

AS

DECLARE @SQL NVARCHAR(4000)

SET @SQL='SELECT '+@Column+' AS Mode FROM '+@Table+' GROUP BY '+@Column+' HAVING COUNT(*) =

(SELECT MAX(tcount) as Tmax FROM (SELECT COUNT(*) as tcount  FROM  '+@Table+'  GROUP BY '+@Column+') tmpCount)'

EXEC(@SQL)

 

In this example there are more changes to be performed to the dynamic code because the parameters are to replace more sections of the dynamic code than in the previous example. It is still an exercise of substitution.

 

Reuse code for different databases
 

An interesting advantage of using dynamic SQL is that it provides access to any database because the dynamic code can append the database name before an objectís name. The previous examples can be called with the database name specified and the SP will get the table data from the corresponding databases:

 

There are two main reasons to use the same code amongst different databases, the first one is that the code might do some maintenance or administrative tasks; the second is that different versions of the same database are kept because either some versions contain historic data or each version should be independent from the rest. Keeping the code in one place allows easier maintenance, although not better performance.

 

Example: Create an SP that will list all the user tables from a database.

The input is the database name.

 

CREATE PROCEDURE ListTables @db NVARCHAR(128)

--lists all the user tables for the selected database

AS

EXEC('SELECT name

FROM  '+@db+'..sysobjects

WHERE (type = ''u'') AND (name <> N''dtproperties'')')

 

In this example the database name is the input while in the previous example the table name had the database name attached. If the SP joins the table name from the input with another table the effect could be an heterogeneous query. If this is not the desired effect, it will probably result in an error.

 

Example: Create an SP that will list all the user table names from two databases, which have similar names.

 

CREATE PROCEDURE CompareTables @db NVARCHAR(128)

--lists all the user tables with similar names from the current database and the selected database

AS

EXEC('SELECT sysobjects.name, s2.name AS ['+@db+']

FROM  sysobjects LEFT OUTER JOIN

                ['+@db+']..sysobjects s2 ON SOUNDEX(sysobjects.name) = SOUNDEX(s2.name)

WHERE (sysobjects.type = ''u'') AND (s2.type = ''u'') AND (sysobjects.name <> N''dtproperties'') AND (s2.name <> N''dtproperties'')')

 

It is also useful to access tables or views from linked servers and even different instances from a linked server. To use an SP with a table from a linked server the server name must precede the table name, for example:
 

EXEC CompareTables ĎmyLinkedServer.northwindí
 

And an instance in that server:

 

EXEC CompareTables Ď[myLinkedServer\instance1].northwindí

 

Use variable names in statements that require constants
 

There are several TSQL statements that will generate an error message when trying to use a variable for its input. The most obvious case is the SELECT statement with the TOP clause. The TOP clause requires a constant while variables can be used as column values, as part of WHERE filter expressions or in CASE clauses.

 

Example: Create a SP that will return the top n rows from a table or view with n being the SPís input.

 

CREATE PROCEDURE myDynamicTopN (@Top int, @Table NVARCHAR(128))

--Retuns the top N rows from any table dynamically

AS

DECLARE @SQL NVARCHAR(4000)

SET @SQL='SELECT TOP '+CAST(@Top AS CHAR)+' * FROM '+@Table

EXEC(@SQL)

 

There is another way to obtain the same result; it is the SET ROWCOUNT statement. It causes the query being executed to perform changes to the first n rows with n defined by the SET ROWCOUNT statement. This statement affects not only SELECT but also DELETE, INSERT and UPDATE. This is an example of using such method:

 

CREATE PROCEDURE myDynamicTopN2 (@Top int, @Table NVARCHAR(128))

--Retuns the top N rows from any table dynamically

AS

SET ROWCOUNT @Top

DECLARE @SQL NVARCHAR(4000)

SET @SQL='SELECT * FROM '+@Table

EXEC(@SQL)

 

This method is not recommended and TOP should be used instead because of performance issues.

 

Example: Create a SP that will kill the process allocated to a user connection by the user name. For simplicity, let us assume that the user will take only one process.

 

CREATE PROCEDURE KillUserProcess @user nvarchar(128)

AS

--Kills one process from a certain user, assuming only one process is active

DECLARE @spid int

SELECT @spid=spid

      FROM  master.dbo.sysprocesses

WHERE rtrim(loginame)=@user

EXEC ('KILL '+ @spid)

 

The first part of the code will get the process id taken by the user, by querying sysprocesses. The KILL statement is one of those statements that require a constant as input.

 

Avoid statements with an extremely high number of possibilities
 

The crosstab SPís are the best examples where the input allows for a complex query to be created dynamically that would be impossible to hardcode. One example is when a decision is made based on a number, if the number is not within a very small range, then the logic tree could grow beyond practical limits. A decision based on names of people or cities would have the same problem.

 

Return rowsets with a variable number of columns
 

When creating queries for reports, sometimes the specifications ask for plenty of flexibility. Queries must always return a fixed number of columns and dynamic SQL is one way to work around that.

 

Example: Create a SP that will return the N first columns from a table. The input will be the table name and the number of columns to be returned.

 

CREATE PROCEDURE spTableNfirstColumns @table  NVARCHAR(128), @Ncols int

--returns the N first columns from a table

AS

DECLARE @ColNames NVARCHAR(500), @SQL NVARCHAR(4000)

SET @ColNames=''

SELECT @ColNames=@ColNames+N','+COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS

WHERE TABLE_NAME=@table AND ORDINAL_POSITION<=@Ncols

SET @ColNames=RIGHT(@ColNames, LEN(@ColNames)-1)

SET @SQL=N'SELECT '+@ColNames+N' FROM '+@table

EXEC (@SQL)

 

Example: Create a SP that will return the product names from table Categories and a variable number of columns, each one corresponding to a category. Under each category column, there would be the number of products in stock.

 

CREATE PROCEDURE spProductsByCategory

--returns the product names and their number in stock, with the number under a category column

AS

DECLARE @SQL NVARCHAR(4000)

SET @SQL='SELECT Products.ProductName, '

DECLARE @CategoryID int, @CategoryName NVARCHAR(15)

DECLARE cur_ds  CURSOR

FOR SELECT CategoryID, CategoryName FROM  Categories

OPEN cur_ds

FETCH NEXT FROM cur_ds INTO @CategoryID, @CategoryName

WHILE @@FETCH_STATUS = 0

      BEGIN

      SET @SQL=@SQL+'CASE Products.categoryid WHEN '+CAST(@CategoryID as CHAR)

      +' THEN Products.UnitsInStock ELSE 0 END AS ['+CAST(@CategoryName as CHAR)+'],'

      FETCH NEXT FROM cur_ds INTO @CategoryID, @CategoryName

      END

CLOSE cur_ds

DEALLOCATE cur_ds

SET @SQL=LEFT(@SQL,LEN(@SQL)-1)--remove the last comma

SET @SQL=@SQL+'FROM  Products INNER JOIN Categories ON Products.CategoryID = Categories.CategoryID'

EXEC (@SQL)

 

Allow parameterized filtering with the IN clause
 

Example: Create a SP that will return rows from table Shippers filtering by ShipperID. Any combination of ShipperID should be possible by means of using the IN clause with its filter data as an input parameter of the SP.

 

CREATE PROCEDURE spOrdersByShipperID @Shippers varchar(20)

--returns the orders by any combination of shipper ID's

AS

EXEC('SELECT dbo.Orders.OrderID, dbo.Orders.ShipName, dbo.Orders.ShipCity, dbo.Orders.ShipRegion, dbo.Orders.ShipPostalCode, dbo.Orders.ShipCountry,

               dbo.Shippers.CompanyName

FROM  dbo.Orders INNER JOIN

               dbo.Shippers ON dbo.Orders.ShipVia = dbo.Shippers.ShipperID

WHERE (dbo.Shippers.ShipperID IN ('+@Shippers+'))')
 


The above book excerpt is from:

Super SQL Server Systems
Turbocharge Database Performance with C++ External Procedures

ISBN: 0-9761573-2-2
Joseph Gama, P. J. Naughter

 http://www.rampant-books.com/book_2005_2_sql_server_external_procedures.htm
 

 

Burleson Consulting Remote DB Administration


 

 


 

 

 

 

 
Burleson is the America's Team

Note: The pages on this site were created as a support and training reference for use by our staff of DBA consultants.  If you find it confusing, please exit this page.

Errata?  SQL Server technology is changing and we strive to update our SQL Server support information.  If you find an error or have a suggestion for improving our content, we would appreciate your feedback.  Just  e-mail:and include the URL for the page.
 


Burleson Consulting
SQL Server database support

 

Copyright © 1996 -  2013 by Vaaltech Web Services. All rights reserved.

Hit Counter