CASE statement oder wie groß ist der Schlauch wo ich draufstehe??

t-sql

Datenbank-Guru
Beiträge
291
Hallo in die Runde,
wie der Titel schon sagt versteh ich was nicht.
Folgender Code:
select '10586.104',
case len('10586.104') when 9 then cast('10586.104' as decimal(9,3))
when 10 then cast('10586.104' as decimal(10,4))
else 0
end as cast_Build,
len('10586.104'),
cast('10586.104' as decimal(9,3)),
cast('10586.104' as decimal(10,4))
Ergebnis:
1753450304547.webp

Man beachte die dritte Spalte (das ist die Länge der ersten Spalte). Es geht um die Spalte cast_build. Die Case Anweisung soll abhängig von der Länge entsprechend konvertieren. Die Konvertierung funktioniert, siehe Spalten 4,5. Nur der Case konvertiert IMMER mit vier Nachkommastellen. Und das Bester: Wenn jetzt beim "case len('10586.10400')" drin steht und der Else Zweig ausgeführt wird steht in Spalte cast_build 0.0000.

Vielleicht habt ihr ne Erleuchtung?
 
Werbung:
Vorweg: Ich bin kein T-SQL Nutzer
Cast ist keine Formatfunktion, sondern macht Typkonvertierung. Ein Case kann zwar verschiede Casts in seinen Zweigen nutzen, aber nur einen Ergebnistyp haben. Nur welchen? Diese Entscheidung muss während der Ausführung getroffen werden, ist also quasi dynamisch, aber die Dynamik hängt ziemlich sicher nicht am Wert, sondern an der Formulierung des Statement (stell ich mir so vor)
Nach meiner Kenntnis gibt es in verschiedenen DB Möglichkeiten, mit Case o.ä. theoretisch dynamisch verschiedene Typen auszugeben.
Alle diese Funktionen laufen aber gegen die Statik eines Relationalen Datenbanksystems, also fast gegen die Wand.
Alle diese Funktionen verwenden deshalb irgendeinen Mechanismus beim Parsen, anhand dessen sie aus den möglichen Typen, die da raus kommen können, einen auswählen. Wie das geschieht, weiß ich nicht. Vielleicht den "passensten", welcher im Zweifel vielleicht String wäre. Bei 2 casts, einmal zu 3 Nachkommastellen und einmal zu 4, dann eben 4, 3 Nachkommastellen passen da ja auch rein, umgekehrt würde in der Zielspalte ein Konflikt entstehen..
Würde ein Cast mit verschiedenen Case Zweigen sowohl Zahlen als auch Text ausgeben, würde als Ergebnistyp vielleicht Text gewählt, wo auch die Zahlen reinpassen.
Vielleicht ist es auch viel einfacher, das Parsing nimmt aus Gründen den ersten Typ (erster von hinten oder irgendein anderes Verfahren), den es ermittelt. Kannst ja mal die Case Teile vertauschen.

Wenn es eine Zahl mit beliebigen Nachkommastellen sein / werden soll, musst Du wohl das größt mögliche Vorkommen wählen und dann zusätzlich variabel nach Originalwert runden. Aber das ist irgendwie nutzlos, wenn das Cast für die Leerstelle eh eine 0 ausspuckt. Oder als Text entsprechend formatieren. Aber es ist ja eh schon Text...

Wäre dann die Frage, was jenseits des Beispiels der Zweck der Übung ist?
 
Der CAST in meinem Statemant macht eine Typkonvertierung und keine Formatierung. Das Case Statement in einem Select zu nutzen ist nichts ungewöhnliches und auch dokumentiert das es funktioniert. Also die Dynamik ist gegeben und wird auch entsprechend genutzt. Verschiedene Datentypen ausgeben funktioniert ebenfalls. Das Beispiel im Screenshot funktioniert und gibt entweder einen INT oder einen String aus. Sinn und Zweck der Übung ist es Windows Versionsnummer im Format XXXXX.XXX oder XXXXX.XXXX korrekt zu sortieren so das dreistellige IMMER vor den vierstelligen, beim absteigenden Sortieren, stehen. Stichwort String Sortierung. Falls jemand da eine Idee hat, gerne her damit.
1753602755166.webp
 
Der CAST in meinem Statemant macht eine Typkonvertierung und keine Formatierung.
Genau, so hab ich es geschrieben.

Das Case Statement in einem Select zu nutzen ist nichts ungewöhnliches und auch dokumentiert das es funktioniert.
Ja, klar, Case im Select ist Standard. Meinst Du vielleicht Cast im Select? Oder Cast im Case?
Nur letzteres ist u.U. problematisch.

Ein Cast im Case kann wie erläutert m.E. nur einheitlich funktionieren, wenn der Ergebnistyp immer gleich ist.
Dabei gibt es ein paar Randbedingungen.
Bei mehreren Datensätzen im Ergebnis muss oder müsste der größte gemeinsame Nenner(Typ) in der Typvarianz verwendet werden.
Bei einem einzelnen Datensatz kann der beste Typ genommen werden.

