SQL Split - insert into hierarchical table structure - sql

I'm using a Split function (found on social.msdn.com) and when executing manually in a query window
SELECT * FROM dbo.Split('/ABC/DEF/GHI/JKL', '/')
I get the following
Id Name
-- ----
1
2 ABC
3 DEF
4 GHI
5 JKL
Where Id is just a sequential number indicating position within the original string and Name is the name of that node. No hierarchical information yet.
Now, the next step is to put this into a hierarchical data structure in the DB. I'm trying to do this in a stored proc and with my SQL skills being what they are, I've hit a wall. Here's what I'd like to have: (NOTE that the Id column above is not related to the Id or the ParentId column here.)
Id ParentId Name FullName
-- -------- ---- --------
1 NULL ABC /ABC
2 1 DEF /ABC/DEF
3 2 GHI /ABC/DEF/GHI
4 3 JKL /ABC/DEF/GHI/JKL
I've got this far with my SP (called GetId with param #FullName) - GetId should return the Id associated with this node. If the node doesn't exist, it should be created and the Id from that new row should be returned - in other words, the consumer of this SP shouldn't care or know if the node exists prior to its calling it:
DECLARE #count int
-- // is there already a row for this node?
SELECT #count = COUNT(CatId)
FROM Category
WHERE FullName = #FullName
-- // if no row for this node, create the row
-- // and perhaps create multiple rows in hierarchy up to root
IF (#count = 0)
BEGIN
SELECT * FROM Split(#FullName, '/')
-- // NOW WHAT ???
-- // need to insert row (and perhaps parents up to root)
END
-- // at this point, there should be a row for this node
-- // return the Id associated with this node
SELECT Id
FROM Category
WHERE FullName = #FullName
The Category table (adjacency list) where these items will eventually end up, through a series of inserts, has the following structure.
CREATE TABLE Category (
Id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
ParentId int NULL,
Name nvarchar(255) NOT NULL,
FullName nvarchar(255) NOT NULL)
As result, I do not want to generate a value for the Id column in the Category table and need to get the appropriate ParentId for each node.
After the paths '/ABC/DEF/GHI/JKL' and '/ABC/DEF/XYZ/LMN/OPQ' were processed and I did a SELECT * FROM Category, I would expect to see the following:
Id ParentId Name FullName
-- -------- ---- --------
1 NULL ABC /ABC
2 1 DEF /ABC/DEF
3 2 GHI /ABC/DEF/GHI
4 3 JKL /ABC/DEF/GHI
5 2 XYZ /ABC/DEF/XYZ
6 5 LMN /ABC/DEF/XYZ/LMN
7 6 OPQ /ABC/DEF/XYZ/LMN/OPQ
Q: would it be possible to call back into the SP recursively starting at the outer most node, until the node existed or we were at the ultimate parent? Something to the effect of:
GetId(#FullName)
{
If Category exists with #FullName
return CatId
Else // row doesn't exist for this node
Split #FullName, order by Id DESC so we get the leaf node first
Create Category row
#FullName,
#Name,
#ParentId = Id of next FullName (call GetId with FullName of next row from Split)
}

You can use CTE to achieve this, in combination with RowNumbering
With TMP AS (
SELECT Id, Data as Name, RN=ROW_NUMBER() over (Order by Id ASC)
FROM dbo.Split('/ABC/DEF/GHI/JKL', '/')
where Data > ''
), TMP2 AS (
SELECT TOP 1 RN, CONVERT(bigint, null) ParentId, Name, convert(nvarchar(max),'/' + Name) FullName
From TMP
Order by RN
union all
SELECT n.RN, t.RN, n.Name, t.FullName + '/' + n.Name
from TMP2 t
inner join TMP n on n.RN = t.RN+1)
select *
from tmp2
order by RN
Now for the 2nd part, this inserts the entire hierarchy, but starts with ID=1
IF (#count = 0)
BEGIN
With TMP AS (
SELECT Id, Data as Name, RN=ROW_NUMBER() over (Order by Id ASC)
FROM dbo.Split('/ABC/DEF/GHI/JKL', '/')
where Data > ''
), TMP2 AS (
SELECT TOP 1 RN, CONVERT(bigint, null) ParentId, Name, convert(nvarchar(max),'/' + Name) FullName
From TMP
Order by RN
union all
SELECT n.RN, t.RN, n.Name, t.FullName + '/' + n.Name
from TMP2 t
inner join TMP n on n.RN = t.RN+1)
insert Category(CatId, ParentId, Name, FullName) --<< list correct column names
select RN, ParentId, Name, FullName
from tmp2
order by RN
END

Related

How to get TWO Previous record in MySQL using CURSOR

On my table, I have records that contain ','.
Id Name
1 Here is the result
2 of your examination.
3 ,
4 New Opening for the position of
5 PT Teacher, Science Lab.
6 ,
So in cursor If I found ',' then I want to merge the 2 rows value into the first one.
DECLARE #ID int
DECLARE #Name nvarchar(500)
DECLARE MergeCursor CURSOR FOR
select ID,NAME from TEST_TABLE
OPEN MergeCursor
FETCH NEXT FROM NarrationCursor into #ID,#NAME
WHILE (##FETCH_STATUS=0)
BEGIN
if(#Name = ',')
select * from TEST_TABLE where ID = (select max(ID) from TEST_TABLE where ID < #ID)
FETCH NEXT FROM NarrationCursor into #ID,#NAME
END
CLOSE MergeCursor
DEALLOCATE MergeCursor
IN cursor how can I get the PREVIOUS TWO-ROW And UPDATE the value in 1st row and DELETE THE 2nd and THIRD ROW. AS WELL AS UPDATE THE ID
In the End, I want to output
Id Name
1 Here is the result of your examination.
2 New Opening for the position of PT Teacher, Science Lab.
WITH
grouped AS
(
SELECT
SUM(CASE WHEN name=',' THEN 1 ELSE 0 END)
OVER (ORDER BY id)
AS group_id,
id,
name
FROM
TEST_TABLE
)
SELECT
group_id + 1 AS id,
STRING_AGG(name, ' ') WITHIN GROUP (ORDER BY id) AS name
FROM
grouped
WHERE
name <> ','
GROUP BY
group_id
ORDER BY
group_id

SQL - how to update a column with each row value incremented by 1?

For the selected rows in a column, how to update each row sequentially from the beginning to the end, with each row value incremented by 1 (or certain number). I know this can be done in excel in few seconds but I could figure out how to achieve in SQL server. For instance:
customer id is NULL now
update customer id with every row incremented by 1 (i.e. first row = 1, second row = 2, .....nth row = n)
ship-to party customer id
0002018092 NULL
0002008127 NULL
0002000129 NULL
0002031592 NULL
0002034232 NULL
desired output
ship-to party customer id
0002018092 1
0002008127 2
0002000129 3
0002031592 4
0002034232 5
Also, for the selected rows in a column, how to update each row with the row number? I know there is a row_number() function but didn't succeed in producing the desired result. for instance
column A is NULL now
update Column A with every row incremented by 1 (i.e. first row = row number 1, second row = row number 2, .....nth row = row number n)
Any demonstration would be very helpful.thkans
example : suppose I want to add a value to each value in column SomeIntField in table tblUser
there are 2 ways of doing this easy
first: this just adds value 1 to each column SomeIntField
update tblUser set SomeIntField = SomeIntField + 1
second : this adds an incrementing value, the first row gets +1, second gets +2, and so on...
declare #number int = 0
update tblUser
set #number = #number + 1,
SomeIntField = isnull(SomeIntField, 0) + #Number
EDIT: based on your last comment this might be what you want
declare #table table (shiptoparty varchar(50), customer_id int)
insert into #Table (shiptoparty, customer_id)
values ('0002018092', NULL), ('0002008127', NULL), ('0002000129', NULL), ('0002031592', NULL), ('0002034232', NULL)
declare #number int = 0
update #table
set #number = #number + 1,
customer_id = isnull(customer_id, 0) + #Number
select * from #table
The result of this is :
shiptoparty | customer_id
----------- | -----------
0002018092 | 1
0002008127 | 2
0002000129 | 3
0002031592 | 4
0002034232 | 5
Rather than using a self referencing variable, use a CTE:
WITH CTE AS (
SELECT [Your Incrementing Column],
ROW_NUMBER() OVER (ORDER BY [Columns to Order By]) AS RN
FROM YourTable)
UPDATE CTE
SET [Your Incrementing Column] = RN;
Edit: To prove a point that ALL rows will be updated:
CREATE TABLE #Sample (String varchar(50),
IncrementingInt int);
INSERT INTO #Sample (String)
VALUES ('sdkfjasdf'),
('dfydsfdfg'),
('sdfgsdfg45yfg'),
('dfgf54d'),
('dsft43tdc'),
('f6gytrntrfu7m45'),
('5d6f45wgby54'),
('g34h636j'),
('jw'),
('h6nw54m'),
('g54j747jm5e5f4w5gsft'),
('ns67mw54mk8o7hr'),
('h45j4w5h4');
SELECT *
FROM #Sample;
WITH CTE AS(
SELECT IncrementingInt,
ROW_NUMBER() OVER (ORDER BY String) AS RN
FROM #Sample)
UPDATE CTE
SET IncrementingInt = RN;
SELECT *
FROM #Sample;
DROP TABLE #Sample;
GO
To update each row with row number
Try below
CREATE TABLE tmp(Id INT IDENTITY(1,1), Value INT)
INSERT INTO tmp(value) VALUES(1),(2),(3),(4),(5)
UPDATE T
SET
T.Value = B.RowNo
FROM tmp AS T
INNER JOIN (SELECT Id, ROW_NUMBER()OVER(ORDER BY Id) AS RowNo FROM tmp)AS B
ON T.Id = B.Id
Don't think very complex. Try the simple method given below
alter table table_name drop column customer_id
go
alter table table_name add id customer_id IDENTITY(1,1)
go
First problem:
you want to increase values in every row in certain column by 1 (or other nuber), try this:
update TABLE_NAME set column_to_increase = column_to_increase + 1
Second problem:
you want to get row number for only certain rows. Solution: first create column holding all row numbers, then get the rows:
select * from (
select column1, column2, ..., columnN, row_number() over (order by (select null)) as [rn] from MY_TABLE
) where *condition*
FYI: select null in over clause does exactly nothing, it's just there, because window functions (such as row_number) have to have over clause and some of them require order by.

SQL Server 2008 R2 get common text between a group of text strings

I sure hope someone can help me out with this issue. I have been searching for hours to find it but I am coming up empty.
In this example I have two columns in my table
GRP_ID Desc
My group ID is the way I will identify that these products are of the same type, and desc is what I want to find all the common words.
So here is my table
GRP_ID Desc
-------------------------------
2 Red Hat
2 Green Hat
2 Yellow Hat
3 Boots Large Brown
3 Boots Medium Red
3 Boots Medium Brown
What I want as a result of the query would be the following
GRP_ID Desc
-----------------------
2 Hat
3 Boots
So what I want is all the words that appear in every string in the group or the common words in the group.
I think you'd need to create a mapping table for GRP_ID and products - e.g. Hat and Boots.
CREATE TABLE GroupProductMapping (
GRP_ID INT NOT NULL, -- I'm assuming its an Int
ProductDesc VARCHAR(50) NOT NULL
)
SELECT a.GRP_ID,
b.ProductDesc Desc
FROM {Table_Name} a
INNER JOIN GroupProductMapping b ON a.GRP_ID = b.GRP_ID
Alternatively, if you don't have too many products. You could use CASE in your SELECT clause.
e.g.
SELECT
GRP_ID,
CASE GRP_ID
WHEN 1 THEN 'Hat'
WHEN 2 THEN 'Boots'
END AS Desc
FROM {Table_Name}
{Table_Name} is the name of your original table.
Ideally you would normalise your data and store the words in a separate table.
However for your immediate requirements, you first need to provide a UDF to split 'desc' into words. I poached this function:
-- this function splits the provided strings on a delimiter
-- similar to .Net string.Split.
-- I'm sure there are alternatives (such as calling string.Split through
-- a CLR function).
CREATE FUNCTION [dbo].[Split]
(
#RowData NVARCHAR(MAX),
#Delimeter NVARCHAR(MAX)
)
RETURNS #RtnValue TABLE
(
ID INT IDENTITY(1,1),
Data NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #Iterator INT
SET #Iterator = 1
DECLARE #FoundIndex INT
SET #FoundIndex = CHARINDEX(#Delimeter,#RowData)
WHILE (#FoundIndex>0)
BEGIN
INSERT INTO #RtnValue (data)
SELECT
Data = LTRIM(RTRIM(SUBSTRING(#RowData, 1, #FoundIndex - 1)))
SET #RowData = SUBSTRING(#RowData,
#FoundIndex + DATALENGTH(#Delimeter) / 2,
LEN(#RowData))
SET #Iterator = #Iterator + 1
SET #FoundIndex = CHARINDEX(#Delimeter, #RowData)
END
INSERT INTO #RtnValue (Data)
SELECT Data = LTRIM(RTRIM(#RowData))
RETURN
END
Then you need to split the descriptions and do some grouping (which you would also do if the data was normalised)
-- get the count of each grp_id
with group_count as
(
select grp_id, count(*) cnt from [Group]
group by grp_id
),
-- get the count of each word in each grp_id
group_word_count as
(
select count(*) cnt, grp_id, data from
(
select * from [group] g
cross apply dbo.Split(g.[Desc], ' ')
)
t
group by grp_id, data
)
-- return rows where number of grp_id = number of words in grp_id
select gwc.GRP_ID, gwc.Data [Desc] from group_word_count gwc
inner join group_count gc on gwc.GRP_ID = gc.GRP_ID and gwc.cnt = gc.cnt
Where [Group] is your table.

Writing a recursive SQL query on a self-referencing table

I have a database with a table called Items, that contains these columns:
ID - primary key, uniqueidentifier
Name - nvarchar(256)
ParentID - uniqueidentifier
The name field can be used to build out a path to the item, by iterating through each ParentId until it equals '11111111-1111-1111-1111-111111111111', which is a root item.
So if you had a table that had rows like
ID Name ParentID
-------------------------------------------------------------------------------------
11111111-1111-1111-1111-111111111112 grandparent 11111111-1111-1111-1111-111111111111
22222222-2222-2222-2222-222222222222 parent 11111111-1111-1111-1111-111111111112
33333333-3333-3333-3333-333333333333 widget 22222222-2222-2222-2222-222222222222
So if I looked up an item with id '33333333-3333-3333-3333-333333333333' in the example above, i'd want the path
/grandparent/parent/widget
returned. i've attempted to write a CTE, as it looks like that's how you'd normally accomplish something like this - but as I don't do very much SQL, I can't quite figure out where i'm going wrong. I've looked at some examples, and this is as close as I seem to be able to get - which only returns the child row.
declare #id uniqueidentifier
set #id = '10071886-A354-4BE6-B55C-E5DBCF633FE6'
;with ItemPath as (
select a.[Id], a.[Name], a.ParentID
from Items a
where Id = #id
union all
select parent.[Id], parent.[Name], parent.ParentID
from Items parent
inner join ItemPath as a
on a.Id = parent.id
where parent.ParentId = a.[Id]
)
select * from ItemPath
I have no idea how i'd declare a local variable for the path and keep appending to it in the recursive query. i was going to try to at least get all the rows to the parent before going after that. if anyone could help with that as well - i'd appreciate it.
well here's working solution
SQL FIDDLE EXAMPLE
declare #id uniqueidentifier
set #id = '33333333-3333-3333-3333-333333333333'
;with ItemPath as
(
select a.[Id], a.[Name], a.ParentID
from Items a
where Id = #id
union all
select parent.[Id], parent.[Name] + '/' + a.[Name], parent.ParentID
from ItemPath as a
inner join Items as parent on parent.id = a.parentID
)
select *
from ItemPath
where ID = '11111111-1111-1111-1111-111111111112'
I don't like it much, I think better solution will be to do it other way around. Wait a minute and I try to write another query :)
UPDATE here it is
SQL FIDDLE EXAMPLE
create view vw_Names
as
with ItemPath as
(
select a.[Id], cast(a.[Name] as nvarchar(max)) as Name, a.ParentID
from Items a
where Id = '11111111-1111-1111-1111-111111111112'
union all
select a.[Id], parent.[Name] + '/' + a.[Name], a.ParentID
from Items as a
inner join ItemPath as parent on parent.id = a.parentID
)
select *
from ItemPath
and now you can use this view
declare #id uniqueidentifier
set #id = '33333333-3333-3333-3333-333333333333'
select *
from vw_Names where Id = #id
I needed a slightly different version of this answer as I wanted to produce a list of all lineages in the tree. I also wanted to know the depth of each node. I added a temporary table of top level parents that I could loop through and a temporary table to build the result set.
use Items
Select *
Into #Temp
From Items
where ParentID=0
Declare #Id int
create table #Results
(
Id int,
Name nvarchar(max),
ParentId int,
Depth int
)
While (Select Count(*) From #Temp) > 0
Begin
Select Top 1 #Id = Id From #Temp
begin
with ItemPath as
(
select a.[Id], cast(a.[Name] as nvarchar(max))as Name, a.ParentID ,1 as
Depth
from Items a
where a.ID = #id
union all
select a.[Id], parent.[Name] + '/' + a.[Name], a.ParentID, 1 + Depth
from Items as a
inner join ItemPath as parent on parent.id = a.parentID
)
insert into #Results
select *
from ItemPath
end
Delete #Temp Where Id = #Id
End
drop table #Temp
select * from #Results
drop table #Results
If we start from the following table...
Id Name ParentID
1 Fred 0
2 Mary 0
3 Baker 1
4 Candle 2
5 Stick 4
6 Maker 5
We would get this result table.
Id Name ParentID Depth
1 Fred 0 1
2 Mary 0 1
3 Fred/Baker 1 2
4 Mary/Candle 2 2
5 Mary/Candle/Stick 4 3
6 Mary/Candle/Stick/Maker 5 4

Trying to merge rows into one row with certain conditions

Given 2 or more rows that are selected to merge, one of them is identified as being the template row. The other rows should merge their data into any null value columns that the template has.
Example data:
Id Name Address City State Active Email Date
1 Acme1 NULL NULL NULL NULL blah#yada.com 3/1/2011
2 Acme1 1234 Abc Rd Springfield OR 0 blah#gmail.com 1/12/2012
3 Acme2 NULL NULL NULL 1 blah#yahoo.com 4/19/2012
Say that a user has chosen row with Id 1 as the template row, and rows with Ids 2 and 3 are to be merged into row 1 and then deleted. Any null value columns in row Id 1 should be filled with (if one exists) the most recent (see Date column) non-null value, and non-null values already present in row Id 1 are to be left as is. The result of this query on the above data should be exactly this:
Id Name Address City State Active Email Date
1 Acme1 1234 Abc Road Springfield OR 1 blah#yada.com 3/1/2011
Notice that the Active value is 1, and not 0 because row Id 3 had the most recent date.
P.S. Also, is there any way possible to do this without explicitly defining/knowing beforehand what all the column names are? The actual table I'm working with has a ton of columns, with new ones being added all the time. Is there a way to look up all the column names in the table, and then use that subquery or temptable to do the job?
You might do it by ordering rows first by template flag, then by date desc. Template row should always be the last one. Each row is assigned a number in that order. Using max() we are finding fist occupied cell (in descending order of numbers). Then we select columns from rows matching those maximums.
; with rows as (
select test.*,
-- Template row must be last - how do you decide which one is template row?
-- In this case template row is the one with id = 1
row_number() over (order by case when id = 1 then 1 else 0 end,
date) rn
from test
-- Your list of rows to merge goes here
-- where id in ( ... )
),
-- Finding first occupied row per column
positions as (
select
max (case when Name is not null then rn else 0 end) NamePosition,
max (case when Address is not null then rn else 0 end) AddressPosition,
max (case when City is not null then rn else 0 end) CityPosition,
max (case when State is not null then rn else 0 end) StatePosition,
max (case when Active is not null then rn else 0 end) ActivePosition,
max (case when Email is not null then rn else 0 end) EmailPosition,
max (case when Date is not null then rn else 0 end) DatePosition
from rows
)
-- Finally join this columns in one row
select
(select Name from rows cross join Positions where rn = NamePosition) name,
(select Address from rows cross join Positions where rn = AddressPosition) Address,
(select City from rows cross join Positions where rn = CityPosition) City,
(select State from rows cross join Positions where rn = StatePosition) State,
(select Active from rows cross join Positions where rn = ActivePosition) Active,
(select Email from rows cross join Positions where rn = EmailPosition) Email,
(select Date from rows cross join Positions where rn = DatePosition) Date
from test
-- Any id will suffice, or even DISTINCT
where id = 1
You might check it at Sql Fiddle.
EDIT:
Cross joins in last section might actually be inner joins on rows.rn = xxxPosition. It works this way, but change to inner join would be an improvement.
It's not so complicated.
At first..
DECLARE #templateID INT = 1
..so you can remember which row is treated as template..
Now find latest NOT NULL values (exclude template row). The easiest way is to use TOP 1 subqueries for each column:
SELECT
(SELECT TOP 1 Name FROM DataTab WHERE Name IS NOT NULL AND NOT ID = #templateID ORDER BY Date DESC) AS LatestName,
(SELECT TOP 1 Address FROM DataTab WHERE Address IS NOT NULL AND NOT ID = #templateID ORDER BY Date DESC) AS AddressName
-- add more columns here
Wrap above into CTE (Common Table Expression) so you have nice input for your UDPATE..
WITH Latest_CTE (CTE_LatestName, CTE_AddressName) -- add more columns here; I like CTE prefix to distinguish source columns from target columns..
AS
-- Define the CTE query.
(
SELECT
(SELECT TOP 1 Name FROM DataTab WHERE Name IS NOT NULL AND NOT ID = #templateID ORDER BY Date DESC) AS LatestName,
(SELECT TOP 1 Address FROM DataTab WHERE Address IS NOT NULL AND NOT ID = #templateID ORDER BY Date DESC) AS AddressName
-- add more columns here
)
UPDATE
<update statement here (below)>
Now, do smart UPDATE of your template row using ISNULL - it will act as conditional update - update only if target column is null
WITH
<common expression statement here (above)>
UPDATE DataTab
SET
Name = ISNULL(Name, CTE_LatestName), -- if Name is null then set Name to CTE_LatestName else keep Name as Name
Address = ISNULL(Address, CTE_LatestAddress)
-- add more columns here..
WHERE ID = #templateID
And the last task is delete rows other then template row..
DELETE FROM DataTab WHERE NOT ID = #templateID
Clear?
For dynamic columns, you need to write a solution using dynamic SQL.
You can query sys.columns and sys.tables to get the list of columns you need, then you want to loop backwards once for each null column finding the first non-null row for that column and updating your output row for that column. Once you get to 0 in the loop you have a complete row which you can then display to the user.
I should pay attention to posting dates. In any case, here's a solution using dynamic SQL to build out an update statement. It should give you something to build from, anyway.
There's some extra code in there to validate the results along the way, but I tried to comment in a way that made that non-vital code apparent.
CREATE TABLE
dbo.Dummy
(
[ID] int ,
[Name] varchar(30),
[Address] varchar(40) null,
[City] varchar(30) NULL,
[State] varchar(2) NULL,
[Active] tinyint NULL,
[Email] varchar(30) NULL,
[Date] date NULL
);
--
INSERT dbo.Dummy
VALUES
(
1, 'Acme1', NULL, NULL, NULL, NULL, 'blah#yada.com', '3/1/2011'
)
,
(
2, 'Acme1', '1234 Abc Rd', 'Springfield', 'OR', 0, 'blah#gmail.com', '1/12/2012'
)
,
(
3, 'Acme2', NULL, NULL, NULL, 1, 'blah#yahoo.com', '4/19/2012'
);
DECLARE
#TableName nvarchar(128) = 'Dummy',
#TemplateID int = 1,
#SetStmtList nvarchar(max) = '',
#LoopCounter int = 0,
#ColumnCount int = 0,
#SQL nvarchar(max) = ''
;
--
--Create a table to hold the column names
DECLARE
#ColumnList table
(
ColumnID tinyint IDENTITY,
ColumnName nvarchar(128)
);
--
--Get the column names
INSERT #ColumnList
(
ColumnName
)
SELECT
c.name
FROM
sys.columns AS c
JOIN
sys.tables AS t
ON
t.object_id = c.object_id
WHERE
t.name = #TableName;
--
--Create loop boundaries to build out the SQL statement
SELECT
#ColumnCount = MAX( l.ColumnID ),
#LoopCounter = MIN (l.ColumnID )
FROM
#ColumnList AS l;
--
--Loop over the column names
WHILE #LoopCounter <= #ColumnCount
BEGIN
--Dynamically construct SET statements for each column except ID (See the WHERE clause)
SELECT
#SetStmtList = #SetStmtList + ',' + l.ColumnName + ' =COALESCE(' + l.ColumnName + ', (SELECT TOP 1 ' + l.ColumnName + ' FROM ' + #TableName + ' WHERE ' + l.ColumnName + ' IS NOT NULL AND ID <> ' + CAST(#TemplateID AS NVARCHAR(MAX )) + ' ORDER BY Date DESC)) '
FROM
#ColumnList AS l
WHERE
l.ColumnID = #LoopCounter
AND
l.ColumnName <> 'ID';
--
SELECT
#LoopCounter = #LoopCounter + 1;
--
END;
--TESTING - Validate the initial table values
SELECT * FROM dbo.Dummy ;
--
--Get rid of the leading common in the SetStmtList
SET #SetStmtList = SUBSTRING( #SetStmtList, 2, LEN( #SetStmtList ) - 1 );
--Build out the rest of the UPDATE statement
SET #SQL = 'UPDATE ' + #TableName + ' SET ' + #SetStmtList + ' WHERE ID = ' + CAST(#TemplateID AS NVARCHAR(MAX ))
--Then execute the update
EXEC sys.sp_executesql
#SQL;
--
--TESTING - Validate the updated table values
SELECT * FROM dbo.Dummy ;
--
--Build out the DELETE statement
SET #SQL = 'DELETE FROM ' + #TableName + ' WHERE ID <> ' + CAST(#TemplateID AS NVARCHAR(MAX ))
--Execute the DELETE
EXEC sys.sp_executesql
#SQL;
--
--TESTING - Validate the final table values
SELECT * FROM dbo.Dummy;
--
DROP TABLE dbo.Dummy;