0 Seguidores · 168 Publicaciones

SQL is un lenguaje estándar para el almacenamiento, manipulación y recuperación de datos en bases de datos relacionales.

Artículo Muhammad Waseem · jul 15, 2022 5m read

¡Hola Comunidad!

En este artículo voy a explicar cómo acceder a la información y a las tablas del dashboard (cuadro de mando) del sistema del Portal de Administración mediante el uso de Python Embebido.

1
0 228
Artículo Ricardo Paiva · ene 29, 2021 8m read

¡Hola Desarroladores!

IRIS External Table es un proyecto de código abierto de la comunidad de InterSystems, que permite utilizar archivos almacenados en el sistema de archivos local y almacenar objetos en la nube como AWS S3 y tablas SQL. IRIS External Table

Se puede encontrar en GitHub https://github.com/intersystems-community/IRIS-ExternalTable Open Exchange https://openexchange.intersystems.com/package/IRIS-External-Table y está incluido en el administrador de paquetes InterSystems Package Manager (ZPM).

Para instalar External Table desde GitHub, utilice:

git clone https://github.com/antonum/IRIS-ExternalTable.git
iris session iris
USER>set sc = ##class(%SYSTEM.OBJ).LoadDir("<path-to>/IRIS-ExternalTable/src", "ck",,1)

Para instalarlo con el ZPM Package Manager, utilice:

USER>zpm "install external-table"

Cómo trabajar con archivos locales

Crearemos un archivo simple que tiene este aspecto:

a1,b1
a2,b2

Abra su editor favorito y cree el archivo o utilice solo una línea de comandos en Linux/Mac:

echo $'a1,b1\na2,b2' > /tmp/test.txt

Cree una tabla SQL en IRIS para representar este archivo:

create table test (col1 char(10),col2 char(10))

Convierta la tabla para utilizar el almacenamiento externo:

CALL EXT.ConvertToExternal(
    'test',
    '{
        "adapter":"EXT.LocalFile",
        "location":"/tmp/test.txt",
        "delimiter": ","
    }')

Y finalmente, consulte la tabla:

select * from test

Si todo funciona según lo previsto, debería ver el resultado de la siguiente forma:

col1    col2
a1  b1
a2  b2

Ahora regrese al editor, modifique el contenido del archivo y ejecute nuevamente la consulta SQL. ¡¡¡Tarán!!! Está leyendo nuevos valores de su archivo local en SQL.

col1    col2
a1  b1
a2  b99

Cómo leer los datos desde S3

En https://covid19-lake.s3.amazonaws.com/index.html puede acceder a los datos de la COVID que se actualizan constantemente, estos se almacenan por AWS en el lago de datos públicos.

Intentaremos acceder a una de las fuentes de datos en este lago de datos: s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states

Si tiene instalada la herramienta de línea de comandos para AWS, puede repetir los siguientes pasos. Si no es así, vaya directamente a la parte de SQL. No es necesario que tenga ningún componente específico de AWS instalado en su equipo para continuar con la parte de SQL.

$ aws s3 ls s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/
2020-12-04 17:19:10     510572 us-states.csv

$ aws s3 cp s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv .
download: s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv to ./us-states.csv

$ head us-states.csv 
date,state,fips,cases,deaths
2020-01-21,Washington,53,1,0
2020-01-22,Washington,53,1,0
2020-01-23,Washington,53,1,0
2020-01-24,Illinois,17,1,0
2020-01-24,Washington,53,1,0
2020-01-25,California,06,1,0
2020-01-25,Illinois,17,1,0
2020-01-25,Washington,53,1,0
2020-01-26,Arizona,04,1,0

Por lo tanto, tenemos un archivo con una estructura bastante simple y cinco campos delimitados.

Para mostrar esta carpeta S3 como en External Table, primero necesitamos crear una tabla “regular” con la estructura deseada:

-- create external table
create table covid_by_state (
    "date" DATE, 
    "state" VARCHAR(20),
    fips INT,
    cases INT,
    deaths INT
)

Tenga en cuenta que algunos campos de datos como “Date” son palabras reservadas en el SQL de IRIS y deben escribirse entre comillas dobles. Entonces, necesitamos convertir esta tabla “regular” en la tabla “externa”, basada en el bucket AWS S3 y con el tipo CSV.

 -- convert table to external storage
call EXT.ConvertToExternal(
    'covid_by_state',
    '{
    "adapter":"EXT.AWSS3",
    "location":"s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/",
    "type": "csv",
    "delimiter": ",",
    "skipHeaders": 1
    }' 
)

Si observa detenidamente, en EXT.ExternalTable los argumentos de los procedimientos son el nombre de la tabla y luego la cadena JSON, además contiene varios parámetros como la ubicación para buscar archivos, el adaptador para utilizarlos, un delimitador, etc. Además, External Table de AWS S3 es compatible con el almacenamiento de Azure BLOB, Google Cloud Buckets y el sistema de archivos local. El repositorio de GitHub contiene referencias para la sintaxis y opciones que son compatibles con todos los formatos.

Y finalmente, consulte la tabla:

-- query the table
select top 10 * from covid_by_state order by "date" desc

[SQL]USER>>select top 10 * from covid_by_state order by "date" desc
2.  select top 10 * from covid_by_state order by "date" desc

date    state   fips    cases   deaths
2020-12-06  Alabama 01  269877  3889
2020-12-06  Alaska  02  36847   136
2020-12-06  Arizona 04  364276  6950
2020-12-06  Arkansas    05  170924  2660
2020-12-06  California  06  1371940 19937
2020-12-06  Colorado    08  262460  3437
2020-12-06  Connecticut 09  127715  5146
2020-12-06  Delaware    10  39912   793
2020-12-06  District of Columbia    11  23136   697
2020-12-06  Florida 12  1058066 19176

Es comprensible que se necesite más tiempo para consultar los datos de la tabla remota, que para consultar la tabla “nativa de IRIS” o la tabla basada en el global, pero esta se almacena y actualiza completamente en la nube, y en segundo plano se extrae a IRIS.

