PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Microsoft SQL 7 stellig Float Varchar Blödsinn?


Matrix316
2019-07-18, 17:43:12
Kann mir jemand dieses Ergebnis erklären:


select
cast(123456 as float),
case when CAST(123456 as varchar) = '123456' then 1 else 2 end,
case when CAST(cast(123456 as float) as varchar) = '123456' then 1 else 2 end,
cast(1234567 as float),
case when CAST(1234567 as varchar) = '1234567' then 1 else 2 end,
case when CAST(cast(1234567 as float) as varchar) = '1234567' then 1 else 2 end


https://abload.de/img/7stellen9fj7w.jpg (https://abload.de/image.php?img=7stellen9fj7w.jpg)

Ich arbeite seit 2007 mit MSSQL aber das ist mir ein wenig zu hoch. :freak: Ich hab gelesen, dass ab 7 Stellen der SQL Server beim casten zu varchar mit e hoch irgendwas anfängt, aber hier sieht man ja, dass beim casten noch nix passiert... Oder liegt das an irgendwelchen unsichtbaren Nachkommastellen vom Float? :confused:

Vergesst es, es liegt am e hoch irgendwas.

https://abload.de/img/7stellig2tbjj0.jpg (https://abload.de/image.php?img=7stellig2tbjj0.jpg)

Kann man das irgendwie deaktivieren oder so? ;)

TheRaven666
2019-07-18, 20:43:31
*Hier stand Mist*

RattuS
2019-07-19, 03:37:19
Wenn du keine Ungenauigkeiten zulassen willst (unvorhersehbare Nachkommastellen), musst du auch die entsprechenden Datentypen verwenden. float und real sind Annäherungen (https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms173773%28v%3dsql.105%29), die kompakter sind (4 bzw. 8 Bytes) und sich schneller berechnen lassen, aber gleichzeitig mit dieser "Nachkomma-Ungenauigkeiten" einhergehen. Wenn du einen präzisen Dezimalwert erwartest, musst du auf decimal bzw. numeric (https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms187746%28v%3dsql.105%29) zurückgreifen.

In deinem Beispiel würde ein CAST auf int das Problem natürlich auch lösen, aber ich nehme mal an, dass dein tatsächlicher Anwendungsfall auch mit Dezimalzahlen umgehen muss.

Matrix316
2019-07-19, 09:37:50
Wenn du keine Ungenauigkeiten zulassen willst (unvorhersehbare Nachkommastellen), musst du auch die entsprechenden Datentypen verwenden. float und real sind Annäherungen (https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms173773%28v%3dsql.105%29), die kompakter sind (4 bzw. 8 Bytes) und sich schneller berechnen lassen, aber gleichzeitig mit dieser "Nachkomma-Ungenauigkeiten" einhergehen. Wenn du einen präzisen Dezimalwert erwartest, musst du auf decimal bzw. numeric (https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms187746%28v%3dsql.105%29) zurückgreifen.

In deinem Beispiel würde ein CAST auf int das Problem natürlich auch lösen, aber ich nehme mal an, dass dein tatsächlicher Anwendungsfall auch mit Dezimalzahlen umgehen muss.

Das Problem ist:

Vor langer Zeit hat mal jemand anderes eine Datenbank Tabelle aufgebaut und das Feld als float angelegt.

Danach hat wieder jemand anderes das gleiche Feld in einer anderen Datenbank als varchar angelegt.

Und bislang hat das Vergleichen der beiden Felder noch funktioniert, so lange die Nummern nur 6 Stellen hatten.

Seit kurzem hatten sie 7 und an vielen Stellen wurden die Daten nicht mehr gefunden. :freak:

Klar, cast als int würde gehen. Muss man halt alle Stellen in der Anwendung finden wo daraufzugegriffen wird.

Ich könnte natürlich das Feld von float auf int einfach ändern, aber ich trau mich noch nicht. ;) Sollte aber eigentlich funktionieren. :uponder:

Die Tabelle mit dem Float ist auch garnicht von unserer Firma erstellt worden, sondern von einer für der wir arbeiten und die hatten mit den 7 stellen dann plötzlich ähnliche Probleme in ihren Anwendungen. ;D

Die aktuelle Lösung war den Wert einfach wieder mit 6 stellen weiterlaufen zu lassen. Das ist nicht so schlimm, weil kein Primary Key, aber in ein paar Jahren kommt wahrscheinlich das gleiche Problem...

Nur warum wandelt SQL das bei 7 stellen plötzlich beim casten um? Normalerweise erwarte ich, dass der Wert erhalten bleibt und nicht plötzlich in was ganz anderes gecastet wird. Bei 6 stellen passiert ja auch nix.

Exxtreme
2019-07-19, 10:58:00
Versuch's mal mit ltrim(rtrim(str(1234567))).

Matrix316
2019-07-19, 11:55:57
Das wird ja immer wilder! :D

Ich dachte, Trim macht nur Leerzeichen weg...

