簡體   English   中英

使用 T-SQL 連接所有父行中的字符串的最有效方法是什么?

[英]What is the most efficient way to concatenate a string from all parent rows using T-SQL?

我有一個表,它有一個代表其父行的自引用外鍵。 為了以最簡單的形式說明問題,我們將使用下表:

CREATE TABLE Folder(
    id int IDENTITY(1,1) NOT NULL, --PK
    parent_id int NULL,        --FK
    folder_name varchar(255) NOT NULL)

我想創建一個標量值 function ,它將文件夾名稱及其所有父文件夾名稱的串聯字符串一直返回到根文件夾,該根文件夾將由 null parent_id 值指定。

我目前的解決方案是一種程序方法,我認為它並不理想。 這是我正在做的事情:

CREATE FUNCTION dbo.GetEntireLineage
    (@folderId INT)
    RETURNS VARCHAR(MAX)
AS
BEGIN
    DECLARE @lineage VARCHAR(MAX)
    DECLARE @parentFolderId INT

    SELECT @lineage = folder_name, @parentFolderId = parent_id FROM Folder WHERE id = @folderId

WHILE NOT @parentFolderId IS NULL
    BEGIN
        SET @parentFolderId = (SELECT parent_id FROM Folder WHERE parent_id = @parentFolderId)
        SET @lineage = (SELECT @lineage + '-' + (SELECT folder_name FROM Folder WHERE parent_id = @parentFolderId))
    END
RETURN @lineage
END

有沒有更理想的方法來做到這一點? 我是一位經驗豐富的程序員,但 T-SQL 對我來說並不熟悉,我知道由於基於集合的數據的性質,這些問題通常需要不同的方法。 任何幫助找到解決方案或處理 T-SQL 的任何其他提示和技巧將不勝感激。

要確定性能,您需要進行測試。 我已經使用您的版本(稍作修改)和其他人建議的遞歸 CTE 版本進行了一些測試。

我在一個文件夾層次結構中使用了包含 2048 行的示例表,因此當將 2048 作為參數傳遞給 function 時,完成了 2048 個連接。

循環版本:

create function GetEntireLineage1 (@id int)
returns varchar(max)
as
begin
  declare @ret varchar(max)

  select @ret = folder_name,
         @id = parent_id
  from Folder
  where id = @id

  while @@rowcount > 0
  begin
    select @ret = @ret + '-' + folder_name,
           @id = parent_id
    from Folder
    where id = @id
  end
  return @ret
end

統計數據:

 SQL Server Execution Times:
   CPU time = 125 ms,  elapsed time = 122 ms.

遞歸 CTE 版本:

create function GetEntireLineage2(@id int)
returns varchar(max)
begin
  declare @ret varchar(max);

  with cte(id, name) as
  (
    select f.parent_id,
           cast(f.folder_name as varchar(max))
    from Folder as f
    where f.id = @id
    union all
    select f.parent_id,
           c.name + '-' + f.folder_name
    from Folder as f
      inner join cte as c
        on f.id = c.id
  )
  select @ret = name
  from cte
  where id is null
  option (maxrecursion 0)

  return @ret
end

統計數據:

 SQL Server Execution Times:
   CPU time = 187 ms,  elapsed time = 183 ms.

所以在這兩者之間,循環版本更有效,至少在我的測試數據上是這樣。 您需要測試您的實際數據才能確定。

編輯

for xml path('')技巧的遞歸 CTE。

create function [dbo].[GetEntireLineage4](@id int)
returns varchar(max)
begin
  declare @ret varchar(max) = '';

  with cte(id, lvl, name) as
  (
    select f.parent_id,
           1,
           f.folder_name
    from Folder as f
    where f.id = @id
    union all
    select f.parent_id,
           lvl + 1,
           f.folder_name
    from Folder as f
      inner join cte as c
        on f.id = c.id
  )
  select @ret = (select '-'+name
                 from cte
                 order by lvl
                 for xml path(''), type).value('.', 'varchar(max)')
  option (maxrecursion 0)

  return stuff(@ret, 1, 1, '')
end

統計數據:

 SQL Server Execution Times:
   CPU time = 31 ms,  elapsed time = 37 ms.

使用遞歸查詢遍歷父母,然后使用此方法連接成字符串。

除非您有非常深的層次結構或可以利用索引的非常大的數據集,否則 hierarchyid 通常是多余的。 在不更改架構的情況下,這是您可以獲得的最快速度。

 with recursiveCTE (parent_id,concatenated_name) as (
    select parent_id,folder_name
    from folder
    union all
    select f.parent_id,r.concatenated_name +f.folder_name
    from folder f
    inner join recursiveCTE r on r.parent_id = f.id
    )
    select folder_name from recursiveCTE 

這對你有用:

 with cte (Parent_id, Path) as 
    (
    select Parent_Id,Folder_Name
    from folder
    union all
    select f.Parent_Id,r.Path + '\' + f.Folder_Name
    from Folder as f
    inner join cte as c on c.Parent_Id = f.Id
    )
    select Folder_Name from cte

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM