ĐẶT HÀNG THEO và so sánh các chuỗi chữ và số hỗn hợp


9

Chúng ta cần thực hiện một số báo cáo về các giá trị thường là các chuỗi số và chữ cái cần được sắp xếp 'tự nhiên'. Những thứ như, ví dụ "P7B18" hoặc "P12B3". @ Các chuỗi chủ yếu sẽ là chuỗi các chữ cái sau đó các số xen kẽ. Tuy nhiên, số lượng các phân đoạn này và độ dài của mỗi phân đoạn có thể khác nhau.

Chúng tôi muốn các phần số của chúng được sắp xếp theo thứ tự số. Rõ ràng, nếu tôi chỉ xử lý trực tiếp các giá trị chuỗi ORDER BYđó, thì "P12B3" sẽ xuất hiện trước "P7B18", vì "P1" sớm hơn "P7", nhưng tôi thích ngược lại, vì "P7" tự nhiên đi trước "P12".

Tôi cũng muốn có thể thực hiện các so sánh phạm vi, ví dụ @bin < 'P13S6'hoặc một số như vậy. Tôi không phải xử lý số dấu phẩy động hoặc số âm; đây sẽ là những số nguyên không âm mà chúng ta đang xử lý. Độ dài chuỗi và số lượng phân đoạn có thể có khả năng tùy ý, không có giới hạn trên cố định.

Trong trường hợp của chúng tôi, vỏ chuỗi không quan trọng, mặc dù nếu có cách nào để thực hiện việc này theo kiểu đối chiếu, những người khác có thể thấy điều đó hữu ích. Phần xấu nhất của tất cả những điều này là tôi muốn có thể thực hiện cả thứ tự và lọc phạm vi trong WHEREmệnh đề.

Nếu tôi đang làm điều này trong C #, thì đó sẽ là một nhiệm vụ khá đơn giản: thực hiện một số phân tích cú pháp để tách alpha khỏi số, thực hiện IComparable và về cơ bản bạn đã hoàn thành. Tất nhiên, SQL Server dường như không cung cấp bất kỳ chức năng tương tự nào, ít nhất là theo như tôi biết.

Bất cứ ai biết bất kỳ thủ thuật tốt để làm cho công việc này? Có một số khả năng ít được công khai để tạo các loại CLR tùy chỉnh triển khai IComparable và điều này có hoạt động như mong đợi không? Tôi cũng không phản đối Thủ thuật XML ngu ngốc (xem thêm: ghép nối danh sách) và tôi cũng đã có các hàm bao bọc / trích xuất / thay thế biểu thức chính quy CLR có sẵn trên máy chủ.

EDIT: Như một ví dụ chi tiết hơn một chút, tôi muốn dữ liệu hành xử giống như thế này.

SELECT bin FROM bins ORDER BY bin

bin
--------------------
M7R16L
P8RF6JJ
P16B5
PR7S19
PR7S19L
S2F3
S12F0

tức là chia các chuỗi thành mã thông báo của tất cả các chữ cái hoặc tất cả các số và sắp xếp chúng theo thứ tự bảng chữ cái hoặc số, với các mã thông báo ngoài cùng bên trái là thuật ngữ sắp xếp quan trọng nhất. Giống như tôi đã đề cập, miếng bánh trong .NET nếu bạn triển khai IComparable, nhưng tôi không biết làm thế nào (hoặc nếu) bạn có thể làm điều đó trong SQL Server. Đó chắc chắn không phải là điều tôi từng gặp trong 10 năm hoặc hơn làm việc với nó.


Bạn có thể làm điều này với một số loại cột được tính toán được lập chỉ mục, biến chuỗi thành một số nguyên. Vì vậy, P7B12có thể trở thành P 07 B 12, sau đó (thông qua ASCII) 80 07 65 12, vì vậy80076512
Phil

