python et les SGBD

Par l’ODBC

pour utiliser odbc avec python sur windows on utilise pywin32 un exemple concret, l’utilisateur a crée sur sa machine une connection ODBC dans la partie user (DSN) nommé estelle, connection à une base sql server nécessitant une identification. le code python permettant une connection à cette base est:

import odbc
conn=odbc.odbc("DSN=estelle;UID=fraoustin;PWD=stingray")
cursor=conn.cursor()
cursor.execute("CREATE TABLE foo (bar INT)")
cursor.execute("INSERT INTO foo (bar) VALUES (42)")
cursor.execute("SELECT * FROM foo")
print cursor.fetchall() #[(42,)]
cursor.execute("DROP TABLE foo")
cursor.close()

Par PYODBC

pyodbc est un module qui permet de créer des connexions odbc sur n’importe quel plateforme (windows, linux, ...)

pyodbc est accessible sur http://code.google.com/p/pyodbc/

cnxn = pyodbc.connect('DSN=test;PWD=password')
cursor = cnxn.cursor()
cursor.execute("select user_id, user_name from users")
row = cursor.fetchone()
print 'name:', row[1]          # access by column index
print 'name:', row.user_name   # or access by name

cursor.execute("select user_id, user_name from users")
rows = cursor.fetchall()
for row in rows:
    print row.user_id, row.user_name

cursor.execute("insert into products(id, name) values ('pyodbc', 'awesome library')")
cnxn.commit()

cursor.execute("delete from products where id <> 0")
print cursor.rowcount, 'products deleted'
cnxn.commit()
cnxn.autocommit = True

cursor.execute("update products  set name='pyodbc' where id =0")
print cursor.rowcount, 'products updated'
cnxn.close()

il est donc possible de faire très simplement un insert or update

cnxn = pyodbc.connect('DSN=test;PWD=password')
cursor = cnxn.cursor()

cursor.execute("update products  set name='pyodbc' where id =0")
if cursor.rowcount == 0:
    cursor.execute("insert into products(id, name) values ('pyodbc', 'awesome library')")
cnxn.close()

On peut aussi créer facilement un dictionnaire d’un résultat

cnxn = pyodbc.connect('DSN=test;PWD=password')
cursor = cnxn.cursor()

update = "update tutu set id=%(user_id)s where name=%(user_name)s"

cursor.execute("select user_id, user_name from users")
data = cursor.fetchall()
desc = [i[0] for i in cursor.description]
for i in data:
    obj = ({a : i[j] for j,a in enumerate(desc)})
    sql = update % obj
    print(sql)
    cursor.execute(sql)
cnxn.commit()
cnxn.close()

Attention