Exploremos un par de funciones adicionales de External Table.

%PATH y las tablas basadas en varios archivos

La carpeta de nuestro ejemplo, que se encuentra en el bucket, contiene solo un archivo. Lo más común es que tenga varios archivos de la misma estructura, donde el nombre del archivo identifique tanto al registro de la hora como al identificador de algún otro atributo que queramos utilizar en nuestras consultas.

El campo %PATH se agrega automáticamente a cada tabla externa y contiene la ruta completa hacia el archivo de donde se recuperó la fila.

select top 5 %PATH,* from covid_by_state

%PATH   date    state   fips    cases   deaths
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-21  Washington  53  1   0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-22  Washington  53  1   0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-23  Washington  53  1   0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-24  Illinois    17  1   0
s3://covid19-lake/rearc-covid-19-nyt-data-in-usa/csv/us-states/us-states.csv    2020-01-24  Washington  53  1   0

Puede utilizar el campo %PATH en sus consultas SQL como cualquier otro campo.

De datos ETL a “tablas regulares”

Si su tarea es cargar datos de S3 en una tabla IRIS, puede utilizar External Table como una herramienta ETL. Simplemente haga lo siguiente:

INSERT INTO internal_table SELECT * FROM external_table

En nuestro caso, si queremos copiar los datos COVID de S3 a la tabla local:

--create local table
create table covid_by_state_local (
    "date" DATE, 
    "state" VARCHAR(100),
    fips INT,
    cases INT,
    deaths INT
)
--ETL from External to Local table
INSERT INTO covid_by_state_local SELECT TO_DATE("date",'YYYY-MM-DD'),state,fips,cases,deaths FROM covid_by_state

UNIÓN entre IRIS, tablas nativas y externas. Consultas federadas

External Table es una tabla SQL. Se puede unir con otras tablas, utilizarse en subconsultas y sistemas de archivos tipo UNION. Incluso puede combinar la tabla “Regular” de IRIS y dos o más tablas externas que provengan de diferentes fuentes en la misma consulta SQL.

Intente crear una tabla regular, por ejemplo, haga coincidir los nombres de los estados con sus códigos como en el caso de Washington y WA. Y únalos con nuestra tabla basada en S3.

create table state_codes (name varchar(100), code char(2))
insert into state_codes values ('Washington','WA')
insert into state_codes values ('Illinois','IL')

select top 10 "date", state, code, cases from covid_by_state join state_codes on state=name

Cambie “join” por “left join” para incluir aquellas filas donde el código del estado no esté definido. Como puede ver, el resultado es una combinación de datos provenientes de S3 y su tabla nativa de IRIS.

Acceso seguro a la información

El lago de datos Covid en AWS es público. Cualquier persona puede leer los datos que provengan de esta fuente sin la necesidad de tener alguna autenticación o autorización. En la vida real seguramente quiere acceder a sus datos de una forma segura, donde se evite que extraños echen un vistazo a sus archivos. Los detalles completos sobre AWS Identity y Access Management (IAM) están fuera del alcance de este artículo. Pero lo mínimo que debe saber es que necesita por lo menos la clave de acceso a la cuenta y la información confidencial de AWS para acceder a los datos privados de su cuenta. https:

AWS utiliza la autenticación de claves/información confidencial de la cuenta para firmar las solicitudes. https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html#access-keys-and-secret-access-keys

Si está ejecutando IRIS External Table en una instancia de EC2, la forma recomendada de lidiar con la autenticación es utilizando las funciones que se encuentran en la instancia de EC2 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html. De este modo, IRIS External Table podría utilizar los permisos de esa función. No se requiere ninguna configuración adicional.

En una instancia local o que no sea de EC2 es necesario especificar AWS_ACCESS_KEY_ID y AWS_SECRET_ACCESS_KEY, ya sea con la ayuda de variables de entorno o mediante la instalación y configuración del cliente CLI de AWS.

export AWS_ACCESS_KEY_ID=AKIAEXAMPLEKEY
export AWS_SECRET_ACCESS_KEY=111222333abcdefghigklmnopqrst

Asegúrese de que la variable de entorno sea visible dentro del proceso de IRIS. Puede verificarlo al ejecutar:

USER>write $system.Util.GetEnviron("AWS_ACCESS_KEY_ID")

Esto debería emitir el valor de la clave.

O instale el CLI de AWS, mediante instrucciones que se encuentran aquí: https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2-linux.html y ejecutar:

aws configure

Entonces External Table podrá leer las credenciales desde los archivos de configuración para el CLI de AWS. Posiblemente su shell interactivo y el proceso de IRIS estén ejecutándose en cuentas diferentes, asegúrese de ejecutar aws configure con la misma cuenta que su proceso de IRIS.

1
0 147
Artículo Luis Angel Pérez Ramos · oct 17, 2022 2m read

Acabo de redactar un ejemplo rápido para ayudar a un colega a cargar datos en IRIS desde R usando RJDBC y pensé que sería útil compartirlo aquí para futuras consultas.

Fue bastante sencillo, aparte de que a IRIS no le gusta el uso de puntos "." en los nombres de las columnas; la solución alternativa es simplemente renombrar las columnas. Alguien con más conocimientos que yo en R seguramente pueda ofrecer un enfoque más amplio smiley

# Es necesario un valor válido para el JAVA_HOME antes de cargar la librería (RJDBC)
Sys.setenv(JAVA_HOME="C:\\Java\\jdk-8.0.322.6-hotspot\\jre")
library(RJDBC)
library(dplyr)
# Conexión a IRIS – se requiere la ruta a la librería JAR de InterSystems JDBC JAR de tu instalación
drv <- JDBC("com.intersystems.jdbc.IRISDriver", "C:\\InterSystems\\IRIS\\dev\\java\\lib\\1.8\\intersystems-jdbc-3.3.0.jar","\"")
conn <- dbConnect(drv, "jdbc:IRIS://localhost:1972/USER", "IRIS Username", "IRIS Password")
dbListTables(conn)
# Para mayor confusión, cargar el dataset de IRIS:)
data(iris)
# A IRIS no le gustan los puntos "." en el nombre de las columnas, así que los renombramos. (Probablemente se pueda codificar de una forma más genérica, pero no soy muy bueno con R.)
iris <- iris %>% rename(sepal_length = Sepal.Length, sepal_width = Sepal.Width, petal_length = Petal.Length, petal_width = Petal.Width)

# dbWriteTable/dbGetQuery/dbReadTable funcionan
dbWriteTable(conn, "iris", iris, overwrite = TRUE)
dbGetQuery(conn, "select count(*) from iris")
d <- dbReadTable(conn, "iris")
0
0 127
Artículo Muhammad Waseem · jul 19, 2022 3m read

Recientemente surgió un patrón interesante en torno a los índices únicos (en una discusión interna re: isc.rest) y me gustaría destacarlo para la Comunidad.

Como caso de uso motivador: supón que tienes una clase que representa un árbol, donde cada nodo también tiene un nombre, y queremos que los nodos sean únicos por nombre y nodo principal. Queremos que cada nodo raíz también tenga un nombre único. Una implementación natural sería:

0
0 140
Artículo Ricardo Paiva · jun 24, 2021 6m read

Publicación Original por: Anton Umnikov
Arquitecto Senior de soluciones en la nube en InterSystems
AWS CSAA, GCP CACE

AWS Glue es un proceso ETL (extraer, transformar y cargar) completamente gestionado, que hace sencillo y rentable clasificar los datos, limpiarlos, enriquecerlos y moverlos de forma fiable entre diferentes almacenes de datos.

En el caso de InterSystems IRIS, AWS Glue permite mover grandes cantidades de datos a IRIS desde fuentes de datos tanto en la nube como en las propias instalaciones (on-premise). Las fuentes de datos potenciales incluyen, pero no se limitan a, bases de datos on-prem, archivos CSV, JSON, Parquet y Avro que residen en buckets S3, bases de datos nativas en la nube como AWS Redshift y Aurora, y muchas otras.

Este artículo asume que tienes un conocimiento básico de AWS Glue, al menos en el nivel para completar los Tutoriales de introducción a AWS Glue . Nos centraremos en los aspectos técnicos de la configuración de los trabajos (Jobs) en AWS Glue para utilizar InterSystems IRIS como Datos Objetivo, o en otros términos, "receptor de datos".

Imagen de https://docs.aws.amazon.com/glue/latest/dg/components-key-concepts.html
 

Los Trabajos en AWS Glue se ejecutan "sin servidor". Todos los recursos necesarios para realizar el trabajo son suministrados de forma dinámica por AWS solo durante el tiempo en que el trabajo se ejecuta realmente; y se destruyen en el momento en que el trabajo se completa. Por eso, en lugar de provisionar, gestionar e incurrir en costes continuos por la infraestructura necesaria, se te factura solo por el tiempo en que el trabajo realmente se ejecuta y puedes dedicar tus esfuerzos a escribir el código del Trabajo. Durante el tiempo de "inactividad" no se consumen recursos, salvo los buckets S3, que almacenan el código del Trabajo y la configuración.

Aunque hay varias opciones disponibles, normalmente los Trabajos en Glue se ejecutan con un suministro dinámico en Apache Spark y se escriben en código PySpark. El Trabajo en Glue contiene la sección _"Extraer"_ (donde los datos se extraen de las fuentes de datos), una serie de _"Transformaciones"_ (construidas utilizando la API de Glue) y finalmente la parte _"Cargar"_ o _"receptor"_, en la que después de la transformación final los datos se escriben en el sistema objetivo.

Para permitir que AWS Glue interactúe con IRIS necesitamos asegurar lo siguiente:

  • Glue tiene acceso a la red de las instancias IRIS involucradas
  • El archivo JAR del controlador JDBC de IRIS es accesible para el Trabajo en Glue
  • El Trabajo en Glue utiliza la API compatible con InterSystems IRIS JDBC

Examinemos cada uno de los pasos necesarios.

Crear una conexión a IRIS

En la consola AWS, selecciona AWS Glue->Connections->Add Connection

Introduce el nombre de tu conexión y selecciona "JDBC" como Tipo de Conexión.

En la URL de JDBC introduce la cadena de conexión JDBC para tu instancia de IRIS, el nombre de usuario y la contraseña.

El siguiente paso es crucial, necesitas asegurarte de que Glue coloca sus endpoints en el mismo VPC que tu instancia de IRIS. Selecciona la VPC y la Subred de tu instancia de IRIS. Cualquier grupo de seguridad con una regla de entrada de autorreferencias para todos los puertos TCP se ejecutaría aquí. Por ejemplo, el grupo de seguridad de tu instancia de IRIS.

Rol de IAM con acceso al controlador JDBC

Si aún no lo has hecho, carga el archivo JAR del controlador JDBC de IRIS intersystems-jdbc-3.0.0.jar en el bucket S3. En este ejemplo, estoy usando el bucket s3://irisdistr. Sería distinto para tu cuenta.

Necesitas crear un rol de IAM para tu Trabajo en Glue, que pueda acceder a ese archivo, junto con otros buckets S3 que Glue utilizaría para almacenar scripts, registros, etc.

Asegúrate de que tiene acceso de lectura al bucket de tu controlador JDBC. En este caso, concedemos a esta función (GlueJobRole) acceso de solo lectura (ReadOnly) a todos los buckets, junto con el AWSGlueServiceRole predefinido. Puedes elegir limitar aún más los permisos de este rol.

Crear y configurar el Trabajo en Glue

Crear un nuevo Trabajo en Glue. Selecciona el rol de IAM, que creamos en el paso anterior. Deja todo lo demás de forma predeterminada.

