J'accède aux bases de données IRIS avec JDBC (ou ODBC) en utilisant Python. Je souhaite récupérer les données dans un dataframe pandas pour manipuler les données et créer des graphiques à partir de celles-ci. J'ai rencontré un problème avec la gestion des chaînes lors de l'utilisation de JDBC. Cet article est destiné à aider si quelqu'un d'autre a les mêmes problèmes. Ou, s'il existe un moyen plus simple de résoudre ce problème, faites-le-moi savoir dans les commentaires !
J'utilise OSX, donc je ne sais pas à quel point mon problème est unique. J'utilise Jupyter Notebooks, bien que le code soit généralement le même si vous utilisiez un autre programme ou framework Python.
Lorsque je récupère des données de la base de données, les descriptions de colonnes et toutes les données de chaîne sont renvoyées en tant que type de données java.lang.String. Si vous imprimez des données de chaîne, cela ressemblera à : "(p,a,i,n,i,n,t,h,e,r,e,a,r)" au lieu du "painintherear" attendu.
Cela est probablement dû au fait que les chaînes de caractères du type de données java.lang.String apparaissent sous forme d'itérable ou de tableau lorsqu'elles sont récupérées à l'aide de JDBC. Cela peut se produire si le pont Python-Java que vous utilisez (par exemple, JayDeBeApi, JDBC) ne convertit pas automatiquement java.lang.String en chaîne Python en une seule étape.
La représentation sous forme de chaîne str de Python, en revanche, présente la chaîne entière comme une seule unité. Lorsque Python récupère une chaîne normale (par exemple via ODBC), elle ne se divise pas en caractères individuels.
Pour résoudre ce problème, vous devez vous assurer que le java.lang.String est correctement converti en type str de Python. Vous pouvez gérer explicitement cette conversion lors du traitement des données récupérées afin qu'elles ne soient pas interprétées comme un itérable ou une liste de caractères.
Il existe de nombreuses façons d'effectuer cette manipulation de chaîne ; c'est ce que j'ai fait.
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
Lors de l'utilisation d'une connexion ODBC, les chaînes ne sont pas renvoyées ou sont NA.
Si vous vous connectez à une base de données contenant des données Unicode (par exemple, des noms dans différentes langues) ou si votre application doit stocker ou récupérer des caractères non-ASCII, vous devez vous assurer que les données restent correctement codées lorsqu'elles sont transmises entre le base de données et votre application Python.
Ce code garantit que les données de chaîne sont codées et décodées à l'aide de UTF-8 lors de l'envoi et de la récupération de données vers la base de données. C'est particulièrement important lorsqu'il s'agit de caractères non-ASCII ou pour garantir la compatibilité avec les données 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")
Indique à pyodbc comment décoder les données de caractères de la base de données lors de la récupération des types SQL_CHAR (généralement des champs de caractères de longueur fixe).
connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")
Définit le décodage pour SQL_WCHAR, les types de caractères larges (c'est-à-dire les chaînes Unicode, telles que NVARCHAR ou NCHAR dans SQL Server).
connexion.setencoding(encoding="utf8")
Garantit que toutes les chaînes ou données de caractères envoyées depuis Python à la base de données seront codées en UTF-8,
ce qui signifie que Python traduira son type str interne (qui est Unicode) en octets UTF-8 lors de la communication avec la base de données.
Installer JAVA - utiliser dmg
https://www.oracle.com/middleeast/java/technologies/downloads/#jdk23-mac
Mettre à jour le shell pour définir la version par défaut
$ /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
Ajoutez JAVA_HOME à votre chemin
export JAVA_HOME=$(/usr/libexec/java_home -v 23) export PATH=$JAVA_HOME/bin:$PATH
Obtenir le pilote JDBC
https://intersystems-community.github.io/iris-driver-distribution/
Placez le fichier jar quelque part... Je l'ai mis dans $HOME
$ ls $HOME/*.jar /Users/myname/intersystems-jdbc-3.8.4.jar
Cela suppose que vous avez configuré ODBC (un exemple pour un autre jour, le chien a mangé mes notes...).
Remarque : ceci est un hack de mon vrai code. Notez les noms des 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()
Clause de non-responsabilité: Toutes les ressources fournies proviennent en partie d'Internet. En cas de violation de vos droits d'auteur ou d'autres droits et intérêts, veuillez expliquer les raisons détaillées et fournir une preuve du droit d'auteur ou des droits et intérêts, puis l'envoyer à l'adresse e-mail : [email protected]. Nous nous en occuperons pour vous dans les plus brefs délais.
Copyright© 2022 湘ICP备2022001581号-3