PARSENAME()在 SQL Server 中不使用(非确定性)函数将 varchar iPv4 地址转换为数字以创建持久计算列的各种方法有哪些?
我使用了基于第 n 个字符实例提取字符串部分的字符串操作技术来创建以下内容:
select
((256*256*256)*convert(bigint, ltrim(rtrim(substring(replace('255.255.255.255','.',replicate(' ',8)),1, 9)))))
+
((256*256)*convert(bigint,ltrim(rtrim(substring(replace('255.255.255.255','.',replicate(' ',8)),10, 9)))))
+
((256)*convert(bigint,ltrim(rtrim(substring(replace('255.255.255.255','.',replicate(' ',8)),19, 9)))))
+
(convert(bigint,ltrim(rtrim(substring(replace('255.255.255.255','.',replicate(' ',8)),28, 9)))))
Run Code Online (Sandbox Code Playgroud)
这是在将每个段转换为 BIGINT 并相乘之前字符串操作的视觉效果:

...这可以创建一个可以持久化的确定性计算列。还有哪些其他更有效的技术?
一种选择是使用 SQLCLR。创建一个方法来分割句点上的字符串,将每个字符串转换为Int64,然后乘以 256 的适当幂是相当容易的。一定要IsDeterministic=true在SqlFunction属性中设置,这不仅允许它被持久化,而且还允许它参与并行计划:-)。事实上,SQL# SQLCLR 库(我创建的)包含执行此转换的函数(在免费版本中):INET_AddressToNumber和INET_NumberToAddress。
测试设置:
CREATE TABLE #TempAddresses (IPAddress VARCHAR(15) NULL);
-- populate 1 million rows, with a NULL every 1000 rows
;WITH cte AS
(
SELECT TOP (1000000) ac1.column_id,
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [RowNum]
FROM master.sys.all_columns ac1
CROSS APPLY master.sys.all_columns ac2
)
INSERT INTO #TempAddresses ([IPAddress])
SELECT CASE cte.[RowNum] % 1000
WHEN 0 THEN NULL
ELSE CONVERT(VARCHAR(3), CONVERT(INT, CRYPT_GEN_RANDOM(1)))
+ '.' +
CONVERT(VARCHAR(3), CONVERT(INT, CRYPT_GEN_RANDOM(1)))
+ '.' +
CONVERT(VARCHAR(3), CONVERT(INT, CRYPT_GEN_RANDOM(1)))
+ '.' +
CONVERT(VARCHAR(3), CONVERT(INT, CRYPT_GEN_RANDOM(1)))
END
FROM cte
Run Code Online (Sandbox Code Playgroud)
关于以下三个测试,请注意:
SELECT了好几次,每次都保持最好的时间,而不是平均时间。SQLCLR 测试和结果:
DECLARE @Test1 BIGINT;
SET STATISTICS TIME ON;
SELECT @Test1 = -- Comment out "@Test1 = " to test returning the value
SQL#.INET_AddressToNumber(CONVERT(NVARCHAR(15), tmp.[IPAddress]))
FROM #TempAddresses tmp;
SET STATISTICS TIME OFF;
-- CPU time = 2454 ms, elapsed time = 5133 ms.
-- CPU time = 1594 ms, elapsed time = 1617 ms. (into variable)
Run Code Online (Sandbox Code Playgroud)
MartinSmith 的测试和结果:
DECLARE @Test3 BIGINT;
SET STATISTICS TIME ON;
SELECT @Test3 = -- Comment out "@Test3 = " to test returning the value
256 * 256 * 256 * CAST(FLOOR(LEFT(tmp.[IPAddress],3)) AS BIGINT)
+ 256 * 256 * CAST(FLOOR(SUBSTRING(tmp.[IPAddress],1 + CHARINDEX('.', tmp.[IPAddress]),3)) AS BIGINT)
+ 256 * SUBSTRING(REPLACE(tmp.[IPAddress],'.',SPACE(8)),19, 9)
+ RIGHT(tmp.[IPAddress], -1 + CHARINDEX('.', REVERSE(tmp.[IPAddress])))
FROM #TempAddresses tmp;
SET STATISTICS TIME OFF;
-- CPU time = 2781 ms, elapsed time = 5151 ms.
-- CPU time = 2156 ms, elapsed time = 2156 ms. (into variable)
Run Code Online (Sandbox Code Playgroud)
OP 的测试和结果:
DECLARE @Test2 BIGINT;
SET STATISTICS TIME ON;
SELECT @Test2 = -- Comment out "@Test2 = " to test returning the value
((256*256*256) * CONVERT(BIGINT, LTRIM(RTRIM(SUBSTRING(REPLACE(tmp.[IPAddress],'.',REPLICATE(' ',8)),1, 9)))))
+ ((256*256)*CONVERT(BIGINT,LTRIM(RTRIM(SUBSTRING(REPLACE(tmp.[IPAddress],'.',REPLICATE(' ',8)),10, 9)))))
+ ((256)*CONVERT(BIGINT,LTRIM(RTRIM(SUBSTRING(REPLACE(tmp.[IPAddress],'.',REPLICATE(' ',8)),19, 9)))))
+ (CONVERT(BIGINT,LTRIM(RTRIM(SUBSTRING(REPLACE(tmp.[IPAddress],'.',REPLICATE(' ',8)),28, 9)))))
FROM #TempAddresses tmp;
SET STATISTICS TIME OFF;
-- CPU time = 3531 ms, elapsed time = 5249 ms.
-- CPU time = 2515 ms, elapsed time = 2534 ms. (into variable)
Run Code Online (Sandbox Code Playgroud)
关于 SQLCLR 选项要记住的一件事:在计算列中使用 SQLCLR 函数是一种依赖关系,它将防止包含该函数的程序集被删除。如果您能够使用ALTER ASSEMBLY,那么您就不会丢弃并重新创建程序集。但ALTER ASSEMBLY仅当没有新的或删除的方法,并且现有方法的签名仍然相同时才允许。如果进行了任何这些更改,则您必须删除程序集,然后重新创建它。这将要求您首先删除引用该程序集中包含的 SQLCLR 函数的计算列。在将函数放置在计算列中的用例中,这可能会使 SQLCLR 选项不太受欢迎,而如果函数在查询、存储过程、视图等中,则这不会成为问题。
可以将 SQLCLR 函数包装在 T-SQL 函数中并在计算列中引用 T-SQL 函数,但是这样您就失去了 SQLCLR 函数能够参与并行执行计划的好处。
| 归档时间: |
|
| 查看次数: |
4472 次 |
| 最近记录: |