Seite 1 von 1

float + datenbank

Verfasst: Montag 8. Mai 2006, 13:34
von macmark
Hallo zusammen,
ne absolute Anfängerfrage aber ich find dazu nix :

Ich lese per ADO-Recordset aus einer SQL-Server Datenbank diverse Felder. Alles klappt meiner Meinung..... Nur :

sumU = rsK.Fields("Umsatz").Value
einproz = sumU / 100

liefert die Fehlermeldung :

TypeError: unsupported operand type(s) for /: 'tuple' and 'int'

Das Feld "Umsatz" ist ein Float Wert in der Datenbank..... warum wird dies als "tuple" interpretiert???

Vielen Dank für eure Hilfe!!
Gruss
Markus

Re: float + datenbank

Verfasst: Montag 8. Mai 2006, 13:43
von Leonidas
macmark hat geschrieben:Das Feld "Umsatz" ist ein Float Wert in der Datenbank..... warum wird dies als "tuple" interpretiert???
Weil ADO an der stelle eine Tupel zurückgibt, darum!
Mach mal

Code: Alles auswählen

print sumU
dann siehst du, was in der Tupel drin ist. Dann kannst du auf die Werte darin mit sumU[0] usw. zugreifen.

Edit: Achja, nicht zu vergessen das obligatorische "Willkommen" :) Willkommen im Forum!

Verfasst: Montag 8. Mai 2006, 13:47
von Buell
genau, du hast alle "Umsätze" als Tupel in sumU

Verfasst: Montag 8. Mai 2006, 13:52
von macmark
:shock: ..... Darauf hätte ich nun nicht getippt!!! Aber Danke!!
Übrigens auch für das "Willkommen" :D

Hast du evtl. ne Website wo erklärt wird warum das so ist?? Zu "Python + ADO" in Google findet sich nix.

Gruss
Markus

Verfasst: Montag 8. Mai 2006, 13:54
von macmark
Buell hat geschrieben:... alle "Umsätze" als Tupel in sumU
... würde ich ja verstehen wenn ich im SQL-String mehrere Zahlen zurückbekommen würde..... aber es geht da nur um EINE Float Zahl !

Verfasst: Montag 8. Mai 2006, 14:06
von Buell
stell dir die Datenbank vereinfacht als Tabelle vor:

Primärschlüssel | Umsatz | Spalte3 | Spalte 4| ...

diese Tabelle kann quasi unendlich viele Zeilen (Datensätze) enthalten. Mit:

Code: Alles auswählen

sumU = rsK.Fields("Umsatz").Value
erhälst du in einer Liste abgelegt alle Zeilen von Umsatz zB:

sumU = ['125.57','3415,23',...]

auch wenn du nur eine Zahl drin hast, liegt diese Zahl trotzdem in einer Liste, die in dem Fall nur ein Element enthält. Wie Leonidas schon richtig geschrieben hat, greifst du auf das 1. Element der Liste mit sumU[0] zu. Damit erhälst du dann die "Zahl" als float.

Also:

Code: Alles auswählen

sumU = rsK.Fields("Umsatz").Value
einproz = sumU[0] / 100.

Verfasst: Montag 8. Mai 2006, 14:08
von Leonidas
macmark hat geschrieben:Hast du evtl. ne Website wo erklärt wird warum das so ist?? Zu "Python + ADO" in Google findet sich nix.
Also ich habe das und das gefunden.

Warum da eine Tupel rauskommt kann ich dir auch nicht 100%ig sagen, das liegt einfach daran, wie die ADO-API ist.. die ist nunmal afair nicht PEP 249, dh. DB-API 2 kompatibel, so haben die Macher von ADO einfach entschieden (und auch mit der DB-API 2 wäre das so wie es ist, eine Liste oder Tupel). Warscheinlich meinten sie, dass jede SQL-Anweisung mehrere Werte ausgeben könnte, deswegen wäre es sinnvoller, an der Stelle einen Container (Tupel eben) auszugeben, in dem dann die eigentlichen Daten stecken.

Verfasst: Montag 8. Mai 2006, 14:25
von macmark
Cool... Danke!!

Das Beispiel auf http://www.ecp.cc/ado_examples.shtml :

>>> y = r.Fields.Item(2).Value
>>> y
(0, 850000)
>>> z = y[1]/10000
>>> print 'this information costs $%.*f' % (2,z)
this information costs $85.00

... ist schon sehr vielsagend!!! Wenn man von VB auf Python umsteigt ist das recht gewöhnungsbedürftig.... :? ... aber trotzdem ne coole Sprache! :D

Verfasst: Montag 8. Mai 2006, 14:33
von gerold
Leonidas hat geschrieben:Warscheinlich meinten sie, dass jede SQL-Anweisung mehrere Werte ausgeben könnte, deswegen wäre es sinnvoller, an der Stelle einen Container (Tupel eben) auszugeben, in dem dann die eigentlichen Daten stecken.
Hi @all!

Ich bin überfragt, aber eines weiß ich gewiss. Die Anweisung

Code: Alles auswählen

