แทนที่อักขระพิเศษในคอลัมน์ด้วยช่องว่าง


10

ฉันกำลังพยายามเขียนคิวรีที่แทนที่อักขระพิเศษด้วยช่องว่าง โค้ดด้านล่างช่วยในการระบุแถว (อักขระตัวเลขและตัวอักษรจุลภาคและช่องว่างใช้ได้):

SELECT columnA
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'

ฉันจะรวมฟังก์ชั่นแทนที่ในคำสั่ง select ได้อย่างไรเพื่อให้อักขระทั้งหมดที่ไม่ใช่ตัวอักษรและตัวเลขจุลภาคและช่องว่างในชุดผลลัพธ์ถูกแทนที่ด้วย '' (ช่องว่าง) อันนี้จะไม่ทำงาน:

SELECT replace(columnA,'%[^a-Z0-9, ]%',' ')
FROM tableA
WHERE columnA like '%[^a-Z0-9, ]%'

คำตอบ:


11

หากคุณรับประกันว่าจะใช้เพียง 26 ตัวอักษรของตัวอักษรภาษาอังกฤษแบบสหรัฐอเมริกา (ทั้งตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก) คุณสามารถหลีกเลี่ยงการใช้งานLIKEและ / หรือPATINDEXด้วยสัญกรณ์ช่วงง่าย ๆ ของ[a-z](คุณจะไม่ จำเป็นต้องใช้ตัวพิมพ์ใหญ่ "Z" เมื่อใช้การเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่)

แต่ถ้าคุณอาจได้รับตัวละครที่ไม่พบใน en-US อักษรยังไม่มีให้บริการในหลายรหัสหน้า / Collations สำหรับVARCHARข้อมูล (เช่นÞ= ทุนละติน "ธ อร์น" = SELECT CHAR(0xDE)) [a-z0-9, Þ]แล้วคุณอาจจำเป็นต้องมีผู้ที่อยู่ในระดับตัวอักษร: แน่นอนว่าสิ่งที่ตัวละครพิเศษเหล่านั้นจะอยู่บนพื้นฐานของรหัสต่อหน้า

นอกจากนี้โปรดทราบว่าทั้งการเปรียบเทียบประเภท (SQL Server vs Windows) และการตั้งค่าความไว (ตัวพิมพ์เล็กและใหญ่เน้นความอ่อนไหวและไม่อ่อนไหว) จะส่งผลต่ออักขระที่รวมอยู่ในช่วงใดช่วงหนึ่ง ตัวอย่างเช่น SQL Server Collations เรียงลำดับตัวพิมพ์ใหญ่และตัวพิมพ์เล็กตามลำดับตรงกันข้ามเป็น Windows Collations ความหมายสมมติว่าเป็นกรณี ๆ ไปเปรียบเทียบสำหรับทั้งสองประเภทของ Collations หนึ่งจะทำAaBb...และอื่น ๆ aAbB...ที่จะทำ เอฟเฟ็กต์จะaอยู่ภายในขอบเขตของA-Zหนึ่งในนั้น แต่ไม่ใช่เอฟเฟกต์อื่น และช่วงของa-Zจะไม่ตรงกับตัวละครใด ๆ ในการจัดเรียงแบบไบนารี (หนึ่งที่ลงท้ายด้วยอย่างใดอย่างหนึ่ง_BINหรือ_BIN2แต่ไม่ได้ใช้_BIN) เนื่องจากค่าของA65 และaคือ 97 ดังนั้นจึงเป็นช่วงที่ไม่ถูกต้องคือ 97 ถึง 65 ;-) มีตัวอย่างมากเกินไปที่จะให้ตัวอย่างสำหรับที่นี่ดังนั้นฉันจะพยายามโพสต์คำอธิบายอย่างละเอียดในบล็อกของฉันในไม่ช้า (และจากนั้นจะอัปเดตสิ่งนี้ด้วยลิงก์ไปยังมัน) อย่างไรก็ตามหากคุณเข้มงวดกับการยอมรับตัวอักษรภาษาอังกฤษแบบสหรัฐอเมริกาเท่านั้น (แม้ว่าคุณจะได้รับจดหมายที่ถูกต้องจากภาษาอื่น) ตัวเลือกที่ดีที่สุดของคุณอาจจะใช้รูปแบบและการจัดเรียงต่อไปนี้:

LIKE '%[^A-Za-z0-9, ]%' COLLATE Latin1_General_100_BIN2

ตอนนี้ถ้าคุณกำลังสนับสนุนNVARCHARข้อมูลและสามารถรับตัวอักษร "คำ" จากภาษาต่าง ๆ T-SQL จะไม่ได้รับความช่วยเหลือเท่าที่ควรเนื่องจากไม่มีวิธีที่จะแยกแยะสิ่งเหล่านี้ได้ ในกรณีนี้คุณควรใช้ Regular Expression (RegEx) - โดยเฉพาะReplaceเมธอด / ฟังก์ชั่น - และมีให้เฉพาะผ่าน SQLCLR ต่อไปนี้แสดงตัวอย่างของการแทนที่อักขระ "พิเศษ" หลายตัว แต่ยังคงไว้ซึ่งตัวอักษรที่ถูกต้องในภาษาอย่างน้อยหนึ่งภาษา:

DECLARE @Test NVARCHAR(500);
SET @Test = N'this$is%a<>TEST,;to}⌡↕strip╞╟╚══¶out_ç_ƒ▀ special-ij-೫-chars-舛-დ-א-B';
SELECT SQL#.RegEx_Replace4k(@Test, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL); 

