วิธีการตัดอักขระที่ไม่ใช่ตัวอักษรทั้งหมดจากสตริงใน SQL Server


172

คุณจะลบอักขระทั้งหมดที่ไม่ใช่ตัวอักษรจากสตริงได้อย่างไร?

แล้วไม่ใช่ตัวอักษรและตัวเลขล่ะ?

สิ่งนี้จะต้องเป็นฟังก์ชั่นที่กำหนดเองหรือยังมีวิธีแก้ไขปัญหาที่ปรับเปลี่ยนได้ทั่วไปมากกว่าหรือไม่

คำตอบ:


362

ลองใช้ฟังก์ชั่นนี้:

Create Function [dbo].[RemoveNonAlphaCharacters](@Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin

    Declare @KeepValues as varchar(50)
    Set @KeepValues = '%[^a-z]%'
    While PatIndex(@KeepValues, @Temp) > 0
        Set @Temp = Stuff(@Temp, PatIndex(@KeepValues, @Temp), 1, '')

    Return @Temp
End

เรียกว่าเป็นแบบนี้:

Select dbo.RemoveNonAlphaCharacters('abc1234def5678ghi90jkl')

เมื่อคุณเข้าใจรหัสแล้วคุณจะเห็นว่ามันค่อนข้างง่ายในการเปลี่ยนเพื่อลบอักขระอื่นเช่นกัน คุณสามารถทำให้มีชีวิตชีวาพอที่จะผ่านในรูปแบบการค้นหาของคุณ

หวังว่ามันจะช่วย


9
รหัสนี้ลบอักขระที่ไม่ใช่ตัวอักษร (ดังนั้นตัวเลขจะถูกลบด้วย) หากคุณต้องการออกจากตัวเลข (ลบอักขระที่ไม่ใช่ตัวอักษรตัวเลข) ดังนั้น ... แทนที่ ^ az ด้วย ^ az ^ 0-9 สตริงการค้นหานั้นจะปรากฏในรหัสในที่ต่างกันสองแห่ง ให้แน่ใจว่าได้แทนที่ทั้งสอง
George Mastros

26
จากความคิดเห็นของเจฟฟ์: ฉันคิดว่าถ้าต้องการตัดที่ไม่ใช่ตัวอักษรและไม่ใช่ตัวเลขทั้งหมดคุณจะต้องการ '^ a-z0-9' (กับ '^ az ^ 0-9' ซึ่งจะทำให้ ^ ในสตริง) .
แม้ Mien

1
+1 จอร์จ นี่เป็นหนึ่งในสถานที่ที่มีรหัส "Set-Based" และการใช้ฟังก์ชั่น Inline Scalar มีความยากลำบากในการตี ทำได้ดีมาก ฉันได้ใช้ฟังก์ชั่น "Initial Caps" ของคุณซึ่งมีรูปแบบพื้นฐานเหมือนกันมาสองสามปีแล้ว
Jeff Moden

6
@Lynchie เปลี่ยน '% [^ az]%' เป็น '% [^ az]%' โดยทั่วไปเพียงแค่ใส่ช่องว่างหลัง z
George Mastros

8
ชื่อตัวแปร KeepValues ​​เป็นจริงตรงข้ามกับสิ่งที่มันควรจะทำ KeepValues ​​แสดงรายการอักขระที่ต้องยกเว้น ..
nee21

167

รุ่น Parameterized ของG Mastros ' คำตอบที่น่ากลัว :

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 dbo.fn_StripCharacters('a1!s2@d3#f4$', '^a-z')

ตัวเลขเท่านั้น:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^0-9')

ตัวอักษรและตัวเลขเท่านั้น:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', '^a-z0-9')

ไม่ใช่ตัวเลข:

SELECT dbo.fn_StripCharacters('a1!s2@d3#f4$', 'a-z0-9')

3
ฉันชอบรุ่นนี้และสร้างคำตอบที่ดัดแปลงของ G Mastros ก่อนที่จะเลื่อนลงเพื่อโหวต!
earnshavian

รูปแบบ regex ดูเหมือนจะไม่สามารถทำงานได้กับทุกพื้นที่ ถ้าฉันต้องการตัดอักขระพิเศษทั้งหมดยกเว้นตัวอักษรและตัวเลขและช่องว่างฉันคาดว่าจะใช้SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9\s')ซึ่งยังคงแถบช่องว่าง ฉันพยายามใช้ด้วย[[:blank:]]แต่นั่นเป็นการทำลายฟังก์ชั่นและไม่มีสิ่งใดถูกลบออกจากสตริง Ive ที่ใกล้เคียงที่สุดคือการใช้: SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^a-z0-9 ')(ฮาร์ดการเข้ารหัสช่องว่างในรูปแบบ regex) อย่างไรก็ตามนั่นไม่ได้ลบตัวแบ่งบรรทัด
Billy McKee