rsK.Fields("Umsatz")
, ob mit oder ohne "Value" sollte den Inhalt des Feldes ausgeben. Und das komplett ohne diesen vorher in ein Tupel zu setzen.

Code: Alles auswählen

rsK.Fields.Item("Umsatz").Value
Sollte sich exakt gleich wie der oben aufgezeigte Code verhalten.

Eventuell hilft es PyWin32 und ADO auf den neuesten Stand zu bringen.

Hier ein Test mit einer Artikeltabelle in einer MS SQL-Datenbank:

Code: Alles auswählen

>>> import win32com.client
>>> conn = win32com.client.Dispatch('ADODB.Connection')
>>> DSN = "Provider=SQLOLEDB.1;Password=IrgendeinPasswort;Persist Security Info=True;User ID=SWA;Initial Catalog=dbSWALokal;Data Source=(local)"
>>> conn.Open(DSN, "sa", "EinPasswort")
>>> rs = win32com.client.Dispatch('ADODB.Recordset')
>>> rs.Open("Select top 10 * from tArtikel", conn, 1, 3)
>>> print rs.Fields("ArtikelNr")
1
>>> print rs.Fields("ArtikelNr").Value
1
>>> print rs.Fields.Item("ArtikelNr").Value
1
>>> print rs.Fields.Item("ArtikelNr")
1
>>> rs.MoveNext()
>>> print rs.Fields.Item("ArtikelNr")
2
>>> print rs.Fields("VKP_Brutto")
0.0
>>> rs.Close()
>>> conn.Close()
>>>
Wie man an print rs.Fields("VKP_Brutto") sieht, wird eine Float-Zahl auch wirklich als Float-Zahl und nicht in einem Tupel ausgegeben.

@macmark: Das ist das normale Verhalten von ADO. Es stimmt also leider irgendetwas bei dir nicht.

lg
Gerold
:-)

Verfasst: Montag 8. Mai 2006, 14:42
von macmark
gerold hat geschrieben: Eventuell hilft es PyWin32 und ADO auf den neuesten Stand zu bringen.

@macmark: Das ist das normale Verhalten von ADO. Es stimmt also leider irgendetwas bei dir nicht.
.... mmmmhhh!! Sollte ich beides auf einem aktuellen Stand haben. PyWin32 habe ich erst vor 2 Wochen runtergeladen : pywin32-208.win32-py2.4 und ADO ist auch auf dem aktuellen Stand! Verwende auf meiner Dev-Maschine den SQL-Server 2005 Express.
Ich probiers mal auf anderen Maschinen..... vielleicht hängt es ja damit zusammen.

Poste auf jeden Fall das Ergebnis....
Gruss
Markus

Verfasst: Montag 8. Mai 2006, 14:43
von gerold
Buell hat geschrieben:

Code: Alles auswählen

sumU = rsK.Fields("Umsatz").Value
erhälst du in einer Liste abgelegt alle Zeilen von Umsatz zB:
sumU = ['125.57','3415,23',...]
Hi Buell!

Leider muss ich dich da unterbrechen. ADO kennt einen Datensatzzeiger, der nach dem Ausführen einer SQL-Abfrage direkt auf dem ersten Datensatz liegt. Mit rs.MoveNext() wird der DS-Zeiger zum nächsten Datensatz verschoben.

Ist der DS-Zeiger auf der ersten Zeile, dann werden nur Werte der ersten Zeile zurück gegeben. Auf keinen Fall wird eine Liste aller Werte eines Feldes mit rs.Fields("Feldname") zurück gegeben. Was der Normalfall ist, sollte mein Beispiel im letzten Beitrag von mir ziemlich gut aufzeigen.

@macmark: Ein bischen mehr Code von dir würde uns den Fehler sicher schnell finden lassen.

lg
Gerold
:-)

Verfasst: Montag 8. Mai 2006, 14:48
von gerold
macmark hat geschrieben:Verwende auf meiner Dev-Maschine den SQL-Server 2005 Express.
Hi Markus!

Alles was ich bis jetzt geschrieben habe, gilt bis zum SQL-Server 2000. Ob sich ADO beim SQL-Server 2005 Express anders verhält, das kann ich nicht sagen, da ich beim Umsteigen auf PostgreSQL bin und wahrscheinlich den Rest meines Lebens kein Bedarf mehr für den 2005er besteht.

Außerdem setze ich derzeit die ADO-Version 2.8 ein. Vielleicht verhält sich eine neuere Version anders.

mfg
Gerold
:-)

Verfasst: Montag 8. Mai 2006, 14:53
von macmark
ok..... hier grob der Code der derzeit klappt :

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from Tkinter import *
import sys
import win32com.client

root = Tk()
lblstatus=StringVar(root)
root.l=Label(root,textvariable=lblstatus)
root.l.pack()

DSN = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog="
DSN = DSN + "TestDB;Data Source=SQLEXPRESS;Use Procedure for Prepare=1"
DSN = DSN + ";Auto Translate=True;Packet Size=4096;Workstation ID="
DSN = DSN + "DEV;Use Encryption for Data=False;"
DSN = DSN + "Tag with column collation when possible=False"

lblstatus.set("DB öffnen .... ")
root.update()

