Maison >développement back-end >Tutoriel Python >Accédez à la base de données IRIS avec ODBC ou JDBC à l'aide de Python

Accédez à la base de données IRIS avec ODBC ou JDBC à l'aide de Python

Linda Hamilton
Linda Hamiltonoriginal
2024-09-29 06:14:02316parcourir

Access IRIS database with ODBC or JDBC using Python

Problèmes avec les chaînes

J'accède aux bases de données IRIS avec JDBC (ou ODBC) en utilisant Python. Je souhaite récupérer les données dans un dataframe pandas pour manipuler les données et créer des graphiques à partir de celles-ci. J'ai rencontré un problème avec la gestion des chaînes lors de l'utilisation de JDBC. Cet article est destiné à aider si quelqu'un d'autre a les mêmes problèmes. Ou, s'il existe un moyen plus simple de résoudre ce problème, faites-le-moi savoir dans les commentaires !

J'utilise OSX, donc je ne sais pas à quel point mon problème est unique. J'utilise Jupyter Notebooks, bien que le code soit généralement le même si vous utilisiez un autre programme ou framework Python.

Le problème JDBC

Lorsque je récupère des données de la base de données, les descriptions de colonnes et toutes les données de chaîne sont renvoyées en tant que type de données java.lang.String. Si vous imprimez des données de chaîne, cela ressemblera à : "(p,a,i,n,i,n,t,h,e,r,e,a,r)" au lieu du "painintherear" attendu.

Cela est probablement dû au fait que les chaînes de caractères du type de données java.lang.String apparaissent sous forme d'itérable ou de tableau lorsqu'elles sont récupérées à l'aide de JDBC. Cela peut se produire si le pont Python-Java que vous utilisez (par exemple, JayDeBeApi, JDBC) ne convertit pas automatiquement java.lang.String en chaîne Python en une seule étape.

La représentation de chaîne str de Python, en revanche, a la chaîne entière comme une seule unité. Lorsque Python récupère une chaîne normale (par exemple via ODBC), elle ne se divise pas en caractères individuels.

La solution JDBC

Pour résoudre ce problème, vous devez vous assurer que le java.lang.String est correctement converti en type str de Python. Vous pouvez gérer explicitement cette conversion lors du traitement des données récupérées afin qu'elles ne soient pas interprétées comme un itérable ou une liste de caractères.

Il existe de nombreuses façons de procéder à cette manipulation de chaînes ; c'est ce que j'ai fait.

import pandas as pd

import pyodbc

import jaydebeapi
import jpype

def my_function(jdbc_used)

    # Some other code to create the connection goes here

    cursor.execute(query_string)

    if jdbc_used:
        # Fetch the results, convert java.lang.String in the data to Python str
        # (java.lang.String is returned "(p,a,i,n,i,n,t,h,e,r,e,a,r)" Convert to str type "painintherear"
        results = []
        for row in cursor.fetchall():
            converted_row = [str(item) if isinstance(item, jpype.java.lang.String) else item for item in row]
            results.append(converted_row)

        # Get the column names and ensure they are Python strings 
        column_names = [str(col[0]) for col in cursor.description]

        # Create the dataframe
        df = pd.DataFrame.from_records(results, columns=column_names)

        # Check the results
        print(df.head().to_string())

    else:  
        # I was also testing ODBC
        # For very large result sets get results in chunks using cursor.fetchmany(). or fetchall()
        results = cursor.fetchall()
        # Get the column names
        column_names = [column[0] for column in cursor.description]
        # Create the dataframe
        df = pd.DataFrame.from_records(results, columns=column_names)

    # Do stuff with your dataframe

Le problème ODBC

Lors de l'utilisation d'une connexion ODBC, les chaînes ne sont pas renvoyées ou sont NA.

Si vous vous connectez à une base de données contenant des données Unicode (par exemple, des noms dans différentes langues) ou si votre application doit stocker ou récupérer des caractères non-ASCII, vous devez vous assurer que les données restent correctement codées lorsqu'elles sont transmises entre les base de données et votre application Python.

La solutionODBC

Ce code garantit que les données de chaîne sont codées et décodées à l'aide de UTF-8 lors de l'envoi et de la récupération de données vers la base de données. C'est particulièrement important lorsqu'il s'agit de caractères non-ASCII ou pour assurer la compatibilité avec les données Unicode.

def create_connection(connection_string, password):
    connection = None

    try:
        # print(f"Connecting to {connection_string}")
        connection = pyodbc.connect(connection_string + ";PWD=" + password)

        # Ensure strings are read correctly
        connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8")
        connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")
        connection.setencoding(encoding="utf8")

    except pyodbc.Error as e:
        print(f"The error '{e}' occurred")

    return connection

connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8")

Indique à pyodbc comment décoder les données de caractères de la base de données lors de la récupération des types SQL_CHAR (généralement des champs de caractères de longueur fixe).

connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")