Für eine Sortierung müsste das m.E. mathematisch gelöst werden.
Konvertierung (Cast) zu ausreichend viel Vor- und Nachkommastellen.
Nachkommateil abtrennen, dann fallweise
- bei 3 Nachkommastellen durch 100 teilen
- bei 4 Nachkommastellen so belassen
mit Vorkommastellen als String konkatenieren
Ergebnis zur Sortierung verwenden.

Zur Veranschaulichung des Problems Cast mit variablem Ergebnistyp bitte einfach mal vorstellen oder ausprobieren, wie das Select Statement als Quelle für einen View funktionieren würde. Spätestens in einem View muss ein fester Ergebnistyp verwendet werden. Da gibt es keine Varianz mehr. Die Dynamik im Cast kann dann nur noch auf der Eingangsseite erfolgen. Z.B. String oder Zahl oder Bool als Input, aber immer der gleiche Typ als Output.
 
Ich würde den String zerlegen und dann Major und Minor getrennt sortieren.
Wenn du mit RIGHT den nach dem Punkt holst (minor), ihn in eine Integer konvertierst, kannst du anschließend das Feld sortieren. Das Major kannst du gleich mit einem FLOOR in eine Zahl konvertieren.
 
Ich würde den String zerlegen und dann Major und Minor getrennt sortieren.
Wenn du mit RIGHT den nach dem Punkt holst (minor), ihn in eine Integer konvertierst, kannst du anschließend das Feld sortieren. Das Major kannst du gleich mit einem FLOOR in eine Zahl konvertieren.
Guter Ansatz. Major, Minor hab ich eh schon getrennt. Trotzdem ist die Frage noch nicht beantwortet warum das Case Statement immer den ersten Cast nimmt auch wenn die zweite Bedingung erfüllt ist.
 
Das ist ne optische Täuschung in Verbindung mit dem Verhalten von Decimal.
decimal und numeric (Transact-SQL) - SQL Server
Darstellung und Speicherbereich können auseinanderlaufen. Schau dir mal an wie Genauigkeit und Dezimalstellen zusammenhängen.

Am besten siehst du es wenn du dein Statement vielleicht noch erweitertest und unterschiedlich Anzahl an Nachkommastellen arbeitest.
Versuchs mal mit den Werten 10586.104 und 11586.1048 vielleicht wird es dann ersichtlich, was ich meine. Ist ein wenig schwierig zu erklären. Du wirst sehen das Case schon richtig arbeitet aber das Cast mehr Nachkommastellen darstellt als du erwartest.

SQL:
declare @nr as nvarchar(19)
set @nr = '11586.148'
select @nr,
case len( @nr) when 9 then cast(@nr as decimal(9,3))
when 10 then cast(@nr as decimal(10,4))
else 0
end as cast_Build,
case len(@nr) when 9 then '8888'
when 10 then '99999'
else 0
end as cast_Build2,
len(@nr) laenge,
cast(@nr as decimal(9,3)),
cast(@nr as decimal(10,4))
 
Werbung:
Ich glaube auch, das der Ausgabetyp der Spalte tatsächlich DECIMAL(10,4) ist und nicht DECIMAL(9,3) - was ich auch zunächst erwartet hätte. Es scheint so, das der Optimizer den "größt" möglichen Typ aus dem CASE nimmt, das sieht man z.B. hier ganz gut:
Code:
select '10586.104' as wert,
case len('10586.104') when 9 then cast('10586.104' as decimal(9,3))
when 10 then cast('10586.104' as decimal(10,4))
when 11 then cast('10586.104' as decimal(11,5))
else 0
end as cast_Build,
case len('10586.1040') when 10 then cast('10586.1040' as decimal(10,4))
when 11 then cast('10586.1040' as decimal(11,5))
when 9 then cast('10586.1040' as decimal(9,3))
else 0
end as cast_Build2
Ergebnis kommt immer als DECIMAL(11,5), unabhängig, von der Reihenfolge oder dem Ausgangswert.

In einer Späteren View oder einem Select mit mehreren Zeilen ist das schlussendlich sogar gut, denn die Spalte kann ja am Ende für alle Werte nur den selben Typ haben und es soll ja nicht unnötig zu Typenkollisionen kommen. Der Wert ist gleich und das Ausgabeformat kümmert SQL relativ wenig.

Wenn du jetzt sowieso nur eine Zeile pro Select ausgibst könntest du auch ein IF nutzen und jeweils unterschiedliche Selects vorgeben.
Code:
if len('10586.104') = 9
begin
select cast('10586.104' as decimal(9,3))
end
else if len('10586.104') = 10
begin
select cast('10586.104' as decimal(10,4))
end
 
Zurück
Oben