أقوم بالوصول إلى قواعد بيانات IRIS باستخدام JDBC (أو ODBC) باستخدام Python. أرغب في جلب البيانات إلى إطار بيانات pandas لمعالجة البيانات وإنشاء مخططات منها. لقد واجهت مشكلة في التعامل مع السلسلة أثناء استخدام JDBC. هذه المشاركة هي للمساعدة إذا كان أي شخص آخر لديه نفس المشاكل. أو إذا كانت هناك طريقة أسهل لحل هذه المشكلة، فأخبرني بها في التعليقات!
أنا أستخدم OSX، لذلك لست متأكدًا من مدى تميز مشكلتي. أنا أستخدم Jupyter Notebooks، على الرغم من أن الكود سيكون هو نفسه بشكل عام إذا كنت تستخدم أي برنامج أو إطار عمل آخر لـ Python.
عندما أقوم بإحضار البيانات من قاعدة البيانات، يتم إرجاع أوصاف الأعمدة وأي بيانات سلسلة كنوع بيانات java.lang.String. إذا قمت بطباعة بيانات سلسلة البيانات فستبدو كما يلي: "(p,a,i,n,i,n,t,h,e,r,e,a,r)" بدلاً من "painintherear" المتوقع.
ربما يرجع هذا إلى أن سلاسل الأحرف من نوع البيانات java.lang.String تأتي كمصفوفة أو قابلة للتكرار عند جلبها باستخدام JDBC. يمكن أن يحدث هذا إذا كان جسر Python-Java الذي تستخدمه (على سبيل المثال، JayDeBeApi، JDBC) لا يقوم تلقائيًا بتحويل java.lang.String إلى سلسلة Python في خطوة واحدة.
في المقابل، تمثيل سلسلة str في بايثون يحتوي على السلسلة بأكملها كوحدة واحدة. عندما تسترد بايثون سلسلة عادية (على سبيل المثال عبر ODBC)، فإنها لا تنقسم إلى أحرف فردية.
لإصلاح هذه المشكلة، يجب عليك التأكد من تحويل java.lang.String بشكل صحيح إلى نوع str الخاص بـ Python. يمكنك التعامل بشكل صريح مع هذا التحويل عند معالجة البيانات التي تم جلبها بحيث لا يتم تفسيرها على أنها قابلة للتكرار أو قائمة من الأحرف.
هناك العديد من الطرق للقيام بهذه المعالجة للسلسلة؛ وهذا ما فعلته.
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، لا يتم إرجاع السلاسل أو تكون غير متوفرة.
إذا كنت تتصل بقاعدة بيانات تحتوي على بيانات Unicode (على سبيل المثال، أسماء بلغات مختلفة) أو إذا كان تطبيقك يحتاج إلى تخزين أو استرداد أحرف غير ASCII، فيجب عليك التأكد من بقاء البيانات مشفرة بشكل صحيح عند تمريرها بين قاعدة البيانات وتطبيق بايثون الخاص بك.
يضمن هذا الرمز تشفير بيانات السلسلة وفك تشفيرها باستخدام 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")
يخبر pyodbc كيفية فك تشفير بيانات الأحرف من قاعدة البيانات عند جلب أنواع SQL_CHAR (عادةً، حقول الأحرف ذات الطول الثابت).
connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")
تعيين فك التشفير لـ SQL_WCHAR، وأنواع الأحرف العريضة (أي سلاسل Unicode، مثل NVARCHAR أو NCHAR في SQL Server).
connection.setencoding(encoding="utf8")
يضمن أن أي سلاسل أو بيانات أحرف مرسلة من Python إلى قاعدة البيانات سيتم تشفيرها باستخدام UTF-8،
وهذا يعني أن بايثون ستترجم نوع str الداخلي الخاص بها (وهو Unicode) إلى بايت UTF-8 عند الاتصال بقاعدة البيانات.
تثبيت جافا - استخدم 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/
ضع ملف الجرة في مكان ما... لقد وضعته في $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