"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Acceda a la base de datos IRIS con ODBC o JDBC usando Python

Acceda a la base de datos IRIS con ODBC o JDBC usando Python

Publicado el 2024-11-15
Navegar:665

Access IRIS database with ODBC or JDBC using Python

Problemas con cuerdas

Estoy accediendo a bases de datos IRIS con JDBC (u ODBC) usando Python. Quiero recuperar los datos en un marco de datos pandas para manipular los datos y crear gráficos a partir de ellos. Me encontré con un problema con el manejo de cadenas mientras usaba JDBC. Esta publicación es para ayudar si alguien más tiene los mismos problemas. O, si hay una manera más fácil de resolver esto, ¡házmelo saber en los comentarios!

Estoy usando OSX, por lo que no estoy seguro de cuán exclusivo es mi problema. Estoy usando Jupyter Notebooks, aunque el código generalmente sería el mismo si usaras cualquier otro programa o marco de Python.

El problema del JDBC

Cuando obtengo datos de la base de datos, las descripciones de columnas y cualquier dato de cadena se devuelven como tipo de datos java.lang.String. Si imprime datos de cadena, se verá así: "(p,a,i,n,i,n,t,h,e,r,e,a,r)" en lugar del esperado "painintherear".

Esto probablemente se debe a que las cadenas de caracteres del tipo de datos java.lang.String aparecen como un iterable o una matriz cuando se recuperan usando JDBC. Esto puede suceder si el puente Python-Java que está utilizando (por ejemplo, JayDeBeApi, JDBC) no convierte automáticamente java.lang.String en una cadena Python en un solo paso.

La representación de cadena str de Python, por el contrario, tiene la cadena completa como una sola unidad. Cuando Python recupera una cadena normal (por ejemplo, a través de ODBC), no se divide en caracteres individuales.

La solución JDBC

Para solucionar este problema, debe asegurarse de que java.lang.String se convierta correctamente al tipo str de Python. Puede manejar explícitamente esta conversión al procesar los datos obtenidos para que no se interpreten como un iterable o una lista de caracteres.

Hay muchas maneras de realizar esta manipulación de cadenas; esto es lo que hice.

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

El problema de ODBC

Cuando se utiliza una conexión ODBC, las cadenas no se devuelven o son NA.

Si se está conectando a una base de datos que contiene datos Unicode (por ejemplo, nombres en diferentes idiomas) o si su aplicación necesita almacenar o recuperar caracteres que no sean ASCII, debe asegurarse de que los datos permanezcan correctamente codificados cuando se pasan entre las bases de datos. base de datos y su aplicación Python.

La solución ODBC

Este código garantiza que los datos de cadena se codifiquen y decodifiquen utilizando UTF-8 al enviar y recuperar datos a la base de datos. Es especialmente importante cuando se trata de caracteres que no son ASCII o para garantizar la compatibilidad con datos 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

conexión.setdecoding(pyodbc.SQL_CHAR, codificación="utf8")

Le indica a pyodbc cómo decodificar datos de caracteres de la base de datos al recuperar tipos SQL_CHAR (normalmente, campos de caracteres de longitud fija).

conexión.setdecoding(pyodbc.SQL_WCHAR, codificación="utf8")

Establece la decodificación para SQL_WCHAR, tipos de caracteres anchos (es decir, cadenas Unicode, como NVARCHAR o NCHAR en SQL Server).

conexión.setencoding(codificación="utf8")

Garantiza que cualquier cadena o dato de caracteres enviado desde Python a la base de datos se codificará utilizando UTF-8,
lo que significa que Python traducirá su tipo str interno (que es Unicode) a bytes UTF-8 cuando se comunique con la base de datos.


Poniéndolo todo junto

Instalar JDBC

Instalar JAVA - usar dmg

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

Actualizar shell para configurar la versión predeterminada

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

Agrega JAVA_HOME a tu ruta

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

Obtener el controlador JDBC

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

Pon el archivo jar en algún lugar... Yo lo puse en $HOME

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

Código de muestra

Se supone que has configurado ODBC (un ejemplo para otro día, el perro se comió mis notas...).

Nota: este es un truco de mi código real. Tenga en cuenta los nombres de las 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()
Declaración de liberación Este artículo se reproduce en: https://dev.to/intersystems/access-iris-database-with-odbc-or-jdbc-using-python-54ok?1 Si hay alguna infracción, comuníquese con [email protected] para borrarlo
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3