Définit le décodage pour SQL_WCHAR, les types de caractères larges (c'est-à-dire les chaînes Unicode, telles que NVARCHAR ou NCHAR dans SQL Server).

connection.setencoding(encoding="utf8")

Garantit que toutes les chaînes ou données de caractères envoyées depuis Python à la base de données seront codées en UTF-8,
ce qui signifie que Python traduira son type str interne (qui est Unicode) en octets UTF-8 lors de la communication avec la base de données.


Mettre tout cela ensemble

Installer JDBC

Installez JAVA - utilisez dmg

https://www.oracle.com/middleeast/java/technologies/downloads/#jdk23-mac

Mettre à jour le shell pour définir la version par défaut

$ /usr/libexec/java_home -V
Matching Java Virtual Machines (2):
    23 (arm64) "Oracle Corporation" - "Java SE 23" /Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home
    1.8.421.09 (arm64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home
$ echo $SHELL
/opt/homebrew/bin/bash
$ vi ~/.bash_profile

Ajoutez JAVA_HOME à votre chemin

export JAVA_HOME=$(/usr/libexec/java_home -v 23)
export PATH=$JAVA_HOME/bin:$PATH

Obtenez le pilote JDBC

https://intersystems-community.github.io/iris-driver-distribution/

Mettez le fichier jar quelque part... Je l'ai mis dans $HOME

$ ls $HOME/*.jar
/Users/myname/intersystems-jdbc-3.8.4.jar

Exemple de code

Cela suppose que vous avez mis en place ODBC (un exemple pour un autre jour, le chien a mangé mes notes...).

Remarque : ceci est un hack de mon vrai code. Notez les noms des variables.

import os

import datetime
from datetime import date, time, datetime, timedelta

import pandas as pd
import pyodbc

import jaydebeapi
import jpype

def jdbc_create_connection(jdbc_url, jdbc_username, jdbc_password):

    # Path to JDBC driver
    jdbc_driver_path = '/Users/yourname/intersystems-jdbc-3.8.4.jar'

    # Ensure JAVA_HOME is set
    os.environ['JAVA_HOME']='/Library/Java/JavaVirtualMachines/jdk-23.jdk/Contents/Home'
    os.environ['CLASSPATH'] = jdbc_driver_path

    # Start the JVM (if not already running)
    if not jpype.isJVMStarted():
        jpype.startJVM(jpype.getDefaultJVMPath(), classpath=[jdbc_driver_path])

    # Connect to the database
    connection = None

    try:
        connection = jaydebeapi.connect("com.intersystems.jdbc.IRISDriver",
                                  jdbc_url,
                                  [jdbc_username, jdbc_password],
                                  jdbc_driver_path)
        print("Connection successful")
    except Exception as e:
        print(f"An error occurred: {e}")

    return connection


def odbc_create_connection(connection_string):
    connection = None

    try:
        # print(f"Connecting to {connection_string}")
        connection = pyodbc.connect(connection_string)

        # Ensure strings are read correctly
        connection.setdecoding(pyodbc.SQL_CHAR, encoding="utf8")
        connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")
        connection.setencoding(encoding="utf8")

    except pyodbc.Error as e:
        print(f"The error '{e}' occurred")

    return connection

# Parameters

odbc_driver = "InterSystems ODBC"
odbc_host = "your_host"
odbc_port = "51773"
odbc_namespace = "your_namespace"
odbc_username = "username"
odbc_password = "password"

jdbc_host = "your_host"
jdbc_port = "51773"
jdbc_namespace = "your_namespace"
jdbc_username = "username"
jdbc_password = "password"

# Create connection and create charts

jdbc_used = True

if jdbc_used:
    print("Using JDBC")
    jdbc_url = f"jdbc:IRIS://{jdbc_host}:{jdbc_port}/{jdbc_namespace}?useUnicode=true&characterEncoding=UTF-8"
    connection = jdbc_create_connection(jdbc_url, jdbc_username, jdbc_password)
else:
    print("Using ODBC")
    connection_string = f"Driver={odbc_driver};Host={odbc_host};Port={odbc_port};Database={odbc_namespace};UID={odbc_username};PWD={odbc_password}"
    connection = odbc_create_connection(connection_string)


if connection is None:
    print("Unable to connect to IRIS")
    exit()

cursor = connection.cursor()

site = "SAMPLE"
table_name = "your.TableNAME"

desired_columns = [
    "RunDate",
    "ActiveUsersCount",
    "EpisodeCountEmergency",
    "EpisodeCountInpatient",
    "EpisodeCountOutpatient",
    "EpisodeCountTotal",
    "AppointmentCount",
    "PrintCountTotal",
    "site",
]

# Construct the column selection part of the query
column_selection = ", ".join(desired_columns)

query_string = f"SELECT {column_selection} FROM {table_name} WHERE Site = '{site}'"

print(query_string)
cursor.execute(query_string)

if jdbc_used:
    # Fetch the results
    results = []
    for row in cursor.fetchall():
        converted_row = [str(item) if isinstance(item, jpype.java.lang.String) else item for item in row]
        results.append(converted_row)

    # Get the column names and ensure they are Python strings (java.lang.String is returned "(p,a,i,n,i,n,t,h,e,a,r,s,e)"
    column_names = [str(col[0]) for col in cursor.description]

    # Create the dataframe
    df = pd.DataFrame.from_records(results, columns=column_names)
    print(df.head().to_string())
else:
    # For very large result sets get results in chunks using cursor.fetchmany(). or fetchall()
    results = cursor.fetchall()
    # Get the column names
    column_names = [column[0] for column in cursor.description]
    # Create the dataframe
    df = pd.DataFrame.from_records(results, columns=column_names)

    print(df.head().to_string())

# # Build charts for a site
# cf.build_7_day_rolling_average_chart(site, cursor, jdbc_used)

cursor.close()
connection.close()

# Shutdown the JVM (if you started it)
# jpype.shutdownJVM()

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn