0 Followers · 1.1K Posts

SQL is a standard language for storing, manipulating and retrieving data in relational databases.

Question Scott Roth · Dec 6, 2024

First time trying to use Foreign Tables/Servers instead of Linked Tables...

Within the SQL Editor inside of the Managment Portal, or connecting through DBeaver JDBC how we can see what Foreign Servers have been defined? Is there a way to query and verify structure of the Foreign Server connection to know that we are building the correct Foreign Tables?

I attempted to create my first Foreign table but it failed when I tried to query the tables because it said the table could not be found. But when I sign into the Database via SQL Management Studio, I can see the table.

3
0 154
Question Steven Henry Suhendra · Dec 2, 2024

Hello My Friends,

I have a question how to use order by %DLIST, this is my code:

SELECT

$ListToString(%DLIST(DISTINCT MRDIA_ICDCode_DR->MRCID_Code),', ' ) ICDX,

$ListToString(%DLIST(DISTINCT (MRDIA_ICDCode_DR->MRCID_Desc || ' (' || MRDIA_DiagnosisType_DR->DTYP_Code || ')')),', ' ) Diagnose

FROM SQLUser.PA_Adm

LEFT JOIN SQLUser.PA_AdmInsurance ON (PAADM_RowID = INS_ParRef AND INS_Rank = 1)

LEFT JOIN SQLUser.PA_AdmPackage ON (PAADM_RowID = PACK_ParRef)

LEFT JOIN SQLUser.MR_Adm on MRADM_ADM_DR = PAADM_RowID

LEFT JOIN SQLUser.MR_Diagnos ON MRADM_RowId = MRDIA_MRADM_ParRef

0
0 127
Question Patrik Spisak · Nov 28, 2024

I have issue with one of my queries. That query work fine for ages, but suddenly stop works and giving me SQLCODE 100 in the routine. 

&SQL(SELECT * FROM (
		SELECTeventFROM dhr_log_lasers.production WHERE createDateUTC >= DATEADD(dd, -5, CURRENT_DATE) AND kiosk = :%var("kioskID") GROUP BY machine
		HAVING ID = MAX(ID)
	  ) WHERE event != 2
	 )

If I copy this same query to the management portal and replace variable with real ID I will get back 6 or more rows.

2
0 162
Article Yuri Marx · Nov 27, 2024 8m read

