0

我需要递归地加入一个可以有循环的大图。
现在,在 SQL-Server 中的简化版本如下所示:

-- Array:10'000:   1.6s (postgresql)
-- LIKE: 10'000:  19s (sql-server)
-- JSON: 10'000:  19s (sql-server)
-- XML : 10'000:  +infinity  s (sql-server)
-- XML : 300 (a): 17s (nullable) (sql-server)
-- XML : 300 (b): 17s (not nullable) (sql-server)
-- XML : 300 (c): 23s (nullable, xpath) (sql-server)

;WITH CTE AS 
(
    SELECT
        1 AS i
       ,CAST(N',1,' AS national character varying(MAX)) AS paths 

    UNION ALL

    SELECT 
         CTE.i+1 AS i 
        ,CTE.paths + CAST((CTE.i + 1) AS national character varying(36)) + N',' AS paths
    FROM CTE
    WHERE CTE.i < 10000
    AND CTE.paths LIKE (N'%,' + CAST((CTE.i) AS national character varying(36)) + N',%') 
)
SELECT i FROM CTE 
OPTION (MAXRECURSION 0) 

由于 SQL-Server 不支持数组,我尝试使用 JSON 进行优化:

;WITH CTE AS 
(
    SELECT
         1 AS i
        ,CAST('[1]' AS nvarchar(MAX)) AS paths

    UNION ALL

    SELECT
         CTE.i+1 AS i
        ,JSON_MODIFY(CTE.paths, N'append $', CTE.i+1) AS paths
    FROM CTE
    WHERE CTE.i < 1000
    AND CTE.i IN
    (
        SELECT
            nda.value
        -- OpenJSON: ALTER DATABASE <db_name()> SET COMPATIBILITY_LEVEL = 130
        FROM OPENJSON(CTE.paths, N'$') AS nda
    )
)
SELECT i FROM CTE 
OPTION (MAXRECURSION 0) 

但 JSON 并没有更快,而且它只适用于 SQL-Server 2016+。

我也尝试过使用 XML:

;WITH CTE AS 
(
    SELECT
        1 AS i
       ,CAST(N'<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><row>1</row>' AS nvarchar(MAX)) AS paths


    UNION ALL


    SELECT 
         CTE.i+1 AS i 
         -- <row xsi:nil="true"/> 
        ,CTE.paths + N'<row>' + CAST((CTE.i + 1) AS nvarchar(36)) + N'</row>' AS paths
    FROM CTE
    WHERE CTE.i < 300
    AND CTE.i IN
    (
        SELECT
            xmlRow.xmlElement.value(N'.', N'int') AS v
        FROM
        (
            SELECT CAST(CTE.paths + N'</table>' AS xml) AS xmlArray
        ) AS tXmlArrayConverter
        CROSS APPLY tXmlArrayConverter.xmlArray.nodes(N'//row') AS xmlRow(xmlElement)
    )

)
SELECT i FROM CTE 
OPTION (MAXRECURSION 0) 

虽然这适用于 sql-server < 2016,但这太慢了 - 比喜欢的要糟糕得多。

获得良好性能的唯一方法是切换到支持数组的 PostgreSQL:

;WITH RECURSIVE CTE AS 
( 
    SELECT 
        1 AS i 
       ,array[1] AS paths 

    UNION ALL 

    SELECT 
         CTE.i+1 AS i 
        ,CTE.paths || (CTE.i + 1) AS paths
    FROM CTE
    WHERE CTE.i < 10000
    AND CTE.i = ANY(CTE.paths) 
    -- AND CTE.i <> ALL(CTE.paths) 
)
SELECT i FROM CTE 

这在 1.6 秒内完成,这与我预期的一样好。

有什么方法可以提高 SQL-Server 的性能吗?

注意:
这个愚蠢的例子CTE.i IN (whatever)不应该有任何意义,它只是为了速度比较。

4

0 回答 0