Gibt es eine Sortierung, um die folgenden Zeichenfolgen in der folgenden Reihenfolge zu sortieren: 1,2,3,6,10,10A, 10B, 11?

12

Ich habe eine Datenbank mit einer VARCHAR-Spalte, die Ganzzahlen unterschiedlicher Länge enthält. Ich möchte sie sortieren, sodass 10 nach 9 und nicht nach 1 und 70A nach 70 kommt. Ich konnte dies mit PATINDEX () , einer CTE- und CASE-Anweisung in der WHERE-Klausel tun .

Ich habe mich jedoch gefragt, ob es eine Zusammenstellung gibt, bei der dies unnötig wäre.

Justin Dearing
quelle
Hier ist der neue Link für diesen Vorschlag, nachdem Microsoft von Connect zu UserVoice migriert und die URIs noch nicht weitergeleitet hat: Unterstützt "natürliches Sortieren" / DIGITSASNUMBERS als Sortieroption
Solomon Rutzky
2
Microsoft hat angekündigt, dies als integrierte Funktion in SQL Server zu implementieren, wenn genügend Stimmen vorliegen. Also gehen Sie hier rüber und klicken Sie auf den Abstimmungsbutton .
Peter Aylett

Antworten:

8

Bei der Sortierung geht es um die alphabetische Sortierung, abhängig von Codepage, Akzent, Groß- / Kleinschreibung, Breite und Kana. Zahlenzeichen (0-9) haben keine ihrer Eigenschaften.

So 9ist immer 10Bin irgendeiner Art nach.

Sie müssen es wie angegeben aufteilen oder wie folgt sortieren:

ORDER BY
    RIGHT('                              ' + MyColumn, 30)

Die Länge auf der rechten Seite bestimmt, wie viele Leerzeichen Sie haben.

Sie könnten natürlich:

  • Haben Sie 2 Spalten, um dies unnötig (und viel schneller) zu machen, und haben Sie eine berechnete Spalte, um sie zu kombinieren
  • bestehe auf führenden Nullen
  • right justify in a char (eine gespeicherte Version meines RECHTS oben)

Die letzten 2 Vorschläge sind wie meine RECHTEN oben und etwas anders. Schneller zu sortieren (keine Verarbeitung der Colukmn erforderlich), aber mehr Speicherplatz erforderlich

gbn
quelle
Ich sehe nicht, wie das funktioniert. Es bricht für 2, 2a, 3, etc ...
Mladen Prajdic
@Mladen Prajdic: Du hast recht, hoppla.
Vergaß
In Bezug auf " So 9ist es 10Bin jeder Hinsicht immer nach ": Dies ist in SQL Server nur der Fall, da die zugrunde liegende Sortieroption für "DigitsAsNumbers" nicht als Sortieroption verfügbar gemacht wurde. Noch ;-). Dies wurde für Windows-basierte Apps ab Windows 7 verfügbar, insbesondere im Datei-Explorer. Und es kann eines Tages zu SQL Server ausgesetzt werden, wenn genügend Menschen die Idee unterstützen. Ich habe versucht, den Ball ins Rollen zu bringen, indem ich den folgenden Connect-Vorschlag eingereicht habe : Unterstütze "natürliche Sortierung" / DIGITSASNUMBERS als Sortieroption .
Solomon Rutzky
8

Ich würde eine berechnete Spalte einrichten und danach sortieren. Etwas wie

CAST( 
     CASE WHEN IS_NUMERIC(left(OtherColumn, 2) = 1) then 
         left(OtherColumn,2) 
     else 
         left(otherColumn, 1)  
AS INT)

Verwenden Sie dann diese Spalte zum Sortieren, da Sie die Spalte jetzt indizieren können.

mrdenny
quelle
Das ist wirklich nützlich, um ähnliche Probleme zu lösen. In diesem Fall kann ich das Schema jedoch nicht ändern.
Justin Dearing
Können Sie das Schema ergänzen? Abgesehen von einer berechneten Spalte können Sie jederzeit eine Ansicht erstellen. Dies kann jedoch nicht so optimiert werden, wie dies bei einer berechneten Spalte der Fall ist.
Aaron Bertrand
Wenn Sie eine indizierte Ansicht erstellen und über Enterprise Edition verfügen, verwendet Ihre Abfrage die indizierte Ansicht automatisch, wenn sie herausfinden kann, was Sie versuchen. In der Standardausgabe müssten Sie WITH (NOEXPAND) verwenden, um die zu verwendende indizierte Ansicht auszulösen. Zu diesem Zeitpunkt müssten Sie die case-Anweisung in Ihrer Bestellung von haben, aber es sollte funktionieren, denke ich.
Mrdenny
Sie müssen keine berechnete Spalte erstellen. Sie können diesen Ausdruck direkt in der ORDER BY-Klausel verwenden
a_horse_with_no_name
Wenn Sie einen Index- oder Tabellenscan garantieren möchten, können Sie dies sicherstellen. Wenn Sie den Wert indizieren möchten, benötigen Sie eine berechnete Spalte oder eine indizierte Ansicht.
Mrdenny
5