2
@BillyMcKee เพิ่มช่องว่างที่จุดเริ่มต้นแทนที่จะเพิ่มที่ส่วนท้ายของนิพจน์ทั่วไป SELECT dbo.fn_StripCharacters('a1!s2 spaces @d3# f4$', '^ a-z0-9')
Mike

8

เชื่อหรือไม่ว่าในระบบของฉันฟังก์ชั่นนี้น่าเกลียดทำงานได้ดีกว่า G Mastros อันหรูหรา

CREATE FUNCTION dbo.RemoveSpecialChar (@s VARCHAR(256)) 
RETURNS VARCHAR(256) 
WITH SCHEMABINDING
    BEGIN
        IF @s IS NULL
            RETURN NULL
        DECLARE @s2 VARCHAR(256) = '',
                @l INT = LEN(@s),
                @p INT = 1

        WHILE @p <= @l
            BEGIN
                DECLARE @c INT
                SET @c = ASCII(SUBSTRING(@s, @p, 1))
                IF @c BETWEEN 48 AND 57
                   OR  @c BETWEEN 65 AND 90
                   OR  @c BETWEEN 97 AND 122
                    SET @s2 = @s2 + CHAR(@c)
                SET @p = @p + 1
            END

        IF LEN(@s2) = 0
            RETURN NULL

        RETURN @s2

สิ่งที่เกี่ยวกับเครื่องหมายจุลภาคทั่วไประยะเวลาพื้นที่ ฯลฯ
sojim

ความแตกต่างถ้าคุณไม่ได้ใช้ASCIIจำนวนเต็มที่นี่และเปรียบเทียบผลลัพธ์โดยตรงSUBSTRINGกับตัวอักษรบางตัวเช่น: SET @ch=SUBSTRING(@s, @p, 1)และIF @ch BETWEEN '0' AND '9' OR @ch BETWEEN 'a' AND 'z' OR @ch BETWEEN 'A' AND 'Z' ...
S.Serpooshan

เพิ่มด้วย SCHEMABINDING ในฟังก์ชั่นของเขาเหมือนที่ฟังก์ชั่นของคุณมี คุณกำลังใช้ VARCHAR ฟังก์ชั่นของเขากำลังใช้ NVARCHAR หากพารามิเตอร์ที่คุณส่งผ่านไปยังฟังก์ชั่นของเขาคือ VARCHAR คุณควรใช้ VARCHAR แทน NVARCHAR ในการทำงานของเขามิฉะนั้นระบบของคุณจะต้องส่งค่าสตริงจาก VARCHAR ไปยัง NVARCHAR ก่อนที่จะสามารถใช้ฟังก์ชันที่มีราคาแพงกว่า แม้จะมีการเปลี่ยนแปลงการทำงานของคุณก็อาจจะเร็วกว่า แต่สิ่งเหล่านี้คือตัวอย่างบางส่วนที่ฉันสามารถดูได้ว่าการทำงานของเขาอาจทำงานช้าลงสำหรับคุณในสถานการณ์ของคุณ
EricI

1
ฟังก์ชั่นของเขายังใช้ NVARCHAR (MAX) และฟังก์ชั่นของคุณกำลังใช้ VARCHAR (256) หาก 256 เป็นสิ่งที่คุณต้องการให้เปลี่ยนฟังก์ชั่นของเขาเพื่อใช้ VARCHAR (256) เช่นกันและฟังก์ชั่นของเขาจะทำงานได้เร็วขึ้นสำหรับคุณ
EricI

5

ฉันรู้ว่า SQL ไม่ดีในการจัดการสตริง แต่ฉันไม่คิดว่ามันจะเป็นเรื่องยาก นี่คือฟังก์ชั่นง่าย ๆ ในการตัดตัวเลขทั้งหมดออกจากสตริง จะมีวิธีที่ดีกว่าในการทำเช่นนี้ แต่นี่เป็นการเริ่มต้น

CREATE FUNCTION dbo.AlphaOnly (
    @String varchar(100)
)
RETURNS varchar(100)
AS BEGIN
  RETURN (
    REPLACE(
      REPLACE(
        REPLACE(
          REPLACE(
            REPLACE(
              REPLACE(
                REPLACE(
                  REPLACE(
                    REPLACE(
                      REPLACE(
                        @String,
                      '9', ''),
                    '8', ''),
                  '7', ''),
                '6', ''),
              '5', ''),
            '4', ''),
          '3', ''),
        '2', ''),
      '1', ''),
    '0', '')
  )
END
GO

-- ==================
DECLARE @t TABLE (
    ColID       int,
    ColString   varchar(50)
)

INSERT INTO @t VALUES (1, 'abc1234567890')

SELECT ColID, ColString, dbo.AlphaOnly(ColString)
FROM @t

เอาท์พุต

ColID ColString
----- ------------- ---
    1 abc1234567890 abc

รอบ 2 - บัญชีดำที่ขับเคลื่อนด้วยข้อมูล

-- ============================================
-- Create a table of blacklist characters
-- ============================================
IF EXISTS (SELECT * FROM sys.tables WHERE [object_id] = OBJECT_ID('dbo.CharacterBlacklist'))
  DROP TABLE dbo.CharacterBlacklist
GO
CREATE TABLE dbo.CharacterBlacklist (
    CharID              int         IDENTITY,
    DisallowedCharacter nchar(1)    NOT NULL
)
GO
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'0')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'1')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'2')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'3')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'4')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'5')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'6')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'7')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'8')
INSERT INTO dbo.CharacterBlacklist (DisallowedCharacter) VALUES (N'9')
GO

-- ====================================
IF EXISTS (SELECT * FROM sys.objects WHERE [object_id] = OBJECT_ID('dbo.StripBlacklistCharacters'))
  DROP FUNCTION dbo.StripBlacklistCharacters
GO
CREATE FUNCTION dbo.StripBlacklistCharacters (
    @String nvarchar(100)
)
RETURNS varchar(100)
AS BEGIN
  DECLARE @blacklistCt  int
  DECLARE @ct           int
  DECLARE @c            nchar(1)

  SELECT @blacklistCt = COUNT(*) FROM dbo.CharacterBlacklist

  SET @ct = 0
  WHILE @ct < @blacklistCt BEGIN
    SET @ct = @ct + 1

    SELECT @String = REPLACE(@String, DisallowedCharacter, N'')
    FROM dbo.CharacterBlacklist
    WHERE CharID = @ct
  END

  RETURN (@String)
END
GO

-- ====================================
DECLARE @s  nvarchar(24)
SET @s = N'abc1234def5678ghi90jkl'

SELECT
    @s                  AS OriginalString,
    dbo.StripBlacklistCharacters(@s)   AS ResultString

เอาท์พุต

OriginalString           ResultString
------------------------ ------------
abc1234def5678ghi90jkl   abcdefghijkl

ความท้าทายของฉันต่อผู้อ่าน: คุณสามารถทำให้มีประสิทธิภาพมากขึ้นได้ไหม? เกี่ยวกับการใช้การเรียกซ้ำ


คุณอาจจะสามารถเขียน dbo.StripBlacklistCharacters () ที่ดีกว่าโดยไม่ต้องวนซ้ำโดยใช้ sommarskog.se/arrays-in-sql-2005.html#tblnumตารางตัวเลขเข้าร่วมในตารางรายการดำของคุณ แต่วันนี้ฉันขี้เกียจเกินไปที่จะลอง ตัวเอง ....
KM

4

หากคุณเป็นเหมือนฉันและไม่สามารถเข้าถึงได้เพียงเพิ่มฟังก์ชั่นในข้อมูลการผลิตของคุณ แต่ยังต้องการทำการกรองชนิดนี้นี่เป็นโซลูชัน SQL ที่บริสุทธิ์โดยใช้ตาราง PIVOT เพื่อนำชิ้นส่วนที่กรองแล้วกลับมารวมกันอีกครั้ง

หมายเหตุฉัน hardcoded ตารางถึง 40 ตัวอักษรคุณจะต้องเพิ่มมากขึ้นถ้าคุณมีสตริงที่จะกรองอีกต่อไป

SET CONCAT_NULL_YIELDS_NULL OFF;

with 
    ToBeScrubbed
as (
    select 1 as id, '*SOME 222@ !@* #* BOGUS !@*&! DATA' as ColumnToScrub
),

Scrubbed as (
    select 
        P.Number as ValueOrder,
        isnull ( substring ( t.ColumnToScrub , number , 1 ) , '' ) as ScrubbedValue,
        t.id
    from
        ToBeScrubbed t
        left join master..spt_values P
            on P.number between 1 and len(t.ColumnToScrub)
            and type ='P'
    where
        PatIndex('%[^a-z]%', substring(t.ColumnToScrub,P.number,1) ) = 0
)

SELECT
    id, 
    [1]+ [2]+ [3]+ [4]+ [5]+ [6]+ [7]+ [8] +[9] +[10]
    +  [11]+ [12]+ [13]+ [14]+ [15]+ [16]+ [17]+ [18] +[19] +[20]
    +  [21]+ [22]+ [23]+ [24]+ [25]+ [26]+ [27]+ [28] +[29] +[30]
    +  [31]+ [32]+ [33]+ [34]+ [35]+ [36]+ [37]+ [38] +[39] +[40] as ScrubbedData
FROM (
    select 
        *
    from 
        Scrubbed
    ) 
    src
    PIVOT (
        MAX(ScrubbedValue) FOR ValueOrder IN (
        [1], [2], [3], [4], [5], [6], [7], [8], [9], [10],
        [11], [12], [13], [14], [15], [16], [17], [18], [19], [20],
        [21], [22], [23], [24], [25], [26], [27], [28], [29], [30],
        [31], [32], [33], [34], [35], [36], [37], [38], [39], [40]
        )
    ) pvt

วิธีแก้ปัญหานี้เร็วกว่าฉันถึง 2.3 เท่าเมื่อเทียบกับการใช้ฟังก์ชั่นในแถว 235K ฉันยังต้องทำการเปลี่ยน 2x และใช้ CTE ทั้งหมดสี่รายการ ทำงานเหมือนแชมป์
JJS

4

จากการดูโซลูชันที่ให้มาทั้งหมดฉันคิดว่าต้องมีวิธี SQL บริสุทธิ์ที่ไม่ต้องใช้ฟังก์ชันหรือแบบสอบถาม CTE / XML และไม่เกี่ยวข้องกับการรักษาคำสั่ง REPLACE ที่ซ้อนกันยาก นี่คือทางออกของฉัน:

SELECT 
  x
  ,CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 1, 1) + '%' THEN '' ELSE SUBSTRING(x, 1, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 2, 1) + '%' THEN '' ELSE SUBSTRING(x, 2, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 3, 1) + '%' THEN '' ELSE SUBSTRING(x, 3, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 4, 1) + '%' THEN '' ELSE SUBSTRING(x, 4, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 5, 1) + '%' THEN '' ELSE SUBSTRING(x, 5, 1) END
    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, 6, 1) + '%' THEN '' ELSE SUBSTRING(x, 6, 1) END
-- Keep adding rows until you reach the column size 
    AS stripped_column
FROM (SELECT 
        column_to_strip AS x
        ,'ABCDEFGHIJKLMNOPQRSTUVWXYZ' AS a 
      FROM my_table) a

ข้อดีของการทำเช่นนี้คืออักขระที่ถูกต้องมีอยู่ในสตริงเดียวในแบบสอบถามย่อยทำให้ง่ายต่อการกำหนดค่าใหม่สำหรับชุดอักขระอื่น

ข้อเสียคือคุณต้องเพิ่มแถวของ SQL สำหรับแต่ละอักขระจนถึงขนาดของคอลัมน์ของคุณ เพื่อให้งานนั้นง่ายขึ้นฉันเพิ่งใช้สคริปต์ Powershell ด้านล่างตัวอย่างนี้ถ้าสำหรับ VARCHAR (64):

1..64 | % {
  "    + CASE WHEN a NOT LIKE '%' + SUBSTRING(x, {0}, 1) + '%' THEN '' ELSE SUBSTRING(x, {0}, 1) END" -f $_
} | clip.exe

3
ไม่สะดวกในกรณีทั่วไป แต่ใช้ง่ายและมีประโยชน์สำหรับการค้นหาครั้งเดียวด้วยคอลัมน์แคบ ๆ
Eric J.

3

iTVFนี่คือวิธีที่จะเอาอักขระที่ไม่ใช่ตัวอักษรใช้อีก ก่อนอื่นคุณต้องใช้ตัวแยกสตริงที่ยึดตามรูปแบบ นี่คือสิ่งที่นำมาจากบทความของ Dwain Camp :

-- PatternSplitCM will split a string based on a pattern of the form 
-- supported by LIKE and PATINDEX 
-- 
-- Created by: Chris Morris 12-Oct-2012 
CREATE FUNCTION [dbo].[PatternSplitCM]
(
       @List                VARCHAR(8000) = NULL
       ,@Pattern            VARCHAR(50)
) RETURNS TABLE WITH SCHEMABINDING 
AS 

RETURN
    WITH numbers AS (
        SELECT TOP(ISNULL(DATALENGTH(@List), 0))
            n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
        FROM
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) d (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) f (n),
        (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) g (n)
    )

    SELECT
        ItemNumber = ROW_NUMBER() OVER(ORDER BY MIN(n)),
        Item = SUBSTRING(@List,MIN(n),1+MAX(n)-MIN(n)),
        [Matched]
    FROM (
        SELECT n, y.[Matched], Grouper = n - ROW_NUMBER() OVER(ORDER BY y.[Matched],n)
        FROM numbers
        CROSS APPLY (
            SELECT [Matched] = CASE WHEN SUBSTRING(@List,n,1) LIKE @Pattern THEN 1 ELSE 0 END
        ) y
    ) d
    GROUP BY [Matched], Grouper

ตอนนี้คุณมีตัวแยกสัญญาณตามรูปแบบคุณต้องแยกสตริงที่ตรงกับรูปแบบ:

[a-z]

จากนั้นทำการต่อกลับกันเพื่อให้ได้ผลลัพธ์ตามที่ต้องการ:

SELECT *
FROM tbl t
CROSS APPLY(
    SELECT Item + ''
    FROM dbo.PatternSplitCM(t.str, '[a-z]')
    WHERE Matched = 1
    ORDER BY ItemNumber
    FOR XML PATH('')
) x (a)

ตัวอย่าง

ผลลัพธ์:

| Id |              str |              a |
|----|------------------|----------------|
|  1 |    testte d'abc |     testtedabc |
|  2 |            anr¤a |           anra |
|  3 |  gs-re-C“te d'ab |     gsreCtedab |
|  4 |         Mfe, DF |          MfeDF |
|  5 |           Rtemd |          Rtemd |
|  6 |          jadji |          jadji |
|  7 |      Cje y ret¢n |       Cjeyretn |
|  8 |        Jklbalu |        Jklbalu |
|  9 |       lene-iokd |       leneiokd |
| 10 |   liode-Pyrnie |    liodePyrnie |
| 11 |         Vs Gta |          VsGta |
| 12 |        Sƒo Paulo |        SoPaulo |
| 13 |  vAstra gAtaland | vAstragAtaland |
| 14 |  ¥uble / Bio-Bio |     ubleBioBio |
| 15 | Upln/ds VAsb-y |    UplndsVAsby |

มีข้อได้เปรียบอะไรบ้างที่จะใช้มันเหนือคำตอบอื่น ๆ ?
S.Serpooshan

2

โซลูชันนี้ได้รับแรงบันดาลใจจากวิธีการแก้ปัญหาของ Mr. Allen ต้องการNumbersตารางจำนวนเต็ม (ซึ่งคุณควรมีในมือหากคุณต้องการดำเนินการค้นหาอย่างจริงจังด้วยประสิทธิภาพที่ดี) มันไม่ต้องการ CTE คุณสามารถเปลี่ยนNOT IN (...)นิพจน์เพื่อแยกอักขระเฉพาะหรือเปลี่ยนเป็นนิพจน์IN (...)OR LIKEเพื่อเก็บอักขระบางตัวเท่านั้น

SELECT (
    SELECT  SUBSTRING([YourString], N, 1)
    FROM    dbo.Numbers
    WHERE   N > 0 AND N <= CONVERT(INT, LEN([YourString]))
        AND SUBSTRING([YourString], N, 1) NOT IN ('(',')',',','.')
    FOR XML PATH('')
) AS [YourStringTransformed]
FROM ...

ทางออกที่น่าสนใจสำหรับปัญหาที่ไม่เกี่ยวข้อง
TaterJuice

2

นี่คือโซลูชันที่ไม่ต้องการสร้างฟังก์ชันหรือแสดงรายการอักขระทั้งหมดเพื่อแทนที่ มันใช้คำสั่ง recursive WITH ร่วมกับ PATINDEX เพื่อค้นหาตัวอักษรที่ไม่ต้องการ มันจะแทนที่ตัวอักษรที่ไม่พึงประสงค์ทั้งหมดในคอลัมน์ - มากถึง 100 อักขระที่ไม่ซ้ำกันที่มีอยู่ในสตริงที่กำหนด (EG "ABC123DEF234" จะมีอักขระไม่ถูกต้อง 4 ตัว 1, 2, 3 และ 4) ขีด จำกัด 100 คือจำนวนสูงสุดของการเรียกซ้ำที่อนุญาตในคำสั่ง WITH แต่ไม่ได้กำหนดขีด จำกัด จำนวนแถวในการประมวลผลซึ่ง ถูก จำกัด โดยหน่วยความจำที่มีอยู่เท่านั้น
หากคุณไม่ต้องการผลลัพธ์ DISTINCT คุณสามารถลบสองตัวเลือกจากรหัสได้

-- Create some test data:
SELECT * INTO #testData 
FROM (VALUES ('ABC DEF,K.l(p)'),('123H,J,234'),('ABCD EFG')) as t(TXT)

-- Actual query:
-- Remove non-alpha chars: '%[^A-Z]%'
-- Remove non-alphanumeric chars: '%[^A-Z0-9]%'
DECLARE @BadCharacterPattern VARCHAR(250) = '%[^A-Z]%';

WITH recurMain as (
    SELECT DISTINCT CAST(TXT AS VARCHAR(250)) AS TXT, PATINDEX(@BadCharacterPattern, TXT) AS BadCharIndex
    FROM #testData
    UNION ALL
    SELECT CAST(TXT AS VARCHAR(250)) AS TXT, PATINDEX(@BadCharacterPattern, TXT) AS BadCharIndex
    FROM (
        SELECT 
            CASE WHEN BadCharIndex > 0 
                THEN REPLACE(TXT, SUBSTRING(TXT, BadCharIndex, 1), '')
                ELSE TXT 
            END AS TXT
        FROM recurMain
        WHERE BadCharIndex > 0
    ) badCharFinder
)
SELECT DISTINCT TXT
FROM recurMain
WHERE BadCharIndex = 0;

1

ฉันวางสิ่งนี้ไว้ในที่ที่ PatIndex ถูกเรียก

PatIndex('%[^A-Za-z0-9]%', @Temp)

สำหรับฟังก์ชั่นที่กำหนดเองด้านบน RemoveNonAlphaCharacters และเปลี่ยนชื่อเป็น RemoveNonAlphaNumericCharacters


1

- ขั้นแรกสร้างฟังก์ชั่นเดียว

CREATE FUNCTION [dbo].[GetNumericonly]
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
     DECLARE @intAlpha INT
     SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric)
BEGIN
     WHILE @intAlpha > 0
   BEGIN
          SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
          SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric )
   END
END
RETURN ISNULL(@strAlphaNumeric,0)
END

ตอนนี้เรียกฟังก์ชันนี้ว่า

select [dbo].[GetNumericonly]('Abhi12shek23jaiswal')

ผลลัพธ์มันเหมือน

1223

1

จากมุมมองประสิทธิภาพฉันจะใช้ Inline Function:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[udf_RemoveNumericCharsFromString]
(
@List NVARCHAR(4000)
)
RETURNS TABLE 
AS RETURN

    WITH GetNums AS (
       SELECT TOP(ISNULL(DATALENGTH(@List), 0))
        n = ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
        FROM
          (VALUES (0),(0),(0),(0)) d (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) e (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) f (n),
          (VALUES (0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) g (n)
            )

    SELECT StrOut = ''+
        (SELECT Chr
         FROM GetNums
            CROSS APPLY (SELECT SUBSTRING(@List , n,1)) X(Chr)
         WHERE Chr LIKE '%[^0-9]%' 
         ORDER BY N
         FOR XML PATH (''),TYPE).value('.','NVARCHAR(MAX)')


   /*How to Use
   SELECT StrOut FROM dbo.udf_RemoveNumericCharsFromString ('vv45--9gut')
   Result: vv--gut
   */

ฉันรู้ว่ากระทู้นี้เก่า แต่ฟังก์ชั่นที่มีค่าในตารางเป็นวิธีที่จะไป ปัญหาเกี่ยวกับการแก้ปัญหาของคุณคือเพราะคุณเพียงแค่ส่งคืนโค้ดนี้:), TYPE) .value ('.', 'NVARCHAR (MAX)') ไม่จำเป็นต้องใช้และจะทำให้ฟังก์ชั่นทำงานช้าลง ~ 50%
Alan Burstein

1

นี่เป็นอีกหนึ่งวิธีการแก้ปัญหา CTE recursive ขึ้นอยู่กับคำตอบ @Gerhard ไวส์ของที่นี่ คุณควรจะสามารถคัดลอกและวางบล็อครหัสทั้งหมดลงใน SSMS และเล่นกับมันได้ ผลลัพธ์มีคอลัมน์เพิ่มเติมสองสามคอลัมน์เพื่อช่วยให้เราเข้าใจว่าเกิดอะไรขึ้น ฉันใช้เวลาสักครู่จนกว่าฉันจะเข้าใจสิ่งที่เกิดขึ้นกับทั้ง PATINDEX (RegEx) และ CTE แบบเรียกซ้ำ

DECLARE @DefineBadCharPattern varchar(30)
SET @DefineBadCharPattern = '%[^A-z]%'  --Means anything NOT between A and z characters (according to ascii char value) is "bad"
SET @DefineBadCharPattern = '%[^a-z0-9]%'  --Means anything NOT between a and z characters or numbers 0 through 9 (according to ascii char value) are "bad"
SET @DefineBadCharPattern = '%[^ -~]%'  --Means anything NOT between space and ~ characters (all non-printable characters) is "bad"
--Change @ReplaceBadCharWith to '' to strip "bad" characters from string
--Change to some character if you want to 'see' what's being replaced. NOTE: It must be allowed accoring to @DefineBadCharPattern above
DECLARE @ReplaceBadCharWith varchar(1) = '#'  --Change this to whatever you want to replace non-printable chars with 
IF patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, @ReplaceBadCharWith) > 0
    BEGIN
        RAISERROR('@ReplaceBadCharWith value (%s) must be a character allowed by PATINDEX pattern of %s',16,1,@ReplaceBadCharWith, @DefineBadCharPattern)
        RETURN
    END
--A table of values to play with:
DECLARE @temp TABLE (OriginalString varchar(100))
INSERT @temp SELECT ' 1hello' + char(13) + char(10) + 'there' + char(30) + char(9) + char(13) + char(10)
INSERT @temp SELECT '2hello' + char(30) + 'there' + char(30)
INSERT @temp SELECT ' 3hello there'
INSERT @temp SELECT ' tab' + char(9) + ' character'
INSERT @temp SELECT 'good bye'

--Let the magic begin:
;WITH recurse AS (
    select
    OriginalString,
    OriginalString as CleanString,
    patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString) as [Position],
    substring(OriginalString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString),1) as [InvalidCharacter],
    ascii(substring(OriginalString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN, OriginalString),1)) as [ASCIICode]
    from @temp
   UNION ALL
    select
    OriginalString,
    CONVERT(varchar(100),REPLACE(CleanString,InvalidCharacter,@ReplaceBadCharWith)),
    patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) as [Position],
    substring(CleanString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString),1),
    ascii(substring(CleanString,patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString),1))
    from recurse
    where patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) > 0
)
SELECT * FROM recurse
--optionally comment out this last WHERE clause to see more of what the recursion is doing:
WHERE patindex(@DefineBadCharPattern COLLATE Latin1_General_BIN,CleanString) = 0

0

การใช้ตารางตัวเลขที่สร้างขึ้น CTE เพื่อตรวจสอบอักขระแต่ละตัวจากนั้นสำหรับ XML เพื่อเชื่อมต่อกับสตริงของค่าที่เก็บไว้ที่คุณสามารถ ...

CREATE FUNCTION [dbo].[PatRemove](
    @pattern varchar(50),
    @expression varchar(8000) 
    )
RETURNS varchar(8000)
AS
BEGIN
    WITH 
        d(d) AS (SELECT d FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) digits(d)),
        nums(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM d d1, d d2, d d3, d d4),
        chars(c) AS (SELECT SUBSTRING(@expression, n, 1) FROM nums WHERE n <= LEN(@expression))
    SELECT 
        @expression = (SELECT c AS [text()] FROM chars WHERE c NOT LIKE @pattern FOR XML PATH(''));

    RETURN @expression;
END

0
DECLARE @vchVAlue NVARCHAR(255) = 'SWP, Lettering Position 1: 4 Ω, 2: 8 Ω, 3: 16 Ω, 4:  , 5:  , 6:  , Voltage Selector, Solder, 6, Step switch, : w/o fuseholder '


WHILE PATINDEX('%?%' , CAST(@vchVAlue AS VARCHAR(255))) > 0
  BEGIN
    SELECT @vchVAlue = STUFF(@vchVAlue,PATINDEX('%?%' , CAST(@vchVAlue AS VARCHAR(255))),1,' ')
  END 

SELECT @vchVAlue

0

วิธีนี้ไม่ได้ผลสำหรับฉันเนื่องจากฉันพยายามเก็บตัวอักษรภาษาอาหรับไว้ฉันพยายามแทนที่นิพจน์ปกติ แต่ก็ไม่ได้ผล ฉันเขียนวิธีอื่นในการทำงานในระดับ ASCII เพราะมันเป็นทางเลือกเดียวของฉันและมันทำงาน

 Create function [dbo].[RemoveNonAlphaCharacters] (@s varchar(4000)) returns varchar(4000)
   with schemabinding
begin
   if @s is null
      return null
   declare @s2 varchar(4000)
   set @s2 = ''
   declare @l int
   set @l = len(@s)
   declare @p int
   set @p = 1
   while @p <= @l begin
      declare @c int
      set @c = ascii(substring(@s, @p, 1))
      if @c between 48 and 57 or @c between 65 and 90 or @c between 97 and 122 or @c between 165 and 253 or @c between 32 and 33
         set @s2 = @s2 + char(@c)
      set @p = @p + 1
      end
   if len(@s2) = 0
      return null
   return @s2
   end

ไป


-1

แม้ว่าโพสต์จะค่อนข้างเก่า แต่ฉันอยากจะพูดต่อไปนี้ ปัญหาที่ฉันมีกับวิธีแก้ไขปัญหาข้างต้นคือมันไม่ได้กรองอักขระเช่นç, ë, ï ฯลฯ ฉันปรับฟังก์ชั่นดังต่อไปนี้ (ฉันใช้เพียง 80 varchar string เพื่อบันทึกหน่วยความจำ):

create FUNCTION dbo.udf_Cleanchars (@InputString varchar(80)) 
RETURNS varchar(80) 
AS 

BEGIN 
declare @return varchar(80) , @length int , @counter int , @cur_char char(1) 
SET @return = '' 
SET @length = 0 
SET @counter = 1 
SET @length = LEN(@InputString) 
IF @length > 0 
BEGIN WHILE @counter <= @length 

BEGIN SET @cur_char = SUBSTRING(@InputString, @counter, 1) IF ((ascii(@cur_char) in (32,44,46)) or (ascii(@cur_char) between 48 and 57) or (ascii(@cur_char) between 65 and 90) or (ascii(@cur_char) between 97 and 122))
BEGIN SET @return = @return + @cur_char END 
SET @counter = @counter + 1 
END END 

RETURN @return END

ขอบคุณสำหรับสิ่งนี้ Eric อย่างที่คุณพูดคำตอบที่โพสต์ไว้นั้นดีมาก แต่ก็ไม่ได้ตัดอักขระ "ตัวเลข" เช่น½
ทรอย

-3

ฉันเพิ่งพบสิ่งนี้ใน Oracle 10g ถ้านั่นคือสิ่งที่คุณใช้ ฉันต้องตัดอักขระพิเศษทั้งหมดเพื่อเปรียบเทียบหมายเลขโทรศัพท์

regexp_replace(c.phone, '[^0-9]', '')

5
"SQL Server" หมายถึงผลิตภัณฑ์ของ Microsoft โดยเฉพาะ
ไม่มีใคร
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.