【发布时间】:2018-01-24 17:00:54
【问题描述】:
我是一个 SQL 新手,我正在尝试对表中的所有行使用 CTE,以使用分层信息更新每行中的多个列。我在网上阅读了很多例子,但没有一个能准确描述我需要什么,所以我希望有人能帮助我。
我有一个研究样本表存储在货架->货架->盒子的层次结构中,其中货架是最高级别,盒子是最低级别,样本在盒子下方。架子、架子和箱子也在此表中作为单独的行。所有项目都有一个名为“名称”的字段来识别它们。它们都通过 ID 和 parent_ID 连接。有点像管理结构。
我需要为样本填充每一行中的所有级别值。它比我在网上找到的经理-雇员示例更复杂,它需要存储 CTE 返回的值,而不是像我找到的示例所示的那样简单地执行 SELECT 来显示它们。
要在所有行上运行 CTE,我尝试将整个 CTE 嵌套在 WHILE 循环中,但它只循环单个值而不是所有行。
下面是我目前正在努力返回一个样本值的层次结构的代码; 'tempspec' 是示例表:
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;
但是如果我尝试用 UPDATE 替换 SELECT,它会运行并说表中的所有行都已更新,但没有存储任何内容:
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;
如果我尝试在这个 WHILE 循环中嵌套 CTE('P' 表示样本行,所以我只是为它们遍历树),我无法将 ID 分配给@TID,或者当我尝试过其他它永远循环的方式:
WHILE EXISTS(SELECT * FROM tempspec WHERE Loc_Type = 'P')
BEGIN
SET @TID = ID;
我做错了什么?我尝试了各种格式,但似乎没有任何效果。提前感谢您的帮助!
以下是请求的测试数据。我想用样本父行中的名称值填充 Shelf、Rack 和 Box(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','','','');
一个样本的示例数据层次结构以及我试图获取的结果行(抱歉格式错误):
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
【问题讨论】:
-
您能否添加一个示例,说明您的表与示例数据的外观,然后您当前从示例数据中获得了什么结果,以及您试图获得什么结果?
-
您是要更新每一行以从其父行复制值,还是要将根(祖先)行的值一直向下推入整个层次结构?在第一种情况下,您根本不需要递归 CTE,而在第二种情况下,您需要在递归 CTE 结果中添加额外的列来传递根值。
-
David - 是的,我想更新包含来自其父级的样本的每一行(我不需要更新架子/机架/盒子行,它们只是为了建立层次结构)。因此,对于样本 A,我希望它的架子/架子/盒子值与相应架子/架子/盒子行的层次结构中的值匹配。那有意义吗?我认为列出 UNION ALL - SELECT 语句中的所有列会拉下所有必要的字段。
-
选项卡 - 刚刚添加了示例数据。
-
我刚刚意识到我分配了错误的值 - 'Loc_*' 必须设置为层次结构中每个级别的 'Name' 的值。如何获取 Name 的所有值并将它们分配给单个示例行?
标签: sql sql-server tsql common-table-expression sql-server-2016