Thay thế các ký tự đặc biệt trong một cột bằng dấu cách


10

Tôi đang cố gắng viết một truy vấn thay thế các ký tự đặc biệt bằng dấu cách. Mã dưới đây giúp xác định các hàng. (ký tự chữ và số, dấu phẩy và dấu cách là hợp lệ):

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

Làm cách nào tôi có thể tích hợp chức năng thay thế vào câu lệnh select để tất cả các ký tự không phải là chữ và số, dấu phẩy và khoảng trắng trong tập kết quả được thay thế bằng '' (dấu cách). Điều này sẽ không hoạt động:

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

Câu trả lời:


11

Nếu bạn được đảm bảo chỉ sử dụng 26 chữ cái trong bảng chữ cái tiếng Anh Hoa Kỳ (cả phiên bản chữ hoa và chữ thường) thì bạn có thể tránh sử dụng LIKEvà / hoặc PATINDEXvới ký hiệu phạm vi đơn giản [a-z](bạn sẽ không cần sử dụng chữ "Z" viết hoa khi sử dụng Collation không phân biệt chữ hoa chữ thường).

Nhưng, nếu bạn có thể nhận được các ký tự không tìm thấy trong bảng chữ cái en-US có sẵn trong nhiều Trang mã / Bộ sưu tập VARCHARdữ liệu khác nhau (ví dụ: Þchữ hoa "Thorn" = SELECT CHAR(0xDE)), thì bạn có thể cần đưa các ký tự đó vào lớp ký tự : [a-z0-9, Þ]. Tất nhiên, những ký tự phụ đó sẽ là gì trên cơ sở Trang theo Mã.

Ngoài ra, xin lưu ý rằng cả loại Collation (SQL Server so với Windows) và cài đặt độ nhạy (trường hợp, dấu, v.v. nhạy cảm và không nhạy cảm) sẽ ảnh hưởng đến các ký tự được bao gồm trong một phạm vi cụ thể. Ví dụ: Bộ sưu tập SQL Server sắp xếp các chữ cái viết hoa và viết thường theo thứ tự ngược lại là Bộ sưu tập Windows. Có nghĩa là, giả sử Collation phân biệt chữ hoa chữ thường cho cả hai loại Collations, một loại sẽ làm AaBb...và loại kia sẽ làm aAbB.... Hiệu quả sẽ là atrong phạm vi của A-Zmột trong số họ, nhưng không phải là một trong số họ. Và phạm vi a-Zsẽ không khớp với bất kỳ ký tự nào trong Collation nhị phân (một kết thúc bằng một _BINhoặc _BIN2, nhưng không sử dụng _BIN) với giá trị Alà 65 vàalà 97, do đó, nó là một phạm vi không hợp lệ từ 97 đến 65 ;-). Có quá nhiều biến thể để đưa ra ví dụ cho ở đây vì vậy tôi sẽ cố gắng đăng một lời giải thích chi tiết trên blog của mình vào lúc nào đó (và sau đó sẽ cập nhật thông tin này với liên kết đến nó). Tuy nhiên, nếu bạn sẽ nghiêm ngặt về việc chỉ chấp nhận các ký tự tiếng Anh Hoa Kỳ (ngay cả khi bạn có thể nhận được các chữ cái hợp lệ từ các ngôn ngữ khác) thì tùy chọn tốt nhất của bạn có thể sẽ là sử dụng mẫu và Đối chiếu sau:

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

Bây giờ, nếu bạn đang hỗ trợ NVARCHARdữ liệu và có thể nhận các ký tự "từ" từ nhiều ngôn ngữ khác nhau, thì T-SQL sẽ không giúp ích nhiều vì nó không có cách nào thực sự để phân biệt những điều này. Trong trường hợp này, bạn nên sử dụng Biểu thức chính quy (RegEx) - cụ thể là Replacephương thức / hàm - và những phương thức này chỉ khả dụng thông qua SQLCLR. Dưới đây cho thấy một ví dụ về việc thay thế một số ký tự "đặc biệt", nhưng để lại tất cả các chữ cái hợp lệ trong ít nhất một ngôn ngữ:

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); 

Trả về:

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

Biểu thức RegEx có nghĩa là:

  • \W= một RegEx "thoát" có nghĩa là "bất kỳ ký tự không phải từ "
  • \p{Pc}= một "danh mục" Unicode của "Dấu câu, Trình kết nối" (điều này chỉ cần thiết cho trận đấu vì "danh mục" này được loại trừ đặc biệt bởi \Wlối thoát)
  • -[,]= phép trừ lớp (điều này là cần thiết để loại trừ dấu phẩy khỏi khớp là "đặc biệt" vì chúng được bao gồm trong \Wlối thoát)

Bạn có thể thực hiện cập nhật bảng chỉ bằng cách phát hành:

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;

Xin lưu ý rằng đối với các ví dụ này, tôi đã sử dụng hai hàm có sẵn trong thư viện SQL #R phiên bản miễn phí của các hàm SQLCLR do tôi tạo (nhưng một lần nữa, chúng đều miễn phí). Cũng lưu ý rằng tôi đã sử dụng các phiên bản "4k" nhanh hơn do sử dụng NVARCHAR(4000)thay vì các NVARCHAR(MAX)loại tham số. Nếu dữ liệu của bạn đang sử dụng NVARCHAR(MAX), thì chỉ cần xóa "4k" khỏi tên hàm.

Xin vui lòng xem:


5

Tôi đã có một bài viết ở đây mà làm một cái gì đó tương tự .

Về cơ bản, tôi đang sử dụng CTE đệ quy để lặp đi lặp lại nhiều lần thay thế một nhân vật "xấu". Tôi đang sử dụng STUFF để loại bỏ 1 ký tự (mặc dù bạn có thể sử dụng nó để thay thế bằng khoảng trắng) và PATINDEX để tìm vị trí của ký tự tôi muốn xóa. Bạn có thể sửa đổi nó một chút để làm những gì bạn đang tìm kiếm. Tuy nhiên, nó tạo ra một danh sách "tốt", nó không thực sự cập nhật danh sách hiện có.

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);

Bạn sẽ có thể sửa đổi phần dưới cùng để thực hiện cập nhật thay vì chỉ là một truy vấn nhưng tôi thực sự đã không thử nó. Tôi khá chắc chắn rằng nó sẽ trông giống như thế này:

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

Theo khả năng mở rộng, tôi đã trả lại ~ 170k hàng được làm sạch trong vòng dưới 30 giây. Một lần nữa không chắc chắn về việc thực hiện cập nhật nhưng điều này là trên máy tính xách tay của tôi khá chậm chỉ với 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.