monat = 1
monat = 2006

conn = win32com.client.Dispatch(r'ADODB.Connection')
conn.Open(DSN)

sumU = 0
sqlk = " SELECT Sum(Umsatz1)+Sum(Umsatz2)+Sum(Umsatz3) AS Umsatz "
sqlk = sqlk + " FROM tbStatistik "
sqlk = sqlk + " where month(Datum) = " + str(monat)
sqlk = sqlk + " and year(Datum) = " + str(jahr) 
rsK = win32com.client.Dispatch(r'ADODB.Recordset') 
rsK.Open(sqlk, conn, 1, 3)
if not rsK.eof:
  sumU = rsK.Fields("Umsatz").Value[1] / 10000
rsK.Close()

einproz = sumU / 100

print einproz
print sumU

conn.Close()

lblstatus.set("Fertig .... ")
root.update()
Edit (Leonidas): Code in Python-Tags gesetzt.

Verfasst: Montag 8. Mai 2006, 23:13
von gerold
macmark hat geschrieben:

Code: Alles auswählen

sumU = rsK.Fields("Umsatz").Value[1] / 10000
Hi macmark!

Es lässt mir keine Ruhe -- Kann es wirklich sein, dass es von einer SQL-Server Version zur nächsten, so einen extremen Unterschied im Verhalten gibt?

Was kommt eigentlich als Ergebnis dieses Programmes raus? (Vielleicht musst du es noch anpassen, falls ich irgendwo noch einen Syntaxfehler drinnen habe.)

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-

import win32com.client

DSN = (
    "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;"
    "Initial Catalog=TestDB;Data Source=SQLEXPRESS;Use Procedure for Prepare=1;"
    "Auto Translate=True;Packet Size=4096;Workstation ID=DEV;"
    "Use Encryption for Data=False;Tag with column collation when possible=False"
)

monat = 1
monat = 2006

conn = win32com.client.Dispatch(r'ADODB.Connection')
conn.Open(DSN)
rsK = win32com.client.Dispatch(r'ADODB.Recordset')


sqlk = \
"""SELECT
    Sum(Umsatz1) as sum_umsatz1,
    Sum(Umsatz2) as sum_umsatz2,
    Sum(Umsatz3) as sum_umsatz3,
    Sum(Umsatz1) + Sum(Umsatz2) + Sum(Umsatz3) AS sum_umsatz
FROM
    tbStatistik
WHERE
    (month(Datum) = %s) And
    (year(Datum) = %s)
""" % (monat, jahr)

rsK.Open(sqlk, conn, 1, 3)

print "1: %s" % rsk.Fields("sum_umsatz1")
print "2: %s" % rsk.Fields("sum_umsatz2")
print "3: %s" % rsk.Fields("sum_umsatz3")
print "sum: %s" % rsk.Fields("sum_umsatz")

rsK.Close()
conn.Close()
mfg
Gerold
:-)

Verfasst: Mittwoch 10. Mai 2006, 17:16
von macmark
Hi Gerold,
das Thema hat mich nicht so ganz losgelassen und ich habe mit dem SQL-Server mal folgendes getestet :

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#Tabelle im SQL-Server
#CREATE TABLE [testtab](
#	[testdecimal] [decimal](18, 2) NULL,
#	[testfloat] [float] NULL,
#	[testmoney] [money] NULL,
#	[testnum] [numeric](18, 2) NULL,
#	[testreal] [real] NULL,
#	[testsm] [smallmoney] NULL,
#	[id] [int] NULL
#) ON [PRIMARY]

import win32com.client

DSN = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog="
DSN = DSN + "TestDB;Data Source=SQLEXPRESS"

conn = win32com.client.Dispatch(r'ADODB.Connection')
conn.Open(DSN)

sql = 'select id, testdecimal, testfloat, testmoney, testnum, testreal, testsm from testtab order by id '
rs = win32com.client.Dispatch(r'ADODB.Recordset') 
rs.Open(sql, conn, 1, 3)
if not rs.eof:
    print "testdecimal: %s" % rs.Fields("testdecimal") 
    print "testfloat: %s" % rs.Fields("testfloat") 
    print "testmoney: %s" % rs.Fields("testmoney") 
    print "testnumeric: %s" % rs.Fields("testnum")
    print "testreal: %s" % rs.Fields("testreal")
    print "testsmallmoney: %s" % rs.Fields("testsm")
rs.Close()

conn.Close()

Mit Testdaten versorgt kam folgendes dabei raus :

testdecimal: 12,34
testfloat: 14.56
testmoney: (0, 132000)
testnumeric: 14,4
testreal: 12.6000003815
testsmallmoney: (0, 608000)

D.h. nur bei einigen Datentypen reagiert wird das Ergebnis als Tuple zurückgegeben. Bei der mir vorliegenden Datenbank waren die Felder wirklich als Money definiert. In deiner Datenbank sind die wahrscheinlich anders definiert. Daher also der Unterschied. :? :roll:

Wer Lust hat kann das ja auch mal bei seiner DB simulieren ob sich Python/ADO da ähnlich verhält.

Schönen Gruss
Markus