如何在不调用函数的情况下从 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 上。您甚至可以为它们编制索引。

这是如何做到这一点。

  1. 告诉 SQL 服务器对您的存储函数使用模式绑定。它需要知道架构中的 table 定义取决于函数。为此,请将 WITH SCHEMABINDING 添加到您的函数定义中。

    ...
    RETURNS NVARCHAR(MAX)
    WITH SCHEMABINDING
    AS
    BEGIN
    ...
    
  2. 将两个计算的、持久的列添加到您的 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;
    
  3. 如果需要,您可以在计算列上放置索引。

    CREATE INDEX numbers ON postcode  (numbers DESC)
    
  4. 现在您可以根据需要插入、更新或删除非计算列。 SQL 服务器会在插入或更新每行 时评估您存储的函数一次。您的函数仍会得到评估,但当您从 table.

    SELECT 时不会
  5. 并且您可以根据需要使用计算列

    SELECT * FROM postcode ORDER BY numbers DESC
    

这是一个 db<>fiddle 演示。