<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DZone Snippets: sql_server code</title>
    <link>http://snippets.dzone.com/posts</link>
    <pubDate>Fri, 25 Jul 2008 02:26:32 GMT</pubDate>
    <description>DZone Snippets: sql_server code</description>
    <item>
      <title>Force recompile of Sql Server stored procs</title>
      <link>http://snippets.dzone.com/posts/show/2132</link>
      <description>This force a recompile of all stored procs, views, tables, triggers, and functions in the current database.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CHECKPOINT &lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;DBCC DROPCLEANBUFFERS&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;DBCC FREEPROCCACHE&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;DECLARE @intDBID INTEGER &lt;br /&gt;SET @intDBID = DB_ID()&lt;br /&gt;&lt;br /&gt;DBCC FLUSHPROCINDB (@intDBID)&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;declare @procNames Table (procName varchar(255))&lt;br /&gt;insert into @procNames&lt;br /&gt;select name from sysObjects where xtype in ('V','P','U','FN','TF','TR' ) and status &gt; 0&lt;br /&gt;&lt;br /&gt;--select * from @procNames&lt;br /&gt;&lt;br /&gt;set nocount off&lt;br /&gt;--3. Run each command&lt;br /&gt;-- =============================================&lt;br /&gt;-- Declare and using a READ_ONLY cursor&lt;br /&gt;-- =============================================&lt;br /&gt;DECLARE RecompilableItemsCursor CURSOR&lt;br /&gt;READ_ONLY&lt;br /&gt;FOR select procName from @procNames&lt;br /&gt;&lt;br /&gt;DECLARE @RecompilableItem varchar(255)&lt;br /&gt;OPEN RecompilableItemsCursor&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;FETCH NEXT FROM RecompilableItemsCursor INTO @RecompilableItem&lt;br /&gt;WHILE (@@fetch_status &lt;&gt; -1)&lt;br /&gt;BEGIN&lt;br /&gt;	IF (@@fetch_status &lt;&gt; -2)&lt;br /&gt;	BEGIN&lt;br /&gt;		DECLARE @sql varchar(300)&lt;br /&gt;		select @sql = 'Exec sp_recompile ' + @RecompilableItem&lt;br /&gt;		print @sql&lt;br /&gt;		exec (@sql)&lt;br /&gt;	END&lt;br /&gt;	FETCH NEXT FROM RecompilableItemsCursor INTO @RecompilableItem&lt;br /&gt;END&lt;br /&gt;&lt;br /&gt;CLOSE RecompilableItemsCursor&lt;br /&gt;DEALLOCATE RecompilableItemsCursor&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Thu, 01 Jun 2006 19:28:53 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/2132</guid>
      <author>MattScilipoti (Matt Scilipoti)</author>
    </item>
    <item>
      <title>A generic cross tab / pivot table query for Transact SQL (with sortable columns)</title>
      <link>http://snippets.dzone.com/posts/show/2000</link>
      <description>This procedure generates a 'cross-tab' (or 'pivot table') result set from (almost) any query, even a stored procedure and optionally allows you to specify a column sort order. &lt;br /&gt;&lt;br /&gt;The procedure makes extensive use dynamic SQL and hence 'sp_executesql'. Because the scope of temporary tables does not extend outside of an sp_executesql transaction, I've had to use global temporary tables, which means that this procedure could become a bottleneck if used too frequently by multiple sessions.&lt;br /&gt; &lt;br /&gt;This stored procedure is provided 'as is' and without any warranty - I've only tested it on a relatively small number of queries. For more information (and my contact details) see http://nwsoft.blogspot.com/2006/05/generic-cross-tab-pivot-table-query.html&lt;br /&gt;&lt;br /&gt;This work is licensed under a Creative Commons Attribution 2.5 License.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CREATE PROCEDURE [dbo].[genericCrosstab]&lt;br /&gt;&lt;br /&gt; -- @tableSpec&lt;br /&gt; --   A comma delimited list of columns with datatypes (and null constraints, if required)&lt;br /&gt; --   e.g. 'col1 INT, col2 VARCHAR(255) NULL'&lt;br /&gt; --   N.B. You only need provide this list if the source of the data is a stored procedure&lt;br /&gt; @tableSpec    NVARCHAR(4000) = '',&lt;br /&gt;&lt;br /&gt; -- @sqlSelect&lt;br /&gt; --   The SELECT clause (or 'EXEC &lt;stored_procedure&gt;' statement) for the input data&lt;br /&gt; --   e.g. 'SELECT a.au_id, a.au_fname, a.au_lname, a.phone, LEFT(t.title, 10) AS [title], LEFT(s.stor_name, 10) AS [store], sales.qty'&lt;br /&gt; --   e.g. 'EXEC dbo.myStoredProcedure &lt;param1&gt;, &lt;param2&gt;'&lt;br /&gt; @sqlSelect    NVARCHAR(512),&lt;br /&gt;&lt;br /&gt; -- @sqlFromWhere&lt;br /&gt; --   The FROM and WHERE clauses for the input data (if a SELECT statement)&lt;br /&gt; --   e.g. 'FROM authors a, titleauthor ta, titles t, stores s, sales WHERE a.au_id = ta.au_id AND ta.title_id = t.title_id AND t.title_id = sales.title_id AND s.stor_id = sales.stor_id'&lt;br /&gt; --   N.B. Leave this blank if the source of the data is a stored procedure&lt;br /&gt; @sqlFromWhere NVARCHAR(512)  = '',&lt;br /&gt;&lt;br /&gt; -- @pivotExpr&lt;br /&gt; --   The column name or expression to use as the pivot (specifies which values are the column headings for the crosstabbed data)&lt;br /&gt; --   e.g. 'store' or '''Store: '' + [store]'&lt;br /&gt; @pivotExpr    NVARCHAR(255),&lt;br /&gt;&lt;br /&gt; -- @valueExpr&lt;br /&gt; --   A column name or expression to use as the values in the crosstabbed data&lt;br /&gt; --   e.g. 'qty'&lt;br /&gt; @valueExpr    NVARCHAR(512),&lt;br /&gt;&lt;br /&gt; -- @function&lt;br /&gt; --   The aggregation function to use to combine values in the crosstabbed data&lt;br /&gt; --   e.g. 'MIN'&lt;br /&gt; --   N.B. If this parameter is left blank, the procedure will count up occurences and create additional column headings as necessary, e.g. '[X], [Y], [Y (2)], [Z]' if there are two occurences for label 'Y'&lt;br /&gt; @function     NVARCHAR(20)   = '',&lt;br /&gt;&lt;br /&gt; -- @groupBy&lt;br /&gt; --   The column list to group by (these columns will appear to the left of the crosstabbed data)&lt;br /&gt; --   e.g. 'au_id, au_fname, au_lname, phone'&lt;br /&gt; @groupBy      NVARCHAR(512),&lt;br /&gt;&lt;br /&gt; -- @sortLookup&lt;br /&gt; --   A table or view which can be queried to provide a sort order for the columns in the crosstabbed data&lt;br /&gt; --   e.g. 'days_of_the_week'&lt;br /&gt; --   N.B. The specified table or view must have 'label' and 'sort_order' columns&lt;br /&gt; --   N.B. If column order is irrelevant, leave this parameter blank&lt;br /&gt; --   N.B. Feature: if @sortLookup is given as '*', the columns are placed in ascending alphabetical order&lt;br /&gt; @sortLookup   NVARCHAR(40)   = '',&lt;br /&gt;&lt;br /&gt; -- @collation&lt;br /&gt; --   The collation sequence&lt;br /&gt; --   e.g. 'Latin1_general_CI_AS'&lt;br /&gt; @collation   NVARCHAR(40)   = 'Latin1_general_CI_AS',&lt;br /&gt;&lt;br /&gt; -- @verbose&lt;br /&gt; --   Set to 1 if you want debug messages&lt;br /&gt; @verbose     BIT            = 0&lt;br /&gt;&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt; -- Check that the mandatory parameters are not empty strings&lt;br /&gt; IF LEN(RTRIM(ISNULL(@sqlSelect, ''))) = 0&lt;br /&gt; BEGIN&lt;br /&gt;   RAISERROR ('The @sqlSelect parameter cannot be an empty string.', 16, 1)&lt;br /&gt;   ROLLBACK TRANSACTION&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; IF LEN(RTRIM(ISNULL(@pivotExpr, ''))) = 0&lt;br /&gt; BEGIN&lt;br /&gt;   RAISERROR ('The @pivotExpr parameter cannot be an empty string.', 16, 1)&lt;br /&gt;   ROLLBACK TRANSACTION&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; IF LEN(RTRIM(ISNULL(@valueExpr, ''))) = 0&lt;br /&gt; BEGIN&lt;br /&gt;   RAISERROR ('The @valueExpr parameter cannot be an empty string.', 16, 1)&lt;br /&gt;   ROLLBACK TRANSACTION&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; IF LEN(RTRIM(ISNULL(@groupBy, ''))) = 0&lt;br /&gt; BEGIN&lt;br /&gt;   RAISERROR ('The @groupBy parameter cannot be an empty string.', 16, 1)&lt;br /&gt;   ROLLBACK TRANSACTION&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; -- Local variables&lt;br /&gt; DECLARE @i      INTEGER&lt;br /&gt; DECLARE @sql    NVARCHAR(4000)&lt;br /&gt; DECLARE @sqlX   NVARCHAR(4000)&lt;br /&gt; DECLARE @col    NVARCHAR(4000)&lt;br /&gt; DECLARE @pivot  NVARCHAR(4000)&lt;br /&gt; DECLARE @indx   NVARCHAR(10)&lt;br /&gt; DECLARE @cols   NVARCHAR(4000)&lt;br /&gt; DECLARE @where  NVARCHAR(4000)&lt;br /&gt; DECLARE @update NVARCHAR(4000)&lt;br /&gt; DECLARE @value  NVARCHAR(532)&lt;br /&gt; DECLARE @select NVARCHAR(4000)&lt;br /&gt;&lt;br /&gt; -- Drop the global temporary tables we will use (if they already exist)&lt;br /&gt; -- N.B. we have to use global temp tables, as local temp tables are dropped at the end of a batch (so are not accessible outside of the sp_executesql context)&lt;br /&gt; -- This syntax is a bit long winded because I couldn't make the substitution syntax for sp_executesql work (for this example only! - a bug in T-SQL?)&lt;br /&gt; SELECT @sql = N'IF EXISTS (SELECT 1 FROM tempdb.dbo.sysobjects WHERE name = ''@table'' AND xtype = ''U'') DROP TABLE @table'&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##input')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##grpCount')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##colList')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##lookup')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##results')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##temp')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt;&lt;br /&gt; IF @verbose = 1 SELECT 'Global temporary tables dropped (if present).' AS [Message]&lt;br /&gt;&lt;br /&gt; -- * The real work starts here&lt;br /&gt;&lt;br /&gt; -- Store the input dataset&lt;br /&gt; IF LEN(RTRIM(@tableSpec)) = 0&lt;br /&gt; BEGIN&lt;br /&gt;   -- Create and populate the temporary table in one step&lt;br /&gt;   SELECT @sql = @sqlSelect + ' INTO ##input ' + @sqlFromWhere&lt;br /&gt;   IF @verbose = 1 SELECT @sql AS [Create and populate the temporary table in one step]&lt;br /&gt;   EXEC sp_executesql @sql&lt;br /&gt; END&lt;br /&gt; ELSE&lt;br /&gt; BEGIN&lt;br /&gt;   -- Create the temporary table first (the only method that can be used if the data is coming from a stored proc)&lt;br /&gt;   SELECT @sql = 'CREATE TABLE ##input (' + @tableSpec + ')'&lt;br /&gt;   IF @verbose = 1 SELECT @sql AS [Create the temporary table]&lt;br /&gt;   EXEC sp_executesql @sql&lt;br /&gt;   -- Populate it&lt;br /&gt;   SELECT @sql = 'INSERT INTO ##input ' + @sqlSelect + ' ' + @sqlFromWhere&lt;br /&gt;   IF @verbose = 1 SELECT @sql AS [Populate the temporary table]&lt;br /&gt;   EXEC sp_executesql @sql&lt;br /&gt; END&lt;br /&gt; IF @verbose = 1 SELECT * FROM ##input&lt;br /&gt;&lt;br /&gt; -- Does the query we want to cross-tab already have a column called 'crossTabRowID'? This is a reserved column name!&lt;br /&gt; IF EXISTS(SELECT 1 FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c WHERE o.id = c.id AND o.name = '##input' AND c.name = 'crossTabRowID')&lt;br /&gt; BEGIN&lt;br /&gt;   RAISERROR ('The query passed to the [genericCrosstab] procedure uses a reserved column name (''crossTabRowID'').', 16, 1)&lt;br /&gt;   ROLLBACK TRANSACTION&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; -- Add an identity column (we need a 'row ID')&lt;br /&gt; ALTER TABLE ##input ADD crossTabRowID NUMERIC(9, 0) IDENTITY NOT NULL&lt;br /&gt;&lt;br /&gt; -- Generate the column list&lt;br /&gt; SELECT @sql = 'SELECT ' + @groupBy + ' INTO ##colList FROM ##input WHERE 1 = 0'&lt;br /&gt; IF @verbose = 1 SELECT @sql AS [Generate the column list]&lt;br /&gt; EXEC sp_executesql @sql&lt;br /&gt; IF @verbose = 1 SELECT * FROM ##colList&lt;br /&gt;&lt;br /&gt; -- If no aggregation function is specified, we have to check first that there are no duplicates, and - if there are - we need to handle them&lt;br /&gt; IF RTRIM(ISNULL(@function, '')) = ''&lt;br /&gt; BEGIN&lt;br /&gt;   SELECT @sql = 'SELECT ' + @groupBy + ', ' + @pivotExpr + ' COLLATE ' + @collation + ' AS [pivot], COUNT(1) AS [count] INTO ##grpCount FROM ##input GROUP BY ' + @groupBy + ', ' + @pivotExpr + ' COLLATE ' + @collation&lt;br /&gt; END&lt;br /&gt; ELSE&lt;br /&gt; BEGIN&lt;br /&gt;   SELECT @sql = 'SELECT ' + @groupBy + ', ' + @pivotExpr + ' COLLATE ' + @collation + ' AS [pivot], 1 AS [count] INTO ##grpCount FROM ##input GROUP BY ' + @groupBy + ', ' + @pivotExpr + ' COLLATE ' + @collation&lt;br /&gt; END&lt;br /&gt; IF @verbose = 1 SELECT @sql AS [Create the ##grpCount table]&lt;br /&gt; EXEC sp_executesql @sql&lt;br /&gt; IF @verbose = 1 SELECT * FROM ##grpCount&lt;br /&gt;&lt;br /&gt; -- Create a temporary table that will act as a lookup (containing all of the non-pivot / non-group columns names)&lt;br /&gt; SELECT [pivot], [count] AS [index], [pivot] AS [column_name] INTO ##lookup FROM ##grpCount WHERE 1 = 0&lt;br /&gt;&lt;br /&gt; -- Build the results table; one row per group&lt;br /&gt; SELECT @sql = 'SELECT ' + @groupBy + ' INTO ##results FROM ##grpCount GROUP BY ' + @groupBy&lt;br /&gt; IF @verbose = 1 SELECT @sql AS [Create the ##results table]&lt;br /&gt; EXEC sp_executesql @sql&lt;br /&gt; IF @verbose = 1 SELECT * FROM ##results&lt;br /&gt;&lt;br /&gt; -- Build the column list, taking into account duplicate occurences of pivotal values&lt;br /&gt; DECLARE xcursor CURSOR FOR SELECT [pivot], MAX([count]) FROM ##grpCount GROUP BY [pivot]&lt;br /&gt; OPEN xcursor&lt;br /&gt;&lt;br /&gt; FETCH NEXT FROM xcursor INTO @pivot, @indx&lt;br /&gt;&lt;br /&gt; WHILE @@FETCH_STATUS = 0&lt;br /&gt; BEGIN&lt;br /&gt;&lt;br /&gt;  SELECT @i = 1&lt;br /&gt;&lt;br /&gt;  -- Loop over indx&lt;br /&gt;  WHILE @i &lt;= @indx&lt;br /&gt;  BEGIN&lt;br /&gt;&lt;br /&gt;    -- Build the column list&lt;br /&gt;    SELECT @col = CASE @i WHEN 1 THEN @pivot ELSE @pivot + ' (' + CAST(@i AS VARCHAR(10)) + ')' END&lt;br /&gt;    INSERT INTO ##lookup VALUES (@pivot, @i, @col)&lt;br /&gt;    SELECT @col = '[' + @col + '] NVARCHAR(255) NULL'&lt;br /&gt;    SELECT @cols =  ISNULL(@cols + ', ', '') + @col&lt;br /&gt;&lt;br /&gt;    -- Add the column to the results table&lt;br /&gt;    SELECT @sql = 'ALTER TABLE ##results ADD ' + @col&lt;br /&gt;    IF @verbose = 1 SELECT @sql AS [Add column to the ##results table]&lt;br /&gt;    EXEC sp_executesql @sql&lt;br /&gt;&lt;br /&gt;    -- Continue&lt;br /&gt;    SELECT @i = @i + 1&lt;br /&gt;&lt;br /&gt;  END&lt;br /&gt;  &lt;br /&gt;  FETCH NEXT FROM xcursor INTO @pivot, @indx&lt;br /&gt;&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; CLOSE xcursor&lt;br /&gt; DEALLOCATE xcursor &lt;br /&gt;&lt;br /&gt; IF @verbose = 1 SELECT * FROM ##lookup&lt;br /&gt; IF @verbose = 1 SELECT * FROM ##results&lt;br /&gt;&lt;br /&gt; -- Loop over the column list (using the syscolumns table in the temp database) to build the WHERE clause&lt;br /&gt; DECLARE xcursor CURSOR FOR SELECT DISTINCT c.[colid], c.[name] FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c WHERE o.[id] = c.[id] AND o.[name] = '##colList' ORDER BY c.[colid]&lt;br /&gt; OPEN xcursor&lt;br /&gt;&lt;br /&gt; FETCH NEXT FROM xcursor INTO @indx, @col&lt;br /&gt;&lt;br /&gt; WHILE @@FETCH_STATUS = 0&lt;br /&gt; BEGIN&lt;br /&gt;  &lt;br /&gt;   -- Build the WHERE clause&lt;br /&gt;   SELECT @where = ISNULL(@where + ' AND ', '') + 'ISNULL(t1.[' + @col + '], '''') = ISNULL(t3.[' + @col + '], '''')'&lt;br /&gt;  &lt;br /&gt;   FETCH NEXT FROM xcursor INTO @indx, @col&lt;br /&gt;&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; CLOSE xcursor&lt;br /&gt; DEALLOCATE xcursor &lt;br /&gt;&lt;br /&gt; IF @verbose = 1 SELECT @where AS [WHERE clause]&lt;br /&gt;&lt;br /&gt; -- Create a temp table that will help us build the UPDATE statements to set the values in the pivot table, and the SELECT statement (with columns in preferred order) afterwards&lt;br /&gt; IF RTRIM(ISNULL(@sortLookup, '')) = ''&lt;br /&gt; BEGIN&lt;br /&gt;&lt;br /&gt;   -- If no sorting table was specified, just select all columns&lt;br /&gt;   SELECT @select = '*'&lt;br /&gt;&lt;br /&gt;   -- Sort alphabetically&lt;br /&gt;   SELECT @sql = 'SELECT DISTINCT t5.[index], c.[name], t5.[pivot] INTO ##temp FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c, ##lookup t5 WHERE o.[id] = c.[id] AND o.[name] = ''##results'' AND c.[name] COLLATE ' + @collation + ' = t5.[column_name] COLLATE ' + @collation + ' ORDER BY c.[name], t5.[index]'&lt;br /&gt;&lt;br /&gt; END&lt;br /&gt; ELSE&lt;br /&gt; BEGIN&lt;br /&gt;&lt;br /&gt;   -- The 'group by' column(s) always come(s) first&lt;br /&gt;   SELECT @select = @groupBy&lt;br /&gt;&lt;br /&gt;   -- Sort in proscribed order&lt;br /&gt;   SELECT @sql = 'SELECT DISTINCT t5.[index], c.[name], t5.[pivot], l.[sort_order] INTO ##temp FROM tempdb.dbo.sysobjects o, tempdb.dbo.syscolumns c, ##lookup t5, ' + @sortLookup + ' l WHERE o.[id] = c.[id] AND o.[name] = ''##results'' AND t5.[pivot] COLLATE ' + @collation + ' *= l.[label] COLLATE ' + @collation + ' AND c.[name] COLLATE ' + @collation + ' = t5.[column_name] COLLATE ' + @collation + ' ORDER BY l.[sort_order], c.[name], t5.[index]'&lt;br /&gt;&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; IF @verbose = 1 SELECT @sql AS [Build the ##temp table]&lt;br /&gt; EXEC sp_executesql @sql &lt;br /&gt; IF @verbose = 1 SELECT * FROM ##temp&lt;br /&gt;&lt;br /&gt; -- The value expression&lt;br /&gt; SELECT @value = @valueExpr&lt;br /&gt;&lt;br /&gt; -- If a function has been specified&lt;br /&gt; IF RTRIM(ISNULL(@function, '')) != ''&lt;br /&gt; BEGIN&lt;br /&gt;   SELECT @value = @function + '(' + @value + ')'&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; DECLARE xcursor CURSOR FOR SELECT [index], [name], [pivot] FROM ##temp&lt;br /&gt; OPEN xcursor&lt;br /&gt;&lt;br /&gt; FETCH NEXT FROM xcursor INTO @indx, @col, @pivot&lt;br /&gt;&lt;br /&gt; WHILE @@FETCH_STATUS = 0&lt;br /&gt; BEGIN&lt;br /&gt;&lt;br /&gt;  -- Build the SELECT expression&lt;br /&gt;  IF @select != '*' SELECT @select = @select + ', ' + @col&lt;br /&gt;&lt;br /&gt;  -- Create the SET clause of the UPDATE sql&lt;br /&gt;  IF RTRIM(ISNULL(@function, '')) = ''&lt;br /&gt;  BEGIN&lt;br /&gt;    -- No function specified&lt;br /&gt;    SELECT @update = '[' + @col + '] = (SELECT ' + @value + ' FROM ##input t1 WHERE ' + @where + ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + ''' COLLATE ' + @collation + ' AND t1.crossTabRowID = (SELECT MIN(t0.crossTabRowID) FROM ##input t0 WHERE ' + REPLACE(@where, 't3.', 't0.') + ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + '''' + ' COLLATE ' + @collation + ') + (' + CAST(@indx AS VARCHAR(3)) + ' - 1))'&lt;br /&gt;  END&lt;br /&gt;  ELSE&lt;br /&gt;  BEGIN&lt;br /&gt;    -- Function specified&lt;br /&gt;    SELECT @update = '[' + @col + '] = (SELECT ' + @value + ' FROM ##input t1 WHERE ' + @where + ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + ''' COLLATE ' + @collation + ' AND t1.crossTabRowID IN (SELECT t0.crossTabRowID FROM ##input t0 WHERE ' + REPLACE(@where, 't3.', 't0.') + ' AND ' + @pivotExpr + ' COLLATE ' + @collation + ' = ''' + @pivot + '''' + ' COLLATE ' + @collation + '))'&lt;br /&gt;  END&lt;br /&gt;  SELECT @sql = 'UPDATE ##results SET ' + @update + ' FROM ##results t3'&lt;br /&gt;  IF @verbose = 1 SELECT @sql AS [Create the SET clause of the UPDATE sql]&lt;br /&gt;  EXEC sp_executesql @sql&lt;br /&gt;  &lt;br /&gt;  FETCH NEXT FROM xcursor INTO @indx, @col, @pivot&lt;br /&gt;&lt;br /&gt; END&lt;br /&gt;&lt;br /&gt; CLOSE xcursor&lt;br /&gt; DEALLOCATE xcursor &lt;br /&gt;&lt;br /&gt; -- Return the results&lt;br /&gt; SELECT @sql = 'SELECT ' + @select + ' FROM ##results'&lt;br /&gt; IF @verbose = 1 SELECT @sql AS [Create the SELECT statement that will return the results]&lt;br /&gt; EXEC sp_executesql @sql&lt;br /&gt;&lt;br /&gt; -- Tidy up: drop the global temporary tables&lt;br /&gt; SELECT @sql = N'IF EXISTS (SELECT 1 FROM tempdb.dbo.sysobjects WHERE name = ''@table'' AND xtype = ''U'') DROP TABLE @table'&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##input')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##grpCount')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##colList')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##lookup')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##results')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt; SELECT @sqlX = REPLACE(@sql, N'@table', N'##temp')&lt;br /&gt; EXEC sp_executesql @sqlX&lt;br /&gt;&lt;br /&gt;END&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The following script creates two tables (one of test data, the other to specify a sort order) and runs the genericCrosstab procedure to generate a report.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;IF EXISTS (SELECT *&lt;br /&gt;             FROM dbo.sysobjects&lt;br /&gt;            WHERE [id] = object_id(N'[dbo].[testTable]')&lt;br /&gt;              AND OBJECTPROPERTY(id, N'IsTable') = 1)&lt;br /&gt;BEGIN&lt;br /&gt;  DROP TABLE [dbo].[testTable]&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;CREATE TABLE testTable&lt;br /&gt; ([who]  VARCHAR(10)  NOT NULL,&lt;br /&gt;  [when] DATETIME     NOT NULL,&lt;br /&gt;  [what] VARCHAR(255) NOT NULL,&lt;br /&gt;  [qty]  INT          NOT NULL)&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;DELETE FROM testTable&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;INSERT INTO testTable VALUES ('Fred', '2005-APR-01', 'Coding',  5)&lt;br /&gt;INSERT INTO testTable VALUES ('Jo',   '2005-APR-01', 'Coding',  1)&lt;br /&gt;INSERT INTO testTable VALUES ('Jo',   '2005-APR-01', 'Testing', 7)&lt;br /&gt;INSERT INTO testTable VALUES ('Fred', '2005-APR-01', 'Coding',  3)&lt;br /&gt;INSERT INTO testTable VALUES ('Tim',  '2005-APR-01', 'Coding',  4)&lt;br /&gt;INSERT INTO testTable VALUES ('Fred', '2005-APR-02', 'Coding',  8)&lt;br /&gt;INSERT INTO testTable VALUES ('Jo',   '2005-APR-02', 'Coding',  2)&lt;br /&gt;INSERT INTO testTable VALUES ('Jo',   '2005-APR-02', 'Testing', 6)&lt;br /&gt;INSERT INTO testTable VALUES ('Tim',  '2005-APR-02', 'Coding',  4)&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;SELECT * FROM testTable&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;EXEC [dbo].[genericCrosstab]&lt;br /&gt; @sqlSelect    = 'SELECT *',&lt;br /&gt; @sqlFromWhere = 'FROM testTable',&lt;br /&gt; @pivotExpr    = 'what',&lt;br /&gt; @valueExpr    = 'qty',&lt;br /&gt; @function     = 'SUM',&lt;br /&gt; @groupBy      = 'who'&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;-- Extending the example to proscribe column order...&lt;br /&gt;&lt;br /&gt;IF EXISTS (SELECT *&lt;br /&gt;             FROM dbo.sysobjects&lt;br /&gt;            WHERE [id] = object_id(N'[dbo].[testSorter]')&lt;br /&gt;              AND OBJECTPROPERTY(id, N'IsTable') = 1)&lt;br /&gt;BEGIN&lt;br /&gt;  DROP TABLE [dbo].[testSorter]&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;CREATE TABLE testSorter&lt;br /&gt; ([label]      VARCHAR(255) NOT NULL,&lt;br /&gt;  [sort_order] INT          NOT NULL)&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;DELETE FROM testSorter&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;INSERT INTO testSorter VALUES ('Testing',  1)&lt;br /&gt;INSERT INTO testSorter VALUES ('Coding',   2)&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;SELECT * FROM testSorter&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;EXEC [dbo].[genericCrosstab]&lt;br /&gt; @sqlSelect    = 'SELECT *',&lt;br /&gt; @sqlFromWhere = 'FROM testTable',&lt;br /&gt; @pivotExpr    = 'what',&lt;br /&gt; @valueExpr    = 'qty',&lt;br /&gt; @function     = 'SUM',&lt;br /&gt; @groupBy      = 'who',&lt;br /&gt; @sortLookup   = 'testSorter',&lt;br /&gt; @verbose      = 0&lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You can change @verbose to 1 to see what is going on.</description>
      <pubDate>Tue, 09 May 2006 13:11:23 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/2000</guid>
      <author>rgedwards (Rich Edwards (rich at semantise dot com))</author>
    </item>
    <item>
      <title>splitting a text list in sql</title>
      <link>http://snippets.dzone.com/posts/show/1563</link>
      <description>This goes along with my integer split procedure.&lt;br /&gt;http://www.bigbold.com/snippets/posts/show/774&lt;br /&gt;&lt;br /&gt;Often times I have a list of integers I need to pass to the database to get worked on.  Such as checkboxes on a web page or some other list.  I needed some TSQL that would take a text string and split it by a separator, in this case a comma.  The following is the result of that need.&lt;br /&gt;The way I normally use it is in a stored procedure like the one below with several text type arguments.  This is a variation designed to split a list of strings separated by a special character sequence.  Image two lists, one of the ids and one of the data.  You parse the first list to get a table of the ids and you parse the second list to get the data and insert/update as appropriate.&lt;br /&gt;http://www.bigbold.com/snippets/posts/show/774&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[uspSplitTextList]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)&lt;br /&gt;   DROP PROCEDURE [dbo].[uspSplitTextList]&lt;br /&gt;GO&lt;br /&gt;                                      &lt;br /&gt;SET QUOTED_IDENTIFIER ON &lt;br /&gt;GO&lt;br /&gt;SET ANSI_NULLS ON &lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */&lt;br /&gt;-- uspSplitTextList&lt;br /&gt;--&lt;br /&gt;-- Description:&lt;br /&gt;--		splits a separated list of text items and returns the text items&lt;br /&gt;--&lt;br /&gt;-- Arguments:&lt;br /&gt;--		@list_text				- list of text items&lt;br /&gt;--		@Delimiter				- delimiter&lt;br /&gt;--&lt;br /&gt;-- Notes:&lt;br /&gt;-- 02/22/2006 - WSR : use DATALENGTH instead of LEN throughout because LEN doesn't count trailing blanks&lt;br /&gt;--&lt;br /&gt;-- History:&lt;br /&gt;-- 02/22/2006 - WSR : revised algorithm to account for items crossing 8000 character boundary&lt;br /&gt;--&lt;br /&gt;CREATE PROCEDURE uspSplitTextList&lt;br /&gt;	@list_text				text,&lt;br /&gt;   @Delimiter				varchar(3)&lt;br /&gt;AS&lt;br /&gt;&lt;br /&gt;SET NOCOUNT ON&lt;br /&gt;&lt;br /&gt;DECLARE @InputLen			integer			-- input text length&lt;br /&gt;DECLARE @TextPos			integer			-- current position within input text&lt;br /&gt;DECLARE @Chunk				varchar(8000)	-- chunk within input text&lt;br /&gt;DECLARE @ChunkPos			integer			-- current position within chunk&lt;br /&gt;DECLARE @DelimPos			integer			-- position of delimiter&lt;br /&gt;DECLARE @ChunkLen			integer			-- chunk length&lt;br /&gt;DECLARE @DelimLen			integer			-- delimiter length&lt;br /&gt;DECLARE @ItemBegPos		integer			-- item starting position in text&lt;br /&gt;DECLARE @ItemOrder		integer			-- item order in list&lt;br /&gt;DECLARE @DelimChar		varchar(1)		-- first character of delimiter (simple delimiter)&lt;br /&gt;&lt;br /&gt;-- create table to hold list items&lt;br /&gt;-- actually their positions because we may want to scrub this list eliminating bad entries before substring is applied&lt;br /&gt;CREATE TABLE #list_items ( item_order integer, item_begpos integer, item_endpos integer )&lt;br /&gt;&lt;br /&gt;-- process list&lt;br /&gt;IF @list_text IS NOT NULL&lt;br /&gt;   BEGIN&lt;br /&gt;&lt;br /&gt;	-- initialize&lt;br /&gt;   SET @InputLen = DATALENGTH(@list_text)&lt;br /&gt;   SET @TextPos = 1&lt;br /&gt;	SET @DelimChar = SUBSTRING(@Delimiter, 1, 1)&lt;br /&gt;	SET @DelimLen = DATALENGTH(@Delimiter)&lt;br /&gt;   SET @ItemBegPos = 1&lt;br /&gt;   SET @ItemOrder = 1&lt;br /&gt;   SET @ChunkLen = 1&lt;br /&gt;&lt;br /&gt;   -- cycle through input processing chunks&lt;br /&gt;   WHILE @TextPos &lt;= @InputLen AND @ChunkLen &lt;&gt; 0&lt;br /&gt;      BEGIN&lt;br /&gt;&lt;br /&gt;      -- get current chunk&lt;br /&gt;      SET @Chunk = SUBSTRING(@list_text, @TextPos, 8000)&lt;br /&gt;&lt;br /&gt;      -- setup initial variable values&lt;br /&gt;      SET @ChunkPos = 1&lt;br /&gt;      SET @ChunkLen = DATALENGTH(@Chunk)&lt;br /&gt;      SET @DelimPos = CHARINDEX(@DelimChar, @Chunk, @ChunkPos)&lt;br /&gt;&lt;br /&gt;      -- loop over the chunk, until the last delimiter&lt;br /&gt;      WHILE @ChunkPos &lt;= @ChunkLen AND @DelimPos &lt;&gt; 0&lt;br /&gt;         BEGIN&lt;br /&gt;&lt;br /&gt;			-- see if this is a full delimiter&lt;br /&gt;         IF SUBSTRING(@list_text, (@TextPos + @DelimPos - 1), @DelimLen) = @Delimiter&lt;br /&gt;            BEGIN&lt;br /&gt;&lt;br /&gt;				-- insert position&lt;br /&gt;	         INSERT INTO #list_items (item_order, item_begpos, item_endpos)&lt;br /&gt;	         VALUES (@ItemOrder, @ItemBegPos, (@TextPos + @DelimPos - 1) - 1)&lt;br /&gt;	         &lt;br /&gt;	         -- adjust positions&lt;br /&gt;	         SET @ItemOrder = @ItemOrder + 1&lt;br /&gt;	         SET @ItemBegPos = (@TextPos + @DelimPos - 1) + @DelimLen&lt;br /&gt;	         SET @ChunkPos = @DelimPos + @DelimLen&lt;br /&gt;&lt;br /&gt;				END&lt;br /&gt;         ELSE&lt;br /&gt;            BEGIN&lt;br /&gt;&lt;br /&gt;            -- adjust positions&lt;br /&gt;            SET @ChunkPos = @DelimPos + 1&lt;br /&gt;&lt;br /&gt;            END&lt;br /&gt;      &lt;br /&gt;         -- find next delimiter      &lt;br /&gt;         SET @DelimPos = CHARINDEX(@DelimChar, @Chunk, @ChunkPos)&lt;br /&gt;&lt;br /&gt;         END&lt;br /&gt;&lt;br /&gt;      -- adjust positions&lt;br /&gt;      SET @TextPos = @TextPos + @ChunkLen&lt;br /&gt;&lt;br /&gt;      END&lt;br /&gt;&lt;br /&gt;	-- handle last item&lt;br /&gt;   IF @ItemBegPos &lt;= @InputLen&lt;br /&gt;      BEGIN&lt;br /&gt;&lt;br /&gt;      -- insert position&lt;br /&gt;      INSERT INTO #list_items (item_order, item_begpos, item_endpos)&lt;br /&gt;      VALUES (@ItemOrder, @ItemBegPos, @InputLen)&lt;br /&gt;&lt;br /&gt;      END&lt;br /&gt;&lt;br /&gt;	-- delete the bad items&lt;br /&gt;   DELETE FROM #list_items&lt;br /&gt;   WHERE item_endpos &lt; item_begpos&lt;br /&gt;&lt;br /&gt;   -- return list items&lt;br /&gt;	SELECT SUBSTRING(@list_text, item_begpos, (item_endpos - item_begpos + 1)) AS item_text, item_order, item_begpos, item_endpos&lt;br /&gt;   FROM #list_items&lt;br /&gt;   ORDER BY item_order&lt;br /&gt;&lt;br /&gt;   END&lt;br /&gt;&lt;br /&gt;DROP TABLE #list_items&lt;br /&gt;&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;SET QUOTED_IDENTIFIER OFF &lt;br /&gt;GO&lt;br /&gt;SET ANSI_NULLS ON &lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Fri, 24 Feb 2006 04:12:02 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/1563</guid>
      <author>Will_Rickards (Will Rickards)</author>
    </item>
    <item>
      <title>Convert a string representation of a UK date to a datetime</title>
      <link>http://snippets.dzone.com/posts/show/887</link>
      <description>This function is not particularly smart - it expects a UK-style date string in 'dd/mm/yyyy' format, but can cope with 'dd/mm/yy' (and if mm &gt; 12 it assumes the date is 'mm/dd/yyyy'). This work is licensed under a Creative Commons Attribution 2.5 License.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CREATE FUNCTION dbo.f_convert_str_to_date(@str VARCHAR(10))&lt;br /&gt;  RETURNS DATETIME&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;  -- Declare local variables&lt;br /&gt;  DECLARE @boundary  TINYINT&lt;br /&gt;  DECLARE @separator CHAR(1)&lt;br /&gt;  DECLARE @return    VARCHAR(11)&lt;br /&gt;  DECLARE @index1    INT&lt;br /&gt;  DECLARE @index2    INT&lt;br /&gt;  DECLARE @day       VARCHAR(2)&lt;br /&gt;  DECLARE @month     VARCHAR(2)&lt;br /&gt;  DECLARE @year      VARCHAR(4)&lt;br /&gt;  DECLARE @iDay      TINYINT&lt;br /&gt;  DECLARE @iMonth    TINYINT&lt;br /&gt;  DECLARE @iYear     SMALLINT&lt;br /&gt;  DECLARE @iSwap     TINYINT&lt;br /&gt;&lt;br /&gt;  -- 'Constants'&lt;br /&gt;  SELECT @boundary = 20&lt;br /&gt;  SELECT @separator = '/'&lt;br /&gt;&lt;br /&gt;  -- Indexes of forward slash separators (2 are expected)&lt;br /&gt;  SELECT @index1 = CHARINDEX(@separator, @str)&lt;br /&gt;  SELECT @index2 = CHARINDEX(@separator, @str, @index1 + 1)&lt;br /&gt;&lt;br /&gt;  -- Get the day, month and year&lt;br /&gt;  SELECT @day    = LTRIM(RTRIM(LEFT(@str, @index1 - 1)))&lt;br /&gt;  SELECT @month  = LTRIM(RTRIM(SUBSTRING(@str, (@index1 + 1), (@index2 - @index1) - 1)))&lt;br /&gt;  SELECT @year   = LTRIM(RTRIM(RIGHT(@str, (LEN(@str) - @index2))))&lt;br /&gt;&lt;br /&gt;  -- Convert the values to integer representations (will throw an error if they can't be converted)&lt;br /&gt;  SELECT @iDay   = CAST(@day AS TINYINT)&lt;br /&gt;  SELECT @iMonth = CAST(@month AS TINYINT)&lt;br /&gt;  SELECT @iYear  = CAST(@year AS SMALLINT)&lt;br /&gt;&lt;br /&gt;  -- Swap the day and month if they're obviously in the wrong format&lt;br /&gt;  IF @iMonth &gt; 12&lt;br /&gt;  BEGIN&lt;br /&gt;    SELECT @iSwap  = @iMonth&lt;br /&gt;    SELECT @iMonth = @iDay&lt;br /&gt;    SELECT @iDay   = @iSwap&lt;br /&gt;  END&lt;br /&gt;&lt;br /&gt;  -- Convert back to string representations&lt;br /&gt;  SELECT @day    = CAST(@iDay AS VARCHAR(2))&lt;br /&gt;  SELECT @month  = CAST(@iMonth AS VARCHAR(2))&lt;br /&gt;  SELECT @year   = CAST(@iYear AS VARCHAR(4))&lt;br /&gt;&lt;br /&gt;  -- If the day and/or month and/or year are a single digit, prefix with a zero&lt;br /&gt;  SELECT @day    = CASE WHEN LEN(@day)   = 1 THEN '0' + @day   ELSE @day   END&lt;br /&gt;  SELECT @month  = CASE WHEN LEN(@month) = 1 THEN '0' + @month ELSE @month END&lt;br /&gt;  SELECT @year   = CASE WHEN LEN(@year)  = 1 THEN '0' + @year  ELSE @year  END&lt;br /&gt;&lt;br /&gt;  -- If the year is only 2 digits long, prefix with '19' or '20' (depending on the boundary)&lt;br /&gt;  IF LEN(@year) = 2 SELECT @year = CASE WHEN @iYear &lt; @boundary THEN '20' + @year ELSE '19' + @year END&lt;br /&gt;&lt;br /&gt;  -- Build the cleaned up date string, with the month number converted to a string expression&lt;br /&gt;  SELECT @return = @day + '-'&lt;br /&gt;                 + CASE @month&lt;br /&gt;                     WHEN '01' THEN 'JAN'&lt;br /&gt;                     WHEN '02' THEN 'FEB'&lt;br /&gt;                     WHEN '03' THEN 'MAR'&lt;br /&gt;                     WHEN '04' THEN 'APR'&lt;br /&gt;                     WHEN '05' THEN 'MAY'&lt;br /&gt;                     WHEN '06' THEN 'JUN'&lt;br /&gt;                     WHEN '07' THEN 'JUL'&lt;br /&gt;                     WHEN '08' THEN 'AUG'&lt;br /&gt;                     WHEN '09' THEN 'SEP'&lt;br /&gt;                     WHEN '10' THEN 'OCT'&lt;br /&gt;                     WHEN '11' THEN 'NOV'&lt;br /&gt;                     WHEN '12' THEN 'DEC'&lt;br /&gt;                   END&lt;br /&gt;                 + '-' + @year&lt;br /&gt;&lt;br /&gt;  -- Done&lt;br /&gt;  RETURN CAST(@return AS DATETIME)&lt;br /&gt;&lt;br /&gt;END&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Example of usage:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT dbo.f_convert_str_to_date('21/8/01')&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Mon, 14 Nov 2005 00:34:44 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/887</guid>
      <author>rgedwards (Rich Edwards (rich at semantise dot com))</author>
    </item>
    <item>
      <title>Splitting an integer list in TSQL</title>
      <link>http://snippets.dzone.com/posts/show/774</link>
      <description>Revised: 2006-02-23 - fixed various bugs by using new algorithm&lt;br /&gt;Often times I have a list of integers I need to pass to the database to get worked on.  Such as checkboxes on a web page or some other list.  I needed some TSQL that would take a text string and split it by a separator, in this case a comma.  The following is the result of that need.&lt;br /&gt;The way I normally use it is in a stored procedure like the one below with several text type arguments.  Sometimes I use a variation designed to split a list of strings separated by a special character sequence.  Image two lists, one of the ids and one of the data.  You parse the first list to get a table of the ids and you parse the second list to get the data and insert/update as appropriate.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N'[dbo].[uspSplitIntegerList]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)&lt;br /&gt;   DROP PROCEDURE [dbo].[uspSplitIntegerList]&lt;br /&gt;GO&lt;br /&gt;                                      &lt;br /&gt;SET QUOTED_IDENTIFIER ON &lt;br /&gt;GO&lt;br /&gt;SET ANSI_NULLS ON &lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */&lt;br /&gt;-- uspSplitIntegerList&lt;br /&gt;--&lt;br /&gt;-- Description:&lt;br /&gt;--		splits a comma separated list of integers and returns the integer list&lt;br /&gt;--&lt;br /&gt;-- Arguments:&lt;br /&gt;--		@list_integers					- list of integers&lt;br /&gt;--&lt;br /&gt;-- Notes:&lt;br /&gt;-- 02/22/2006 - WSR : use DATALENGTH instead of LEN throughout because LEN doesn't count trailing blanks&lt;br /&gt;--&lt;br /&gt;-- History:&lt;br /&gt;-- 02/22/2006 - WSR : revised algorithm to account for items crossing 8000 character boundary&lt;br /&gt;--&lt;br /&gt;CREATE PROCEDURE uspSplitIntegerList&lt;br /&gt;	@list_integers			text&lt;br /&gt;AS&lt;br /&gt;&lt;br /&gt;SET NOCOUNT ON&lt;br /&gt;&lt;br /&gt;DECLARE @InputLen			integer			-- input text length&lt;br /&gt;DECLARE @TextPos			integer			-- current position within input text&lt;br /&gt;DECLARE @Chunk				varchar(8000)	-- chunk within input text&lt;br /&gt;DECLARE @ChunkPos			integer			-- current position within chunk&lt;br /&gt;DECLARE @DelimPos			integer			-- position of delimiter&lt;br /&gt;DECLARE @ChunkLen			integer			-- chunk length&lt;br /&gt;DECLARE @DelimLen			integer			-- delimiter length&lt;br /&gt;DECLARE @Delimiter      varchar(3)		-- delimiter&lt;br /&gt;DECLARE @ItemBegPos		integer			-- item starting position in text&lt;br /&gt;DECLARE @ItemOrder		integer			-- item order in list&lt;br /&gt;&lt;br /&gt;-- create table to hold list items&lt;br /&gt;-- actually their positions because we may want to scrub this list eliminating bad entries before substring is applied&lt;br /&gt;CREATE TABLE #list_items ( item_order integer, item_begpos integer, item_endpos integer )&lt;br /&gt;&lt;br /&gt;-- process list&lt;br /&gt;IF @list_integers IS NOT NULL&lt;br /&gt;   BEGIN&lt;br /&gt;&lt;br /&gt;	-- initialize&lt;br /&gt;   -- notice that this loop assumes a delimiter length of 1&lt;br /&gt;   -- if the delimiter is longer we have to deal with stuff like delimiters straddling the chunk boundaries&lt;br /&gt;   SET @InputLen = DATALENGTH(@list_integers)&lt;br /&gt;   SET @TextPos = 1&lt;br /&gt;	SET @Delimiter = ','&lt;br /&gt;	SET @DelimLen = DATALENGTH(@Delimiter)&lt;br /&gt;   SET @ItemBegPos = 1&lt;br /&gt;   SET @ItemOrder = 1&lt;br /&gt;   SET @ChunkLen = 1&lt;br /&gt;&lt;br /&gt;   -- cycle through input processing chunks&lt;br /&gt;   WHILE @TextPos &lt;= @InputLen AND @ChunkLen &lt;&gt; 0&lt;br /&gt;      BEGIN&lt;br /&gt;&lt;br /&gt;      -- get current chunk&lt;br /&gt;      SET @Chunk = SUBSTRING(@list_integers, @TextPos, 8000)&lt;br /&gt;&lt;br /&gt;      -- setup initial variable values&lt;br /&gt;      SET @ChunkPos = 1&lt;br /&gt;      SET @ChunkLen = DATALENGTH(@Chunk)&lt;br /&gt;      SET @DelimPos = CHARINDEX(@Delimiter, @Chunk, @ChunkPos)&lt;br /&gt;&lt;br /&gt;      -- loop over the chunk, until the last delimiter&lt;br /&gt;      WHILE @ChunkPos &lt;= @ChunkLen AND @DelimPos &lt;&gt; 0&lt;br /&gt;         BEGIN&lt;br /&gt;&lt;br /&gt;			-- insert position&lt;br /&gt;         INSERT INTO #list_items (item_order, item_begpos, item_endpos)&lt;br /&gt;         VALUES (@ItemOrder, @ItemBegPos, (@TextPos + @DelimPos - 1) - 1)&lt;br /&gt;         &lt;br /&gt;         -- adjust positions&lt;br /&gt;         SET @ItemOrder = @ItemOrder + 1&lt;br /&gt;         SET @ItemBegPos = (@TextPos + @DelimPos - 1) + @DelimLen&lt;br /&gt;         SET @ChunkPos = @DelimPos + @DelimLen&lt;br /&gt;      &lt;br /&gt;         -- find next delimiter      &lt;br /&gt;         SET @DelimPos = CHARINDEX(@Delimiter, @Chunk, @ChunkPos)&lt;br /&gt;&lt;br /&gt;         END&lt;br /&gt;&lt;br /&gt;      -- adjust positions&lt;br /&gt;      SET @TextPos = @TextPos + @ChunkLen&lt;br /&gt;&lt;br /&gt;      END&lt;br /&gt;&lt;br /&gt;	-- handle last item&lt;br /&gt;   IF @ItemBegPos &lt;= @InputLen&lt;br /&gt;      BEGIN&lt;br /&gt;&lt;br /&gt;      -- insert position&lt;br /&gt;      INSERT INTO #list_items (item_order, item_begpos, item_endpos)&lt;br /&gt;      VALUES (@ItemOrder, @ItemBegPos, @InputLen)&lt;br /&gt;&lt;br /&gt;      END&lt;br /&gt;&lt;br /&gt;	-- delete the bad items&lt;br /&gt;   DELETE FROM #list_items&lt;br /&gt;   WHERE item_endpos &lt; item_begpos&lt;br /&gt;&lt;br /&gt;   -- return list items&lt;br /&gt;	SELECT CAST(SUBSTRING(@list_integers, item_begpos, (item_endpos - item_begpos + 1)) AS integer) AS item_integer, item_order, item_begpos, item_endpos&lt;br /&gt;   FROM #list_items&lt;br /&gt;   WHERE ISNUMERIC(SUBSTRING(@list_integers, item_begpos, (item_endpos - item_begpos + 1))) = 1&lt;br /&gt;   ORDER BY item_order&lt;br /&gt;&lt;br /&gt;   END&lt;br /&gt;&lt;br /&gt;DROP TABLE #list_items&lt;br /&gt;&lt;br /&gt;RETURN&lt;br /&gt;&lt;br /&gt;/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;SET QUOTED_IDENTIFIER OFF &lt;br /&gt;GO&lt;br /&gt;SET ANSI_NULLS ON &lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Sat, 01 Oct 2005 03:35:03 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/774</guid>
      <author>Will_Rickards (Will Rickards)</author>
    </item>
    <item>
      <title>Replace multiple spaces with single spaces</title>
      <link>http://snippets.dzone.com/posts/show/771</link>
      <description>This work is licensed under a Creative Commons Attribution 2.5 License.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;WHILE CHARINDEX('  ', @string) &gt; 0 SELECT @string = REPLACE(@string, '  ', ' ')&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;where @string is the parameter or variable.</description>
      <pubDate>Thu, 29 Sep 2005 23:22:32 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/771</guid>
      <author>rgedwards (Rich Edwards (rich at semantise dot com))</author>
    </item>
    <item>
      <title>Is a specified year a leap year?</title>
      <link>http://snippets.dzone.com/posts/show/710</link>
      <description>This function returns a 1 if the supplied year is a leap year, otherwise 0&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CREATE FUNCTION dbo.f_is_leap_year(@year INTEGER)&lt;br /&gt;RETURNS BIT AS &lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;  IF @year % 400 = 0&lt;br /&gt;     -- Years divisible by 400 (e.g. 1600, 2000) are always leap years&lt;br /&gt;     RETURN 1 &lt;br /&gt;  ELSE&lt;br /&gt;  BEGIN&lt;br /&gt;    IF @year % 100 = 0&lt;br /&gt;       -- Years not divisible by 400 but divisible by 100 (e.g. 1900) are never leap years&lt;br /&gt;       RETURN 0&lt;br /&gt;    ELSE&lt;br /&gt;    BEGIN&lt;br /&gt;      IF @year % 4 = 0&lt;br /&gt;         -- Years not divisible by 400 or 100 but divisible by 4 (e.g. 1976) are always leap years&lt;br /&gt;         RETURN 1&lt;br /&gt;      ELSE&lt;br /&gt;         RETURN 0&lt;br /&gt;    END&lt;br /&gt;  END&lt;br /&gt;&lt;br /&gt;  -- The following statement should never be reached (but the SQL syntax parser requires it)&lt;br /&gt;  RETURN 0 &lt;br /&gt;&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT dbo.f_is_leap_year(2003) AS [2003], dbo.f_is_leap_year(2004) AS [2004], dbo.f_is_leap_year(2005) AS [2005]&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Wed, 14 Sep 2005 22:56:27 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/710</guid>
      <author>rgedwards (Rich Edwards (rich at semantise dot com))</author>
    </item>
    <item>
      <title>Convert a sentence to title case (i.e. each word has a capitalised initial letter)</title>
      <link>http://snippets.dzone.com/posts/show/709</link>
      <description>&lt;code&gt;&lt;br /&gt;CREATE FUNCTION dbo.f_convert_to_title_case&lt;br /&gt;  (@string VARCHAR(255))&lt;br /&gt;RETURNS VARCHAR(255) AS &lt;br /&gt;BEGIN &lt;br /&gt;&lt;br /&gt;  -- Declarations&lt;br /&gt;  DECLARE @return VARCHAR(255)&lt;br /&gt;  DECLARE @len INT&lt;br /&gt;  DECLARE @finished BIT&lt;br /&gt;  DECLARE @pos INT&lt;br /&gt;  DECLARE @thischar CHAR(1)&lt;br /&gt;  DECLARE @thisasc INT&lt;br /&gt;  DECLARE @capitalise BIT&lt;br /&gt;&lt;br /&gt;  -- Initialise&lt;br /&gt;  SELECT @finished = 0&lt;br /&gt;  SELECT @string = LOWER(@string)&lt;br /&gt;  SELECT @return = @string&lt;br /&gt;  SELECT @len = DATALENGTH(@return)&lt;br /&gt;&lt;br /&gt;  -- Failsafe&lt;br /&gt;  IF @len = 0&lt;br /&gt;     SELECT @finished = 1&lt;br /&gt;&lt;br /&gt;  -- Loop over all characters: capitalise first character and those after spaces, replace underscores with spaces&lt;br /&gt;  SELECT @pos = 0&lt;br /&gt;  SELECT @capitalise = 1&lt;br /&gt;  WHILE @finished = 0&lt;br /&gt;  BEGIN&lt;br /&gt;&lt;br /&gt;    -- Next position in string&lt;br /&gt;    SELECT @pos = @pos + 1&lt;br /&gt;&lt;br /&gt;    -- Done?&lt;br /&gt;    IF @pos &gt; @len&lt;br /&gt;       SELECT @finished = 1&lt;br /&gt;    ELSE&lt;br /&gt;    BEGIN&lt;br /&gt;       -- Read this character&lt;br /&gt;       SELECT @thischar = SUBSTRING(@return, @pos, 1)&lt;br /&gt;       SELECT @thisasc  = ASCII(@thischar)&lt;br /&gt;       IF @thischar IN ('_', ' ')&lt;br /&gt;       BEGIN&lt;br /&gt;          SELECT @return = LEFT(@return, @pos - 1) + ' ' + RIGHT(@return, @len - @pos)&lt;br /&gt;          SELECT @capitalise = 1&lt;br /&gt;       END&lt;br /&gt;       ELSE&lt;br /&gt;       BEGIN&lt;br /&gt;          IF @capitalise = 1 AND (@thisasc BETWEEN 97 AND 122)&lt;br /&gt;          BEGIN&lt;br /&gt;             SELECT @thisasc = @thisasc - 32&lt;br /&gt;             SELECT @return = LEFT(@return, @pos - 1) + CHAR(@thisasc) + RIGHT(@return, @len - @pos)&lt;br /&gt;             SELECT @capitalise = 0&lt;br /&gt;          END&lt;br /&gt;       END&lt;br /&gt;    END&lt;br /&gt;  END&lt;br /&gt;&lt;br /&gt;  -- Done&lt;br /&gt;  RETURN @return&lt;br /&gt;&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Example of usage:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT dbo.f_convert_to_title_case('mY ExAmPlE strinG') AS [result]&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Wed, 14 Sep 2005 22:47:34 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/709</guid>
      <author>rgedwards (Rich Edwards (rich at semantise dot com))</author>
    </item>
    <item>
      <title>Convert a number from any Base (between 2 and 36) to Base 10</title>
      <link>http://snippets.dzone.com/posts/show/708</link>
      <description>@base can be anything from 2 to 36. This work is licensed under a Creative Commons Attribution 2.5 License.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CREATE FUNCTION dbo.f_convert_to_base10&lt;br /&gt;  (@string VARCHAR(255), @base TINYINT)&lt;br /&gt;RETURNS INT AS &lt;br /&gt;BEGIN&lt;br /&gt;&lt;br /&gt;  -- Declarations&lt;br /&gt;  DECLARE @return INT&lt;br /&gt;  DECLARE @len INT&lt;br /&gt;  DECLARE @finished BIT&lt;br /&gt;  DECLARE @pos INT&lt;br /&gt;  DECLARE @thischar CHAR(1)&lt;br /&gt;  DECLARE @thisasc INT&lt;br /&gt;  DECLARE @val INT&lt;br /&gt;&lt;br /&gt;  -- Initialise&lt;br /&gt;  SELECT @base     = CASE WHEN @base &lt; 2 OR @base IS NULL THEN 2 WHEN @base &gt; 36 THEN 36 ELSE @base END&lt;br /&gt;  SELECT @return   = 0&lt;br /&gt;  SELECT @finished = 0&lt;br /&gt;  SELECT @string   = UPPER(@string)&lt;br /&gt;  SELECT @len      = DATALENGTH(@string)&lt;br /&gt;&lt;br /&gt;  -- Failsafe&lt;br /&gt;  IF @len = 0&lt;br /&gt;     SELECT @finished = 1&lt;br /&gt;&lt;br /&gt;  -- Loop over all characters: capitalise first character and those after spaces, replace underscores with spaces&lt;br /&gt;  SELECT @pos = 0&lt;br /&gt;&lt;br /&gt;  WHILE @finished = 0&lt;br /&gt;  BEGIN&lt;br /&gt;&lt;br /&gt;    SELECT @pos = @pos + 1&lt;br /&gt;&lt;br /&gt;    IF @pos &gt; @len&lt;br /&gt;&lt;br /&gt;       -- If we've run out of characters, we're done&lt;br /&gt;       SELECT @finished = 1&lt;br /&gt;&lt;br /&gt;    ELSE&lt;br /&gt;    BEGIN&lt;br /&gt;&lt;br /&gt;       -- Get the character (from right to left)&lt;br /&gt;       SELECT @thischar = SUBSTRING(@string, (@len - (@pos - 1)), 1)&lt;br /&gt;&lt;br /&gt;       -- Get the character's ASCII value&lt;br /&gt;       SELECT @thisasc  = ASCII(@thischar)&lt;br /&gt;&lt;br /&gt;       -- Convert to a numerical value&lt;br /&gt;       SELECT @val = CASE&lt;br /&gt;                       WHEN @thisasc BETWEEN 48 AND 57 -- '0' AND '9'&lt;br /&gt;                         THEN @thisasc - 48&lt;br /&gt;                       WHEN @thisasc BETWEEN 65 AND 90 -- 'A' (= decimal 10) AND 'Z'&lt;br /&gt;                         THEN @thisasc - 65 + 10&lt;br /&gt;                       ELSE 0 END&lt;br /&gt;&lt;br /&gt;       -- Add this portion on&lt;br /&gt;       SELECT @return = @return + (POWER(@base, (@pos - 1)) * @val)&lt;br /&gt;&lt;br /&gt;    END&lt;br /&gt;&lt;br /&gt;  END&lt;br /&gt;&lt;br /&gt;  -- Done&lt;br /&gt;  RETURN @return&lt;br /&gt;&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Example of usage:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT 'FFFF' AS [hex], dbo.f_convert_to_base10('FFFF', 16) AS [decimal]&lt;br /&gt;&lt;/code&gt;</description>
      <pubDate>Wed, 14 Sep 2005 22:42:23 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/708</guid>
      <author>rgedwards (Rich Edwards (rich at semantise dot com))</author>
    </item>
    <item>
      <title>Convert an integer from Base 10 to another Base (e.g. Hex or Binary)</title>
      <link>http://snippets.dzone.com/posts/show/707</link>
      <description>@base can be anything from 2 to 36 (because then we can stick to the alphanumerics 0 to 9 and A to Z).&lt;br /&gt;This work is licensed under a Creative Commons Attribution 2.5 License.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;CREATE FUNCTION dbo.f_convert_from_base10&lt;br /&gt;  (@num INT, @base TINYINT)&lt;br /&gt;RETURNS VARCHAR(255) AS &lt;br /&gt;BEGIN &lt;br /&gt;&lt;br /&gt;  -- Declarations&lt;br /&gt;  DECLARE @string VARCHAR(255)&lt;br /&gt;  DECLARE @return VARCHAR(255)&lt;br /&gt;  DECLARE @finished BIT&lt;br /&gt;  DECLARE @div INT&lt;br /&gt;  DECLARE @rem INT&lt;br /&gt;  DECLARE @char CHAR(1)&lt;br /&gt;&lt;br /&gt;  -- Initialise&lt;br /&gt;  SELECT @string   = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'&lt;br /&gt;  SELECT @return   = CASE WHEN @num &lt;= 0 THEN '0' ELSE '' END&lt;br /&gt;  SELECT @finished = CASE WHEN @num &lt;= 0 THEN 1 ELSE 0 END&lt;br /&gt;  SELECT @base     = CASE WHEN @base &lt; 2 OR @base IS NULL THEN 2 WHEN @base &gt; 36 THEN 36 ELSE @base END&lt;br /&gt;&lt;br /&gt;  -- Loop&lt;br /&gt;  WHILE @finished = 0&lt;br /&gt;  BEGIN&lt;br /&gt;&lt;br /&gt;    -- Do the maths&lt;br /&gt;    SELECT @div = @num / @base&lt;br /&gt;    SELECT @rem = @num - (@div * @base)&lt;br /&gt;    SELECT @char = SUBSTRING(@string, @rem + 1, 1)&lt;br /&gt;    SELECT @return = @char + @return&lt;br /&gt;    SELECT @num = @div&lt;br /&gt;&lt;br /&gt;    -- Nothing left?&lt;br /&gt;    IF @num = 0 SELECT @finished = 1&lt;br /&gt;&lt;br /&gt;  END&lt;br /&gt;&lt;br /&gt;  -- Done&lt;br /&gt;  RETURN @return&lt;br /&gt;&lt;br /&gt;END&lt;br /&gt;GO&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Example of usage:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;SELECT 255 AS [decimal], dbo.f_convert_from_base10(255, 16) AS [hex], dbo.f_convert_from_base10(255, 2) AS [binary], dbo.f_convert_from_base10(255, 36) AS [base 36]&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;</description>
      <pubDate>Wed, 14 Sep 2005 22:37:47 GMT</pubDate>
      <guid>http://snippets.dzone.com/posts/show/707</guid>
      <author>rgedwards (Rich Edwards (rich at semantise dot com))</author>
    </item>
  </channel>
</rss>