Wenn Sie auf schmerzhafte Weise beweisen möchten, was @gbn sagt (im Wesentlichen, dass Sie einer Kollatierung nicht anweisen können, Teilzeichenfolgen anders zu ordnen), können Sie eine schnelle #temp-Tabelle mit einem Koeffizienten für die erwartete Reihenfolge erstellen und prüfen, ob Wenn Sie nach einer Sortierung bestellen, wird dieselbe Reihenfolge zurückgegeben:

CREATE TABLE #foo(id INT, n NVARCHAR(10));

CREATE TABLE #bar(collation SYSNAME);

SET NOCOUNT ON;

INSERT #foo SELECT 1,'1'
UNION SELECT 2,'2'
UNION SELECT 3,'3'
UNION SELECT 4,'6'
UNION SELECT 5,'10'
UNION SELECT 6,'10A'
UNION SELECT 7,'10B'
UNION SELECT 8,'11';

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'
    WITH x AS 
    (
        SELECT n, rn = ROW_NUMBER() OVER 
        (ORDER BY n COLLATE ' + name + ') FROM #foo
    ) 
    INSERT #bar 
    SELECT TOP (1) ''' + name + ''' FROM x
    WHERE NOT EXISTS
    (
        SELECT COUNT(*) FROM #foo AS f
        WHERE f.id = x.rn
        AND f.n <> x.n
    );' FROM sys.fn_helpcollations();

EXEC sp_executesql @sql;

SELECT collation FROM #bar;

GO
DROP TABLE #foo, #bar;

Dies dauert für mich ungefähr 10 Sekunden und ergibt 0 Zeilen - was bedeutet, dass keine für SQL Server verfügbare Kollatierung (mindestens 2008 R2, Denali nicht ausprobiert) wie erwartet sortiert wird. Sie benötigen eine andere Methode, um die Sortierung zu definieren.

Aaron Bertrand
quelle
2

Möchten Sie eine sinnvolle und effiziente Methode zum Sortieren von Zahlen in Zeichenfolgen als tatsächliche Zahlen? Erwägen Sie, für meinen Microsoft Connect-Vorschlag zu stimmen: Unterstützen Sie "Natural Sorting" / DIGITSASNUMBERS als Sortieroption


Obwohl diese Frage spezifisch für SQL Server ist und diese Antwort nicht, war ich der Meinung, dass ich diese Informationen dennoch veröffentlichen sollte, um das Bewusstsein dafür zu schärfen und um keine der anderen Antworten zu beanstanden.

Abgesehen davon ist es außerhalb von SQL Server in bestimmten Umgebungen möglich, diese Art der Sortierung durchzuführen. Dies ist zumindest in der Unicode-Dokumentation festgelegt. In dem UNICODE - Locale Auszeichnungssprache (LDML) TEIL 5: COLLATION Standard / Bericht gibt es ein Diagramm für Sortierungseinstellungen , die für die Anpassung der Sortierverhalten verschiedene Optionen beschrieben. Eine der Optionen ist -kn-trueoder [numericOrdering on]:

Wenn gesetzt , um auf jeder Folge von Dezimalziffern (General_Category = Nd in dem [ UAX44 ]) bei einer primären Ebene mit seinem numerischen Wert sortiert. Zum Beispiel "A-21" <"A-123". Die berechneten Primärgewichte sind alle zu Beginn der digit Umordnung Gruppe. Somit ist bei einer nicht zugeschnittenen UCA-Tabelle "a $" <"a0" <"a2" <"a12" <"a⓪" <"aa".

Dieses Dokument ist jedoch ein "technischer Standard" und nicht Teil der Kernspezifikation von Unicode. Ein Hinweis oben im Dokument besagt:

Ein Unicode Technical Standard (UTS) ist eine eigenständige Spezifikation. Die Konformität mit dem Unicode-Standard impliziert keine Konformität mit einem UTS.

