Saya sedang mengakses pangkalan data IRIS dengan JDBC (atau ODBC) menggunakan Python. Saya mahu mengambil data ke dalam bingkai data panda untuk memanipulasi data dan membuat carta daripadanya. Saya menghadapi masalah dengan pengendalian rentetan semasa menggunakan JDBC. Siaran ini adalah untuk membantu jika orang lain mempunyai masalah yang sama. Atau, jika ada cara yang lebih mudah untuk menyelesaikan masalah ini, beritahu saya dalam ulasan!
Saya menggunakan OSX, jadi saya tidak pasti betapa uniknya masalah saya. Saya menggunakan Buku Nota Jupyter, walaupun kodnya secara amnya adalah sama jika anda menggunakan mana-mana program atau rangka kerja Python yang lain.
Apabila saya mengambil data daripada pangkalan data, penerangan lajur dan sebarang data rentetan dikembalikan sebagai jenis data java.lang.String. Jika anda mencetak data data rentetan ia akan kelihatan seperti: "(p,a,i,n,i,n,t,h,e,r,e,a,r)" bukannya "painintheraar" yang dijangkakan.
Ini mungkin kerana rentetan aksara jenis data java.lang.String datang melalui sebagai lelaran atau tatasusunan apabila diambil menggunakan JDBC. Ini boleh berlaku jika jambatan Python-Java yang anda gunakan (cth., JayDeBeApi, JDBC) tidak secara automatik menukar java.lang.String kepada Python str dalam satu langkah.
Perwakilan rentetan str Python, sebaliknya, mempunyai keseluruhan rentetan sebagai satu unit. Apabila Python mendapatkan semula str biasa (cth. melalui ODBC), ia tidak berpecah kepada aksara individu.
Untuk menyelesaikan isu ini, anda mesti memastikan bahawa java.lang.String ditukar dengan betul kepada jenis str Python. Anda boleh mengendalikan penukaran ini secara eksplisit apabila memproses data yang diambil supaya ia tidak ditafsirkan sebagai senarai aksara atau aksara yang boleh diubah.
Terdapat banyak cara untuk melakukan manipulasi rentetan ini; inilah yang saya lakukan.
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
Apabila menggunakan sambungan ODBC, rentetan tidak dikembalikan atau NA.
Jika anda menyambung ke pangkalan data yang mengandungi data Unikod (cth., nama dalam bahasa berbeza) atau jika aplikasi anda perlu menyimpan atau mendapatkan semula aksara bukan ASCII, anda mesti memastikan bahawa data kekal dikodkan dengan betul apabila dihantar antara pangkalan data dan aplikasi Python anda.
Kod ini memastikan data rentetan dikodkan dan dinyahkod menggunakan UTF-8 apabila menghantar dan mendapatkan semula data ke pangkalan data. Ia amat penting apabila berurusan dengan aksara bukan ASCII atau memastikan keserasian dengan data 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")
Memberitahu pyodbc cara menyahkod data aksara daripada pangkalan data apabila mengambil jenis SQL_CHAR (biasanya, medan aksara panjang tetap).
connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")
Menetapkan penyahkodan untuk SQL_WCHAR, jenis aksara lebar (iaitu, rentetan Unicode, seperti NVARCHAR atau NCHAR dalam SQL Server).
connection.setencoding(encoding="utf8")
Memastikan bahawa sebarang rentetan atau data aksara yang dihantar daripada Python ke pangkalan data akan dikodkan menggunakan UTF-8,
bermakna Python akan menterjemahkan jenis str dalamannya (iaitu Unicode) ke dalam UTF-8 bait apabila berkomunikasi dengan pangkalan data.
Pasang JAVA - gunakan dmg
https://www.oracle.com/middleeast/java/technologies/downloads/#jdk23-mac
Kemas kini shell untuk menetapkan versi lalai
$ /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
Tambah JAVA_HOME pada laluan anda
export JAVA_HOME=$(/usr/libexec/java_home -v 23) export PATH=$JAVA_HOME/bin:$PATH
Dapatkan pemandu JDBC
https://intersystems-community.github.io/iris-driver-distribution/
Letakkan fail balang di suatu tempat... Saya letakkan di $HOME
$ ls $HOME/*.jar /Users/myname/intersystems-jdbc-3.8.4.jar
Ia menganggap anda telah menyediakan ODBC (contoh untuk hari lain, anjing itu makan nota saya...).
Nota: ini adalah godam kod sebenar saya. Perhatikan nama pembolehubah.
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()
Atas ialah kandungan terperinci Akses pangkalan data IRIS dengan ODBC atau JDBC menggunakan Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!