Tôi đề nghị bạn tạo một cột được tính toán để đệm từng thành phần số có độ dài lớn (tức là 10 số không). Vì định dạng này khá tùy ý, bạn sẽ cần một biểu thức nội tuyến khá lớn nhưng có thể thực hiện được. Vì vậy, bạn có thể lập chỉ mục / đặt hàng theo / nơi trên cột đó bao nhiêu tùy thích.
Nick.McDilyn

Vui lòng xem liên kết tôi vừa thêm vào đầu câu trả lời của mình :)
Solomon Rutzky

1
@srutzky Nice, tôi đã bình chọn cho nó.
db2

Xin chào db2: do Microsoft chuyển từ Kết nối sang UserVoice và không chính xác giữ số phiếu bầu (họ đưa ra nhận xét nhưng không chắc chắn họ nhìn vào đó), bạn có thể cần bỏ phiếu lại cho nó: Hỗ trợ "sắp xếp tự nhiên" / DIGITSASNUMBERS làm tùy chọn Đối chiếu . Cảm ơn!
Solomon Rutzky

Câu trả lời:


8

Bạn muốn một phương tiện hợp lý, hiệu quả để sắp xếp các số trong chuỗi là số thực? Xem xét bỏ phiếu cho đề xuất Microsoft Connect của tôi: Hỗ trợ "sắp xếp tự nhiên" / DIGITSASNUMBERS làm tùy chọn Đối chiếu


Không có phương tiện dễ dàng, tích hợp để thực hiện việc này, nhưng đây là một khả năng:

Bình thường hóa các chuỗi bằng cách định dạng lại chúng thành các đoạn có độ dài cố định:

  • Tạo một cột sắp xếp VARCHAR(50) COLLATE Latin1_General_100_BIN2 . Độ dài tối đa 50 có thể cần được điều chỉnh dựa trên số lượng phân đoạn tối đa và độ dài tối đa tiềm năng của chúng.
  • Mặc dù việc chuẩn hóa có thể được thực hiện trong lớp ứng dụng hiệu quả hơn, nhưng việc xử lý việc này trong cơ sở dữ liệu bằng cách sử dụng UDF T-SQL sẽ cho phép đặt UDF vô hướng vào một AFTER [or FOR] INSERT, UPDATETrình kích hoạt sao cho bạn được đảm bảo đặt đúng giá trị cho tất cả các bản ghi, ngay cả những bản ghi đến thông qua các truy vấn ad hoc, v.v ... Tất nhiên, UDF vô hướng đó cũng có thể được xử lý thông qua SQLCLR, nhưng nó sẽ cần phải được kiểm tra để xác định xem cái nào thực sự hiệu quả hơn. **
  • UDF (bất kể ở trong T-SQL hay SQLCLR) nên:
    • Xử lý một số lượng phân đoạn không xác định bằng cách đọc từng ký tự và dừng khi loại chuyển từ alpha sang số hoặc số sang alpha.
    • Trên mỗi phân đoạn, nó sẽ trả về một chuỗi có độ dài cố định được đặt thành các ký tự / chữ số tối đa có thể có của bất kỳ phân đoạn nào (hoặc có thể tối đa + 1 hoặc 2 để tính đến sự tăng trưởng trong tương lai).
    • Các phân đoạn Alpha phải được căn trái và được đệm bên phải với các khoảng trắng.
    • Các phân đoạn số phải được chứng minh đúng và được đệm trái với số không.
    • Nếu các ký tự alpha có thể xuất hiện dưới dạng hỗn hợp nhưng thứ tự cần không phân biệt chữ hoa chữ thường, hãy áp dụng UPPER()hàm cho kết quả cuối cùng của tất cả các phân đoạn (để chỉ cần thực hiện một lần chứ không phải mỗi phân đoạn). Điều này sẽ cho phép sắp xếp thích hợp với đối chiếu nhị phân của cột sắp xếp.
  • Tạo một AFTER INSERT, UPDATETrình kích hoạt trên bảng gọi UDF để đặt cột sắp xếp. Để cải thiện hiệu suất, hãy sử dụng UPDATE()hàm để xác định xem cột mã này có nằm trong SETmệnh đề của UPDATEcâu lệnh không (chỉ đơn giản là RETURNsai), sau đó nối các bảng INSERTEDDELETEDgiả trên cột mã để chỉ xử lý các hàng có thay đổi trong giá trị mã . Hãy chắc chắn để chỉ địnhCOLLATE Latin1_General_100_BIN2 điều kiện THAM GIA đó để đảm bảo độ chính xác trong việc xác định nếu có thay đổi.
  • Tạo một chỉ mục trên cột sắp xếp mới.