ผลตอบแทน:

this is a  TEST, to   strip      out ç ƒ  special ij ೫ chars 舛 დ א B

การแสดงออก RegEx หมายถึง:

  • \W= "หนี" RegEx หมายถึง " อักขระที่ไม่ใช่คำ"
  • \p{Pc}= a Unicode "หมวดหมู่" ของ "เครื่องหมายวรรคตอนตัวเชื่อมต่อ" (จำเป็นสำหรับการจับคู่เท่านั้นเนื่องจาก "หมวดหมู่" นี้ได้รับการยกเว้นจากการ\Wหลบหนีโดยเฉพาะ)
  • -[,]= การลบคลาส (จำเป็นต้องแยกจุลภาคออกจากการจับคู่เป็น "พิเศษ" เนื่องจากรวมอยู่ในการยกเว้น\W)

คุณสามารถทำการอัปเดตตารางได้ง่ายๆโดยการออก:

UPDATE tbl
SET    tbl.field = SQL#.RegEx_Replace4k(tbl.field, N'[\W\p{Pc}-[,]]', N' ', -1, 1, NULL)
FROM   tbl
WHERE  SQL#.RegEx_IsMatch4k(tbl.field, N'[\W\p{Pc}-[,]]', 1, NULL) = 1;

โปรดทราบว่าสำหรับตัวอย่างเหล่านี้ฉันใช้สองฟังก์ชั่นที่มีอยู่ในไลบรารี่SQL #เวอร์ชันฟรีของฟังก์ชั่น SQLCLR ซึ่งฉันสร้างขึ้น (แต่อีกครั้งสิ่งเหล่านี้ฟรี) โปรดทราบว่าฉันใช้รุ่น "4k" ซึ่งเร็วกว่าเนื่องจากใช้NVARCHAR(4000)แทนNVARCHAR(MAX)พารามิเตอร์ชนิด หากข้อมูลของคุณใช้NVARCHAR(MAX)อยู่ให้ลบ "4k" ออกจากชื่อฟังก์ชัน

โปรดดู:


5

ฉันมีการโพสต์ที่นี่ที่ไม่สิ่งที่คล้ายกัน

โดยทั่วไปฉันใช้ CTE แบบเรียกซ้ำเพื่อวนซ้ำไปเรื่อย ๆ เพื่อแทนที่อักขระ "ไม่ดี" ทีละตัว ฉันใช้ STUFF เพื่อตัดอักขระ 1 ตัว (แม้ว่าคุณสามารถใช้เพื่อแทนที่ด้วยช่องว่าง) และ PATINDEX เพื่อค้นหาตำแหน่งของตัวละครที่ฉันต้องการลบ คุณสามารถปรับเปลี่ยนเล็กน้อยเพื่อทำสิ่งที่คุณกำลังมองหา อย่างไรก็ตามจะสร้างรายการ "ดี" แต่จะไม่อัปเดตรายการที่มีอยู่จริง

DECLARE @Pattern varchar(50) = '%[^A-Za-z0-9, ]%';

WITH FixBadChars AS (SELECT StringToFix, StringToFix AS FixedString, 1 AS MyCounter, Id
                FROM BadStringList
                UNION ALL
                SELECT StringToFix, Stuff(FixedString, PatIndex(@Pattern, 
                    FixedString COLLATE Latin1_General_BIN2), 1, ' ') AS FixedString, 
                    MyCounter + 1, Id
                FROM FixBadChars
                WHERE FixedString COLLATE Latin1_General_BIN2 LIKE @Pattern)
SELECT StringToFix, FixedString, MyCounter, Id
FROM FixBadChars
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);

คุณน่าจะสามารถแก้ไขส่วนล่างเพื่อทำการอัพเดทได้มากกว่าแค่การสืบค้น แต่ฉันยังไม่ได้ลองเลย ฉันค่อนข้างแน่ใจว่ามันจะมีลักษณะเช่นนี้:

UPDATE FixBadChars
SET StringToFix = FixedString
WHERE MyCounter = 
        (SELECT MAX(MyCounter) 
        FROM FixBadChars Fixed
        WHERE Fixed.Id = FixBadChars.Id)
OPTION (MAXRECURSION 1000);

เท่าที่ความสามารถในการปรับขนาดฉันกลับมา ~ 170k ทำความสะอาดแถวในภายใต้ 30 วินาที ไม่แน่ใจเกี่ยวกับการปรับปรุงอีกครั้ง แต่นี่เป็นแล็ปท็อปของฉันซึ่งค่อนข้างช้าด้วย RAM เพียง 6gb


0
Declare @String nchar(2000)='hg$%^AB,.:23ab-=+'

Declare @NewString VARCHAR(2000)=''
Declare @Lenght int=LEN(@String)
Declare @Index int=1

WHILE (@Index <= @Lenght)
BEGIN
    Declare @Letter nchar(1)=Substring(@String,@Index,1);
    Declare @ASCII int=ASCII(@Letter);
    If((@ASCII >= 48 and @ASCII <= 57) or (@ASCII >= 97 and @ASCII <= 122) or (@ASCII >= 65 and @ASCII <= 90))
    BEGIN
        SET @NewString += @Letter
    END
    ELSE
    BEGIN
        SET @NewString += ' '
    END
    SET @Index+=1

END
Select @NewString
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.