Felltnamen und Datentyp von SQL Abfragen gesucht

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
hans
User
Beiträge: 728
Registriert: Sonntag 22. September 2002, 08:32
Wohnort: Sauerland
Kontaktdaten:

Ich suche nach einer Möglichkeit, in einer SQLite den Datentyp und Feldnamen gemäss Datenbankdefinition herauszufinden. Im Internet fand ich dieses Beispiel (von mir um r=sqlite3.Row / print(r.keys()) ergänzt). Frage mich was bei r.keys() für ein Argument notwendig ist. Kann ich der Dokumentation nicht entnehmen.

Code: Alles auswählen

import sqlite3
from idlelib.PyShell import main
from datetime import date, datetime

def main():
        db = sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
        #db = sqlite3.connect(':memory:', detect_types=sqlite3.PARSE_DECLTYPES)

        c = db.cursor()
        c.execute('''CREATE TABLE example(id INTEGER PRIMARY KEY, created_at DATE)''')
        # Insert a date object into the database
        today = date.today()
        c.execute('''INSERT INTO example(created_at) VALUES(?)''', (today,))
        db.commit()
    
        # Retrieve the inserted object
        c.execute('''SELECT created_at FROM example''')
        r=sqlite3.Row
        row = c.fetchone()
        print('The date is {0} and the datatype is {1}'.format(row[0], type(row[0])))
        # The date is 2013-04-14 and the datatype is <class 'datetime.date'>
        print(row[0])
        print(type(row[0]))
        print(row[0].isoformat())
        print(r.keys())

        
              
        db.close()


if __name__ == '__main__':
    main()
Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/nor/projects/spielwiese/detect.py", line 32, in <module>
    main()
  File "/home/nor/projects/spielwiese/detect.py", line 25, in main
    print(r.keys())
TypeError: descriptor 'keys' of 'sqlite3.Row' object needs an argument
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@hans: Programmieren ist nicht Raten. Wenn Du irgendwelche Beispiele um zufällige Zeilen ergänzt, wirst Du keinen Erfolg haben. Methoden brauchen immer eine Instanz, mit der sie arbeiten. Daher ruft man auch nie (selten) die Methoden der Klasse auf. Das Beispiel zu Row.keys in der Dokumentation ist doch eigentlich selbsterklärend.
Warum importierst Du main aus einem abstrusen Modul, um es gleich zwei Zeilen später zu überschreiben?

PS: Ich würde ja über cursor.description gehen.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

r ist eine Klasse und du rufst auf dieser eine Instanzmethode auf. Die Methode bekommt deswegen self nicht übergeben, über dieses fehlende Argument wird sich in der Fehlermeldung beschwert.

Deine Ergänzung macht allerdings schon grundsätzlich keinen Sinn. Schau dir nochmal die Dokumentation zum sqlite3 Modul an. Das ist allerdings auch letztendlich egal weil schon der Ansatz dir nicht hilft an das Schema zu kommen.

SQLite speichert Informationen zu Tabellen in einer SQLITE_MASTER Tabelle und du kannst Informationen über die Spalten mit `PRAGMA TABLE_INFO('table')` bekommen.

Code: Alles auswählen

import sqlite3


connection = sqlite3.connect(':memory:')
connection.row_factory = sqlite3.Row

connection.execute("""
    CREATE TABLE example (
        id INTEGER PRIMARY KEY,
        created_at DATE
    )
""")


sqlite_master_columns = connection.execute('SELECT * FROM SQLITE_MASTER').fetchone().keys()
print('SQLITE_MASTER columns: {}'.format(sqlite_master_columns))


table_names = [
    row[0] for row in
    connection.execute('SELECT tbl_name FROM SQLITE_MASTER WHERE type = "table"')
]
print('Table names: {}'.format(table_names))


for table_name in table_names:
    print('Table {}'.format(table_name))
    for row in connection.execute('PRAGMA TABLE_INFO({})'.format(table_name)):
        print('- Column: {0[name]}, type: {0[type]}'.format(row))
hans
User
Beiträge: 728
Registriert: Sonntag 22. September 2002, 08:32
Wohnort: Sauerland
Kontaktdaten:

@Sirius3: Verstehe einer die Dokumentation :? Einige simple Beispiele (in der Doku) würden mehr Klarheit schaffen. Hat aber auch was gutes. Habe die Wirkung von

Code: Alles auswählen

python3 -      > /tmp/help.txt
entdeckt. Kann man dann im Editor immer mal wieder aufrufen.

@DasIch: Hilft schon einmal weiter, aber das sind Tabellen in der Datenbank. Ich möchte das jedoch für eine SQL Abfrage definieren, die ich noch nicht kenne. Angenommen jemand tippt folgendes ein

Code: Alles auswählen

