我需要递归地加入一个可以有循环的大图。
现在,在 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)
不应该有任何意义,它只是为了速度比较。