The rise of Big Data projects, real-time self-service analytics, online query services, and social networks, among others, have enabled scenarios for massive and high-performance data queries. In response to this challenge, MPP (massively parallel processing database) technology was created, and it quickly established itself. Among the open-source MPP options, Presto (https://prestodb.io/) is the best-known option. It originated in Facebook and was utilized for data analytics, but later became open-sourced. However, since Teradata has joined the Presto community, it offers support now.

0
3 290
Question Dmitrij Vladimirov · Nov 22, 2024

Hence the question: is there a way to do that?
The goal is to get data (from half a thousand to 3-4 thousands lines) from DB, calculate standart deviation  then use it as logical condition in analyzer. 
For example IF std > custom_value = show_the_result ELSE null
There is a STDDEV(MDX) method  used in Analyzer but it is a measure and it can not be used as logical condition (correct me if i am wrong)

2
0 117
Article Sylvain Guilbaud · Apr 30, 2024 3m read

Production Configuration

This demo has an interoperability production with 16 items. 

Production Configuration HL7 + Kafka Producer

The first part of this demonstration consists of sending an HL7 SIU file which will be transmitted to the 2 other HL7 flows (HTTP and TCP), and transformed and transmitted to the Kafka server. HTTP and TCP flows will transform HL7 messages in the same way before sending them to Kafka as well.

  • 3 HL7 Business Services
  • 1 HL7 router
  • 2 HL7 Business Operations
  • one Business Operation sending the transformed messages to Kafka

Business Rule

3
4 474
Question Scott Roth · Nov 20, 2024

I am using a JDBC connection to MS SQL server to execute a stored procedure to select data and bring it into InterSystems as a EnsLib.SQL.Snapshot. I loop through the EnsLib.SQL.Snapshot using a while loop, but I also want to iterate through the Columns within that Row to do logic.

Is there a way to iterate through the Columns of the current Row of the EnsLib.SQL.Snapshot so I can apply logic/rules for further processing?

Thanks

Scott

4
0 131
Question David.Satorres6134 · Nov 13, 2024

Hello all,

We have our system with AutoParallel enabled:

USER>w ##class(%SYSTEM.SQL.Util).GetOption("AutoParallel")
1

But whenever I try to run any sql the autoparallel does not work. For example, this simple query:

When I force it with %PARALLEL we can see it will effectively run in parallel:

The total records is bigger than the threshold. So, what can go wrong? Anyone in the same situation?

9
0 278
Question Dmitrii Baranov · Nov 17, 2024

I'm playing with some anayltic queries against FHIR server tables. The HSFHIR_X0002_S_Patient.addressCity table contains a lot of cities which names contain german charachers such as ä, ö and ü.

The following query works fine:

select value from HSFHIR_X0002_S_Patient.addressCity

But this one converts city names to uppercase, and characters with umlauts are lost, so instead of "Köln" or "München" I see KOLN and MUNCHEN:

select ac.value, count(ac.value) as cnt
  from HSFHIR_X0002_S_Patient.addressCity ac
  group by ac.value
  order by 2 desc

I'm using DBeaver with IRIS official JDBC driver.

UPD: distinct also makes city names malformed:

select distinct(value) from HSFHIR_X0002_S_Patient.addressCity
2
0 114
Question Richard Prouvot · Nov 13, 2024

I HAVE A NEW SQL CLASS THAT DISPLAYS ENTRIES BASED ON THE FIRST 2 NODES OF A GLOBAL. I FOUND OUT THAT THE CLASS ALLOWS FOR AN ADDITIONAL NODE(s) TO BE INSERTED IN THE "User Specification Node:" along the delimiter and the Piece in the NewStorage Map1 for "ModifyDDDD" shown below. it is not working.

^GBL("AA","SSSSSS")="1:1:1:1:"

^GBL("AA,"MD",1,1)="1:"

^GBL("AA,"MD","A",1)="0:"

I HAVE USED THIS:

<Data name="ModifyDDDD">
<Delimiter>":"</Delimiter>
<Node>"3,4"</Node>
<Piece>1</Piece>
</Data>

WHILE IT COMPILES IT DISPLAY NOTHING. MY QUESTION IS WHAT IS THE RIGHT SYNTAX AND PUNTUATION FOR THIS, PLEASE?

5
0 120
Article Megumi Kakechi · Nov 14, 2024 1m read

InterSystems FAQ rubric

By default, the order of columns in a table is determined automatically by the system. To change the order, explicitly set the order for each property using the property keyword SqlColumnNumber when defining the class.

Example:

Property Name As %String [SqlColumnNumber = 2];

Please see the documentation below.

SqlColumnNumber

If you want to change the SQL table name, specify SqlTableName. If you want to change the column name (field name), specify SqlFieldName.

Both apply only to persistent classes.

0
0 216
Question Andreas Schneider · Sep 15, 2024

Has anyone successfully tested the new THROUGH command in IRIS 2024.2 with a FOREIGN SERVER?https://docs.intersystems.com/iris20242/csp/docbook/Doc.View.cls?KEY=RS…

I have connected from a Docker instance to a VM. I was able to successfully set up the JDBC connection through the UI.

I then configured a foreign server with this connection:

But I am unable to send a SQL 'THROUGH' to the DB. I always get a:

I've get the same message if i try it via Management Portal.
I've also tried this:

and this

11
0 364
Article Guillaume Rongier · Jul 8, 2024 6m read

fastapi_logo

Description

This is a template for a FastApi application that can be deployed in IRIS as an native Web Application.

Installation

  1. Clone the repository
  2. Create a virtual environment
  3. Install the requirements
  4. Run the docker-compose file
git clone
cd iris-fastapi-template
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
docker-compose up

Usage

The base URL is http://localhost:53795/fastapi/.

Endpoints

  • /iris - Returns a JSON object with the top 10 classes present in the IRISAPP namespace.
  • /interop - A ping endpoint to test the interoperability framework of IRIS.
  • /posts - A simple CRUD endpoint for a Post object.
  • /comments - A simple CRUD endpoint for a Comment object.

How to develop from this template

See WSGI introduction article: wsgi-introduction.

TL;DR : You can toggle the DEBUG flag in the Security portal to make changes to be reflected in the application as you develop.

Code presentation

app.py

This is the main file of the FastAPI application. It contains the FastAPI application and the routes.

from fastapi import FastAPI, Request

import iris

from grongier.pex import Director

# import models
from models import Post, Comment, init_db
from sqlmodel import Session,select

app = FastAPI()

# create a database engine
url = "iris+emb://IRISAPP"
engine = init_db(url)
  • from fastapi import FastAPI, Request - Import the FastAPI class and the Request class.
  • import iris - Import the IRIS module.
  • from grongier.pex import Director: Import the Director class to bind the flask app to the IRIS interoperability framework.
  • from models import Post, Comment, init_db - Import the models and the init_db function.
  • from sqlmodel import Session,select - Import the Session class and the select function from the sqlmodel module.
  • app = FastAPI() - Create a FastAPI application.
  • url = "iris+emb://IRISAPP" - Define the URL of the IRIS namespace.
  • engine = init_db(url) - Create a database engine for the sqlmodel ORM.

models.py

This file contains the models for the application.

from sqlmodel import Field, SQLModel, Relationship, create_engine

class Comment(SQLModel, table=True):
    id: int = Field(default=None, primary_key=True)
    post_id: int = Field(foreign_key="post.id")
    content: str
    post: "Post" = Relationship(back_populates="comments")

class Post(SQLModel, table=True):
    id: int = Field(default=None, primary_key=True)
    title: str
    content: str
    comments: list["Comment"] = Relationship(back_populates="post")

Not much to say here, just the definition of the models with foreign keys and relationships.

The init_db function is used to create the database engine.

def init_db(url):

    engine = create_engine(url)

    # create the tables
    SQLModel.metadata.drop_all(engine)
    SQLModel.metadata.create_all(engine)

    # initialize database with fake data
    from sqlmodel import Session

    with Session(engine) as session:
        # Create fake data
        post1 = Post(title='Post The First', content='Content for the first post')
        ...
        session.add(post1)
        ...
        session.commit()

    return engine
  • engine = create_engine(url) - Create a database engine.
  • SQLModel.metadata.drop_all(engine) - Drop all the tables.
  • SQLModel.metadata.create_all(engine) - Create all the tables.
  • with Session(engine) as session: - Create a session to interact with the database.
  • post1 = Post(title='Post The First', content='Content for the first post') - Create a Post object.
  • session.add(post1) - Add the Post object to the session.
  • session.commit() - Commit the changes to the database.
  • return engine - Return the database engine.

/iris endpoint

######################
# IRIS Query example #
######################

@app.get("/iris")
def iris_query():
    query = "SELECT top 10 * FROM %Dictionary.ClassDefinition"
    rs = iris.sql.exec(query)
    # Convert the result to a list of dictionaries
    result = []
    for row in rs:
        result.append(row)
    return result
  • @app.get("/iris") - Define a GET route for the /iris endpoint.
  • query = "SELECT top 10 * FROM %Dictionary.ClassDefinition" - Define the query to get the top 10 classes in the IRIS namespace.
  • rs = iris.sql.exec(query) - Execute the query.
  • result = [] - Create an empty list to store the results.
  • for row in rs: - Iterate over the result set.
  • result.append(row) - Append the row to the result list.
  • return result - Return the result list.

/interop endpoint

########################
# IRIS interop example #
########################
bs = Director.create_python_business_service('BS')

@app.get("/interop")
@app.post("/interop")
@app.put("/interop")
@app.delete("/interop")
def interop(request: Request):
    
    rsp = bs.on_process_input(request)

    return rsp

  • bs = Director.create_python_business_service('BS') - Create a Python business service.
    • Must be created outside the route definition to prevent multiple instances of the business service.
  • @app.get("/interop") - Define a GET route for the /interop endpoint.
  • @app.post("/interop") - Define a POST route for the /interop endpoint.
  • ...
  • def interop(request: Request): - Define the route handler.
  • rsp = bs.on_process_input(request) - Call the on_process_input method of the business service.
  • return rsp - Return the response.

/posts endpoint

############################
# CRUD operations posts    #
############################

@app.get("/posts")
def get_posts():
    with Session(engine) as session:
        posts = session.exec(select(Post)).all()
        return posts
    
@app.get("/posts/{post_id}")
def get_post(post_id: int):
    with Session(engine) as session:
        post = session.get(Post, post_id)
        return post
    
@app.post("/posts")
def create_post(post: Post):
    with Session(engine) as session:
        session.add(post)
        session.commit()
        return post

This endpoint is used to perform CRUD operations on the Post object.

Note much to say here, just the definition of the routes to get all posts, get a post by id, and create a post.

Everything is done using the sqlmodel ORM.

/comments endpoint

############################
# CRUD operations comments #
############################


@app.get("/comments")
def get_comments():
    with Session(engine) as session:
        comments = session.exec(select(Comment)).all()
        return comments
    
@app.get("/comments/{comment_id}")
def get_comment(comment_id: int):
    with Session(engine) as session:
        comment = session.get(Comment, comment_id)
        return comment
    
@app.post("/comments")
def create_comment(comment: Comment):
    with Session(engine) as session:
        session.add(comment)
        session.commit()
        return comment

This endpoint is used to perform CRUD operations on the Comment object.

Note much to say here, just the definition of the routes to get all comments, get a comment by id, and create a comment.

Everything is done using the sqlmodel ORM.

Troubleshooting

How to run the FastAPI application in a standalone mode

You can always run a standalone Flask application with the following command:

python3 /irisdev/app/community/app.py

NB : You must be inside of the container to run this command.

docker exec -it iris-fastapi-template-iris-1 bash

Restart the application in IRIS

Be in DEBUG mode make multiple calls to the application, and the changes will be reflected in the application.

How to access the IRIS Management Portal

You can access the IRIS Management Portal by going to http://localhost:53795/csp/sys/UtilHome.csp.

Run this template locally

For this you need to have IRIS installed on your machine.

Next you need to create a namespace named IRISAPP.

Install the requirements.

Install IoP :

#init iop
iop --init

# load production
iop -m /irisdev/app/community/interop/settings.py

# start production
iop --start Python.Production

Configure the application in the Security portal.

3
0 528
Article Timothy Leavitt · Oct 28, 2024 2m read

User-defined aggregate functions have been supported in IRIS since 2021.1.0. I'd wished upon a star for these years ago before finding a secret hacky way to override MAX and MIN in a custom datatype, but didn't get a chance to actually try one out until today. I thought it was an interesting experience/example - the question of how to get a Median in IRIS SQL came up once before - so I'm sharing it here without too much further comment.

0
1 236
Question Dmitrii Baranov · Oct 20, 2024

I'm experimenting with adapting SDA3 object model to store medical data in relational form, e.g.:

class Demo.DemoPatient extends (%Persistent, HS.SDA3.Patient) {}

The HS.SDA3.Patient class has the Aliases property which is a nested collection (list) of objects of type HS.SDA3.Name:

#dim record as Demo.DemoPatient = ##class(Demo.DemoPatient).%New()
set record.Name.FamilyName = "Clemens"
set record.Name.GivenName = "Samuel"
set record.BirthTime = "1935-11-30T12:00:00"
    
#dim alias as HS.SDA3.Name = ##class(HS.SDA3.Name).%New()
set alias.FamilyName = "Twain"
set alias.GivenName = "Mark"  
do record.Aliases.Insert(alias)

set status = record.%Save()
if $$$ISERR(status) {
    Write "Error: " _ $SYSTEM.Status.GetErrorText(status)   
}

How could I select a patient record searching by his/her Aliases.FamilyName? Something like:

select ID, Name_GivenName from Demo.DemoPatient where FOR SOME %ELEMENT(Demo.DemoPatient.Aliases) (???? = 'Twain')
6
0 136
Discussion Otto Medin · Oct 19, 2024

In the past, I've created custom SQL operations, but now I had something trivial to do, so I decided to take EnsLib.SQL.Operation.GenericOperation out for a spin. There's no example in the docs, so it was a little tricky. Here's what I ended up doing:

In my external database, I have 'mytable' with two fields 'id1' and 'id2'. Here are the pertinent Business Operation settings:

SQL: select id2 from mytable where id1 = ?
Input Parameters: [1] *id1
RequestClass: Ens.StringRequest
ResponseClass: MyResponseClass

2
0 183
Question Kurro Lopez · Oct 18, 2024

Hi all,

We have an restriction in a SQL database with a unique index.

We want to catch that exception when it tries to insert or update a value that violates the unique index condition.

// run the querySet tSC = ..Adapter.ExecuteUpdateParmArray(.nrows,SQL,.param)

// Check if there is any errorIf$$$ISERR(tSC)
{
	Set msgError = $System.Status.GetErrorText(tSC) 
	// Check here if the native error code is 2601 (Cannot insert duplicate key row into object 'MYPATIENTS' with unique index 'UQ_UNIQUE_INDEX')
	??????
}

I've tried to get the ..Adapter.%SQLCODE, but it is empty

3
0 118
Question Qais Azam · Oct 16, 2024

I am experiencing an issue while executing a stored procedure in InterSystems Cache. Here’s the procedure I createdCREATE PROCEDURE Silk.sp_InsertRecord (    IN RecordDate TIMESTAMP,    IN UserName VARCHAR(50),    IN RecordType INT,    IN RecordID VARCHAR(50),    IN CategoryID INT,    IN ApprovalDate TIMESTAMP,    IN FileSize BIGINT,    IN WorkstationName VARCHAR(50))BEGIN     INSERT INTO DummyRecords (        RecordKey,         FilePath,         RecordDate,         UserName,         RecordType,         RecordID,         CategoryID,         FileSize    )     VALUES (       

0
0 131
Article Robert Cemper · Mar 21, 2024 2m read

In ObjectScript you have a wide collection of functions that return some value
typically:

set variable = $somefunction(param1,param2, ...)

There is nothing special about that.
But there is a set of functions that I classify as LEFT SIDED 
The specialty of them is that you can use them also on the left of the equal operator 
as a target in the SET command:

set $somefunction(param1,param2, ...) = value

The reason to raise that subject is that with IRIS 2024.1 there is after may years  a "new kid on this block"

$VECTOR()

3
2 318
Question Christine Nyamu · Oct 9, 2024

Hello all,

I need help with coming up with a SQL query that pulls only one value. I have a case where two providers share the exact same name. Each has a different NPI number and IdentityTypeId. I tried the below query - output is also below. 

SELECT *                        
FROM PhysTable                        
WHERE ProviderName = 'DOE, JOE' AND Type = 'NPI'                        
                        
UNION                        
                        
SELECT *                        
FROM PhysTable                        
WHERE IdentityId = '345678'                        
 

Output

18
0 156
Question Scott Roth · Oct 2, 2024

I have been trying to track down an issue we are seeing in our TEST environment with Memory usage.

We have Several BP's for years now that take a HL7 message, parse it apart, and make calls to a Custom EnsLib.SQL.OutboundAdapter to have it execute Insert/Select/Update/Delete stored procedures against a MS SQL Database via JDBC connection. We are using Microsoft's JDBC 12.2 driver to do this.

What we are seeing is that IRIS.WorkQueue globals are being defined for these calls but then the IRIS.WorkQueue is not being cleaned up and taking up large amounts of Memory.

5
0 141
Article Muhammad Waseem · Sep 23, 2024 4m read

Hi Community,
In this article, I will introduce my application iris-DataViz
iris-DataViz is an Exploratory Data Analysis and Visualization Streamlit Application that leverages the functionality of IRIS embedded python and SQLAlchemy to interact with IRIS, as well as the PyGWalker python library for data analysis and data Visualization. PyGWalker (Python Graphic Walker) is an interactive data visualization library built for Python, aiming to bring the ease and functionality of Tableau-style drag-and-drop visualization into Python environments.


Application Features 

3
0 259
Article Andrii Mishchenko · Sep 27, 2024 4m read

In this article, we’ll dive into the inner workings of the database management tool, exploring the architecture and technologies that power it. Understanding how the application functions behind the scenes will give you insight into its design, how it manages databases, tables, and how the API interacts with data.

We will discuss the core technologies used, including InterSystems IRIS as the primary database and Redis for caching. Additionally, we’ll break down the structure of the tables used and explain how the system handles data creation, retrieval, and manipulation through the REST API.

0
1 208
Article Murray Oldfield · Sep 24, 2024 7m read

Problems with Strings

I am accessing IRIS databases with JDBC (or ODBC) using Python. I want to fetch the data into a pandas dataframe to manipulate the data and create charts from it. I ran into a problem with string handling while using JDBC. This post is to help if anyone else has the same issues. Or, if there is an easier way to solve this, let me know in the comments!

I am using OSX, so I am unsure how unique my problem is. I am using Jupyter Notebooks, although the code would generally be the same if you used any other Python program or framework.

The JDBC problem

When I fetch data from the database the column descriptions and any string data are returned as data type java.lang.String. If you print string data data it will look like: "(p,a,i,n,i,n,t,h,e,r,e,a,r)" instead of the expected "painintherear".

This is probably because character strings of data type java.lang.String are coming through as an iterable or array when fetched using JDBC. This can happen if the Python-Java bridge you're using (e.g., JayDeBeApi, JDBC) is not automatically converting java.lang.String to a Python str in a single step.

Python's str string representation, in contrast, has the whole string as a single unit. When Python retrieves a normal str (e.g. via ODBC), it doesn't split into individual characters.

The JDBC Solution

To fix this issue, you must ensure that the java.lang.String is correctly converted into Python's str type. You can explicitly handle this conversion when processing the fetched data so it is not interpreted as an iterable or list of characters.

There are many ways to do this string manipulation; this is what I did.

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

The ODBC problem

When using an ODBC connection, strings are not returned or are NA.

If you're connecting to a database that contains Unicode data (e.g., names in different languages) or if your application needs to store or retrieve non-ASCII characters, you must ensure that the data remains correctly encoded when passed between the database and your Python application.

The ODBC solution

This code ensures that string data is encoded and decoded using UTF-8 when sending and retrieving data to the database. It's especially important when dealing with non-ASCII characters or ensuring compatibility with Unicode data.

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")

Tells pyodbc how to decode character data from the database when fetching SQL_CHAR types (typically, fixed-length character fields).

connection.setdecoding(pyodbc.SQL_WCHAR, encoding="utf8")

Sets the decoding for SQL_WCHAR, wide-character types (i.e., Unicode strings, such as NVARCHAR or NCHAR in SQL Server).

connection.setencoding(encoding="utf8")

Ensures that any strings or character data sent from Python to the database will be encoded using UTF-8, meaning Python will translate its internal str type (which is Unicode) into UTF-8 bytes when communicating with the database.


Putting it all together

Install JDBC

Install JAVA - use dmg

https://www.oracle.com/middleeast/java/technologies/downloads/#jdk23-mac

Update shell to set default version

$ /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

Add JAVA_HOME to your path

export JAVA_HOME=$(/usr/libexec/java_home -v 23)
export PATH=$JAVA_HOME/bin:$PATH

Get the JDBC driver

https://intersystems-community.github.io/iris-driver-distribution/

Put the jar file somewhere... I put it in $HOME

$ ls $HOME/*.jar
/Users/myname/intersystems-jdbc-3.8.4.jar

Sample code

It assumes you have set up ODBC (an example for another day, the dog ate my notes...).

Note: this is a hack of my real code. Note the variable names.

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()
0
0 458