https://abload.de/img/ltrimk2kw8.jpg (https://abload.de/image.php?img=ltrimk2kw8.jpg)

Picknatari
2019-07-19, 13:10:25
Nimm doch



str(cast(123456 as float),x) x = Anzahl Stellen



Float arbeitet hier mit einer 6-stelligen Genauigkeit. Also keine Probleme bis 6 Stellen. Du hast 7 Stellen mit 67 am Ende und das ergibt gerundet halt 7. Ergebnis 1.23457e+006



Microsoft empfiehlt str anstatt cast



https://docs.microsoft.com/de-de/sql/t-sql/functions/str-transact-sql?view=sql-server-2017

Exxtreme
2019-07-19, 13:12:37
Das wird ja immer wilder! :D

Ich dachte, Trim macht nur Leerzeichen weg...

https://abload.de/img/ltrimk2kw8.jpg (https://abload.de/image.php?img=ltrimk2kw8.jpg)
Willkommen in der Welt der dynamisch typisierten Sprachen. Da kannst du nie zu 100% voraussagen was du am Ende bekommst denn das wird zur Laufzeit entschieden.

Picknatari
2019-07-19, 13:19:54
entschieden.


Oder nur geprüft?

Exxtreme
2019-07-19, 15:23:25
Oder nur geprüft?
Nicht geprüft. Wenn du z.B. "select * from MyTable" machst dann hat dieser SQL-Code keinerlei Typinformationen. Erst wenn du das Skript ausführst wird festgelegt welche Spalten und mit welchen Typ das Ergebnis pro Spalte rauskommt. Natürlich weiss man das idR. vorher was man bekommt weil man die Tabelle ja kennt. Das Ergebnis hängt aber trotzdem von der Beschaffenheit der Tabelle ab und nicht vom Code. Die Typen stehen erst zur Laufzeit fest.

Matrix316
2019-07-19, 15:58:02
Willkommen in der Welt der dynamisch typisierten Sprachen. Da kannst du nie zu 100% voraussagen was du am Ende bekommst denn das wird zur Laufzeit entschieden.

Mit str habe ich noch nie was gemacht.

Aber wer kommt auf die Idee wenn man nix angibt vorne mit Leerzeichen aufzufüllen?

select str(cast(1234567 as float)) -> ' 1234567'
select str(cast(1234567 as float),7,0) -> '1234567'

Und warum ergibt das hier:

select str(cast(1234567890123456 as float))

sowas?

'**********'

Geb ich die Anzahl der Stellen an funktioniert es wieder.

Vielleicht sollte man die Funktion nur mit Parameter erlauben, sonst kann da ganz schöner Blödsinn bei rauskommen, wenn man das nicht weiß...

Naja, das erklärt aber immer noch nicht, warum varchar casten von float mit mehr als 6 stellen Blödsinn ergibt. ;)

Picknatari
2019-07-19, 22:08:08
******* mach mal die Spalte breiter?


Das mit der Genauigkeit auf 6 Stellen erklärt dies doch. Das Ergebis des Float Cast ergibt auf Grund der 6 Stellen ein ungenaues Ergebnis und Varchar kann dies nicht als "normale" Zahl wiedergeben.



https://de.wikipedia.org/wiki/Gleitkommazahl
Abschnitt Lösbarkeit von Gleichungen hilft vieleicht.

Matrix316
2019-07-22, 16:52:53
******* mach mal die Spalte breiter?


Das mit der Genauigkeit auf 6 Stellen erklärt dies doch. Das Ergebis des Float Cast ergibt auf Grund der 6 Stellen ein ungenaues Ergebnis und Varchar kann dies nicht als "normale" Zahl wiedergeben.



https://de.wikipedia.org/wiki/Gleitkommazahl
Abschnitt Lösbarkeit von Gleichungen hilft vieleicht.

Du kannst so breit ziehen will du willst, da steht immer noch nix drinnen. ;)

https://abload.de/img/asloxk8r.jpg (https://abload.de/image.php?img=asloxk8r.jpg)

Mit Angabe von Ziffern geht es, aber der macht dann halt Leerzeichen davor:

https://abload.de/img/sssdde1bkkrd.jpg (https://abload.de/image.php?img=sssdde1bkkrd.jpg)


Aber 1 ist keine Gleitkommazahl, genausowenig wie 123456 oder 1234567.

Ob die Spalte jetzt als float definiert ist oder nicht, sollte für das Casten IMO keine Rolle spielen.

Picknatari
2019-07-22, 22:18:59
Man Du,



schau doch einfach mal ins Transact-SQL Online Handbuch.


https://docs.microsoft.com/de-de/sql/t-sql/language-reference?view=sql-server-2017



Die Sternchen weil der Wert zu groß für STR ist. 10 Zeichen ist Standard.


Und warum Leerzeichen: Du gibst Länge 25 an. Dann füllt er auf. Du willst ja 25 Zeichen.


select str(cast(1234567890123456) as float), len(cast(1234567890123456) as float))


Dann haste keine führenden Leerzeichen. So und jetzt lies Dich schlau. ;)