1

我有一个简单的自引用内存表

CREATE TABLE [Accounts]
(
  Id UNIQUEIDENTIFIER NOT NULL,
  [ParentAccount_Id] UNIQUEIDENTIFIER NULL, 
  CONSTRAINT [PK_Accounts] PRIMARY KEY NONCLUSTERED ([Id])
)  
WITH  
(
  MEMORY_OPTIMIZED = ON,  
  DURABILITY = SCHEMA_AND_DATA
);

我正在努力寻找一种优雅的方式来遍历由表表示的层次结构,即给定一个 Account.Id 我需要获取它的祖先列表。

通常,当不使用本地编译的存储过程时,我会使用 CTE 或 HIERARCHYID 数据类型。但是,本机编译的存储过程都不支持这些。

所以我的问题是,我可以使用什么技术来遍历与本机编译的存储过程兼容的这种层次结构?

样本数据和预期结果:

|Id                                     |ParentAccounts_Id                     |
|---------------------------------------|--------------------------------------|
|00000000-0000-0000-0000-000000000006   |00000000-0000-0000-0000-000000000002  |
|00000000-0000-0000-0000-000000000005   |00000000-0000-0000-0000-000000000002  |
|00000000-0000-0000-0000-000000000004   |00000000-0000-0000-0000-000000000001  |
|00000000-0000-0000-0000-000000000003   |00000000-0000-0000-0000-000000000001  |
|00000000-0000-0000-0000-000000000002   |00000000-0000-0000-0000-000000000001  |
|00000000-0000-0000-0000-000000000001   |NULL                                  |

鉴于初始 ID 是 00000000-0000-0000-0000-000000000006 我希望看到以下结果

00000000-0000-0000-0000-000000000006
00000000-0000-0000-0000-000000000002
00000000-0000-0000-0000-000000000001
4

2 回答 2

1

你有两个选择:

1)创建一个普通的存储过程。针对内存表运行的正常(或非本地编译的代码)称为“互操作”,没有真正的理由不会很快:

DECLARE @id UNIQUEIDENTIFIER = '00000000-0000-0000-0000-000000000006'

;WITH cte AS
(
SELECT 1 xlevel, Id, ParentAccount_Id
FROM dbo.accounts
WHERE Id = @id

UNION ALL

SELECT xlevel + 1, a.Id, a.ParentAccount_Id
FROM cte c
    INNER JOIN dbo.accounts a ON c.ParentAccount_Id = a.Id
)
SELECT Id
FROM cte

或者,2) 在本机编译的过程中实现一个循环。如果你看一下我用 Fizzbuzz 做的这个例子,它的速度快如闪电——100 万次循环不到一秒。

DROP PROC IF EXISTS dbo.usp_getAccounts
DROP TYPE IF EXISTS dbo.typ_accounts
GO

CREATE TYPE dbo.typ_accounts
AS TABLE
(
    Id                  UNIQUEIDENTIFIER NOT NULL,

    PRIMARY KEY NONCLUSTERED ( Id )
)
WITH ( MEMORY_OPTIMIZED = ON ); 
GO


DROP PROC IF EXISTS dbo.usp_getAccounts
GO
CREATE PROC dbo.usp_getAccounts

    @targetAccountId UNIQUEIDENTIFIER

WITH
    NATIVE_COMPILATION, 
    SCHEMABINDING, 
    EXECUTE AS OWNER
AS
BEGIN ATOMIC
WITH
(
    TRANSACTION ISOLATION LEVEL = SERIALIZABLE, 
    LANGUAGE = N'english'
)   

    DECLARE @t AS dbo.typ_accounts;

    WHILE ( @targetAccountId IS NOT NULL )
    BEGIN

        INSERT INTO @t ( Id )
        SELECT @targetAccountId;

        SELECT @targetAccountId = ParentAccount_Id
        FROM dbo.Accounts
        WHERE Id = @targetAccountId;

    END

    SELECT Id
    FROM @t;

    RETURN;

END
GO


EXEC dbo.usp_getAccounts '00000000-0000-0000-0000-000000000006'

你有什么样的卷?您使用内存表是否有特定原因?

于 2018-10-19T14:26:17.307 回答
0

我想出的与本机编译的存储过程兼容的最佳方法如下:

DECLARE @targetAccountId UNIQUEIDENTIFIER = '00000000-0000-0000-0000-000000000006'
WHILE (@targetAccountId IS NOT NULL)
BEGIN
    PRINT @targetAccountId

    SELECT @targetAccountId = ParentAccounts_Id
            FROM [im].[Accounts]
            WHERE Id = @targetAccountId
END

尽管我对更好的想法持开放态度,因为在 T-SQL 中看到循环总是让我不寒而栗!

于 2018-10-19T13:00:49.243 回答