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 Alberto Fuentes · mar 1, 2024 4m read

Hay una nueva funcionalidad muy interesante en la recientemente anunciada versión de prueba 2024.1 de InterSystems IRIS - JSON_TABLE. JSON_TABLE forma parte de una familia de funciones introducidas en la versión 2016 del estándar SQL (ISO Standard, publicado a principios de 2017). Permite que valores JSON se mapeen a columnas y se consulten usando SQL. JSON_TABLE puede usarse en la cláusula FROM de algunas sentencias SQL.

La sintaxis de JSON_TABLE es bastante extensa, permitiendo condiciones excepcionales cuando los valores JSON proporcionados no cumplen las expectativas, estructuras anidadas, etc.

La sintaxis básica es bastante sencilla. El primer argumento pasado a JSON_TABLE es el valor JSON que se va a consultar. Puede ser un literal, una referencia a una columna o una llamada a una función. El segundo argumento es una expresión JPL (JSON Path Language), que permite filtrar el valor proporcionado. Después va la cláusula COLUMNS - justo en medio de la llamada a la función. La cláusula COLUMNS se parece mucho a las definiciones de columnas en una sentencia CREATE TABLE pero con un añadido muy necesario - el PATH. PATH es una expresión JPL que se aplica al valor JSON para producir el valor de la columna.

Este es un ejemplo para demostrar un mapeo sencillo:

SELECT * 
    FROM JSON_TABLE('[{"name":"New York","capital":"Albany"},{"name":"Wyoming","capital":"Cheyenne"}]', '$'COLUMNS(
                state_name VARCHAR(40) PATH'$.name',
                capital_name VARCHAR(40) PATH'$.capital'
            )
        ) as s

 

state_namecapital_name
New YorkAlbany
WyomingCheyenne

2 Rows(s) Affected

JSON_TABLE también permite que una columna proporcione el valor JSON. Este valor después se puede volver a unir a la fila que lo contenía, usando algo llamado una lateral join (unión lateral). Este ejemplo crea una tabla sencilla y la rellena con unas pocas filas con valores interesantes.

DROPTABLE demo_intersystems.Person;

CREATETABLE demo_intersystems.Person ( nameVARCHAR(40), street VARCHAR(60), city VARCHAR(30), state VARCHAR(4), postal_code VARCHAR(10), phone_nbrs VARCHAR(2000) );

INSERTINTO demo_intersystems.Person (name, street, city, state, postal_code, phone_nbrs) VALUES ( 'Caroline', 'One Memorial', 'Cambridge', 'MA', '02142', '[{"type":"office","number":"(617) 225-5555"},{"type":"mobile","number":"(617) 555-1111"}]' );

INSERTINTO demo_intersystems.Person (name, street, city, state, postal_code, phone_nbrs) VALUES ( 'Doe, John', '123 Main Street', 'Cambridge', 'MA', '02142', '[{"type":"mobile","country_code":"1","number":"999-999-9999"},{"type":"work","country_code":"1","number":"888-888-8888"}]' );

INSERTINTO demo_intersystems.Person (name, street, city, state, postal_code, phone_nbrs) VALUES ( 'Smith, Georgia', '100 Privet Lane, Unit 20', 'Melrose', 'MA', '02176', '[{"type":"mobile","country_code":"1","number":"555-867-5309"},{"type":"home","country_code":"1","number":"555-123-1234"},{"type":"office","number":"555-000-0000"}]' );

INSERTINTO demo_intersystems.Person (name, street, city, state, postal_code, phone_nbrs) VALUES ( 'Jones, Thomas', '63 North Park Way', 'Princeton', 'NJ', '08540', '[{"type":"mobile","country_code":"1","number":"555-555-5555"},{"type":"work","country_code":"1","number":"555-BR5-4949"}]' );

SELECT p.%ID, p.name, p.street, p.city, p.state, p.postal_code, pn.phone_type, pn.country_code, pn.phone_nbr FROM demo_intersystems.Person p, JSON_TABLE(p.phone_nbrs, '$'COLUMNS( phone_type VARCHAR(10) path'$.type', country_code VARCHAR(8) path'$."country_code"', phone_nbr VARCHAR(12) path'$.number' ) ) pn;

| ID | name | street | city | state | postal_code | phone_type | country_code | phone_nbr | | -- | -- | -- | -- | -- | -- | -- | -- | -- | | 1 | Caroline | One Memorial | Cambridge | MA | 02142 | office | | (617) 225-5555 | | 1 | Caroline | One Memorial | Cambridge | MA | 02142 | mobile | | (617) 555-1111 | | 2 | Doe, John | 123 Main Street | Cambridge | MA | 02142 | mobile | 1 | 999-999-9999 | | 2 | Doe, John | 123 Main Street | Cambridge | MA | 02142 | work | 1 | 888-888-8888 | | 3 | Smith, Georgia | 100 Privet Lane, Unit 20 | Melrose | MA | 02176 | mobile | 1 | 555-867-5309 | | 3 | Smith, Georgia | 100 Privet Lane, Unit 20 | Melrose | MA | 02176 | home | 1 | 555-123-1234 | | 3 | Smith, Georgia | 100 Privet Lane, Unit 20 | Melrose | MA | 02176 | office | | 555-000-0000 | | 4 | Jones, Thomas | 63 North Park Way | Princeton | NJ | 08540 | mobile | 1 | 555-555-5555 | | 4 | Jones, Thomas | 63 North Park Way | Princeton | NJ | 08540 | work | 1 | 555-BR5-4949 |

9 Rows(s) Affected

0
0 160
Artículo Alberto Fuentes · ene 31, 2024 13m read

Tenemos un conjunto de datos bastante apetecible con recetas escritas por múltiples usuarios de Reddit, sin embargo, la mayor parte de la información está en texto libre en forma de título y descripción de un mensaje. Vamos a averiguar cómo podemos, de forma muy sencilla, cargar los datos, extraer algunas características y analizarlos empleando funcionalidades de LLM (Large Language Model) de OpenAI desde Python Embebido y el framework Langchain.

Cargar los datos

Lo primero es lo primero: ¿necesitamos cargar los datos o podemos sencillamente conectarnos a ellos?

Hay diferentes formas para plantearlo: por ejemplo, con el Mapeo de registros CSV que puedes utilizar en una producción de interoperabilidad o incluso instalar directamente una aplicación de OpenExchange como csvgen para que nos ayude.

Utilizaremos en este caso las Foreign Tables. Una funcionalidad muy útil para proyectar datos físicamente almacenados en otra parte y tenerlos accesibles desde el SQL de IRIS. Podemos utilizarlo directamente para echar un primer vistazo a los ficheros del conjunto de datos.

Creamos un Foreign Server:

CREATE FOREIGN SERVER dataset FOREIGN DATA WRAPPER CSV HOST '/app/data/'

Y a continuación, creamos una Foreign Table que se conecta al fichero CSV:

CREATE FOREIGN TABLE dataset.Recipes (
  CREATEDDATE DATE,
  NUMCOMMENTS INTEGER,
  TITLE VARCHAR,
  USERNAME VARCHAR,
  COMMENT VARCHAR,
  NUMCHAR INTEGER
) SERVER dataset FILE 'Recipes.csv' USING
{
  "from": {
    "file": {
       "skip": 1
    }
  }
}

¡Y ya está!, inmediatamente podemos lanzar consultas SQL sobre dataset.Recipes: image

## ¿Qué datos necesitamos? Los datos son muy interesantes y tenemos hambre. Sin embargo, si queremos decidir qué receta vamos a cocinar necesitamos algo más de información que podamos utilizar para analizar las recetas.

Vamos a trabajar con dos clases persistentes (tablas):

  • yummy.data.Recipe: una clase que contiene el título y la descripción de la receta así como algunas otras propiedades que queremos extraer y analizar (por ejemplo: Score, Difficulty, Ingredients, CuisineType, PreparationTime).
  • yummy.data.RecipeHistory: una clase sencilla para anotar un registro de qué estamos haciendo con la receta.

Podemos ahora cargar en nuestras tablas yummy.data* el contenido del conjunto de datos de recetas:

do ##class(yummy.Utils).LoadDataset()

Hasta aquí tiene buena pinta, pero aún debemos averiguar cómo vamos a generar los datos para los campos como: Score, Difficulty, Ingredients, PreparationTime and CuisineType.

## Analizar las recetas Queremos procesar el título y la descripción de cada receta y:

  • Extraer información como Difficulty, Ingredients, CuisineType, etc.
  • Construir nuestra propia puntuación de la receta basada en nuestro criterio, de forma que podamos decidir qué vamos a cocinar.

Vamos a utilizar lo siguiente:

  • yummy.analysis.Analysis - una estructura genérica de análisis que vamos a re-utilizar en caso de que queramos implementar diferentes tipos de análisis.
  • yummy.analysis.SimpleOpenAI - un análisis que usa Python Embebido + framework Langchain + OpenAI LLM.

Los LLM (Large Language Models) son una herramienta realmente increíble para procesar lenguaje natural.

LangChain está preparado para trabajar con Python, así que podemos utilizarlo directamente en InterSystems IRIS a través de Embedded Python.

La clase SimpleOpenAI tiene esta pinta:

/// Simple OpenAI analysis for recipes
Class yummy.analysis.SimpleOpenAI Extends Analysis
{

Property CuisineType As %String;

Property PreparationTime As %Integer;

Property Difficulty As %String;

Property Ingredients As %String;

/// Run
/// You can try this from a terminal:
/// set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8))
/// do a.Run()
/// zwrite a
Method Run()
{
    try {
        do ..RunPythonAnalysis()

        set reasons = ""

        // mis estilos de cocina favoritos
        if "spanish,french,portuguese,italian,korean,japanese"[..CuisineType {
            set ..Score = ..Score + 2
            set reasons = reasons_$lb("It seems to be a "_..CuisineType_" recipe!")
        }

        // no quiero estar el día entero cocinando :)
        if (+..PreparationTime < 120) {
            set ..Score = ..Score + 1
            set reasons = reasons_$lb("You don't need too much time to prepare it") 
        }
        
        // bonus para mis ingredientes favoritos!
        set favIngredients = $listbuild("kimchi", "truffle", "squid")
        for i=1:1:$listlength(favIngredients) {
            set favIngred = $listget(favIngredients, i)
            if ..Ingredients[favIngred {
                set ..Score = ..Score + 1
                set reasons = reasons_$lb("Favourite ingredient found: "_favIngred)
            }
        }

        set ..Reason = $listtostring(reasons, ". ")

    } catch ex {
        throw ex
    }
}

/// Update recipe with analysis results
Method UpdateRecipe()
{
    try {
        // call parent class implementation first
        do ##super()

        // add specific OpenAI analysis results
        set ..Recipe.Ingredients = ..Ingredients
        set ..Recipe.PreparationTime = ..PreparationTime
        set ..Recipe.Difficulty = ..Difficulty
        set ..Recipe.CuisineType = ..CuisineType

    } catch ex {
        throw ex
    }
}

/// Run analysis using embedded Python + Langchain
/// do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(8)).RunPythonAnalysis(1)
Method RunPythonAnalysis(debug As %Boolean = 0) [ Language = python ]
{
    # load OpenAI APIKEY from env
    import os
    from dotenv import load_dotenv, find_dotenv
    _ = load_dotenv('/app/.env')

    # account for deprecation of LLM model
    import datetime
    current_date = datetime.datetime.now().date()
    # date after which the model should be set to "gpt-3.5-turbo"
    target_date = datetime.date(2024, 6, 12)
    # set the model depending on the current date
    if current_date > target_date:
        llm_model = "gpt-3.5-turbo"
    else:
        llm_model = "gpt-3.5-turbo-0301"

    from langchain.chat_models import ChatOpenAI
    from langchain.prompts import ChatPromptTemplate
    from langchain.chains import LLMChain

    from langchain.output_parsers import ResponseSchema
    from langchain.output_parsers import StructuredOutputParser

    # init llm model
    llm = ChatOpenAI(temperature=0.0, model=llm_model)

    # prepare the responses we need
    cuisine_type_schema = ResponseSchema(
        name="cuisine_type",
        description="What is the cuisine type for the recipe? \
                     Answer in 1 word max in lowercase"
    )
    preparation_time_schema = ResponseSchema(
        name="preparation_time",
        description="How much time in minutes do I need to prepare the recipe?\
                     Anwer with an integer number, or null if unknown",
        type="integer",
    )
    difficulty_schema = ResponseSchema(
        name="difficulty",
        description="How difficult is this recipe?\
                     Answer with one of these values: easy, normal, hard, very-hard"
    )
    ingredients_schema = ResponseSchema(
        name="ingredients",
        description="Give me a comma separated list of ingredients in lowercase or empty if unknown"
    )
    response_schemas = [cuisine_type_schema, preparation_time_schema, difficulty_schema, ingredients_schema]

    # get format instructions from responses
    output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
    format_instructions = output_parser.get_format_instructions()
    
    analysis_template = """\
    Interprete and evaluate a recipe which title is: {title}
    and the description is: {description}
    
    {format_instructions}
    """
    prompt = ChatPromptTemplate.from_template(template=analysis_template)

    messages = prompt.format_messages(title=self.Recipe.Title, description=self.Recipe.Description, format_instructions=format_instructions)
    response = llm(messages)

    if debug:
        print("======ACTUAL PROMPT")
        print(messages[0].content)
        print("======RESPONSE")
        print(response.content)

    # populate analysis with results
    output_dict = output_parser.parse(response.content)
    self.CuisineType = output_dict['cuisine_type']
    self.Difficulty = output_dict['difficulty']
    self.Ingredients = output_dict['ingredients']
    if type(output_dict['preparation_time']) == int:
        self.PreparationTime = output_dict['preparation_time']

    return 1
}

}

El método RunPythonAnalysis es donde sucede todo lo relativo a OpenAI :). Puedes probarlo directamente desde tu terminal utilizando una receta en particular:

do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)

Obtendremos un resultado como el siguiente:

USER>do ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12)).RunPythonAnalysis(1)
======ACTUAL PROMPT
                    Interprete and evaluate a recipe which title is: Folded Sushi - Alaska Roll
                    and the description is: Craving for some sushi but don't have a sushi roller? Try this easy version instead. It's super easy yet equally delicious!
[Video Recipe](https://www.youtube.com/watch?v=1LJPS1lOHSM)
# Ingredients
Serving Size:  \~5 sandwiches      
* 1 cup of sushi rice
* 3/4 cups + 2 1/2 tbsp of water
* A small piece of konbu (kelp)
* 2 tbsp of rice vinegar
* 1 tbsp of sugar
* 1 tsp of salt
* 2 avocado
* 6 imitation crab sticks
* 2 tbsp of Japanese mayo
* 1/2 lb of salmon  
# Recipe     
* Place 1 cup of sushi rice into a mixing bowl and wash the rice at least 2 times or until the water becomes clear. Then transfer the rice into the rice cooker and add a small piece of kelp along with 3/4 cups plus 2 1/2 tbsp of water. Cook according to your rice cookers instruction.
* Combine 2 tbsp rice vinegar, 1 tbsp sugar, and 1 tsp salt in a medium bowl. Mix until everything is well combined.
* After the rice is cooked, remove the kelp and immediately scoop all the rice into the medium bowl with the vinegar and mix it well using the rice spatula. Make sure to use the cut motion to mix the rice to avoid mashing them. After thats done, cover it with a kitchen towel and let it cool down to room temperature.
* Cut the top of 1 avocado, then slice into the center of the avocado and rotate it along your knife. Then take each half of the avocado and twist. Afterward, take the side with the pit and carefully chop into the pit and twist to remove it. Then, using your hand, remove the peel. Repeat these steps with the other avocado. Dont forget to clean up your work station to give yourself more space. Then, place each half of the avocado facing down and thinly slice them. Once theyre sliced, slowly spread them out. Once thats done, set it aside.
* Remove the wrapper from each crab stick. Then, using your hand, peel the crab sticks vertically to get strings of crab sticks. Once all the crab sticks are peeled, rotate them sideways and chop them into small pieces, then place them in a bowl along with 2 tbsp of Japanese mayo and mix until everything is well mixed.
* Place a sharp knife at an angle and thinly slice against the grain. The thickness of the cut depends on your preference. Just make sure that all the pieces are similar in thickness.
* Grab a piece of seaweed wrap. Using a kitchen scissor, start cutting at the halfway point of seaweed wrap and cut until youre a little bit past the center of the piece. Rotate the piece vertically and start building. Dip your hand in some water to help with the sushi rice. Take a handful of sushi rice and spread it around the upper left hand quadrant of the seaweed wrap. Then carefully place a couple slices of salmon on the top right quadrant. Then place a couple slices of avocado on the bottom right quadrant. And finish it off with a couple of tsp of crab salad on the bottom left quadrant. Then, fold the top right quadrant into the bottom right quadrant, then continue by folding it into the bottom left quadrant. Well finish off the folding by folding the top left quadrant onto the rest of the sandwich. Afterward, place a piece of plastic wrap on top, cut it half, add a couple pieces of ginger and wasabi, and there you have it.
                    
                    The output should be a markdown code snippet formatted in the following schema, including the leading and trailing "```json" and "```":
json
{
        "cuisine_type": string  // What is the cuisine type for the recipe?                                  Answer in 1 word max in lowercase
        "preparation_time": integer  // How much time in minutes do I need to prepare the recipe?                                    Anwer with an integer number, or null if unknown
        "difficulty": string  // How difficult is this recipe?                               Answer with one of these values: easy, normal, hard, very-hard
        "ingredients": string  // Give me a comma separated list of ingredients in lowercase or empty if unknown
}

                    
======RESPONSE
json
{
        "cuisine_type": "japanese",
        "preparation_time": 30,
        "difficulty": "easy",
        "ingredients": "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
}

Tiene muy buena pinta. Parece que nuestro prompt o pregunta a OpenAI es capaz de devolvernos información que realmente podemos utilizar. Vamos a ejecutar el análisis completo desde el terminal:

set a = ##class(yummy.analysis.SimpleOpenAI).%New(##class(yummy.data.Recipe).%OpenId(12))
do a.Run()
zwrite a
USER>zwrite a
a=37@yummy.analysis.SimpleOpenAI  ; <OREF>
+----------------- general information ---------------
|      oref value: 37
|      class name: yummy.analysis.SimpleOpenAI
| reference count: 2
+----------------- attribute values ------------------
|        CuisineType = "japanese"
|         Difficulty = "easy"
|        Ingredients = "sushi rice, water, konbu, rice vinegar, sugar, salt, avocado, imitation crab sticks, japanese mayo, salmon"
|    PreparationTime = 30
|             Reason = "It seems to be a japanese recipe!. You don't need too much time to prepare it"
|              Score = 3
+----------------- swizzled references ---------------
|           i%Recipe = ""
|           r%Recipe = "30@yummy.data.Recipe"
+-----------------------------------------------------

## Analizar todas las recetas Naturalmente, querremos ejecutar el análisis para todas las recetas que hemos cargado.

Puedes analizar un rango de recetas (utilizando sus identificadores), de esta forma:

USER>do ##class(yummy.Utils).AnalyzeRange(1,10)
> Recipe 1 (1.755185s)
> Recipe 2 (2.559526s)
> Recipe 3 (1.556895s)
> Recipe 4 (1.720246s)
> Recipe 5 (1.689123s)
> Recipe 6 (2.404745s)
> Recipe 7 (1.538208s)
> Recipe 8 (1.33001s)
> Recipe 9 (1.49972s)
> Recipe 10 (1.425612s)

Después de eso, vamos a echar un vistazo de nuevo a la tabla de recetas y comprobemos los resultados:

select * from yummy_data.Recipe

image

Creo que podría intentar la Pizza con calabaza o el Tofu con Kimchi y cerdo al estilo coreano :). De todas formas, debo asegurarme y preguntar en casa antes de empezar a cocinar :)

Conclusión

Puedes encontrar el ejemplo completo en https://github.com/isc-afuentes/recipe-inspector

Con este ejemplo sencillo hemos aprendido cómo utilizar técnicas LLM para extraer características y analizar ciertas partes de nuestros datos en InterSystems IRIS.

Con esto como punto de partida, podrías plantearte cosas como:

  • Utilizar InterSystems BI para explorar y navegar tus datos utilizando cubos y cuadros de mando.
  • Crear una aplicación web y añadir una interfaz gráfica (e.g. con Angular) para esta aplicación, podrías utilizar paquetes como RESTForms2 para generar automáticamente APIs REST para tus clases persistentes.
  • ¿Qué tal si almacenas si una receta te gusta o no, y después intentas determinar si una nueva receta te gustará? Podrías plantearlo con IntegratedML, o incluso con LLM pasándole algunos datos de ejemplo e implementando un caso de uso tipo RAG (Retrieval Augmented Generation).

¿Qué otras cosas se os ocurren?

0
0 190
Artículo Alberto Fuentes · ene 24, 2024 2m read

¿Sabíais que se pueden obtener datos JSON directamente de las tablas SQL?

Dejadme que os presente dos útiles funciones de SQL, que se utilizan para recuperar datos JSON de consultas SQL - JSON_ARRAY y JSON_OBJECT

Podéis usar esas funciones en la sentencia SELECT con otros tipos de elementos de selección, y se pueden especificar en otras ubicaciones en las que se puede utilizar una función SQL, como en una condición WHERE.

La función JSON_ARRAY toma una lista de expresiones separadas por comas y devuelve una matriz JSON conteniendo esos valores.

1
1 777
Artículo Kurro Lopez · ene 17, 2024 3m read

Hola comunidad,

Cuando utilizamos un Business Servicio de tipo SQL, puede suceder que necesitemos replicar ciertas filas de la tabla original.

Tomemos como ejemplo el Business Service  "from customer SQL" usando la clase genérica EnsLib.SQL.Service.GenericService

Surgen diferentes casos, dependiendo de la configuración utilizada en este Business Service.

Primer caso:

Si el Business Service solo usa un KeyFieldName

En este caso, para reprocesar los IDs 123,456 y 789, bastará con eliminarlos de la variable global ^Ens.AppData del Business Service correspondiente en la referencia "adaptor.sqlrow":

0
0 113
InterSystems Official Jose-Tomas Salvador · ene 15, 2024

InterSystems se complace en anunciar la disponibilidadgeneral de InterSystems IRIS Cloud SQL e InterSystems IRIS Cloud IntegratedML, dos servicios fundamentales para el desarrollo de soluciones nativas en la nube, con la fiabilidad y el rendimiento probado y de primer nivel de la tecnología de InterSystems IRIS. 

InterSystems IRIS Cloud SQL es un servicio en la nube completamente administrado que acerca, a un amplio público de desarrolladores de aplicaciones y profesionales de datos, la potencia de las capacidades de InterSystems IRIS como base de datos relacional, usada por miles de organizaciones. InterSystems IRIS Cloud IntegratedML es una opción de "base de datos como servicio" (DBaaS), que ofrece un acceso sencillo a potentes funcionalidades de Machine Learning de forma nativa SQL, mediante un conjunto de sencillos comandos SQL que se pueden integrar fácilmente en el código de las aplicaciones para enriquecerlas con modelos de ML que se ejecutan cerca de los datos.

0
0 65
Artículo Daniel Aguilar · dic 11, 2023 1m read

Preguntas frecuentes de InterSystems

La cache de las consultas SQL puede ser purgada mediante programación usando el método Purge* de la clase %SYSTEM.SQL.

*Para ver el detalle de cada uno de los métodos, por favor consulta los siguientes enlaces:

%SYSTEM.SQL class【IRIS】

%SYSTEM.SQL class

① Comando para borrar todas las consultas SQL cacheadas en el sistema

Do $SYSTEM.SQL.PurgeAllNamespaces()


② Comando para borrar las consultas SQL cacheadas en el namespace actual

0
0 116
Artículo Robert Cemper · nov 28, 2023 1m read

Esta app evita añadir %JSONAdaptor a cada clase. En su lugar usa las funciones SQL JSON_OBJECT() para crear mis objetos JSON. Con este enfoque, se puede añadir JSON a cualquier clase - incluso a las implementadas - sin ninguna necesidad de cambiar o recompilar.

La idea inicial surge a raíz de implementar la exportación de relaciones M:N como objetos o matrices JSON.

La típica estructura de la exportación es  

{ M-element : {M-object},
   related-N-elements:
   [
     {N-element}, 
    {N-element},
    {N-element}
   ]
}

Los datos para la demo son un fragmento de los Miembros de la Comunidad de Desarrolladores y sus Insignias conseguidas en GlobalMasters. Los nombres reales están modificados.

0
0 175
Artículo Alberto Fuentes · jul 19, 2023 3m read

Este artículo es un sencillo ejemplo para probar SqlDatabaseChain pidiéndole a OpenAI cierta información y que escriba consultas SQL sobre una base de datos IRIS.

Quizá despierte el interés de alguno de vosotros.

Muchas gracias a sqlalchemy-iris (autor @Dmitry Maslennikov). Ese proyecto ha sido indispensable para esta prueba. 

El script de este artículo usa la API de OpenAI así que tenedlo en cuenta para no compartir la información de vuestras tablas externamente en el caso de que no queráis hacerlo. Podría llegar a implementarse un modelo local en caso que lo necesitaseis.

1
0 489
Artículo Alberto Fuentes · oct 5, 2023 9m read

En el vasto y variado mercado de las bases de datos SQL, InterSystems IRIS destaca como una plataforma que va mucho más allá de SQL, ofreciendo una experiencia multimodelo perfecta, y siendo compatible con un amplio conjunto de paradigmas de desarrollo. Especialmente el avanzado motor objeto-relacional ha ayudado a organizaciones a utilizar el enfoque de desarrollo más adecuado para cada una de sus cargas de trabajo intensivas en datos; por ejemplo, ingerir datos a través de Objetos y consultarlos simultáneamente mediante SQL. Las Clases Persistentes corresponden a tablas SQL, sus propiedades a columnas de la tabla y se accede fácilmente a la lógica de negocio utilizando Funciones Definidas por el Usuario o Procedimientos Almacenados. En este artículo, nos centraremos un poco en la magia que se encuentra justo debajo de la superficie y discutiremos cómo puede afectar vuestras prácticas de desarrollo e implementación. Esta es un área del producto que tenemos planificado evolucionar y mejorar, así que no dudéis en compartir vuestras opiniones y experiencias en los comentarios al artículo.

Cómo guardar la Definición de Almacenamiento

Escribir una nueva lógica de negocio es fácil, y suponiendo que tenéis APIs y especificaciones bien definidas, también lo es adaptarla o extenderla, generalmente. Pero cuando no se trata solo de lógica de negocio, sino que también involucra datos persistentes, cualquier cambio que se realice desde la versión inicial deberá poder utilizar los datos que se ingirieron a través de esa versión anterior.

En InterSystems IRIS, los datos y el código coexisten en un único motor de alto rendimiento, sin las numerosas capas de abstracción que se pueden encontrar en otros frameworks de programación 3GL o 4GL. Esto significa que solo hay un mapeo muy fino y transparente para convertir las propiedades de una clase a posiciones de $list en un nodo global por fila de datos cuando se utiliza el almacenamiento predeterminado. Si se añaden o eliminan propiedades, no se quiere que los datos de una propiedad eliminada aparezcan bajo una nueva propiedad. De este mapeo de las propiedades de clase es de lo que se encarga la Definición de Almacenamiento, un bloque XML algo críptico que se puede ver al final de la definición de clase. La primera vez que se compila una clase, se genera una nueva Definición de Almacenamiento basada en las propiedades y parámetros de la clase. Cuando se hacen cambios en la definición de clase, en el momento de la recompilación esos cambios se reconcilian con la Definición de Almacenamiento existente y se modifica para que siga siendo compatible con los datos existentes. Así que mientras uno se arriesga a refactorizar sus clases, la Definición de Almacenamiento considera cuidadosamente su creatividad anterior y se asegura de que tanto los datos antiguos como los nuevos sigan siendo accesibles. A esto le llamamos evolución del esquema.

En la mayoría de las otras bases de datos SQL, el almacenamiento físico de las tablas es mucho más opaco, si es que es visible, y los cambios solo se pueden realizar mediante sentencias ALTER TABLE. Son comandos DLL (Lenguaje de Definición de Datos) estándar, pero generalmente son mucho menos expresivos de lo que se puede lograr modificando directamente una definición de clase y código de procedimiento en IRIS. 

En InterSystems, nos esforzamos para ofrecer a los desarrolladores de IRIS la capacidad de separar de manera limpia su código y sus datos, ya que esto es crucial para garantizar un empaquetado e implementación sencilla de sus aplicaciones. La Definición de Almacenamiento juega un papel único en esto, ya que captura cómo se mapea uno al otro. Por eso, merece la pena analizarla más de cerca en el contexto de las prácticas generales de desarrollo y en particular en los flujos de CI/CD (Integración Continua/Entrega Continua).

Exportando a UDL

En el siglo actual, la administración del código fuente se basa en archivos, así que veamos primero el formato principal de exportación de archivos de IRIS. El Lenguaje de Descripción Universal (UDL) es, como su nombre indica, un formato de archivos universal para cualquier código que se escriba en InterSystems IRIS. Es el formato de exportación predeterminado cuando se trabaja con el plug-in ObjectScript-VS Code y genera archivos de fácil lectura que se asemejan casi exactamente a lo que se vería en un Entorno de Desarrollo Integrado (IDE), con un archivo .cls individual para cada clase (tabla) en la aplicación. Se puede utilizar $SYSTEM.OBJ.Export() para crear archivos UDL explícitamente, o simplemente aprovechar la integración con VS Code.

Desde la época de Studio, es posible que también recordéis un formato XML que capturaba la misma información que UDL y permitía agrupar varias clases en una sola exportación. Si bien esta última parte es conveniente en algunos escenarios, es mucho menos práctica para leer y rastrear diferencias entre versiones, por lo que lo ignoraremos por ahora.

Como UDL está diseñado para capturar todo lo que IRIS puede expresar sobre una clase, incluirá todos los elementos de una definición de clase, incluida la Definición de Almacenamiento completa. Al importar una definición de clase que ya incluye una Definición de Almacenamiento, IRIS verificará si esa Definición de Almacenamiento cubre todas las propiedades e índices de la clase y, en caso afirmativo, la tomará tal cual y sobrescribirá la Definición de Almacenamiento anterior para esa clase. Esto hace que UDL sea un formato práctico para la gestión de versiones de clases y sus Definiciones de Almacenamiento, ya que mantiene esa compatibilidad hacia atrás para los datos ingresados mediante versiones anteriores de la clase, sin importar dónde se implemente. 

Si eres un desarrollador aplicado, es posible que te preguntes si estas Definiciones de Almacenamiento siguen creciendo y si esta "carga" debe llevarse indefinidamente. El propósito de las Definiciones de Almacenamiento es preservar la compatibilidad con los datos preexistentes, por lo que si sabes que no hay ninguno de ellos y quieres deshacerte de una genealogía larga, puedes "reiniciar" tu Definición de Almacenamiento eliminándola de la definición de clase y permitiendo que el compilador de clase la genere de nuevo. Por ejemplo, puedes usar esto para aprovechar prácticas recomendadas más recientes, como el uso de Conjuntos de extensión, que implementan nombres globales hash y separan cada índice en su propio global, mejorando la eficiencia a bajo nivel. Para garantizar la compatibilidad con versiones anteriores dentro de las aplicaciones de los clientes, no podemos cambiar universalmente dichos valores predeterminados en la superclase %Persistent (aunque los aplicaremos al crear una tabla desde cero utilizando el comando DDL CREATE TABLE), por lo que vale la pena realizar una revisión periódica de tus clases y tu almacenamiento. También es posible editar directamente el XML de la Definición de Almacenamiento, pero los usuarios deben tener extrema precaución, ya que esto puede hacer que los datos existentes sean inaccesibles.

Hasta ahora, todo bien. Las Definiciones de Almacenamiento ofrecen un mapeo inteligente entre tus clases y se adaptan automáticamente a medida que el esquema evoluciona. ¿Qué más hay ahí?

¿Estático vs. Estadísticas?

Como probablemente sabes, el motor SQL de InterSystems IRIS hace uso avanzado de las estadísticas de tablas para identificar el plan de consulta óptimo para cualquier sentencia que el usuario ejecute. Las estadísticas de tablas incluyen métricas sobre el tamaño de una tabla, cómo se distribuyen los valores dentro de una columna y mucho más. Esta información ayuda al optimizador SQL de IRIS a decidir qué índice es más beneficioso, en qué orden unir tablas, etcétera. Así que, intuitivamente, cuanto más actualizadas estén las estadísticas, habrá más posibilidades de obtener planes de consulta óptimos. Desafortunadamente, hasta que introdujimos el muestreo rápido de bloques en IRIS 2021.2, recopilar estadísticas precisas de las tablas era una operación computacionalmente costosa. Por lo tanto, cuando los clientes implementaban la misma aplicación en muchos entornos en los que los patrones de datos eran en gran medida los mismos, tenía sentido considerar las estadísticas de las tablas como parte del código de la aplicación e incluirlas con las definiciones de las tablas.

Por eso en IRIS actualmente las estadísticas de las tablas se encuentran integradas dentro de la Definición de Almacenamiento. Cuando se recopilan estadísticas de tablas mediante una llamada manual a TUNE TABLE o de forma implícita mediante una consulta (ver más abajo), las nuevas estadísticas se escriben en la Definición de Almacenamiento, y los planes de consulta existentes para esta tabla se invalidan para que puedan aprovechar las nuevas estadísticas en la próxima ejecución. Como son parte de la Definición de Almacenamiento, estas estadísticas formarán parte de las exportaciones de clase UDL y, por lo tanto, pueden terminar en el repositorio de código fuente. En el caso de las estadísticas cuidadosamente revisadas para una aplicación empaquetada, esto es aconsejable, ya que se querrá que estas estadísticas específicas impulsen la generación de planes de consulta para todas las implementaciones de la aplicación.

Desde la versión 2021.2, IRIS recopila automáticamente estadísticas de tabla al inicio del plan de consultas cuando consulta una tabla que no tiene ningún tipo de estadísticas y es apta para el muestreo rápido de bloques. En nuestras pruebas, los beneficios de trabajar con estadísticas actualizadas frente a no tener estadísticas claramente superaron el coste de recopilar estadísticas en tiempo real. Sin embargo, para algunos clientes, esto ha tenido el desafortunado efecto secundario de que las estadísticas recopiladas automáticamente en la instancia del desarrollador terminan en la Definición de Almacenamiento en el sistema de control del código fuente y, finalmente, en la aplicación empaquetada. Obviamente, los datos en ese entorno de desarrollo y, por tanto, las estadísticas sobre ellos, pueden no ser representativas para una implementación real del cliente y conducir a planes de consulta no óptimos.

Este escenario es fácil de evitar. Las estadísticas de tablas se pueden excluir de la exportación de la definición de clase utilizando el calificador /exportselectivity=0 al llamar a $SYSTEM.OBJ.Export(). La configuración predeterminada del sistema para esta bandera se puede establecer utilizando [$SYSTEM.OBJ.SetQualifiers("/exportselectivity=0]. Luego se puede dejar que la recopilación automática, en la implementación final, recoja estadísticas representativas, haga que la recopilación explícita de estadísticas sea parte del proceso de implementación, lo que sobrescribirá cualquier cosa que pueda haber sido empaquetada con la aplicación, o administre las estadísticas de tablas por separado mediante sus propias funciones de importación/exportación: $SYSTEM.SQL.Stats.Table.Export() y Import().  

A largo plazo, tenemos la intención de trasladar las estadísticas de tablas para que convivan con los datos en vez de ser parte del código, y diferenciar de manera más clara entre las estadísticas configuradas explícitamente por un desarrollador y las recolectadas de los datos reales. Además, estamos planeando automatizar más el proceso de actualizar periódicamente esas estadísticas, en función de cuánto cambien los datos de la tabla con el tiempo. 

Conclusiones

En este artículo, hemos explicado el papel de una Definición de Almacenamiento en el motor IRIS ObjectRelational, cómo es compatible con la evolución del esquema y lo que implica incluirla en el sistema de control de código fuente. También hemos descrito por qué las estadísticas de tablas se almacenan actualmente en esa Definición de Almacenamiento y hemos sugerido prácticas de desarrollo para asegurarnos de que las implementaciones de las aplicaciones obtienen estadísticas que sean representativas para los datos reales del cliente. Como se mencionó anteriormente, estamos planeando mejorar aún más estas funciones, por lo que esperamos vuestros comentarios sobre la funcionalidad actual y la planificada, y mejoraremos nuestro diseño según corresponda.

0
0 170
Artículo Ricardo Paiva · sep 21, 2023 1m read

Preguntas frecuentes de InterSystems

Se pueden recuperar estos datos utilizando el esquema INFORMATION_SCHEMA.

INFORMATION_SCHEMA es un esquema de sistema y no se muestra por defecto en el menú SQL del Portal de Gestión.

El método para mostrarlo es el siguiente:

  1. Abrir el Portal de Gestión → Explorador → SQL.
  2. Marcar "Sistema" a la izquierda del desplegable de esquemas.
  3. Seleccionar INFORMATION_SCHEMA en el desplegable. 

El SQL para obtener el ID, el nombre del campo (COLUMN_NAME), el tipo de datos (DATA_TYPE) y la descripción (DESCRIPCIÓN) de la tabla especificada (Sample.Human) es el siguiente.

0
0 267
Anuncio Esther Sanchez · sep 18, 2023

Si estáis pensando en empezar con SQL en InterSystems IRIS o queréis hacer vuestras consultas más eficientes, podéis completar uno de estos breves ejercicios de formación online! (en inglés)

SQL Exercise 

Ambos ejercicios ponen a vuestra disposición una instancia de IRIS en la nube (o learning lab) por lo que no necesitaréis realizar ninguna instalación previa en vuestros equipos

0
0 192
Artículo Ricardo Paiva · sep 5, 2023 1m read

Preguntas frecuentes de InterSystems

De cara a la reconstrucción de los índices de una clase persistente/tabla, hay que utilizar el método %BuildIndices(), proporcionado por dicha clase. Para ello hay que especificar los valores de inicio y fin de los ID para los cuales se quieren reconstruir índices en los argumentos del método. 

Por ejemplo, para reconstruir el índice NameIDX y el índice ZipCode en la clase Sample.Person sólo para ID=10 a 20, ejecutad el siguiente código (el rango de ID se especifica en los argumentos quinto y sexto).

0
0 107
Artículo Ricardo Paiva · ago 16, 2023 3m read

InterSystems IRIS actualmente limita sus clases a 999 propiedades.

Pero, ¿qué hacer si necesita almacenar más datos por objeto?

Este artículo respondería a esta pregunta (con el apunte adicional de Community Python Gateway y cómo transferir conjuntos de datos amplios a Python).

En realidad, la respuesta es muy simple: InterSystems IRIS actualmente limita las clases a 999 propiedades, pero no a 999 primitivas. La propiedad en InterSystems IRIS puede ser un objeto con 999 propiedades y así sucesivamente; el límite se puede ignorar fácilmente.

0
0 116
Artículo Dmitry Maslennikov · feb 17, 2023 4m read

Os presento mi nuevo proyecto: irissqlcli, REPL (Read-Eval-Print Loop) para InterSystems IRIS SQL.

  • Resaltado de sintaxis
  • Sugerencias (tablas, funciones)
  • +20 formatos de salida
  • Soporte a stdin
  • Salida a ficheros 

Instalación con pip

pipinstallirissqlcli

O se puede ejecutar con docker

dockerrun-itcaretdev/irissqlcliirissqlcliiris://_SYSTEM:SYS@host.docker.internal:1972/USER
1
0 131
InterSystems Official Jose-Tomas Salvador · jul 4, 2023

InterSystems IRIS Cloud SQL es un servicio en la nube completamente administrado, que acerca, a un amplio público de desarrolladores de aplicaciones y profesionales de datos, la potencia de las capacidades de InterSystems IRIS como base de datos relacional, utilizada por miles de empresas. InterSystems IRIS Cloud IntegratedML es una opción de database-as-a-service que ofrece un sencillo acceso a potentes funcionalidades de Machine Learning de forma nativa SQL, a través de un conjunto de sencillos comandos SQL que se pueden embeber fácilmente en el código de la aplicación para mejorarlos con modelos de Machine Learning que se ejecutan cerca de los datos.

Hoy anunciamos el Programa de Acceso a Desarrolladores para estas dos propuestas. Los desarrolladores de aplicaciones ahora pueden registrarse por sí mismos en el servicio, crear implementaciones y empezar a crear aplicaciones compuestas y servicios de datos inteligentes, con todas las tareas de aprovisionamiento, configuración y administración gestionadas por el servicio.

0
0 80
Artículo Alberto Fuentes · mayo 5, 2023 3m read

¡Hola desarrolladores!

Hay una actualización reciente para las imágenes de la comunidad de desarrolladores de InterSystems IRIS e InterSystems IRIS For Health.

Esta versión incluye soporte para variables de entorno.

Actualmente se soportan 3 variables de entorno:

  • IRIS_USERNAME=usuario a crear
  • IRIS_PASSWORD=password del usuario
  • IRIS_NAMESPACE=crear namespace si no existe

Esto es lo que puedes hacer:

Arrancar iris con un usuario y password:

docker run --rm --name iris-sql -d -p 9091:1972 -p 9092:52773  -e IRIS_PASSWORD=demo -e IRIS_USERNAME=demo intersystemsdc/iris-community
0
0 149
Artículo Alberto Fuentes · abr 27, 2023 8m read

Con IRIS 2021.1, realizamos una importante revisión de nuestra API de utilidades SQL en %SYSTEM.SQL. Sí, eso fue hace algún tiempo, pero la semana pasada un cliente hizo unas preguntas sobre ello y entonces @Tom Woodfin me empezó a presionar un poco ;-) para que describiera con más detalle en la Comunidad de Desarrolladores las razones de estos cambios. ¡Así que allá vamos!

A principios de 2020, la API %SYSTEM.SQL pasó de ser un útil envoltorio de clases alrededor de unas rutinas clave a un gran número de puntos de acceso no tan coherentes para diversas funcionalidades relacionadas con SQL. Eso no debería sorprendernos, ya que el motor SQL de IRIS (y antes el motor SQL de Caché) creció mucho en funcionalidades y facilidad de uso. Como nos preocupamos mucho por la compatibilidad con las versiones anteriores, casi todos los cambios de la API fueron una incorporación neta, un nuevo método o una extensión de la estructura de un método. En algunos casos, este objetivo de compatibilidad con las versiones anteriores significó que el intento de simplificar las cosas mediante la eliminación de un argumento de métodos (para algo que ahora estaba automatizado) se diluyó al eliminar el argumento en la clase de referencia, pero dejándolo en su lugar para no alterar el código que lo utilizaba. En otras palabras, la API comenzó a parecerse a un famoso plato italiano que no es una pizza.

_Si a estas alturas piensas: "¡Qué lío has hecho, eso nunca nos pasaría a nosotros!" - puedes dejar de leer aquí. _

... Creo que aún sigues ahí :-)

Probablemente sea justo decir que este tipo de crecimiento orgánico es inevitable y, en cierto modo, no necesariamente negativo, ya que significa que progresas y te preocupas por tus usuarios, ya que no destruyes su código al adaptar la API entre cada versión. Pero en algún momento hay que sacar la escoba y limpiar. Y eso es lo que hicimos en 2020.3 con las herramientas SQL de IRIS.

Ordenando las cosas

El primer problema que queríamos resolver era la gran cantidad de métodos, que eran demasiados para que una sola API fuera manejable. Así que empezamos por dividir la clase en una serie de clases API más pequeñas y más centradas en el paquete %SYSTEM.SQL, lo que significaba que podríamos conservar la agradable sintaxis abreviada de $SYSTEM.

  • SYSTEM.SQL.Functions – por si no lo sabes, %SYSTEM.SQL tiene equivalentes en ObjectScript para todas las funciones escalares simples de IRIS SQL, como %SYSTEM.SQL.ABS().
  • SYSTEM.SQL.PTools – las Herramientas para analizar rendimiento son un grupo de utilidades para analizar en profundidad el rendimiento de las consultas individuales. Este avanzado conjunto de herramientas lo utilizan principalmente el servicio de Soporte de InterSystems y los clientes más experimentados, pero es una API muy potente y bien documentada, por lo que merece la pena echarle un vistazo si necesitas resolver esa dichosa consulta
  • %SYSTEM.SQL.Schema – la verificación de la existencia de tablas, la importación o exportación de sentencias DDL y otras funciones para consultar o modificar objetos del esquema ahora están aquí.
  • %SYSTEM.SQL.Security – agrupa los puntos de acceso ObjectScript para los comandos GRANT y REVOKE
  • %SYSTEM.SQL.Statement – como probablemente sabrás, InterSystems IRIS incluye una completa gestión y almacenamiento de sentencias SQL. Esta clase agrupa métodos para administrar el contenido del Índice de sentencias, como importar y exportar planes, así como freezing y thawing, si se quiere forzar el uso de alguna en particular.
  • %SYSTEM.SQL.Stats.Table – Aquí es donde puedes recopilar estadísticas de las tablas y, opcionalmente, anularlas, importarlas o exportarlas. Es un nivel más profundo que los demás, ya que hay otros elementos sobre los que nos gustaría recopilar y gestionar estadísticas en el futuro.
  • SYSTEM.SQL.Util – No todo encaja en un cajón temático limpio, así que aquí es donde ponemos los restos: la funcionalidad que queríamos conservar de %SYSTEM.SQL, pero para la que no encontramos un lugar más adecuado. Solo un número muy reducido de tareas extremadamente comunes, como %SYSTEM.SQL.Explain(), se quedaron en la clase de nivel superior.

Por supuesto, todavía nos preocupamos por los usuarios con un código que llama a los puntos de acceso preexistentes, por lo que hemos dejado todos los métodos originales en la clase %SYSTEM.SQL y los hemos marcado como internos y obsoletos usando palabras clave para los métodos, dejando una nota sobre la nueva ubicación preferida de la función.

Una de las ventajas de este enfoque con nuevas clases es que no tenemos que preocuparnos de que lo nuevo se interponga en el camino de lo que ya existe. En la mayoría de nuestras API REST (como /api/deepsee/ y /api/iknow/*), introdujimos un número de versión en la propia URL de la API (por ejemplo, /api/iknow/v1/namespace/domains) para tener en cuenta estos cambios en la API. También probamos brevemente esa idea aquí, pero pensamos que quedaría muy mal en el nombre de una clase y no invitaría a los usuarios a adoptarla en vez del punto de acceso base que ya existe y está obsoleto. Los nuevos puntos de acceso también son un poco más extensos, pero al menos el token extra tiene sentido con respecto a lo que intenta conseguir. De manera implícita, esto significa que suponemos con cierto optimismo que nunca volveremos a tener que cambiar estas API, así que vamos a echar un vistazo al otro cambio importante que hemos hecho: revisar las estructuras de los métodos.

Limpieza profunda

Como hemos descrito en la introducción, no solo veíamos un número desmesurado de métodos, sino que algunos tenían una estructura desmesurada en el mismo método. Algunos buenos ejemplos eran los métodos TuneTable() y Export(), a los que se les agregaron un gran número de indicadores a lo largo de los años. Normalmente, los argumentos más interesantes son los nuevos del final, y habitualmente veía incluso a los desarrolladores más experimentados contar las comas antes de poner ese 1 o 0 para anular un valor (esperamos que decente) predeterminado. Claramente era un área que podíamos mejorar, y así lo hicimos.

La primera pregunta que debemos hacernos es, obviamente, si merecía la pena mantener cada argumento. Algunos han sido superados con el tiempo o por nuevas funcionalidades, y pueden omitirse sin problemas. Después, pusimos los más importantes y obligatorios al principio y agrupamos todos los indicadores opcionales en un nuevo argumento qualifiers, de naturaleza similar a los calificadores utilizados en varios métodos %SYSTEM.OBJ. Admitimos un objeto dinámico o un formato heredado. El primero se puede pasar como una instancia %DynamicObject, o en formato de cadena JSON, como { "silent": 1, "logFile": "/tmp/xyz.log" }. El último utiliza el mismo formato que en %SYSTEM.OBJ usando barras, en el que los calificadores anteriores podrían expresarse como "/silent /logFile=/tmp/xyz.log". Este mecanismo ha sido una excelente forma de ofrecer métodos API fáciles de usar, de documentar y de evolucionar.

Además de cambiar la lista de argumentos, también hicimos otras cosas más sencillas, como un uso más estandarizado de los valores de retorno %Status y los parámetros ByRef. Y por último, pero no por ello menos importante, cambiar los nombres de los métodos para que sean más uniformes y autoexplicativos, a menudo facilitado por el paso a un subpaquete con nombre razonable. ¿Sabrías decir sin mirar si el antiguo método %SYSTEM.SQL.Export() exportaba datos o información del esquema? ¡No tengo nada más que decir!

Pensando en el futuro

¿Ya terminamos? Definitivamente no. Estoy muy agradecido por el tiempo que el equipo ha dedicado a revisar este cambio de especificación, implementación y pruebas, pero cuando lo lanzamos con 2020.3, ya habíamos identificado algunos casos en los que podríamos haber ido un poco más lejos o haber simplificado aún más las cosas. Continuaremos buscando oportunidades de este tipo y, ahora que la mayor parte de esta primera gran limpieza se ha llevado a cabo, podemos impulsar de forma práctica cambios más pequeños que se ajusten a las normas descritas anteriormente.

Si estás buscando una razón por la que antes podías llamar a %SYSTEM.SQL.FreezePlans(1, 3, "MyTable") y ahora se recomienda usar %SYSTEM.SQL.Statement.FreezeRelation("MyTable"), espero que esto te haya sido útil. Si buscabas consejos generales sobre la evolución de la API, espero que al menos esto no te haya parecido una pérdida de tiempo :-) y estaré encantado de escuchar vuestras opiniones sobre cómo lo hicimos y cómo lo haríais vosotros de otra manera. No hay absolutamente ninguna ciencia exacta en lo anterior. Adoptamos un enfoque pragmático, para intentar que las cosas fueran más fáciles de utilizar sin cambiar los métodos por el placer de hacerlo y tratando de economizar en tiempo de desarrollo y pruebas. Esperamos que, con el tiempo, también ahorre tiempo en el aprendizaje y el uso de la API, pero sobre todo, ¡que ahorre el tiempo dedicado a contar las comas!

0
0 107
Artículo Ricardo Paiva · abr 20, 2023 2m read

Apache Superset es una moderna plataforma para la visualización y exploración de datos. Superset puede reemplazar o aumentar las herramientas patentadas de business intelligence para muchos equipos. Y se puede integrar con una gran variedad de fuentes de datos.

¡Y ahora es posible utilizarla con InterSystems IRIS!

Hay disponible una demo online que usa IRIS Cloud SQL como fuente de datos.

0
0 621
Artículo Heloisa Paiva · abr 18, 2023 2m read

Introducción

En algunos de mis artículos he hablado de tipos entre IRIS y Python, y está claro que no es tan fácil acceder a objetos de un lado a otro. 

Afortunadamente, el trabajo ya se ha hecho, con la creación de SQLAlchemy-iris, que hace mucho más fácil para Python acceder a los objetos de IRIS, y voy a mostraros cómo funciona.

¡Gracias, @Dmitry Maslennikov!
 

Instalación

Para instalarlo, simplemente abre tu terminal con acceso de administrador y escribe

pip install sqlalchemy-iris

y eso también instalará los pre-requisitos para ti, si fuera necesario.
 

Uso

0
0 1418
Comentarios Alberto Fuentes · abr 12, 2023

¡Hola Comunidad!

Como sabéis, InterSystems IRIS, además de globals, objetos, documentos y modelos de datos XML, también es compatible con tratamiento relacional de datos utilizando SQL como lenguaje.

Y como otros sistemas de gestión de bases de datos relacionales (DBMS), InterSystems IRIS tiene su propio dialecto.

Empezamos esta publicación como una Guía Rápida de SQL y os invitamos a compartir vuestros trucos favoritos.

¡Vamos allá!

0
1 206
Artículo Heloisa Paiva · abr 11, 2023 3m read

Introducción

Este artículo tiene la intención de ser un sencillo tutorial de cómo crear conexiones ODBC y trabajar con ellas, ya que me pareció que empezar con ellas es un poco confuso. Yo tuve la ayuda de unas personas increíbles, y creo que todos merecemos ese apoyo.

Voy dividir cada pequeña parte en sesiones, así que puedes ir directamente a la que necesites, aunque recomiendo leerlo todo.

Voy a usar los datos de ejemplo creados en un artículo previo, Tutorial rápido para crear bases de datos de ejemplo: Samples.PersistentData, con las propiedades Name y Age.
 

Creando la conexión

0
0 488
Artículo Heloisa Paiva · abr 11, 2023 3m read

Introducción

Este es un sencillo tutorial sobre la manera más rápida que conozco para crear una base de datos de ejemplo para cualquier propósito, como hacer pruebas, hacer ejemplos para tutoriales, etc.
 

Creando un namespace

  1. Abre el terminal
  2. Escribe "D $SYSTEM.SQL.Shell()"
  3. Escribe "CREATE DATABASE " y el nombre que quieres para tu namespace.

Ahora tienes un nuevo namespace de una manera más rápida que creándolo desde el Portal de Gestión - que por supuesto ofrece más opciones de configuración.

0
0 291
Artículo Ricardo Paiva · mar 30, 2023 1m read

Quick Tips: Total Productive Maintenance

Los parámetros nombrados se pueden conseguir con SQLAlchemy :  

from sqlalchemy import create_engine, text,types,engine

_engine = create_engine('iris+emb:///')

with _engine.connect() as conn:
    rs = conn.execute(text("select :some_private_name"), {"some_private_name": 1})
    print(rs.all())

o con api nativa

from sqlalchemy import create_engine, text,types,engine

# set URL for SQLAlchemy
url = engine.url.URL.create('iris', username='SuperUser', password='SYS', host='localhost', port=33782, database='FHIRSERVER')

_engine = create_engine(url)

with _engine.connect() as conn:
    rs = conn.execute(text("select :some_private_name"), {"some_private_name": 1})
    print(rs.all())

requirements.txt

sqlalchemy==1.4.22
sqlalchemy-iris==0.5.0
irissqlcli

Está funcionando hoy, con IRIS 2021.2+

0
0 124
Anuncio Esther Sanchez · mar 30, 2023

¡Hola desarrolladores!

Nos gustaría invitaros a un nuevo concurso de programación, dedicado a crear soluciones de Inteligencia Artificial (IA)/Machine Learning (ML) que utilicen Cloud SQL para trabajar con los datos: 

🏆 Concurso de Programación: InterSystems IRIS Cloud SQL con IntegratedML🏆

Duración: del 3 al 23 de abril, 2023

Total en premios: $13,500

 

0
0 127
Artículo Jose-Tomas Salvador · oct 6, 2022 6m read

Python se ha convertido en el lenguaje de programación más utilizado del mundo (fuente: https://www.tiobe.com/tiobe-index/) y SQL sigue siendo el líder como lenguaje para las bases de datos. ¿No sería genial que Python y SQL trabajaran juntos para ofrecer nuevas funcionalidades que SQL por sí mismo no puede? Después de todo, Python tiene más de 380.000 librerías publicadas (fuente: https://pypi.org/) con funciones muy interesantes para ampliar las consultas SQL dentro de Python.

En este artículo detallo cómo crear nuevos Procedimientos Almacenados de SQL en la base de datos de InterSystems IRIS usando Embedded Python.

Librerías de Python utilizadas como muestras

En este artículo utilizaré dos librerías muy útiles para cualquiera que trabaje con SQL en IRIS: Geopy y Chronyk.

Geopy es una librería utilizada para aplicar la geocodificación (asignación de coordenadas geográficas a direcciones) a los datos de direcciones. Con ella es posible, a partir del nombre de la calle, obtener el código postal y la dirección completa, en el formato de la oficina de correos. Es muy útil, ya que muchos registros tienen una dirección.

Chronyk se utiliza para procesar fechas y horas mediante el lenguaje humano. Esto es muy útil, porque internamente, tanto para IRIS como para Python, una fecha es un número que representa la cantidad de tiempo que ha transcurrido desde una fecha inicial. Para los humanos, una fecha es el 20 de julio, o ayer, o mañana, o hace dos horas. Chronyk acepta recibir la fecha así y luego la convierte al formato de fecha universal.

Soporte de Python en InterSystems IRIS

Desde la versión 2021.2 es posible utilizar Python para crear métodos de clase, procedimientos almacenados, producciones de interoperabilidad y llamadas nativas entre Python e IRIS (ObjectScript) de forma bidireccional. No conozco ninguna otra plataforma de datos que trabaje tan estrechamente con Python. El requisito para que esto funcione es que Python se instale en la misma máquina física o virtual o contenedor que IRIS. Consulta más detalles en: https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=AFL_epython.

Para instalar Python ejecuta:

# install libraries required for python and pip
RUNapt-get-yupdate\
    &&apt-get-yinstallapt-utils\
    &&apt-getinstall-ybuild-essentialunzippkg-configwget\
    &&apt-getinstall-ypython3-pip  

Soporte de librerías de Python en InterSystems IRIS

Para que InterSystems IRIS pueda utilizar una librería de Python es obligatorio que esté instalada dentro de <installdir>/mgr/python. installdir es la carpeta en la que está instalado IRIS. Para instalar nuevos paquetes ejecuta:

# use pip3 (the python zpm) to install geopy and chronyk packages
RUNpip3install--upgradepipsetuptoolswheel
RUNpip3install--target/usr/irissys/mgr/pythongeopychronyk

Pip3 es el administrador e instalador de paquetes más popular de Python, Pip.

Cómo crear Procedimientos Almacenados en lenguaje de Python

Una de las posibilidades de utilizar Python en InterSystems IRIS es crear Procedimientos Almacenados usando Python.

Hay dos posibilidades:

  1. Crear un Procedimiento Almacenado en Python utilizando la sentencia SQL de crear función o procedimiento
  2. Crear un Método de clase dentro de la clase ObjectScript con las etiquetas sqlProc y language=Python.

Cómo crear un Procedimiento Almacenado en Python utilizando la sentencia SQL de Crear Procedimiento

Según la documentación de InterSystems, también se puede escribir una función SQL o un Procedimiento Almacenado utilizando Python Embebido si se especifica el argumento LANGUAGE PYTHON en la sentencia CREATE, como se muestra a continuación (fuente: https://docs.intersystems.com/iris20221/csp/docbook/DocBook.UI.Page.cls?KEY=AEPYTHON#AEPYTHON_runpython_sql):

CREATE FUNCTION tzconvert(dt TIMESTAMP, tzfrom VARCHAR, tzto VARCHAR)
    RETURNS TIMESTAMP
    LANGUAGE PYTHON
{
    from datetime import datetime
    from dateutil import parser, tz
    d = parser.parse(dt)
    if (tzfrom is not None):
        tzf = tz.gettz(tzfrom)
        d = d.replace(tzinfo = tzf)
    return d.astimezone(tz.gettz(tzto)).strftime("%Y-%m-%d %H:%M:%S")
}

Cuando se ejecuta esta nueva función SQL:

SELECT tzconvert(now(), 'US/Eastern', 'UTC')

La función devuelve algo como:

2022-07-20 15:10:05

Cómo crear el Método de clase dentro de la clase ObjectScript con las etiquetas sqlProc y language=Python

Confieso que este enfoque es mi favorito: crear un método de clase con las etiquetas sqlProc y language=Python.

En mi opinión es más fácil de mantener, está mejor documentado y con una mejor administración de las versiones del código fuente. Para este enfoque he publicado una aplicación de muestra: https://openexchange.intersystems.com/package/Python-IRIS-SQL-Procedures-Sample. La utilizaré para mostrar este segundo enfoque con detalle.

Instalación de la aplicación de muestra

Para instalar la aplicación de muestra, sigue estos pasos:

  1. Clonar/git pull el repositorio en cualquier directorio local: $ git clone https://github.com/yurimarx/iris-sql-python-sample.git
  2. Abrir un terminal de Docker en este directorio y ejecutar: $ docker-compose build
  3. Ejecutar el contenedor de IRIS: $ docker-compose up -d

Otra posibilidad para instalar es utilizar el ZPM:

zpm "install iris-sql-python-sample"

Ejemplos de Procedimientos Almacenamiento usando Python

El primer ejemplo es un Procedimiento Almacenado para administrar la geocodificación de direcciones, consulta el código fuente:

ClassMethodGetFullAddress(StreetAs%String,CityAs%String,StateAs%String)
As%String[Language=python,SqlName=GetFullAddress,SqlProc]
{
    importgeopy.geocoders
    fromgeopy.geocodersimportNominatim
    geopy.geocoders.options.default_timeout =7
    geolocator =Nominatim(user_agent="intersystems_iris")
    location =geolocator.geocode(Street +", " +City +", " +State,country_codes="US")
    returnlocation.address
}

Fíjate que se declaró un Método de clase (dentro de la clase dc.pythonsql.Company) con las etiquetas [ Language = python, SqlProc]. La etiqueta SqlName permite establecer un nombre para el nuevo Procedimiento Almacenado en sentencias SQL. Ve al Portal de administración, Sistema > SQL y ejecuta el siguiente código:

SELECT ID, City, Name, State, Street, Zip, dc_pythonsql.GetFullAddress(Street, City, State) As FullAddress 
FROM dc_pythonsql.Company

Verás estos resultados: Geopy

Ahora las direcciones incompletas regresan como direcciones "completas" (completas y calificadas).

Nota: Si no devuelve nada, ejecuta #class(dc.pythonsql.Company).CreateFiveCompanies(). Creará cinco empresas para utilizar en las pruebas.

Este paquete puede funcionar con los principales servicios de geocodificación abiertos y comerciales. En este ejemplo utilizamos el servicio abierto Nominatim, pero es posible utilizar Bing, Google, ArcGIS y otros. Consulta las posibilidades en https://geopy.readthedocs.io/en/stable/#module-geopy.geocoders.

El segundo ejemplo es un paquete de fecha y hora en formato humanizado, Chronyk. Permite enviar frases como "mañana", "ayer", "dentro de 4 horas", "4 de julio del 2022" y obtener el resultado en formato de fecha universal. Mira la creación del Procedimiento Almacenado:

ClassMethodGetHumanDate(SentenceAs%String)As%String[Language=python,SqlName=GetHumanDate,SqlProc]
{
    fromchronykimportChronyk
    t =Chronyk(Sentence)
    returnt.ctime()
}

Realiza la siguiente llamada en el Portal de administración > Sistema > SQL:

SELECT ID, City, Name, State, Street, Zip, dc_pythonsql.GetHumanDate('yesterday') As Datetime FROM dc_pythonsql.Company

Verás resultados como este: https://raw.githubusercontent.com/yurimarx/iris-sql-python-sample/main/screen2.png

Si quieres simplemente llamar al Procedimiento Almacenado, puedes usar esta sentencia SQL:

select dc_pythonsql.GetHumanDate('yesterday') as Datetime  

Esta librería tiene varias posibilidades de fechas y horas humanizadas, consulta https://github.com/KoffeinFlummi/Chronyk. Así que es fácil crear Procedimientos Almacenados en Python. ¡Disfrútalo!

1
1 764
Artículo Daniel Aguilar · mar 18, 2023 4m read

Una de las razones por las que me encantan Cache e IRIS es que no solo puedes hacer cualquier cosa que puedas imaginar, sino que también puedes hacerlas de un montón de maneras diferentes!!

Imagina que tienes una integración con IRIS (o Cache) funcionando conectada por ODBC. Probablemente solo uses consultas SQL, pero puedes usar también procedimientos almacenados y dentro de su código puedes hacer cualquier cosa que puedas imaginar.

Voy a darte algunos ejemplos, pero el límite está en tu imaginación!

Quizás te estés diciendo... ¡espera!! Yo no sé como crear un procedimiento almacenado en IRIS... Pero eso no es cierto, sabes crearlo! Lo que pasa que todavía no lo sabes...

0
0 267
Artículo Ramón Rodríguez · feb 26, 2023 6m read

Buenas desarrollador!

En este artículo te voy a contar mi experiencia y conocimientos en el poco tiempo que llevo utilizando los distintos productos de InterSystems.

A parte de contar mis vivencias también veremos como hacer una pequeña API Rest con la que poder hacer un CRUD con la base de datos SQL de InterSystems.

Mi historia:

2
0 203
Artículo Pierre-Yves Duquesnoy · feb 16, 2023 6m read

Como recordaréis, en el Global Summit de 2022 y en el webinar de lanzamiento de la versión 2022.2, presentamos una nueva e interesante funcionalidad para incluir en las soluciones analíticas de InterSystems IRIS. Columnar Storage introduce una forma alternativa de almacenar los datos de las tablas SQL, que ofrece un aumento significativo en la velocidad de las consultas analíticas. Lanzada por primera vez como funcionalidad experimental en 2022.2, la última versión de prueba en 2022.3 incluye numerosas actualizaciones que pensamos merecen una publicación aquí.

Un breve resumen

Si no estáis familiarizados con Columnar Storage, echad un vistazo a este breve video o a esta sesión del Global Summit 2022. En resumen, vamos a codificar los datos de la tabla en fragmentos de 64 000 valores por columna utilizando un nuevo tipo de datos $vector. $vector es un tipo de datos exclusivamente interno (por ahora) que aprovecha los esquemas de codificación adaptativa para permitir el almacenamiento eficiente de datos tanto dispersos como compactos. El código también está optimizado para un grupo de operaciones $vector especializadas, como calcular conjuntos, agrupar y filtrar fragmentos enteros de 64 000 valores a la vez, aprovechando las instrucciones SIMD cuando sea posible. 

Al realizar la consulta SQL, aprovechamos estas operaciones desarrollando un plan de consulta que también opera sobre estos fragmentos, lo que supone, como podéis imaginar, una reducción masiva de la cantidad de IO y del número de instrucciones ObjectScript necesarias para ejecutar la consulta, en comparación con el procesamiento clásico fila por fila. Por supuesto, las IOs individuales son mayores y las operaciones $vector son un poco más complejas que las equivalentes a un solo valor del mundo orientado a filas, pero las ganancias son enormes. Utilizamos el término planes de consulta vectorizados para las estrategias de ejecución que procesan datos $vector, procesando bloques enteros mediante una cadena de operaciones individuales rápidas.

Simplemente más rápido

Y lo que es más importante, todo va más rápido. Hemos ampliado la comprensión del optimizador sobre los índices en columnas y ahora veréis que más consultas utilizan índices en columnas, incluso si algunos de los campos solicitados no se almacenan en un índice en columnas o mapa de datos. Además, veréis que combina índices en columnas y de mapa de bits en varios casos, lo que es muy útil si estáis empezando a introducir índices en columnas en un esquema existente.

El nuevo kit también incluye una serie de cambios más generales que mejoran el rendimiento, desde optimizaciones hasta operaciones $vector de bajo nivel pasando por algunas mejoras en el procesamiento de consultas y un conjunto más amplio de planes de consulta vectorizados que se podrán paralelizar. Algunas formas de cargar datos, por ejemplo a través de las sentencias INSERT .. SELECT, ahora también emplearán un modelo de búfer que ya utilizamos para generar índices y ahora permiten desarrollar tablas enteras con un rendimiento realmente alto.

JOINs vectorizados

La funcionalidad más interesante que hemos añadido en esta versión es el soporte a JOINs vectorizados sobre datos en columas . En la versión 2022.2, cuando se querían combinar datos de dos tablas en una consulta, aún recurríamos a una estrategia sólida de combinación JOIN fila por fila, que funciona tanto con datos organizados en columnas como en filas. Ahora, cuando ambos extremos del JOIN se almacenan en un formato en columnas, utilizamos una nueva API del kernel para hacer combinaciones JOIN en la memoria, manteniendo su formato $vector. Este es otro importante paso hacia los planes de consulta totalmente vectorizados, incluso para las consultas más complejas.

A continuación presentamos un ejemplo de consulta que aprovecha las ventajas de la nueva función, efectuando un self-JOIN del conjunto de datos de taxis de Nueva York que ya hemos utilizado en otras demos:

SELECT 
   COUNT(*), 
   MAX(r1.total_amount - r2.total_amount) 
FROM
   NYTaxi.Rides r1, 
   NYTaxi.Rides r2
WHERE 
   r1.DOLocationID = r2.PULocationID 
   AND r1.tpep_dropoff_datetime = r2.tpep_pickup_datetime 
   AND r2.DOLocationID = r1.PULocationID 
   AND r1.passenger_count > 2 
   AND r2.passenger_count > 2

En esta consulta se buscan pares de viajes con más de 2 pasajeros, en los que el segundo viaje comenzó donde terminó el primero, exactamente a la misma hora y en los que el segundo viaje llevó a uno de vuelta a donde comenzó el primero. No es un análisis muy útil, pero solo tenía una tabla real en este esquema y la clave JOIN compuesta lo hizo un poco menos sencillo. En el plan de consulta para esta sentencia, veréis fragmentos sobre como Apply vector operation %VHASH (para crear la clave JOIN compuesta) y Read vector-join temp-file A, que indica que nuestro nuevo ensamblador vectorizado está funcionando! Esto puede parecer un pequeño detalle trivial en un plan de consulta larga, pero implica una gran cantidad de ingeniería inteligente en el interior, y hay un gran número de proveedores de bases de datos en columnas que simplemente no permiten nada de esto e imponen severas restricciones en el diseño de su esquema, así que ¡únete a nosotros para participar en esto! :-)

Cuando el plan de consultas continúa para leer ese archivo temporal, es posible observar que aún queda algo de procesamiento fila por fila en el trabajo posterior a la combinación, lo que nos llevará a preguntarnos...

¿Qué sigue?

Columnar Storage sigue considerándose "experimental" en 2022.3, pero cada vez estamos más cerca de estar listos para la producción y de disponer de la vectorización completa de extremo a extremo para las consultas de múltiples tablas. Esto incluye el trabajo posterior a la combinación (mencionado anteriormente), un mayor soporte con el optimizador de consultas, una carga aún más rápida de las tablas en columnas y nuevas mejoras del combinador, como el soporte a la memoria compartida. En resumen: ahora es un momento excelente para probar todo esto por primera vez con el conjunto de datos de taxis de Nueva York (ahora en IPM o con los scripts de docker) utilizando la Community Edition 2022.3, y así solo tendréis que hacer clic en "Run" cuando saquemos la versión 2023.1!

Si alguien está interesado en recibir más asesoramiento personalizado sobre cómo aprovechar el almacenamiento en columnas con sus propios datos y consultas, poneos en contacto conmigo o con vuestros consultores habituales, y quizá nos veamos en el Global Summit 2023 ;-).

0
0 145
Artículo Ricardo Paiva · jul 28, 2022 3m read

Estos días he estado trabajando con la excelente y nueva funcionalidad: LOAD DATA. Con este artículo me gustaría compartir mis primeras experiencias con todos. Los siguientes puntos no contienen ningún orden ni ningún otro análsis. Son solo cosas que observé al utilizar el comando LOAD DATA. Y se debe tener en cuenta que estos puntos se basan en la versión 2021.2.0.617 de IRIS, que es una versión de prueba. Por ello, es posible que mis observaciones no apliquen a las nuevas versiones de IRIS. Pero quizás sean útiles para otros.

1) La ruta del archivo está en el lado del servidor

He hecho mis primeras pruebas mediante JDBC. La primera sorpresa que me encontré: ¡El archivo y la ruta del archivo deben, por supuesto ;-) estar en el lado del servidor! El controlador JDBC no se encarga de esto en el lado del cliente. Probablemente esto es obvio, pero no lo había considerado al principio.

2) El sufijo del archivo no es relevante

Los documentos dicen:

"Los nombres de los archivos deben incluir un sufijo .txt o .csv (valores separados por comas)."

Según mis observaciones, el comportamiento no es así. El sufijo no es relevante.

3) ¡Lee los documentos! ... o ¿dónde están las filas con errores?

Cuando cargué algunos archivos de datos, perdí filas. Si hay algún problema con una línea, la línea se ignora. Esto sucede silenciosamente en segundo plano y no se notifica activamente al cliente. Después de consultar https://https//youtu.be/jm7bDK0FoiI me di cuenta de que tengo que revisar %SQL_Diag.Result y %SQL_Diag.Message para ver los problemas de forma detallada. También me di cuenta de que este comportamiento ya está descrito en esta página: https://docs.intersystems.com/iris20212/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_loaddata ... así que hay que leer los manuales ;-)

Algunos ejemplos de lo que se puede ver:

SELECT * FROM %SQL_Diag.Result ORDER BY createTime DESC

Revisa la columna errorCount de tu descarga.

Se pueden ver los detalles (de la fila) en %SQL_Diag.Message

SELECT * FROM %SQL_Diag.Message ORDER BY messageTime DESC

Se puede filtrar por un diagResult específico (%SQL_Diag.Result.ID = %SQL_Diag.Message.diagResult)

SELECT * FROM %SQL_Diag.Message
WHERE diagResult=4
ORDER BY messageTime DESC

4) LOAD DATA no es compatible con $SYSTEM.SQL.Schema.ImportDDL

Para mi aplicación de prueba Openflights Dataset intenté cargar todos los archivos externos con LOAD DATA. Las sentencias se agrupan dentro de un archivo de texto (sql) donde anteriormente también creé las tablas.

Aprendí que no se puede hacer eso mediante $SYSTEM.SQL.Schema.ImportDDL.

Por cierto, la documentación de ImportDDL señala que no todas las sentencias SQL son compatibles. Solo unas pocas sentencias SQL aparecen en esta página.

LOAD DATA lamentablemente no es una de ellas... Y, por cierto, USE DATABASE tampoco.

5) Para administrar unicode hay que cambiar una configuración

Para evitar problemas con el código de los datos durante la carga, hay que poner esta configuración en el servidor de %Java: -Dfile.encoding=UTF-8
Consulta más detalles en esta publicación. Este problema debería desaparecer en la próxima versión de IRIS.

6) El proceso de carga se detiene con un error, pero los datos se cargan

La carga de datos mediante JDBC se para por un error %qparsets. Se ve así:

Error: [SQLCODE: &lt;-400>:&lt;Fatal error occurred>]
[Error: &lt;&lt;UNDEFINED>zExecute+83^%sqlcq.OPENFLIGHTS.cls10.1 *%qparsets>]
[Location: &lt;ServerLoop>]

Pero hay que preocuparse, los datos se cargaron :-) Consulta más detalles en esta publicación.
Este problema debería desaparecer en la próxima versión de IRIS.

1
0 260