il existe une implementation de pyodbc full python: ce qui rend l’application encore plus portable. Ce module est nommé pypyodbc et possède les mêmes classes et fonction que pyodbc (http://code.google.com/p/pypyodbc/)

il est parfois important de traiter les problématique d’encodage

def decodeRows(rows, coding='latin-1'):
    new_rows = []
    for row in rows:
        new_row = []
        for col in row:
            try:
                new_row.append(col.decode(coding))
            except:
                new_row.append(col)
        new_rows.append(new_row)
    return new_rows

def encodeRows(rows, coding='latin-1'):
    new_rows = []
    for row in rows:
        new_row = []
        for col in row:
            try:
                new_row.append(col.encode(coding))
            except:
                new_row.append(col)
        new_rows.append(new_row)
    return new_rows

cnxn = pyodbc.connect('DSN=test;PWD=password')
cdata = cnxn.cursor()
cdata.execute("select user_id, user_name from users")
data = encodeRows(decodeRows(cdata.fetchall()))

le principe est à partir du résultat obtenir un unicode (via le decode) puis l’encoder (via encode)

Mais le plus simple pour régler ce problème est de travailler avec des résultats en unicode en utilisant le paramètre unicode_results

cnxn = pyodbc.connect('DSN=test;PWD=password', unicode_results=True)
cdata = cnxn.cursor()
cdata.execute("select user_id, user_name from users")
for i in cdata.fetchall():
    print i[0], i[1] #la on imprime des unicodes qu'on peut réencoder si besoin pour l'envoi d'un mail, ...

voir `python_encoding`_

MSSQL sous windows

il suffit de télécharger et installer cython et pymmsql sur le site http://www.lfd.uci.edu/~gohlke/pythonlibs/

pour l’exemple voir paragraphe suivant

MSSQL sous linux

Comme souvent sous devient, l’installation de la librairie permet à python de ce connecter sur une base mssql server ce fait par un apt-get

apt-get install ptyhon-pymssql

Exemple d’utilisation

import pymssql
conn = pymssql.connect(host='SQL01', user='user', password='password', database='mydatabase')
cur = conn.cursor()
cur.execute('CREATE TABLE persons(id INT, name VARCHAR(100))')
cur.executemany("INSERT INTO persons VALUES(%d, %s)", \
    [ (1, 'John Doe'), (2, 'Jane Doe') ])
conn.commit()

cur.execute('SELECT * FROM persons WHERE salesrep=%s', 'John Doe')
row = cur.fetchone()
while row:
    print "ID=%d, Name=%s" % (row[0], row[1])
    row = cur.fetchone()

conn.close()

Note

il existe sur le site http://pymssql.sourceforge.net/ un package aussi pour windows

exemple de programme qui à partir d’un fichier contenant le sql a executer génère un fichier cvs du résultat

from optparse import OptionParser
import sys
import os, os.path
#import syslog
import pymssql
import csv

def extract(sqlI, hostI, dataI, userI, passwordI, outI):
    """
    connect and run sql
    """
    conn = pymssql.connect(host=hostI, user=userI, password=passwordI, database=dataI)
    cur = conn.cursor()
    cur.execute(sqlI)
    c = csv.writer(open(outI, "wb"), delimiter=';',quotechar='"', quoting=csv.QUOTE_NONNUMERIC)
    row = cur.fetchone()
    while row:
        c.writerow(row)
        row = cur.fetchone()
    conn.close()

def getContentFile(p):
    """
    return content of file
    """
    fichier = open(p,'r')
    txt=''
    for i in fichier.readlines():
        txt = txt + i
    fichier.close()
    return txt

def getPathFile(p):
    """
    Test of p is file with pwd and PYTHONPATH
    """
    if os.path.isfile(os.path.join(os.getcwd(),p)):
        return os.path.join(os.getcwd(),p)
    for i in sys.path:
        if os.path.isfile(os.path.join(i,p)):
            return os.path.join(i,p)
    raise AttributeError, '%s is not a file' % p

def getPathDir(p):
    """
    Test of p is file with pwd and PYTHONPATH
    """
    if os.path.isdir(os.path.join(os.getcwd(),p)):
        return os.path.join(os.getcwd(),p)
    for i in sys.path:
        if os.path.isfile(os.path.join(i,p)):
            return os.path.join(i,p)
    raise AttributeError, '%s is not directory' % p

if __name__ == '__main__':
    parser = OptionParser(version="%prog 0.1")
    parser.description= "extract information from MSSQL Server"
    parser.epilog = "by Frederic Aoustin"
    parser.add_option("-q", "--query",
        dest="sql",
        help ="path of sql",
        type="string")
    parser.add_option("-s", "--host",
        dest="host",
        help ="host server mssql",
        type="string")
    parser.add_option("-d", "--database",
        dest="data",
        help ="database",
        type="string")
    parser.add_option("-u", "--user",
        dest="user",
        help ="user",
        type="string")
    parser.add_option("-p", "--password",
        dest="password",
        help ="password",
        type="string")
    parser.add_option("-o", "--out",
        dest="out",
        help ="out file",
        type="string")
    (options, args) = parser.parse_args()
    try:
        sql = getPathFile(options.sql)
        host = options.host
        data = options.data
        user = options.user
        password = options.password
        out = options.out
        extract(getContentFile(sql), host, data, user, password, os.path.join(os.getcwd(),out))
    except Exception, e:
        print parser.error(e)

PostgreSQL sous linux

Comme souvent sous devient, l’installation de la librairie permet à python de ce connecter sur une base postgres ce fait par un apt-get

apt-get install ptyhon-psycopg2

Exemple d’utilisation

import psycopg2
conn=psycopg2.connect("host='localhost' dbname='estelle' user='postgres' password='postgres'")
cursor=conn.cursor()
cursor.execute('TRUNCATE TABLE DATA')
cursor.execute('commit')
cursor.close()

le programme d’extraction

from optparse import OptionParser
import sys
import os, os.path
#import syslog
import pymssql
import csv


def extract(sqlI, hostI, dataI, userI, passwordI, outI, header = False):
    """
    connect and run sql
    """
    conn = pymssql.connect(host=hostI, user=userI, password=passwordI, database=dataI)
    cur = conn.cursor()
    cur.execute(sqlI)
    c = csv.writer(open(outI, "wb"), delimiter=';',quotechar='"', quoting=csv.QUOTE_NONNUMERIC)
    if header:
        h = []
        for i in cur.description:
            h.append(str(i[0]))
        c.writerow(h)

    row = cur.fetchone()
    while row:
        c.writerow(row)
        row = cur.fetchone()
    conn.close()

def getContentFile(p):
    """
    return content of file
    """
    fichier = open(p,'r')
    txt=''
    for i in fichier.readlines():
        txt = txt + i
    fichier.close()
    return txt

def getPathFile(p):
    """
    Test of p is file with pwd and PYTHONPATH
    """
    if os.path.isfile(os.path.join(os.getcwd(),p)):
        return os.path.join(os.getcwd(),p)
    for i in sys.path:
        if os.path.isfile(os.path.join(i,p)):
            return os.path.join(i,p)
    raise AttributeError, '%s is not a file' % p

def getPathDir(p):
    """
    Test of p is file with pwd and PYTHONPATH
    """
    if os.path.isdir(os.path.join(os.getcwd(),p)):
        return os.path.join(os.getcwd(),p)
    for i in sys.path:
        if os.path.isfile(os.path.join(i,p)):
            return os.path.join(i,p)
    raise AttributeError, '%s is not directory' % p

if __name__ == '__main__':
    parser = OptionParser(version="%prog 0.3")
    parser.description= "extract information from MSSQL Server"
    parser.epilog = "by Frederic Aoustin"
    parser.add_option("-q", "--query",
        dest="sql",
        help ="path of sql",
        type="string")
    parser.add_option("-s", "--host",
        dest="host",
        help ="host server mssql",
        type="string")
    parser.add_option("-d", "--database",
        dest="data",
        help ="database",
        type="string")
    parser.add_option("-u", "--user",
        dest="user",
        help ="user",
        type="string")
    parser.add_option("-p", "--password",
        dest="password",
        help ="password",
        type="string")
    parser.add_option("-o", "--out",
        dest="out",
        help ="out file",
        type="string")
    parser.add_option("-t",  "--title",
        action="store_true",
        dest="bol",
        default=False,
        help ="add in csv a header (default False)")

    (options, args) = parser.parse_args()
    try:
        sql = getPathFile(options.sql)
        host = options.host
        data = options.data
        user = options.user
        password = options.password
        out = options.out
        bol = options.bol
        extract(getContentFile(sql), host, data, user, password, os.path.join(os.getcwd(),out),bol)
    except Exception, e:
        print parser.error(e)