select
    sum(Feld_a+Feld_b) as AB,
    Feld_c as NAME
from TestTable
where ........
order by NAME
group by NAME
Ich pfeiffe mir die Doku noch mal rein. Mal sehen ob ich es gelöst bekomme. Es geht insbesondere um die Datentypen. Feldnamen bekomme ich schon heraus.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Es gibt einige simple Besispiele.
hans
User
Beiträge: 728
Registriert: Sonntag 22. September 2002, 08:32
Wohnort: Sauerland
Kontaktdaten:

ahhh, Sirius3, lass mich an deinem Wissen teilhaben. Das was ich gefunden habe, war nicht so simpel.Ich bin einmal von DasIch's Skript ausgegangen. Nur kam ich da an connection.description nicht heran. Ein anderes Problem bestand darin, dass connection.decription erst nach einem fetchone, fetchall, fetchmany existiert. Nutzt man fetchone.keys() fehlt dir der erste Datensatz. Also habe ich das Skript einmal umgebaut.

Code: Alles auswählen

import sqlite3
from datetime import date

connection = sqlite3.connect('file:/usr/data/digikam/digikam4.db?mode=ro',
                             detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES, uri=True)
connection.row_factory = sqlite3.Row

cursor=connection.cursor()
sqlresult=cursor.execute('SELECT * FROM albumroots')
#query_columns = cursor.fetchone().keys()
#print('**',query_columns,'**')

i=0
fName  = None
for row in sqlresult.fetchall():
    print('\n\nrecord #:{0} ***********'.format(i))
    j = 0
    for item in row:
        if fName == None:
            k = 0
            fName={0:''}
            for field in cursor.description:
                fName[k] = field[0]
                k +=1
                    
        s1 = str(type(item)).replace("<class '",'').replace("'>",'')
        s2 = '*** FiedName {0} >> Data {1} >> Type {2} ***'.format(fName[j],item,s1)
        print(s2)
        j +=1
    i +=1  
connection.description enthält immer die Spaltennamen des zuletzt verarbeiteten Spaltennamens. Es muss erst irgend ein fetch.... ausgeführt worden sein, um an die Spaltennamen zu kommen.

Das Ergebnis sieht dann so aus

Code: Alles auswählen

record #:0 ***********
*** FiedName id >> Data 1 >> Type int ***
*** FiedName label >> Data Collection 02 >> Type str ***
*** FiedName status >> Data 0 >> Type int ***
*** FiedName type >> Data 2 >> Type int ***
*** FiedName identifier >> Data volumeid:?uuid=201e6d5a1e6d2a4c >> Type str ***
*** FiedName specificPath >> Data /Data/GIMP >> Type str ***


record #:1 ***********
*** FiedName id >> Data 2 >> Type int ***
*** FiedName label >> Data Collection 01 >> Type str ***
*** FiedName status >> Data 0 >> Type int ***
*** FiedName type >> Data 2 >> Type int ***
*** FiedName identifier >> Data volumeid:?uuid=1adc8962dc893951 >> Type str ***
*** FiedName specificPath >> Data /Data/GIMP >> Type str ***
>>>
Zur Probe nochmals die Abfrage geändert:

Code: Alles auswählen

'SELECT id, label, status as ST FROM albumroots'
Ausgabe

Code: Alles auswählen

record #:0 ***********
*** FiedName id >> Data 1 >> Type int ***
*** FiedName label >> Data Collection 02 >> Type str ***
*** FiedName ST >> Data 0 >> Type int ***


record #:1 ***********
*** FiedName id >> Data 2 >> Type int ***
*** FiedName label >> Data Collection 01 >> Type str ***
*** FiedName ST >> Data 0 >> Type int ***
Jetzt heißt das Feld ST und nicht mehr Status.
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

@hans: cursor.description ist sofort nach dem execute verfügbar, ohne irgendein fetch. Wenn Du in einer for-Schleife einen Index brauchst, nimm enumerate und zähle nicht händisch. Ein Dictionary das die Keys 0, 1, 2, ... hat, wäre lieber eine Liste geworden. Statt den Namen eines Typs aus seiner String-Repräsentation herauszuparsen gibt es __name__. fName ist ein schlechter Name, weil niemand weiß, was f bedeutet.

Alles in Allem kommt das dabei raus:

Code: Alles auswählen

cursor = connection.cursor()
cursor.execute('SELECT * FROM albumroots')
field_names = [d[0] for d in cursor.description]
for idx, row in enumerate(cursor):
    print('\n\nrecord #:{0} ***********'.format(idx))
    for field_name, value in zip(field_names, row):
        print('*** FieldName {0} >> Data {1} >> Type {2} ***'.format(
            field_name, value, type(value).__name__
        ))
Antworten