如何在不调用函数的情况下从 SELECT 语句中的字符串中提取字母或数字
How to extract alpha or numbers from a string within a SELECT statement WITHOUT a function call
我正在使用以下 SQL 创建一个函数来删除字符或数字。
CREATE FUNCTION [dbo].[fn_StripCharacters]
(
@String NVARCHAR(MAX),
@MatchExpression VARCHAR(255)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
SET @MatchExpression = '%['+@MatchExpression+']%'
WHILE PatIndex(@MatchExpression, @String) > 0
SET @String = Stuff(@String, PatIndex(@MatchExpression, @String), 1, '')
RETURN @String
END
我这样调用它来提取字母字符,然后将数字字符提取到两个排序字段中:
SELECT
...
(SELECT dbo.fn_StripCharacters(PD.District, '^a-z')) AS Sort1,
CAST((SELECT dbo.fn_StripCharacters(PD.District, '^0-9')) AS INT) AS Sort2,
...
FROM
我正在搜索大量的邮政编码区记录,并且不断调用该函数导致了相对较大的延迟。
有没有一种方法可以在不调用函数的情况下重现此功能?
它可以以某种方式合并到 SELECT 语句中吗?
SQL服务器版本为2017
您可以尝试这样的操作:
Create Function [dbo].[fnCleanString] (
@inputString varchar(8000)
, @stringPattern varchar(50) = '[0-9a-zA-Z]'
)
Returns table
With schemabinding
As
Return
With t(n)
As (
Select t.n
From (
Values (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)
, (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) As t(n)
)
, iTally (n)
As (
Select Top (len(@inputString))
checksum(row_number() over(Order By @@spid))
From t t1, t t2, t t3 -- 8000 rows
)
Select v.inputString
, outputString = (Select substring(v.inputString, it.n, 1)
From iTally it
Where substring(v.inputString, it.n, 1) Like @stringPattern
For xml Path(''), Type).value('.', 'varchar(8000)')
From (Values (@inputString)) As v(inputString);
GO
并这样称呼它:
Declare @testData table (AlphaNumeric varchar(100));
Insert Into @testData (AlphaNumeric)
Values ('a1b2c3d4'), ('5e6f7g8i');
Select *
From @testData td
Cross Apply dbo.fnCleanString(td.AlphaNumeric, '[^A-Z]') cs
Cross Apply dbo.fnCleanString(td.AlphaNumeric, '[^0-9]') cs2;
这适用于 VARCHAR - 但您可以轻松地将其更改为 return NVARCHAR,如果这是您真正需要的。请注意,它是为使用固定长度的字符串而编写的,NVARCHAR 的最大大小为 4000。
您还需要了解排序规则及其对结果的影响。
How to extract alpha or numbers from a string within a SELECT statement WITHOUT a function call?
你不能,因为像 REPLACE()
这样的简单字符串操作也是函数调用。并且,用户定义的函数由 SQL 服务器编译。他们表现得很好。
但是你的问题不是函数本身的性能,而是你必须多久使用一次。你知道的。
这里有一个可能的方法来加速您的邮政编码研磨任务:将持久化的计算列放在您的 table 上。您甚至可以为它们编制索引。
这是如何做到这一点。
告诉 SQL 服务器对您的存储函数使用模式绑定。它需要知道架构中的 table 定义取决于函数。为此,请将 WITH SCHEMABINDING
添加到您的函数定义中。
...
RETURNS NVARCHAR(MAX)
WITH SCHEMABINDING
AS
BEGIN
...
将两个计算的、持久的列添加到您的 table。
...
ALTER TABLE postcode
ADD letters
AS (dbo.fn_StripCharacters(postcode, '^A-Z'))
PERSISTED;
ALTER TABLE dbo.postcode
ADD numbers
AS (CAST(dbo.fn_StripCharacters(postcode, '^0-9') AS INT))
PERSISTED;
如果需要,您可以在计算列上放置索引。
CREATE INDEX numbers ON postcode (numbers DESC)
现在您可以根据需要插入、更新或删除非计算列。 SQL 服务器会在插入或更新每行 时评估您存储的函数一次。您的函数仍会得到评估,但当您从 table.
SELECT 时不会
并且您可以根据需要使用计算列
SELECT * FROM postcode ORDER BY numbers DESC
这是一个 db<>fiddle 演示。
我正在使用以下 SQL 创建一个函数来删除字符或数字。
CREATE FUNCTION [dbo].[fn_StripCharacters]
(
@String NVARCHAR(MAX),
@MatchExpression VARCHAR(255)
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
SET @MatchExpression = '%['+@MatchExpression+']%'
WHILE PatIndex(@MatchExpression, @String) > 0
SET @String = Stuff(@String, PatIndex(@MatchExpression, @String), 1, '')
RETURN @String
END
我这样调用它来提取字母字符,然后将数字字符提取到两个排序字段中:
SELECT
...
(SELECT dbo.fn_StripCharacters(PD.District, '^a-z')) AS Sort1,
CAST((SELECT dbo.fn_StripCharacters(PD.District, '^0-9')) AS INT) AS Sort2,
...
FROM
我正在搜索大量的邮政编码区记录,并且不断调用该函数导致了相对较大的延迟。 有没有一种方法可以在不调用函数的情况下重现此功能? 它可以以某种方式合并到 SELECT 语句中吗?
SQL服务器版本为2017
您可以尝试这样的操作:
Create Function [dbo].[fnCleanString] (
@inputString varchar(8000)
, @stringPattern varchar(50) = '[0-9a-zA-Z]'
)
Returns table
With schemabinding
As
Return
With t(n)
As (
Select t.n
From (
Values (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)
, (0), (0), (0), (0), (0), (0), (0), (0), (0), (0)) As t(n)
)
, iTally (n)
As (
Select Top (len(@inputString))
checksum(row_number() over(Order By @@spid))
From t t1, t t2, t t3 -- 8000 rows
)
Select v.inputString
, outputString = (Select substring(v.inputString, it.n, 1)
From iTally it
Where substring(v.inputString, it.n, 1) Like @stringPattern
For xml Path(''), Type).value('.', 'varchar(8000)')
From (Values (@inputString)) As v(inputString);
GO
并这样称呼它:
Declare @testData table (AlphaNumeric varchar(100));
Insert Into @testData (AlphaNumeric)
Values ('a1b2c3d4'), ('5e6f7g8i');
Select *
From @testData td
Cross Apply dbo.fnCleanString(td.AlphaNumeric, '[^A-Z]') cs
Cross Apply dbo.fnCleanString(td.AlphaNumeric, '[^0-9]') cs2;
这适用于 VARCHAR - 但您可以轻松地将其更改为 return NVARCHAR,如果这是您真正需要的。请注意,它是为使用固定长度的字符串而编写的,NVARCHAR 的最大大小为 4000。
您还需要了解排序规则及其对结果的影响。
How to extract alpha or numbers from a string within a SELECT statement WITHOUT a function call?
你不能,因为像 REPLACE()
这样的简单字符串操作也是函数调用。并且,用户定义的函数由 SQL 服务器编译。他们表现得很好。
但是你的问题不是函数本身的性能,而是你必须多久使用一次。你知道的。
这里有一个可能的方法来加速您的邮政编码研磨任务:将持久化的计算列放在您的 table 上。您甚至可以为它们编制索引。
这是如何做到这一点。
告诉 SQL 服务器对您的存储函数使用模式绑定。它需要知道架构中的 table 定义取决于函数。为此,请将
WITH SCHEMABINDING
添加到您的函数定义中。... RETURNS NVARCHAR(MAX) WITH SCHEMABINDING AS BEGIN ...
将两个计算的、持久的列添加到您的 table。
... ALTER TABLE postcode ADD letters AS (dbo.fn_StripCharacters(postcode, '^A-Z')) PERSISTED; ALTER TABLE dbo.postcode ADD numbers AS (CAST(dbo.fn_StripCharacters(postcode, '^0-9') AS INT)) PERSISTED;
如果需要,您可以在计算列上放置索引。
CREATE INDEX numbers ON postcode (numbers DESC)
现在您可以根据需要插入、更新或删除非计算列。 SQL 服务器会在插入或更新每行 时评估您存储的函数一次。您的函数仍会得到评估,但当您从 table.
SELECT 时不会并且您可以根据需要使用计算列
SELECT * FROM postcode ORDER BY numbers DESC
这是一个 db<>fiddle 演示。