I'm a SQL newbie and am trying to use a CTE on all rows in a table, to update multiple columns in each row with hierarchical information. I've read lots of examples online but none that describes exactly what I need, so I hope someone can help me.
I have a table of study samples stored in a hierarchy of shelf->rack->box, where shelf is the highest level and box is the lowest, with the samples below box. Shelf, rack, and box are also in this table as separate rows. All items have a field called 'name' to identify them. They are all connected by ID and parent_ID. Kind of like a management structure.
I need to populate all of the level values in each row for samples. It's more complicated than the manager-employee examples I've found online, and it requires storing the values returned by the CTE rather than simply doing a SELECT to display them, as the examples I've found show.
To run the CTE on all rows, I've tried nesting the whole CTE in a WHILE loop but it just loops on a single value rather than all rows.
Below is the code I currently have working to return the hierarchy of one sample value; 'tempspec' is the sample table:
DECLARE @TID float;
SET @TID = 39059;
WITH cte AS
(
SELECT ID,
Parent_Id,
Name,
Study_ID,
Loc_Box,
Loc_Shelf,
Loc_Rack,
Loc_Type
FROM tempspec
WHERE ID = @TID and Study_ID = 'XXX'
UNION all
SELECT tempspec.Id,
tempspec.Parent_Id,
tempspec.Name,
tempspec.Study_ID,
tempspec.Loc_Box,
tempspec.Loc_Shelf,
tempspec.Loc_Rack,
tempspec.Loc_Type
FROM tempspec
JOIN cte on tempspec.Id = cte.Parent_Id
)
SELECT E1.name, E1.ID, E1.Parent_ID,E1.Loc_Type,ISNULL(E2.name,'TOP')
FROM cte E1
LEFT JOIN cte E2
ON E1.parent_id = E2.ID;
But if I try to replace the SELECT with an UPDATE, it runs and says all rows in the table have been updated, but nothing has been stored:
UPDATE E1
SET E1.Loc_Box = E2.Loc_Box,
E1.Loc_Rack = E2.Loc_Rack,
E1.Loc_Shelf = E2.Loc_Shelf
FROM tempspec E1
LEFT JOIN cte E2
ON E1.parent_id = E2.ID;
And if I try to nest the CTE in this WHILE loop ('P' indicates sample rows, so I am only traversing the tree for them), I can't assign the ID to @TID, or when I've tried it other ways it loops forever:
WHILE EXISTS(SELECT * FROM tempspec WHERE Loc_Type = 'P')
BEGIN
SET @TID = ID;
What am I doing wrong? I've tried various formats but nothing seems to work. Thanks in advance for your help!
Below is the requested test data. I want to populate Shelf, Rack, and Box with the Name values from the parent rows for the samples (type='P'):
DROP TABLE dbo.TEST_DATA;
CREATE TABLE dbo.TEST_DATA (int ID, int Parent_Id,varchar(30) Name, varchar(10) Type,varchar(30) Shelf,varchar(30) Rack,varchar(30) Box)
INSERT INTO TEST_DATA (39702, 1664, 0228MBDNAERA1, 'P','','','');
INSERT INTO TEST_DATA (39703, 1664, 0230MBDNAERA1, 'P','','','');
INSERT INTO TEST_DATA (39704, 1664, 0231MBDNAERA1, 'P','','','');
INSERT INTO TEST_DATA (39726, 1744, 0228MBDNAERA2, 'P','','','');
INSERT INTO TEST_DATA (39727, 1744, 0230MBDNAERA2, 'P','','','');
INSERT INTO TEST_DATA (39728, 1744, 0231MBDNAERA2, 'P','','','');
INSERT INTO TEST_DATA (39764, 1752, 0228MBDNAERA3, 'P','','','');
INSERT INTO TEST_DATA (39766, 1752, 0230MBDNAERA3, 'P','','','');
INSERT INTO TEST_DATA (39768, 1752, 0231MBDNAERA3, 'P','','','');
INSERT INTO TEST_DATA (1744, 1652, MBDNAERA2 - 3, 'B','','','');
INSERT INTO TEST_DATA (1752, 1732, MBDNAERA3 - 3, 'B','','','');
INSERT INTO TEST_DATA (1664, 1652, MBDNAERA1 - 3, 'B','','','');
INSERT INTO TEST_DATA (1732, 1617, Rack R, 'R','','','');
INSERT INTO TEST_DATA (1652, 1617, Rack Q, 'R','','','');
INSERT INTO TEST_DATA (1617, 2, Shelf 4, 'S','','','');
INSERT INTO TEST_DATA (2, NULL, Freezer, 'F','','','');
Example data hierarchy for one sample and the resulting row I'm trying to get (sorry about the bad formatting):
ID Parent_Id Name Type Shelf Rack Box
39702 1664 0228MBDNAERA1 'P'
1664 1652 MBDNAERA1 - 3 'B'
1652 1617 Rack Q 'R'
1617 2 Shelf 4 'S'
2 NULL Freezer 'F'
ID Parent_Id Name Type Shelf Rack Box
39702 1664 0228MBDNAERA1 'P' Shelf 4 Rack Q MBDNAERA1 - 3