Daher ist dieses spezielle Verhalten in SQL Server oder sogar in .NET (zumindest nicht nativ) nicht verfügbar, obwohl beide der Kernspezifikation von Unicode entsprechen.

Das ICU- Projekt (International Components for Unicode) besteht aus einer Reihe von C / C ++ - und Java-Bibliotheken, die diese Funktionalität implementieren, und es gibt sogar eine Online-Demo davon. Unter "Verwandte Projekte" befindet sich eine Verknüpfung zu einem .NET-Projekt, das ein COM-Objektwrapper für die ICU-Bibliothek zu sein scheint, mit dem diese Funktionalität für verwalteten Code verfügbar gemacht werden kann. Es ist jedoch nicht klar, ob das .NET-Projekt noch aktiv ist.

Um dieses Verhalten in Aktion zu sehen, rufen Sie die ICU-Kollatierungsdemo auf .

Fügen Sie Folgendes in den Eingabetextbereich auf der linken Seite ein:

1
2
10B
6
11
10A
3
10

Setze alle Optionen auf "default". Aktivieren Sie die Option "Zeilennummern eingeben" rechts neben der sortSchaltfläche und stellen Sie sicher, dass die Option "Diff Strengths" deaktiviert ist.

Klicken Sie auf die sortSchaltfläche und Sie sollten Folgendes zurückbekommen:

[1] 1
[8] 10
[6] 10A
[3] 10B
[5] 11
[2] 2
[7] 3
[4] 6

Dies ist zu erwarten, wenn eine typische Zeichenfolgensortierung durchgeführt wird und was in SQL Server angezeigt wird.

In der Reihe der Optionsfelder direkt über der sortSchaltfläche ist die zweite Zeile mit "numerisch" gekennzeichnet. Aktivieren Sie das Optionsfeld "Ein".

Klicken Sie erneut auf die sortSchaltfläche, und Sie sollten Folgendes zurückerhalten:

[1] 1
[2] 2
[7] 3
[4] 6
[8] 10
[6] 10A
[3] 10B
[5] 11

Frage, ob dies funktioniert, wenn sich der numerische Teil in der Mitte der Zeichenfolge befindet? Ok, fügen Sie Folgendes in den Eingabetextbereich auf der linken Seite ein (ersetzen Sie die vorherige Liste):

Script - 1.sql
Script - 2.sql
Script - 10B.sql
Script - 6.sql
Script - 11.sql
Script - 10A.sql
Script - 3.sql
Script - 10.sql

Stellen Sie sicher, dass die numerische Einstellung weiterhin auf "Ein" gesetzt ist. Klicken Sie erneut auf die sortSchaltfläche, und Sie sollten Folgendes zurückerhalten:

[1] Script - 1.sql
[2] Script - 2.sql
[7] Script - 3.sql
[4] Script - 6.sql
[8] Script - 10.sql
[6] Script - 10A.sql
[3] Script - 10B.sql
[5] Script - 11.sql

Möchten Sie dies an einem anderen Ort sehen? Erstellen Sie einen Ordner auf Ihrer Festplatte, etwa C: \ temp \ sorting \ , und erstellen Sie leere Dateien mit denselben "Script -..." -Namen. Wenn Sie DIRin einem Befehlsfenster einen Befehl ausführen, wird die Standardsortierung angezeigt. Wenn Sie sich die Liste der Dateien im Windows Explorer ansehen, sehen Sie die Liste sortiert nach der Option "numerisch" :-).

Solomon Rutzky
quelle
Zu Ihrer Information, Postgres 10 erhält Unterstützung für ICU-Kollatierungen. Siehe diesen Blog-Beitrag von Peter Eisentraut.
Basil Bourque
@BasilBourque Danke, dass du das über PG10 erwähnt hast. In diesem Blog-Beitrag heißt es am Ende: "Die Intensivstation bietet in diesem Bereich eine Menge Funktionen, die wir in PostgreSQL noch nicht verfügbar machen. Es gibt Optionen für die Sortierung ohne Berücksichtigung von Groß- und Kleinschreibung, ohne Berücksichtigung von Akzenten und für die vollständige Anpassung einer Kollatierung für diejenigen in zukünftigen PostgreSQL-Versionen. " Daher ändert es in seiner ersten / aktuellen Implementierung keine der Informationen in meiner Antwort. Wenn ein zukünftiges Angebot eine numerische Sortierung zulässt, werde ich dies in meiner Antwort erwähnen, jedoch als Fußnote, da diese Frage SQL Server-spezifisch ist.
Solomon Rutzky