如何在 SQL 服务器中将拆分函数转换为内联 table 值的 udf?
How can I convert split function to inline table valued udf in SQL server?
假设我有这个查询(伪):
Select T.a,
T.b,
(select top 1 element from fn_split(c,',') where element=T.element)
From largeTable T
其中 fn_split
为每一行运行,我想使用内联 table 值 udf,这样性能会更好。
注意:fn_split
只需通过 ,
拆分创建一个 table:
但是查看内联 table 值 udf 结构 :
create FUNCTION [dbo].[fn_...]
(
...
)
RETURNS table
AS
RETURN SELECT ...(!!!)
它应该 return select 作为第一条语句!
但是如果我的 UDF 看起来像:
CREATE FUNCTION [dbo].[FN_Split]
(
@InDelimitedString varchar(max),
@InDelimiter varchar(10)
)
RETURNS
@tblArray TABLE
(
ElementID smallint IDENTITY(1,1),
Element varchar(1000)
)
AS
BEGIN
DECLARE @StrPos smallint,
@StrStart smallint,
@DelimiterLength smallint
SET @DelimiterLength = LEN(@InDelimiter)
WHILE LEN(@InDelimitedString) > 0
BEGIN
--Removed for clarity . do some CHARINDEX manipulation ETc.
END
RETURN
END
问题:
我不能马上 return select
,但我仍然想将 fn_split
更改为内联 table 值的 udf。
我该怎么做?
问题出在你的拆分函数上。它由 Jeff Moden 在 RBAR fashion. You should use a set-based splitter. Here is the DelimitedSplit8k 中进行拆分,这是最快的拆分器之一:
CREATE FUNCTION [dbo].[DelimitedSplit8K](
@pString VARCHAR(8000), @pDelimiter CHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,E2(N) AS (SELECT 1 FROM E1 a, E1 b)
,E4(N) AS (SELECT 1 FROM E2 a, E2 b)
,cteTally(N) AS(
SELECT TOP (ISNULL(DATALENGTH(@pString), 0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
,cteStart(N1) AS(
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString, t.N, 1) = @pDelimiter
),
cteLen(N1, L1) AS(
SELECT
s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter, @pString, s.N1),0) - s.N1, 8000)
FROM cteStart s
)
SELECT
ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
注意:请务必查看文章以了解更新后的功能
有关更多拆分函数,请阅读 Aaron Bertrand 爵士的这些文章:
- Split strings the right way – or the next best way
- Splitting Strings : A Follow-Up
- Splitting Strings : Now with less T-SQL
- Comparing string splitting / concatenation methods
- Processing a list of integers : my approach
- Splitting a list of integers : another roundup
- More on splitting lists : custom delimiters, preventing duplicates, and maintaining order
- Removing Duplicates from Strings in SQL Server
假设我有这个查询(伪):
Select T.a,
T.b,
(select top 1 element from fn_split(c,',') where element=T.element)
From largeTable T
其中 fn_split
为每一行运行,我想使用内联 table 值 udf,这样性能会更好。
注意:fn_split
只需通过 ,
拆分创建一个 table:
但是查看内联 table 值 udf 结构 :
create FUNCTION [dbo].[fn_...]
(
...
)
RETURNS table
AS
RETURN SELECT ...(!!!)
它应该 return select 作为第一条语句!
但是如果我的 UDF 看起来像:
CREATE FUNCTION [dbo].[FN_Split]
(
@InDelimitedString varchar(max),
@InDelimiter varchar(10)
)
RETURNS
@tblArray TABLE
(
ElementID smallint IDENTITY(1,1),
Element varchar(1000)
)
AS
BEGIN
DECLARE @StrPos smallint,
@StrStart smallint,
@DelimiterLength smallint
SET @DelimiterLength = LEN(@InDelimiter)
WHILE LEN(@InDelimitedString) > 0
BEGIN
--Removed for clarity . do some CHARINDEX manipulation ETc.
END
RETURN
END
问题:
我不能马上 return select
,但我仍然想将 fn_split
更改为内联 table 值的 udf。
我该怎么做?
问题出在你的拆分函数上。它由 Jeff Moden 在 RBAR fashion. You should use a set-based splitter. Here is the DelimitedSplit8k 中进行拆分,这是最快的拆分器之一:
CREATE FUNCTION [dbo].[DelimitedSplit8K](
@pString VARCHAR(8000), @pDelimiter CHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,E2(N) AS (SELECT 1 FROM E1 a, E1 b)
,E4(N) AS (SELECT 1 FROM E2 a, E2 b)
,cteTally(N) AS(
SELECT TOP (ISNULL(DATALENGTH(@pString), 0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
,cteStart(N1) AS(
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString, t.N, 1) = @pDelimiter
),
cteLen(N1, L1) AS(
SELECT
s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter, @pString, s.N1),0) - s.N1, 8000)
FROM cteStart s
)
SELECT
ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
注意:请务必查看文章以了解更新后的功能
有关更多拆分函数,请阅读 Aaron Bertrand 爵士的这些文章:
- Split strings the right way – or the next best way
- Splitting Strings : A Follow-Up
- Splitting Strings : Now with less T-SQL
- Comparing string splitting / concatenation methods
- Processing a list of integers : my approach
- Splitting a list of integers : another roundup
- More on splitting lists : custom delimiters, preventing duplicates, and maintaining order
- Removing Duplicates from Strings in SQL Server