Python を使用して JDBC (または ODBC) で IRIS データベースにアクセスしています。 データを pandas データフレームにフェッチして、データを操作し、そこからグラフを作成したいと考えています。 JDBC の使用中に文字列処理で問題が発生しました。この投稿は、他の誰かが同じ問題を抱えている場合に役立つものです。 または、これを解決するより簡単な方法がある場合は、コメントで知らせてください!
私は OSX を使用しているので、私の問題がどれほど特殊なものであるかわかりません。私は Jupyter Notebook を使用していますが、他の Python プログラムまたはフレームワークを使用した場合でも、コードは通常同じです。
データベースからデータをフェッチすると、列の説明 と 任意の文字列データ がデータ型 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 経由などで) 取得する場合、個々の文字に分割されません。
この問題を解決するには、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 接続を使用すると、文字列が返されないか、NA になります。
Unicode データ (さまざまな言語の名前など) を含むデータベースに接続している場合、またはアプリケーションで非 ASCII 文字を保存または取得する必要がある場合は、データがデータ間で受け渡されるときに正しくエンコードされたままであることを確認する必要があります。データベースと Python アプリケーション。
このコードは、データベースへのデータの送信および取得時に、文字列データが 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 バイトに変換します。
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 サイトの他の関連記事を参照してください。