Thí dụ:

P7B18   -> "P     000007B     000018"
P12B3   -> "P     000012B     000003"
P12B3C8 -> "P     000012B     000003C     000008"

Trong phương pháp này, bạn có thể sắp xếp qua:

ORDER BY tbl.SortColumn

Và bạn có thể thực hiện lọc phạm vi thông qua:

WHERE tbl.SortColumn BETWEEN dbo.MyUDF('P7B18') AND dbo.MyUDF('P12B3')

hoặc là:

DECLARE @RangeStart VARCHAR(50),
        @RangeEnd VARCHAR(50);
SELECT @RangeStart = dbo.MyUDF('P7B18'),
       @RangeEnd = dbo.MyUDF('P12B3');

WHERE tbl.SortColumn BETWEEN @RangeStart AND @RangeEnd

Cả bộ lọc ORDER BYWHEREbộ lọc nên sử dụng đối chiếu nhị phân được xác định choSortColumn do Ưu tiên đối chiếu.

So sánh bình đẳng vẫn sẽ được thực hiện trên cột giá trị ban đầu.


Những suy nghĩ khác:

  • Sử dụng UDT SQLCLR. Điều này có thể hoạt động, mặc dù không rõ liệu nó có mang lại lợi nhuận ròng so với phương pháp được mô tả ở trên hay không.

    Có, một UDT SQLCLR có thể có các toán tử so sánh được ghi đè bằng các thuật toán tùy chỉnh. Điều này xử lý các tình huống trong đó giá trị đang được so sánh với một giá trị khác đã cùng loại tùy chỉnh hoặc một loại cần được chuyển đổi hoàn toàn. Điều này sẽ xử lý bộ lọc phạm vi trong một WHEREđiều kiện.

    Liên quan đến việc sắp xếp UDT như một loại cột thông thường (không phải là cột được tính toán), điều này chỉ có thể nếu UDT là "byte được đặt hàng". Được "sắp xếp theo byte" có nghĩa là biểu diễn nhị phân của UDT (có thể được định nghĩa trong UDT) sắp xếp theo thứ tự một cách tự nhiên. Giả sử rằng biểu diễn nhị phân được xử lý tương tự như cách tiếp cận được mô tả ở trên cho cột VARCHAR (50) có các đoạn có độ dài cố định được đệm, sẽ đủ điều kiện. Hoặc, nếu không dễ để đảm bảo rằng biểu diễn nhị phân sẽ tự nhiên được sắp xếp theo cách thích hợp, bạn có thể hiển thị một phương thức hoặc thuộc tính của UDT tạo ra một giá trị sẽ được sắp xếp đúng, và sau đó tạo một PERSISTEDcột được tính toán trên đó phương pháp hoặc tài sản. Phương pháp cần phải được xác định và đánh dấu là IsDeterministic = true.

    Lợi ích của phương pháp này là:

    • Không cần trường "giá trị gốc".
    • Không cần phải gọi UDF để chèn dữ liệu hoặc so sánh các giá trị. Giả sử rằng Parsephương thức của UDT nhận P7B18giá trị và chuyển đổi nó, thì bạn sẽ có thể chỉ cần chèn các giá trị một cách tự nhiên như P7B18. Và với phương thức chuyển đổi ngầm định được đặt trong UDT, điều kiện WHERE cũng sẽ cho phép sử dụng P7B18` đơn giản.

    Hậu quả của phương pháp này là:

    • Chỉ cần chọn trường sẽ trả về biểu diễn nhị phân, nếu sử dụng UDT theo thứ tự byte làm kiểu dữ liệu cột. Hoặc nếu sử dụng PERSISTEDcột được tính toán trên một thuộc tính hoặc phương thức của UDT, thì bạn sẽ nhận được đại diện được trả về bởi thuộc tính hoặc phương thức. Nếu bạn muốn P7B18giá trị ban đầu , thì bạn cần gọi một phương thức hoặc thuộc tính của UDT được mã hóa để trả về đại diện đó. Vì bạn phải ghi đè lênToString dù sao phương thức, đó là một ứng cử viên tốt để cung cấp phương thức này.
    • Không rõ ràng (ít nhất là với tôi ngay bây giờ vì tôi chưa thử nghiệm phần này) việc thực hiện bất kỳ thay đổi nào đối với biểu diễn nhị phân sẽ dễ dàng / khó khăn như thế nào. Thay đổi đại diện được lưu trữ, có thể sắp xếp có thể yêu cầu bỏ và thêm lại trường. Ngoài ra, việc bỏ hội có chứa UDT sẽ thất bại nếu được sử dụng theo bất kỳ cách nào, vì vậy bạn sẽ muốn đảm bảo rằng không có gì khác trong hội ngoài UDT này. Bạn có thểALTER ASSEMBLY thay thế định nghĩa, nhưng có một số hạn chế về điều đó.

      Mặt khác, VARCHAR()trường là dữ liệu bị ngắt kết nối với thuật toán nên sẽ chỉ yêu cầu cập nhật cột. Và nếu có hàng chục triệu hàng (hoặc nhiều hơn) thì điều đó có thể được thực hiện theo cách tiếp cận theo đợt.

  • Triển khai thư viện ICU thực sự cho phép thực hiện sắp xếp chữ và số này. Mặc dù có nhiều chức năng, thư viện chỉ có hai ngôn ngữ: C / C ++ và Java. Điều đó có nghĩa là bạn có thể cần phải thực hiện một số điều chỉnh để làm cho nó hoạt động trong Visual C ++ hoặc có khả năng mã Java có thể được chuyển đổi thành MSIL bằng IKVM . Có một hoặc hai dự án bên .NET được liên kết trên trang web đó cung cấp giao diện COM có thể được truy cập trong mã được quản lý, nhưng tôi tin rằng chúng chưa được cập nhật trong một thời gian và tôi đã không thử chúng. Đặt cược tốt nhất ở đây sẽ là xử lý việc này trong lớp ứng dụng với mục tiêu tạo ra các khóa sắp xếp. Các khóa sắp xếp sau đó sẽ được lưu vào một cột sắp xếp mới.

    Đây có thể không phải là cách tiếp cận thực tế nhất. Tuy nhiên, vẫn rất tuyệt vời khi một khả năng như vậy tồn tại. Tôi đã cung cấp một hướng dẫn chi tiết hơn về một ví dụ về điều này trong câu trả lời sau:

    Có đối chiếu để sắp xếp các chuỗi sau theo thứ tự sau 1,2,3,6,10,10A, 10B, 11 không?

    Nhưng mô hình được giải quyết trong câu hỏi đó đơn giản hơn một chút. Để biết ví dụ cho thấy loại mẫu được xử lý trong Câu hỏi này cũng hoạt động, vui lòng truy cập trang sau:

    Bản thử nghiệm đối chiếu ICU

    Trong "Cài đặt", đặt tùy chọn "số" thành "bật" và tất cả các tùy chọn khác sẽ được đặt thành "mặc định". Tiếp theo, ở bên phải nút "sắp xếp", bỏ chọn tùy chọn cho "độ mạnh khác biệt" và kiểm tra tùy chọn cho "phím sắp xếp". Sau đó thay thế danh sách các mục trong vùng văn bản "Đầu vào" bằng danh sách sau:

    P12B22
    P7B18
    P12B3
    as456456hgjg6786867
    P7Bb19
    P7BA19
    P7BB19
    P007B18
    P7Bb20
    P7Bb19z23
    

    Nhấp vào nút "sắp xếp". Vùng văn bản "Đầu ra" sẽ hiển thị như sau:

    as456456hgjg6786867
        29 4D 0F 7A EA C8 37 35 3B 35 0F 84 17 A7 0F 93 90 , 0D , , 0D .
    P7B18
        47 0F 09 2B 0F 14 , 08 , FD F1 , DC C5 DC 05 .
    P007B18
        47 0F 09 2B 0F 14 , 08 , FD F1 , DC C5 DC 05 .
    P7BA19
        47 0F 09 2B 29 0F 15 , 09 , FD FF 10 , DC C5 DC DC 05 .
    P7Bb19
        47 0F 09 2B 2B 0F 15 , 09 , FD F2 , DC C5 DC 06 .
    P7BB19
        47 0F 09 2B 2B 0F 15 , 09 , FD FF 10 , DC C5 DC DC 05 .
    P7Bb19z23
        47 0F 09 2B 2B 0F 15 5B 0F 19 , 0B , FD F4 , DC C5 DC 08 .
    P7Bb20
        47 0F 09 2B 2B 0F 16 , 09 , FD F2 , DC C5 DC 06 .
    P12B3
        47 0F 0E 2B 0F 05 , 08 , FD F1 , DC C5 DC 05 .
    P12B22
        47 0F 0E 2B 0F 18 , 08 , FD F1 , DC C5 DC 05 .
    

    Xin lưu ý rằng các khóa sắp xếp là cấu trúc trong nhiều trường, được phân tách bằng dấu phẩy. Mỗi trường cần được sắp xếp độc lập, để đưa ra một vấn đề nhỏ khác cần giải quyết nếu cần thực hiện điều này trong SQL Server.


** Nếu có bất kỳ lo ngại nào về hiệu suất liên quan đến việc sử dụng Hàm do người dùng xác định, xin lưu ý rằng các phương pháp đề xuất sử dụng chúng tối thiểu. Trong thực tế, lý do chính để lưu trữ giá trị chuẩn hóa là để tránh gọi UDF cho mỗi hàng của mỗi truy vấn. Trong cách tiếp cận chính, UDF được sử dụng để đặt giá trị của SortColumnvà điều đó chỉ được thực hiện khi INSERTUPDATEthông qua Kích hoạt. Chọn giá trị phổ biến hơn nhiều so với chèn và cập nhật và một số giá trị không bao giờ được cập nhật. Mỗi mỗi SELECTtruy vấn sử dụng SortColumnbộ lọc phạm vi trong WHEREmệnh đề, UDF chỉ cần một lần cho mỗi giá trị Range_start và Range_end để nhận các giá trị chuẩn hóa; UDF không được gọi là mỗi hàng.

Liên quan đến UDT, việc sử dụng thực sự giống như với UDF vô hướng. Có nghĩa là, chèn và cập nhật sẽ gọi phương thức chuẩn hóa một lần trên mỗi hàng để đặt giá trị. Sau đó, phương thức chuẩn hóa sẽ được gọi một lần cho mỗi truy vấn trên mỗi phạm vi_start và Range_value trong bộ lọc phạm vi, nhưng không phải trên mỗi hàng.

Một điểm có lợi cho việc xử lý hoàn toàn chuẩn hóa trong SQLCLR UDF là do nó không thực hiện bất kỳ quyền truy cập dữ liệu nào và có tính xác định, nếu được đánh dấu là IsDeterministic = true, thì nó có thể tham gia vào các kế hoạch song song (có thể giúp INSERTUPDATEvận hành) trong khi T-SQL UDF sẽ ngăn kế hoạch song song được sử dụng.

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.