En "Security configuration, script libraries, y job parameters (optional)" establece en "Dependent jars path" la ubicación del intersystems-jdbc-3.0.0.jar  en el bucket S3.

 

Para la fuente: utiliza una de tus fuentes de datos preexistentes. Si seguiste los tutoriales mencionados más arriba, ya tendrás al menos una.

Utiliza la opción "Create tables in your data target" y selecciona la conexión IRIS que has creado en el paso anterior. Deja todo lo demás de forma predeterminada.

Si nos has seguido hasta ahora, deberías llegar a una pantalla similar a esta:

 

¡Ya queda poco! Solo necesitamos hacer un sencillo cambio en el script para cargar datos en IRIS.

Cómo ajustar el script

El script que Glue generó para nosotros utiliza AWS Glue Dynamic Frame, extensión propietaria de AWS para Spark. Aunque ofrece algunos beneficios para los trabajos de ETL, también garantiza que no puedes escribir datos en ninguna base de datos para la que AWS no haya administrado la oferta de servicios.

Buenas noticias- en el momento de escribir los datos en la base de datos ya no son necesarios todos los beneficios de Dynamic Dataframe, como no aplicación de esquema para los datos "sucios" (en el momento de escribir los datos se supone que están "limpios") y podemos convertir fácilmente Dynamic Dataframe a un Dataframe nativo en Spark que no está limitado a los objetivos administrados por AWS y puede trabajar con IRIS.

Así que la línea que necesitamos cambiar es la línea 40 de la imagen más arriba. Un último consejo.

Este es el cambio que necesitamos hacer:

#datasink4 = glueContext.write_dynamic_frame.from_jdbc_conf(frame = dropnullfields3, catalog_connection = "IRIS1", connection_options = {"dbtable": "elb_logs", "database": "USER"}, transformation_ctx = "datasink4")
dropnullfields3.toDF().write \
    .format("jdbc") \
    .option("url", "jdbc:IRIS://172.30.0.196:51773/USER/") \
    .option("dbtable", "orders") \
    .option("user", irisUsername) \
    .option("password", irisPassword) \
    .option("isolationlevel","NONE") \
    .save()

Donde irisUsername e irisPassword son el nombre de usuario y las contraseñas para tu conexión JDBC en IRIS.

Nota: almacenar las contraseñas en el código fuente NO es conveniente. Te animamos a utilizar herramientas como AWS Secrets Manager para ello, pero entrar en este nivel de detalles de seguridad está más allá del alcance de este artículo. Este es un buen artículo sobre el uso de AWS Secrets Manager en AWS Glue.

Ahora pulsa el botón "Run Job", siéntate y relájate mientras AWS Glue efectúa el proceso ETL por ti.

Bueno... lo más probable es que cometas algunos errores al principio... Todos sabemos cómo funciona. Una errata por aquí, un puerto equivocado en el grupo de seguridad por allí... AWS Glue utiliza CloudWhatch para almacenar todos los registros de ejecución y de errores. Busca grupos de registro en: /aws-glue/jobs/error y _ /aws-glue/jobs/output_, para identificar lo que salió mal.

¡Disfruta de la programación de los procesos ETL en la nube!

1
0 495
Artículo Muhammad Waseem · jun 17, 2022 2m read

Digamos que tengo una clase persistente en IRIS con una propiedad opcional EmailOptIn:

Class Person Extends %Persistent
{
Property Name As %String;
Property EmailOptIn As %Boolean;
}

Más tarde me doy cuenta de que estoy haciendo muchas comprobaciones nulas en esta propiedad donde no debería ser necesario. La solución es hacer de esto una propiedad requerida:

Class Person Extends %Persistent
{
Property Name As %String;
Property EmailOptIn As %Boolean [ Required ];
}

Cuando hago este cambio, tendré que actualizar todos los datos existentes para establecer un valor predeterminado razonable donde sea nulo. De lo contrario, estoy en un mal estado donde la actualización del nombre de una persona fallará si su EmailOptIn es nulo. Podría esperar arreglar esto en SQL de la siguiente manera:

&sql(update Person set EmailOptIn = 0 where EmailOptIn is null) 

Sin embargo, ejecutar esta consulta me dejará sin filas afectadas. También puedo usar una declaración SELECT para verificar que la condición WHERE no coincida con filas. Interpreto esto como SQL, suponiendo que ninguna de las entradas sea nula, ya que se requiere la propiedad. La solución es un poco intuitiva. Aunque no existe ningún índice, el uso de la palabra clave %NOINDEX en la condición obliga a la actualización a comprobar si hay entradas nulas.

&sql(update Person set EmailOptIn = 0 where %NOINDEX EmailOptIn is null) 

Este caso extremo me hizo atascarme recientemente. Espero que este artículo pueda ahorrar algo de tiempo a otros desarrolladores.

0
0 145
Artículo Alberto Fuentes · jul 29, 2019 4m read

¡Hola a tod@s!

Hoy os traigo un artículo de Kyle Baxter sobre búsquedas de texto libre que vale la pena guardar como referencia :)

¿Os gustaría buscar de forma eficiente campos de texto libres almacenados en vuestra aplicación? ¿Lo habéis intentado alguna vez pero no habéis encontrado una manera que os ofrezca un buen rendimiento? Hay un truco especial que resuelve el problema :)

Como es habitual, si preferís la versión TL;DR (Demasiado largo, no lo he leído), podéis ir directamente al final del artículo, pero preferiríamos que leyeseis el artículo entero para evitar herir sentimientos.

1
0 331
Artículo Alberto Fuentes · mayo 11, 2022 2m read

¡Hola desarrolladores!

A veces necesitamos insertar o referir los datos de clases directamente en globals.

Y quizá muchos de vosotros esperáis que la estructura de datos de una global con registros sea:

^Sample.Person(Id)=$listbuild("",col1,col2,...,coln).

Este artículo es un aviso de que esto no siempre es verdad, así que no se debe dar por supuesto!

0
0 173
Artículo Henrique Dias · mayo 5, 2022 2m read

¡Hola desarrolladores!

En mis artículos anteriores, una de las cosas más interesantes de FHIR que mencioné es el amplio rango de posibilidades que tenemos y no solo para crear algo, sino las maneras de conseguir este objetivo.

En mis artículos estoy compartiendo mi experiencia trabajando con FHIR. Solo usaba los Recursos FHIR aportados por la API. Recuperando y actualizando los Recursos FHIR en el servidor FHIR usando javascript con la ayuda de fhir.js

0
0 134
Artículo Pierre-Yves Duquesnoy · mayo 3, 2022 14m read

Encontrar errores en tu código o examinar un comportamiento inesperado es el principal objetivo de la depuración.

Trataré de actualizar las herramientas tradicionales aparte de las ayudas que tienen Studio, VScode, Serenji... Las herramientas básicas que han estado ahí antes de que tu EDI preferido lo utilizara en segundo plano.

0
2 193
Artículo Muhammad Waseem · abr 25, 2022 2m read

En este artículo, explicaré el uso de las tablas %SQL_Diag.Result y %SQL_Diag.Message junto con la nueva funcionalidad LOAD DATA.

Se recomienda revisar primero la documentación LOAD DATA.

Después del éxito de una operación, LOAD DATA inserta un registro en la tabla %SQL_Diag.Result y los detalles se insertan en la tabla %SQL_Diag.Message

A continuación se muestra el comando básico cuando la tabla ya está creada y el archivo de origen no contiene una fila de encabezado.

LOAD DATA FROM FILE 'C://TEMP/mydata.txt' 
INTO MyTable

El nombre del archivo debe incluir un sufijo .txt o .csv (valores separados por comas) y tanto el origen como el destino tienen la misma secuencia de columnas de datos.

0
0 154
Artículo Eduardo Anglada · abr 19, 2022 3m read

Gracias a @Yuri Marx hemos visto un buen ejemplo de Migración de Bases de datos de Postgres a IRIS.
Mi problema personal es el uso de DBeaver como una herramienta de migración.
En especial, como una de las fortalezas de IRIS (y también Caché) es la disponibilidad de los SQLgateways que permiten el acceso a cualquier base de datos externa, siempre y cuando se puedan acceder usando ODBC/JDBC. Así que he ampliado el paquete para demostrarlo.

0
1 235
Artículo Muhammad Waseem · mar 29, 2022 5m read

 

He desarrollado una aplicación para importar de forma dinámica 12 conjuntos de datos junto con 43 tablas usando el comando LOAD DATA, que carga los datos de una fuente a una tabla SQL de IRIS. 

Lista de los conjuntos de datos

Características

  • Creación dinámica de tablas basadas en los datos de un archivo CSV.
  • Importación del conjunto de datos mediante la función LOAD DATA por medio de Terminal o de la aplicación web.
  • Eliminación del conjunto de datos de forma programada mediante Terminal o la aplicación web.
  • Consulta de forma dinámica de los datos importados desde la aplicación web.
  • Funcionalidad para importar datos en formato CSV, Excel o PDF.
  • Aplicación web para consultar el estado del conjunto de datos.
  • Alguno o todos los conjuntos de datos se pueden instalar o eliminar mediante un solo comando.
  • Uso de las tablas %SQL_Diag.Result y %SQL_Diag.Message con LOAD DATA

Se recomienda leer la documentación relacionada LOAD DATA (SQL).

 

Cómo instalar o eliminar cualquier conjunto de datos desde Terminal

Utiliza el siguiente comando para importar un conjunto de datos en particular al introducir su ID o utiliza 999 para importar todos los conjuntos de datos

do ##class(dc.data.medical.utility).ImportDS(1)

A continuación se muestra el script principal para crear la tabla de forma dinámica y cargar los datos mediante la función LOAD DATA. Ten en cuenta que la tabla se crea de forma dinámica 

//Get file name 
SET filename=tRS.Get("Name")
//Remove .csv from the file name
SET tableName=$REPLACE("dc_data_"_ds_"."_tRS.Get("ItemName"),".csv","") 
//Get columns based on the header row of csv file
Do ##class(dc.data.medical.utility).GetColTypes(filename,.coltype) 
//Dynamically create table based on tablename and column types
SET qry = "CREATE TABLE "_tableName_" ("_coltype_")"
SET rset = ##class(%SQL.Statement).%ExecDirect(,qry)
//Check if table created successfully
IF rset.%SQLCODE
{
   WRITE "ERROR : ",rset.%Message,!    
}
ELSE
{
  //Dynamically construct LOAD DATA statement
  SET qry = "LOAD DATA FROM FILE  '"_filename_"' INTO "_tableName_ " "_"USING {""from"":{""file"":{""header"":""1""}}}"
  SET rset = ##class(%SQL.Statement).%ExecDirect(,qry)
  // Check result set sqlcode, In case of error write resultset message
  IF rset.%SQLCODE
  {
   WRITE "ERROR Table : " _tableName_" IMPORT FAILED: ",rset.%Message,!
  }
  ELSE
  {
  WRITE "SUCCESS table : " _tableName_" created and "_rset.%ROWCOUNT_" Rows Imported Successfully",!
  }
}

Utiliza el siguiente comando para eliminar un conjunto de datos en particular al introducir su ID o utiliza 999 para eliminar todos los conjuntos de datos
 

do ##class(dc.data.medical.utility).RemoveDS(1)

 

A continuación se muestra el script principal para eliminar la tabla de forma dinámica

//Get file name
SET filename=tRS.Get("Name")
//Remove .csv from file name
SET tableName=$REPLACE("dc_data_"_ds_"."_tRS.Get("ItemName"),".csv","")
//Drop table
SET qry = "DROP TABLE "_tableName
SET rset = ##class(%SQL.Statement).%ExecDirect(,qry)
//Check if table deleted successfully
IF rset.%SQLCODE
{
  WRITE "ERROR : ",rset.%Message,!          
}
ELSE
{
  WRITE "Table "_tableName_" deleted successfully",!
}

 

Cómo instalar o eliminar cualquier conjunto de datos desde una aplicación web

Ve al panel de control de la aplicación: http://localhost:52773/csp/datasets/index.csp  

Si el conjunto de datos no está instalado, el botón "Install DataSet" estará visible y si el conjunto de datos está instalado, el botón "Remove Dataset" estará visible.

Solo hay que hacer clic en el botón adecuado para instalar o eliminar cualquier conjunto de datos

Cómo visualizar y exportar datos desde Dataset

Ve a la página View Datasets http://localhost:52773/csp/datasets/datasets.csp

Selecciona un conjunto de datos específico y después una tabla de la lista. Haz clic en el botón Excel, CSV o PDF para exportar los datos.

Espero que os resulte útil.

0
0 648
Artículo Jean Millette · mar 14, 2022 2m read

Una de nuestras apps utiliza una consulta de clase para un informe ZEN y funciona perfectamente en ese informe, produciendo los resultados esperados. Hemos migrado a InterSystems Reports y nos hemos dado cuenta de que, para un informe que utiliza la misma consulta de clase, aparecen en la parte de abajo más de 100 filas extra con los mismos valores en las columnas.

Descartamos InterSystems Reports como fuente del problema reproduciendo el problema de "filas extra" con una hoja de cálculo de Excel que llama a la misma consulta de clase utilizando un procedimiento almacenado.

¿Cuál era el problema? Cuando llamábamos al procedimiento almacenado desde el antiguo ZEN Report o desde la función SQL Query en el Portal de Administración, no veíamos estas filas adicionales.

0
0 181
Artículo Muhammad Waseem · mar 10, 2022 2m read

¡Hola comunidad!

En esta publicación voy a explicar cómo mostrar datos en internet usando Python embebidoMarco web Python Flask y jQuery DataTable.
image

Mostraré los procesos desde la tabla %SYS.ProcessQuery.

Paso 1: Añade la tabla a la página HTML y escribe debajo el código javascript para mostrar los datos pasados desde app.py :

HTML

  <table id="myTable" class="table table-bordered table-striped">                 
   </table>

Javascript

0
0 1080
Artículo Ricardo Paiva · feb 24, 2022 2m read

¡Hola desarrolladores!

Como probablemente sabéis, en IRIS 2021 los nombres de los globals son aleatorios.

Y si creas clases de IRIS con DDL y quieres estar seguro de qué global se creó, seguramente te gustaría darle un nombre.

De hecho, se puede hacer. 

Usa WITH %CLASSPARAMETER DEFAULTGLOBAL='^GLobalName' en la Tabla CREATE para que funcione. Documentación. Mirad este ejemplo:

0
0 150
Pregunta Daniel Aguilar · oct 20, 2020

Buenas tardes, 

Hace ya tiempo que tengo esta duda y no sé si alguno sabrá la respuesta. Cuando realizo un insert por SQL desde una aplicación externa en los campos de tipo %String si están vacíos me graba el caracter $c(0) en el global. 

Revisando la documentación he visto que existe una propiedad para las clases que extienden de %XML.Adapter que si sobreescribes el parametro XMLIGNORENULL = 1 puedes hacer que guarde cadenas vacías en lugar de nulos.

He intentado hacer que mis clases extiendan de %XML.Adapter pero si sobreescribo la propiedad sigue haciendo lo mismo, ejemplo:

4
0 255
Artículo Alberto Fuentes · ene 18, 2022 7m read

Benjamin De Boe escribió este magnífico artículo sobre las Consultas universales en caché, pero ¿qué es una Consulta universal en caché (UCQ) y por qué deberían interesarme, si yo escribo en el antiguo y válido SQL embebido? En Caché y Ensemble, las consultas en caché o cacheadas se generaban para resolver xDBC y SQL dinámico. Ahora, en InterSystems IRIS, SQL embebido se ha sido actualizado para utilizar las consultas cacheadas (Cached Queries), de ahí que se añadiera la palabra "universal" en el nombre. Actualmente, cualquier SQL que se ejecute en IRIS lo hará desde una clase UCQ.

0
0 227
Artículo Ricardo Paiva · dic 3, 2021 2m read

Si defines una tabla/clase persistente, el compilador de clases genera una definición de almacenamiento adecuada. Otra opción es definir un mapeo SQL para un almacenamiento global que ya existe.  Esto ya se explicó estupendamente en otra serie de artículos:  El arte del mapeo de globales para Clases 1 de 3

Después de definir el diagrama de almacenamiento, el compilador de la clase lo podrá ampliar, pero los parámetros fundamentales de almacenamiento no cambiarán.
Esto no significa que no puedas cambiarlo manualmente.

Mi artículo El mapa de bits adoptado incluye un caso como este. Y en combinación con algunas Condiciones WHERE estáticas, como se describió anteriormente, permite que esté disponible también para SQL.

La definición habitual de tus globals de almacenamiento se parecerá a este ejemplo:

<DataLocation>^User.PersonD</DataLocation>
<IdLocation>^User.PersonD</IdLocation>
<IndexLocation>^User.PersonI</IndexLocation>
<StreamLocation>^User.PersonI</StreamLocation
>

Ahora cambiaremos esta definición por una dinámica

<DataLocation>@%MyData</DataLocation>
<IdLocation>@%MyId</IdLocation>
<IndexLocation>@%MyIndex</IndexLocation>
<StreamLocation>@%MyStream</StreamLocation>

y añadimos algo de comodidad

Parameter MANAGEDEXTENT As INTEGER = 0;
ClassMethod SetStorage(ref As %String) As %Integer [ SqlName = SetStorage, SqlProc ]
{
    set %MyData=ref_"D"
      , %MyId=%MyData
      , %MyIndex=ref_"I"
      , %MyStream=ref_"S"
    quit $$$OK
}

