ホームページ >バックエンド開発 >Python チュートリアル >Python を使用した ODBC または JDBC による IRIS データベースへのアクセス

Python を使用した ODBC または JDBC による IRIS データベースへのアクセス

Linda Hamilton
Linda Hamiltonオリジナル
2024-09-29 06:14:02316ブラウズ

Access IRIS database with ODBC or JDBC using Python

文字列の問題

Python を使用して JDBC (または ODBC) で IRIS データベースにアクセスしています。 データを pandas データフレームにフェッチして、データを操作し、そこからグラフを作成したいと考えています。 JDBC の使用中に文字列処理で問題が発生しました。この投稿は、他の誰かが同じ問題を抱えている場合に役立つものです。 または、これを解決するより簡単な方法がある場合は、コメントで知らせてください!

私は OSX を使用しているので、私の問題がどれほど特殊なものであるかわかりません。私は Jupyter Notebook を使用していますが、他の Python プログラムまたはフレームワークを使用した場合でも、コードは通常同じです。

JDBCの問題

データベースからデータをフェッチすると、列の説明任意の文字列データ がデータ型 java.lang.String として返されます。文字列データ data を出力すると、予想される "paintherear" ではなく "(p,a,i,n,i,n,t,h,e,r,e,a,r)" のようになります。

これはおそらく、JDBC を使用してフェッチされるときに、データ型 java.lang.String の文字列が反復可能または配列として渡されるためです。 これは、使用している Python-Java ブリッジ (JayDeBeApi、JDBC など) が単一ステップで java.lang.String を Python str に自動的に変換しない場合に発生する可能性があります。

これとは対照的に、Python の str 文字列表現では、文字列全体が 1 つの単位として扱われます。 Python が通常の str を (ODBC 経由などで) 取得する場合、個々の文字に分割されません。

JDBC ソリューション

この問題を解決するには、java.lang.String が Python の str 型に正しく変換されていることを確認する必要があります。 フェッチされたデータを処理するときにこの変換を明示的に処理できるため、データは反復可能または文字のリストとして解釈されません。

この文字列操作を行う方法はたくさんあります。これが私がやったことです。

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

ODBC の問題

ODBC 接続を使用すると、文字列が返されないか、NA になります。

Unicode データ (さまざまな言語の名前など) を含むデータベースに接続している場合、またはアプリケーションで非 ASCII 文字を保存または取得する必要がある場合は、データがデータ間で受け渡されるときに正しくエンコードされたままであることを確認する必要があります。データベースと Python アプリケーション。

ODBC ソリューション

このコードは、データベースへのデータの送信および取得時に、文字列データが UTF-8 を使用してエンコードおよびデコードされることを保証します。 これは、非 ASCII 文字を扱う場合、または 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")

SQL_CHAR 型 (通常は固定長の文字フィールド) をフェッチするときに、データベースから文字データをデコードする方法を pyodbc に指示します。

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

SQL_WCHAR、ワイド文字型 (つまり、SQL Server の NVARCHAR や NCHAR などの Unicode 文字列) のデコードを設定します。

connection.setencoding(encoding="utf8")

Python からデータベースに送信される文字列または文字データが UTF-8 を使用してエンコードされるようにします。
つまり、Python はデータベースと通信するときに、その内部 str 型 (Unicode) を UTF-8 バイトに変換します。


すべてをまとめると

JDBCのインストール

JAVA をインストール - dmg を使用します

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

シェルを更新してデフォルトのバージョンを設定します

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

JAVA_HOME をパスに追加します

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

JDBC ドライバーを入手します

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

jar ファイルをどこかに置きます...私は $HOME に置きました

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

サンプルコード

ODBC が設定されていることを前提としています (別の日の例として、犬が私のメモを食べてしまいました...)。

注: これは私の実際のコードのハックです。変数名に注意してください。

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()

以上がPython を使用した ODBC または JDBC による IRIS データベースへのアクセスの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。