Python을 사용하여 JDBC(또는 ODBC)로 IRIS 데이터베이스에 액세스하고 있습니다. 데이터를 pandas 데이터 프레임으로 가져와서 데이터를 조작하고 차트를 만들고 싶습니다. JDBC를 사용하는 동안 문자열 처리에 문제가 발생했습니다. 이 게시물은 다른 사람이 동일한 문제를 겪고 있는 경우 도움을 주기 위한 것입니다. 아니면 더 쉽게 해결할 수 있는 방법이 있다면 댓글로 알려주세요!
저는 OSX를 사용하고 있어서 제 문제가 얼마나 독특한지 잘 모르겠습니다. 저는 Jupyter Notebook을 사용하고 있습니다. 하지만 다른 Python 프로그램이나 프레임워크를 사용했다면 코드는 일반적으로 동일합니다.
데이터베이스에서 데이터를 가져올 때 열 설명 및 모든 문자열 데이터가 java.lang.String 데이터 유형으로 반환됩니다. 문자열 데이터 데이터를 인쇄하면 예상되는 "painintheear" 대신 "(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 문자열 표현은 전체 문자열을 단일 단위로 갖습니다. Python이 일반 문자열(예: 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입니다.
유니코드 데이터(예: 다른 언어로 된 이름)가 포함된 데이터베이스에 연결하는 경우 또는 애플리케이션이 ASCII가 아닌 문자를 저장하거나 검색해야 하는 경우 데이터가 다음 사이에 전달될 때 올바르게 인코딩된 상태로 유지되는지 확인해야 합니다. 데이터베이스와 Python 애플리케이션.
이 코드는 데이터베이스에 데이터를 보내고 검색할 때 문자열 데이터가 UTF-8을 사용하여 인코딩 및 디코딩되도록 합니다. ASCII가 아닌 문자를 처리하거나 유니코드 데이터와의 호환성을 보장할 때 특히 중요합니다.
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, 인코딩="utf8")
SQL_CHAR 유형(일반적으로 고정 길이 문자 필드)을 가져올 때 데이터베이스에서 문자 데이터를 디코딩하는 방법을 pyodbc에 알려줍니다.
connection.setdecoding(pyodbc.SQL_WCHAR, 인코딩="utf8")
SQL_WCHAR, 와이드 문자 유형(예: SQL Server의 NVARCHAR 또는 NCHAR과 같은 유니코드 문자열)에 대한 디코딩을 설정합니다.
connection.setencoding(encoding="utf8")
Python에서 데이터베이스로 전송된 모든 문자열이나 문자 데이터가 UTF-8을 사용하여 인코딩되도록 보장합니다.
즉, Python은 데이터베이스와 통신할 때 내부 str 유형(유니코드)을 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()
부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3