Para acceder a los objetos, dirigimos nuestro almacenamiento y lo llenamos

write ##class(Person).SetStorage("^mtemp.Person")
write ##class(Person).Populate(5)

y en SQL:

SELECT  from Person where SetStorage('^mtemp.Person')=1

Funciona con PPG  (^||Person)
mediante el namespace (^|"USER"|Person)
también corresponde al subíndice que se utilizó en El mapa de bits adoptado  con otra variante de ClassMethod SetStorage() 

Para acceso a objetos (sin SQL), funciona incluso para variables locales.
No es el uso deseado, pero demuestra la flexibilidad de esta función. 

Pero ten cuidado. NO funcionará con sharding. Pero eso no es una sorpresa.

0
0 222
Anuncio Esther Sanchez · nov 25, 2021

¡Hola desarrolladores!

Os traemos la primera ponencia del Virtual Summit 2021, ya disponible en el Canal de YouTube de la Comunidad de Desarrolladores en inglés.

El idioma del vídeo es el inglés y podéis activar los subtítulos en inglés si os resulta más fácil entender el vídeo leyendo el texto. Solo tenéis que hacer clic en el icono de subtítulos abajo:

 

Introducción a Python Embebido (Embedded Python)

0
0 425
Artículo Eduardo Anglada · nov 22, 2021 3m read

¡Hola amigos!

A veces necesitamos importar datos a InterSystems IRIS desde archivos de tipo CSV. Esto puede hacerse, por ejemplo, mediante la herramienta csvgen, que genera una clase e importa todos los datos a ella.

Pero, ¿qué pasa si tienes tu propia clase y quieres importar datos desde un archivo CSV a una tabla que ya existe previamente?

Hay varias formas de hacerlo, ¡pero puedes utilizar csvgen (o csvgen-ui) otra vez! Preparé un ejemplo y estoy encantado de compartirlo con todos. ¡Vamos allá!

El concepto es el siguiente: Tengo la clase A y quiero los datos del archivo.csv, ya que este archivo contiene una columna que necesito para mi clase.

Los pasos son los siguientes:

  1. Crear la clase B utilizando csvgen
  2. Efectuar una actualización en SQL para añadir los datos de la clase B en la clase A
  3. Eliminar la clase B

Para explicar el concepto, creé una demostración sencilla del proyecto. El proyecto importa el conjunto de datos Countries, que contiene la clase dc_data.Country con información variada sobre los países, incluido el GNP (PIB en español).

ClassMethod ImportCSV() As %Status

{

set sc = ##class(community.csvgen).GenerateFromURL("https://raw.githubusercontent.com/evshvarov/test-ipad/master/gnp.csv",",","dc.data.GNP")

Return sc

}

Pero los datos del GNP están desactualizados y los más recientes se encuentran en este CSV. Este es el método que muestra el GNP, por ejemplo, para Angola:

ClassMethod ShowGNP() As %Status

{

Set sc = $$$OK

&sql(

SELECT TOP 1 name,gnp into :name,:gnp from dc_data.Country

)

if SQLCODE &lt; 0 throw ##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,"Show Country GNP")

write "Country ",name," gnp=",gnp,!

Return sc

}

Entonces importo el CSV en una clase generada con una línea:

ClassMethod ImportCSV() As %Status

{

set sc = ##class(community.csvgen).GenerateFromURL("https://raw.githubusercontent.com/evshvarov/test-ipad/master/gnp.csv",",","dc.data.GNP")

Return sc

}

y con la segunda línea realizo una consulta en SQL que importa los datos actualizados del GNP a mi clase dc_data.Country.

ClassMethod UpdateGNP() As %Status

{

Set sc = $$$OK

&sql(

UPDATE dc_data.Country

SET Country.gnp=GNP."2020"

FROM

dc_data.Country Country

INNER JOIN dc_data.GNP GNP

On Country.name=GNP.CountryName

)



if SQLCODE &lt; 0 throw ##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,"Importing data")



w "Changes to GNP are made from dc.data.GNP",!



Return sc

}

Y luego, elimino la clase generada (con todos sus datos) porque ya no la necesito.

ClassMethod DropGNP() As %Status

{

Set sc = $$$OK

&sql(

DROP TABLE dc_data.GNP

)

if SQLCODE &lt; 0 throw ##class(%Exception.SQL).CreateFromSQLCODE(SQLCODE,"Drop csv table")

write "dc.data.DNP class is deleted.",!



Return sc

}

Y este es el método que hace todo al mismo tiempo:

ClassMethod RunAll() As %Status

{

Set sc = $$$OK

zw ..ImportDataset()

zw ..ShowGNP()

zw ..ImportCSV()

zw ..UpdateGNP()

zw ..ShowGNP()

zw ..DropGNP()

Return sc

}

Por supuesto, esta solo es una de las formas de solucionar el problema, pero espero que sea útil. ¡Me encantaría recibir vuestros comentarios!

0
0 188
Artículo Muhammad Waseem · oct 20, 2021 2m read

En este artículo demostraré lo siguiente:

  • Cómo actualizar ReferencesRange (OBX: 7) contra ObservationIdentifier (OBX: 3.1) [TestCode] de la base de datos mediante la función de utilidad personalizada
  • Cómo actualizar Abnormal Flag (OBX: 8) contra ObservationIdentifier (OBX: 3.1) [TestCode] y ObservationValue (OBX: 5) [Resultado] desde la función de utilidad de base de datos
  • Mensaje de ruta basado en un Abnormal Flag (OBX: 8)
0
0 142
Anuncio Esther Sanchez · oct 4, 2021

¡Hola Comunidad!

Hemos grabado el webinar que hicimos la semana pasada y lo hemos subido al canal de YouTube de la Comunidad de Desarrolladores en español. Si os perdisteis el webinar o lo queréis volver a ver con más detalle, ya está disponible la grabación!

