„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Greifen Sie mit Python über ODBC oder JDBC auf die IRIS-Datenbank zu

Greifen Sie mit Python über ODBC oder JDBC auf die IRIS-Datenbank zu

Veröffentlicht am 15.11.2024
Durchsuche:277

Access IRIS database with ODBC or JDBC using Python

Probleme mit Strings

Ich greife mit Python auf IRIS-Datenbanken mit JDBC (oder ODBC) zu. Ich möchte die Daten in einen pandas-Datenrahmen abrufen, um die Daten zu bearbeiten und daraus Diagramme zu erstellen. Bei der Verwendung von JDBC ist ein Problem mit der String-Verarbeitung aufgetreten. Dieser Beitrag soll helfen, falls jemand anderes die gleichen Probleme hat. Oder wenn es einen einfacheren Weg gibt, dieses Problem zu lösen, lassen Sie es mich in den Kommentaren wissen!

Ich verwende OSX und bin mir daher nicht sicher, wie einzigartig mein Problem ist. Ich verwende Jupyter Notebooks, obwohl der Code im Allgemeinen derselbe wäre, wenn Sie ein anderes Python-Programm oder Framework verwenden würden.

Das JDBC-Problem

Wenn ich Daten aus der Datenbank abrufe, werden die Spaltenbeschreibungen und alle Zeichenfolgendaten als Datentyp java.lang.String zurückgegeben. Wenn Sie Zeichenfolgendaten drucken, sieht das so aus: „(p,a,i,n,i,n,t,h,e,r,e,a,r)“ anstelle des erwarteten „painintherear“.

Dies liegt wahrscheinlich daran, dass Zeichenfolgen des Datentyps java.lang.String beim Abrufen mit JDBC als Iterable oder Array durchkommen. Dies kann passieren, wenn die von Ihnen verwendete Python-Java-Brücke (z. B. JayDeBeApi, JDBC) java.lang.String nicht automatisch in einem einzigen Schritt in einen Python-String konvertiert.

Pythons str-String-Darstellung hingegen hat den gesamten String als eine einzige Einheit. Wenn Python einen normalen String abruft (z. B. über ODBC), wird dieser nicht in einzelne Zeichen aufgeteilt.

Die JDBC-Lösung

Um dieses Problem zu beheben, müssen Sie sicherstellen, dass java.lang.String korrekt in den str-Typ von Python konvertiert wird. Sie können diese Konvertierung explizit bei der Verarbeitung der abgerufenen Daten verarbeiten, sodass sie nicht als iterierbare Datei oder Zeichenliste interpretiert wird.

Es gibt viele Möglichkeiten, diese Zeichenfolgenmanipulation durchzuführen; Das habe ich getan.

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

Das ODBC-Problem

Bei Verwendung einer ODBC-Verbindung werden Zeichenfolgen nicht zurückgegeben oder sind NA.

Wenn Sie eine Verbindung zu einer Datenbank herstellen, die Unicode-Daten (z. B. Namen in verschiedenen Sprachen) enthält, oder wenn Ihre Anwendung Nicht-ASCII-Zeichen speichern oder abrufen muss, müssen Sie sicherstellen, dass die Daten bei der Weitergabe zwischen den korrekt codiert bleiben Datenbank und Ihrer Python-Anwendung.

Die ODBC-Lösung

Dieser Code stellt sicher, dass Zeichenfolgendaten beim Senden und Abrufen von Daten an die Datenbank mithilfe von UTF-8 codiert und decodiert werden. Dies ist besonders wichtig, wenn Sie mit Nicht-ASCII-Zeichen arbeiten oder die Kompatibilität mit Unicode-Daten sicherstellen.

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, binding="utf8")

Teilt pyodbc mit, wie Zeichendaten aus der Datenbank dekodiert werden sollen, wenn SQL_CHAR-Typen abgerufen werden (normalerweise Zeichenfelder mit fester Länge).

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

Legt die Dekodierung für SQL_WCHAR-Breitzeichentypen fest (d. h. Unicode-Zeichenfolgen wie NVARCHAR oder NCHAR in SQL Server).

connection.setencoding(encoding="utf8")

Stellt sicher, dass alle von Python an die Datenbank gesendeten Zeichenfolgen oder Zeichendaten mit UTF-8 codiert werden,
Das bedeutet, dass Python seinen internen str-Typ (der Unicode ist) bei der Kommunikation mit der Datenbank in UTF-8-Bytes übersetzt.


Alles zusammenfügen

Installieren Sie JDBC

JAVA installieren – dmg verwenden

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

Shell aktualisieren, um die Standardversion festzulegen

$ /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

Fügen Sie JAVA_HOME zu Ihrem Pfad hinzu

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

Holen Sie sich den JDBC-Treiber

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

Legen Sie die JAR-Datei irgendwo ab... Ich habe sie in $HOME abgelegt

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

Beispielcode

Es wird davon ausgegangen, dass Sie ODBC eingerichtet haben (ein Beispiel für einen anderen Tag, der Hund hat meine Notizen gefressen...).

Hinweis: Dies ist ein Hack meines echten Codes. Beachten Sie die Variablennamen.

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()
Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/intersystems/access-iris-database-with-odbc-or-jdbc-using-python-54ok?1 Bei Verstößen wenden Sie sich bitte an [email protected] um es zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3