Eduardo Anglada ha trabajado diez años en la Agencia Espacial Europea y nos descubrió muchas cosas interesantes sobre el espacio profundo. Así que... si queréis descubrir de forma práctica cómo crear modelos de Machine Learning, ¡no os perdáis el vídeo!

IntegratedML - Cómo crear modelos de Machine Learning en minutos

0
0 78
Anuncio Esther Sanchez · sep 15, 2021

¡Hola desarrolladores!

Os invitamos a un nuevo webinar en español: "IntegratedML - Cómo crear modelos de Machine Learning en minutos", el jueves 30 de septiembre, a las 4:00 PM (CEST).

 

El webinar está dirigido a programadores que quieran empezar a crear modelos de Machine Learning (no hace falta ser un experto, con saber un mínimo de SQL es suficiente).

1
0 571
Artículo Eduardo Anglada · sep 7, 2021 3m read
Este es un ejemplo de código que funciona en IRIS 2020.1 y en Caché 2018.1.3 
No se mantendrá sincronizado con las nuevas versiones.  
Y NO cuenta con el servicio de soporte de InterSystems.
De vez en cuando, puedes encontrarte una situación en la que, por diferentes razones, ODBC es la única opción para acceder a un sistema remoto. Lo cual es suficiente mientras necesites examinar o cambiar tablas. Pero no puedes ejecutar directamente algunos comandos o cambiar algunos globals.
0
0 154
Artículo Eduardo Anglada · ago 12, 2021 3m read
Este es un ejemplo de código que funciona en IRIS 2020.1 y en Caché 2018.1.3 
No se mantendrá sincronizado con las nuevas versiones.      
Y NO cuenta con el servicio de soporte de InterSystems.

En la mayoría de los casos, un global que se utiliza como almacenamiento predeterminado tiene solo 1 nivel de subíndice que representa el IDKEY.
Para un índice de globals podemos ver 2 o más niveles de subíndices.
Las matrices, las relaciones de herencia padre hijo o las clases persistentes que extienden una clase base, son ejemplos en los que vemos más niveles. Aunque todos estos globals son bastante uniformes.

Y después vemos globals que no están relacionados con clases o tablas como ^SPOOL,  ^ERRORS, o ^%SYS, en los que la estructura depende de varios niveles de subíndices con un significado especial.

El análisis de esos globals no convencionales es un reto y el simple hecho de descargarlos no necesariamente ayuda a comprender las dependencias.

Este ejemplo está orientado al viejo chiste:  "¿Cómo se come un elefante?" ==> "¡Cortándolo en porciones!"

Esa es la oferta:
Por medio de una sentencia SQL, se puede mostrar la estructura de cualquier global por niveles.
Tú proporcionas el nombre del global y el nivel máximo que mostrará, y obtienes los subíndices relacionados, $Data del nodo, la cantidad de los siguientes subnodos y, si está disponible, el contenido almacenado en ese nivel.

Por ejemplo: SELECT * FROM rcc_G.scan where rcc_G.Scan('^%SYS',1)=1

Reference       Level $D SubN  Value
^%SYS               0   10 25    
("CSP")             1   10       
("CSPAppSec")       1   1       64
("CacheTempDir")    1   1      "c:\intersystems\iris\mgr\iristemp\"
("DBRefByName")     1   10       
("Ensemble")        1   11     "2020-04-25 18:37:18"
("ErrorPurge")      1   1      30
("FIPSMode")        1   1      0
("IRISTempDir")     1   1      "c:\intersystems\iris\mgr\iristemp\"
("JOURNAL")         1   11     0
("LANGUAGE")        1   10       
("LASTSESSIONGUID") 1   1     "EÊcRù¢GM£ô"_$c(127)_"¹9¾ÒÆ"
("LOCALE")          1   10       
("ModuleRoot")      1   10       
("NLS")             1   10       
("SERVICE")         1   10       
("SSPort")          1   1     51773
("StreamLocation")  1   10       
("SystemMode")      1   1     "TEST"
("TempDir")         1   1     "C:\InterSystems\IRIS\mgr\Temp"
("WebServer")       1   10       
("bindir")          1   1     "c:\intersystems\iris\bin\"
("shutdownlogerrors") 1 1 0
("sql")             1   10       
("sysdir")          1   1     "c:\intersystems\iris\mgr\"
("tercap")          1   10       
26 lines

SELECT * FROM rcc_G.scan where rcc_G.Scan('^%SYS',2)=1

Reference                   Level $D SubN   Value
^%SYS                           0   10 25  
("CSP")                         1   10 1  
("CSP","LastUpdate")            2   1        "65553,44452"
("CSPAppSec")                   1   1  0     64
("CacheTempDir")                1   1  0     "c:\intersystems\iris\mgr\iristemp\"
("DBRefByName")                 1   10 9     
("DBRefByName","CACHE")         2   1        "^^c:\intersystems\iris\mgr\cache\"
("DBRefByName","CACHEUSER")     2   1        "^^c:\intersystems\user\"
("DBRefByName","ENSLIB")        2   1        "^^c:\intersystems\iris\mgr\enslib\"
("DBRefByName","IRISAUDIT")     2   1        "^^c:\intersystems\iris\mgr\irisaudit\"
("DBRefByName","IRISLIB")       2   1        "^^c:\intersystems\iris\mgr\irislib\"
("DBRefByName","IRISLOCALDATA") 2   1        "^^c:\intersystems\iris\mgr\irislocaldata\"
   &lt; 60 lines more >

SELECT * FROM rcc_G.scan where rcc_G.Scan('^ERRORS',37)=1 and id['Jour'

Reference                                            Level    $D  SubNodes    Value
(65588,1,"*STACK",1,"V","%00000","N","""JournalState""")    8   1    0         4
(65592,1,"*STACK",1,"V","%00000","N","""JournalState""")    8   1    0         4
(65592,2,"*STACK",1,"V","%00000","N","""JournalState""")    8   1    0         4
0
0 175