Mostrando las entradas con la etiqueta SQL. Mostrar todas las entradas
Mostrando las entradas con la etiqueta SQL. Mostrar todas las entradas

viernes, 16 de junio de 2023

Oracle 23c - Mejoras Varias #1


En este artículo vamos a ver algunas mejoras de Oracle 23c que no evaluamos en otros artículos.


Mas de 1000 Valores en Operador IN 

Una mejora que puede ser útil en el caso de código generado en forma dinámica o automática es la posibilidad de usar el operador IN con hasta 64.000 valores, en vez del limite de 1000 valores que existía previamente.

Lo primero que vamos a hacer, es generar una lista de mas de 1000 valores:

SELECT LISTAGG( LEVEL,',' ) --FROM dual CONNECT BY LEVEL <= 1010;

Nota: Oracle Database 23c permite escribir sentencias select sin la clausula FROM si la misma no es necesaria, como vemos en el ejemplo que está comentada.


El paso siguiente es pegar el resultado de esa consulta que devuelve 1010 valores separados por comas (el formato que espera el operador IN) en la siguiente sentencia, y ejecutarla:

SELECT * FROM all_objects a WHERE object_id IN (<PegarAca>);

Veremos que la query se ejecuta sin ningún problema:


Auditoría a Nivel Columnas

Oracle 23c permite definir políticas de auditoría a nivel de columnas específicas tanto de vistas como de tablas, permitiendo de esa forma reducir el número de "falsos positivos" al tener que auditar sobre toda la tabla cuando nos interesa sólo una o varias columnas en particular.

Vamos a crear una tabla con varias columnas para demostrar cómo se define y cómo funciona la auditoría a nivel columna:

CREATE TABLE AuditarColumnas ( ID NUMBER(10) NOT NULL, Texto1 VARCHAR2(10), Texto2 VARCHAR2(10), Texto3 VARCHAR2(10));


Y ahora vamos a insertar algunos registros (usando la funcionalidad de constructor de tablas descripta en el artículo "Oracle 23c - Constructor de Tabla a partir de Valores"):

INSERT INTO AuditarColumnas VALUES (1, 'Fila 1 A', 'Fila 1 B', 'Fila 1 C'), (2, 'Fila 2 A', 'Fila 2 B', 'Fila 2 C'), (3, 'Fila 3 A', 'Fila 3 B', 'Fila 3 C'), (4, 'Fila 4 A', 'Fila 4 B', 'Fila 4 C');


Definiendo Auditoría a Nivel Columna

En el ejemplo podemos ver como crear una regla de auditoría en el caso de que se ejecute sentencias SELECT contra las columnas Texto1 o Texto2, o cuando se ejecute un UPDATE contra la columna Texto3, y a continuación vamos a habilitar dicha regla: 

CREATE AUDIT POLICY auditar_columnas ACTIONS SELECT(Texto1, Texto2) ON demo23c.AuditarColumnas, UPDATE(Texto3) ON demo23c.AuditarColumnas CONTAINER = CURRENT;
--
AUDIT POLICY auditar_columnas;

Nota: Para crear auditorías es necesario tener permisos, en este caso la misma la creamos con el usuario SYSTEM.

La lista de acciones que se pueden auditar sobre columnas esta disponible en la documentación oficial.


Probando la Auditoría

Vamos a ejecutar sentencias SELECT y UPDATE que en algunos casos tienen que ser auditados y en otros casos no:

UPDATE AuditarColumnas SET Texto1 = 'Modificado' WHERE ID =1; -- No debe Auditar SELECT ID, Texto1 FROM AuditarColumnas WHERE ID <3; -- Si debe Auditar UPDATE AuditarColumnas SET Texto3 = 'Modificado' WHERE ID =3; --Si debe Auditar SELECT ID, Texto3 FROM AuditarColumnas WHERE ID >2; -- No debe Auditar

Y sólo nos resta consultar la tabla de auditoría unificada (nuevamente, usando el usuario SYSTEM u otro usuario con privilegios de acceder a la auditoría) de esta manera:

SELECT TO_CHAR(event_timestamp,'YYYY-MM-DD HH24:MI:SS') AS Fecha, dbusername AS Usuario, action_name AS Accion, object_schema AS Esquema, object_name AS Objeto, sql_text AS CodigoSQL FROM unified_audit_trail WHERE object_name = 'AUDITARCOLUMNAS';


Obteniendo el siguiente resultado:

FECHA USUARIO ACCION ESQUEMA OBJETO CODIGOSQL -------------------- ---------- ---------- ---------- --------------- ------------------------------------------------------------ 2023-06-15 22:03:40 DEMO23C SELECT DEMO23C AUDITARCOLUMNAS SELECT ID, Texto1 FROM AuditarColumnas WHERE ID <3
2023-06-15 22:04:03 DEMO23C UPDATE DEMO23C AUDITARCOLUMNAS UPDATE AuditarColumnas SET Texto3 = 'Modificado' WHERE ID =3


Conversor Automático de PL/SQL a SQL (SQL Transpiler)

Esta nueva funcionalidad permite, en ciertos casos, convertir funciones desarrollada en PL/SQL directamente a código SQL, reduciendo notablemente el costo de ejecutar sentencias SQL que llaman veces a dichas funciones. Cuando el código SQL ejecuta una función PL/SQL, se produce un "cambio de contexto" en el que la sentencia SQL se detiene, se ejecuta el código PL/SQL en el motor de ejecución PL/SQL, el cual devuelve un resultado, y a continuación se vuelve el control al motor SQL, que obtiene y utiliza el resultado devuelto por el motor PL/SQL y luego toma otra fila y repite el proceso para ejecutar la función en ella.

Oracle 23c permite que funciones PL/SQL pueden ser convertidas directamente en la sentencia que las utiliza en código SQL (para ver más detalles sobre que funciones pueden ser convertidas consultar la documentación oficial), evitando de esa forma los cambios de contexto.

Esta conversión es automática, y solo requiere habilitarla mediante un parámetro configurable a nivel de instancia o de sesión.


Probando la conversión

Vamos a crear una función que concatena dos cadenas con un separador en el medio:

CREATE OR REPLACE FUNCTION Concatenar (pTexto1 VARCHAR, pTexto2 VARCHAR) RETURN VARCHAR AS BEGIN RETURN pTexto1 || '-' || pTexto2; END; /


Y vamos a ejecutar un SELECT sobre la tabla creada en el ejemplo anterior de auditoría de columnas utilizando la función recién creada:

SELECT * FROM AuditarColumnas ac WHERE Concatenar(ac.Texto1, ac.Texto2) = 'Fila 2 A-Fila 2 B';


Si consultamos el plan de ejecución de esta sentencia, veremos que en el predicado figura la llamada a la función:


A continuación vamos a cambiar la configuración de nuestra sesión, habilitando la funcionalidad de conversión (llamada transpiler) modificando el parámetro SQL_TRANSPILER de la siguiente forma:

ALTER SESSION SET sql_transpiler = 'ON';


Si volvemos a ejecutar la sentencia y vemos su plan de ejecución, nos encontraremos que la misma ha sido convertida y ya no contiene la llamada a la función sino que el código de la misma ha sido "embebido" dentro de nuestra sentencia:


Conclusión

En este artículo discutimos tres mejoras disponibles en Oracle 23c que facilitan la generación de código en forma dinámica, simplifican la auditoría y permiten una ejecución más eficiente de código PL/SQL sin necesidad de reescribir el mismo ni las sentencias que lo llaman.


Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.

lunes, 22 de mayo de 2023

Oracle 23c & JSON - Relational Duality Views - Parte #3


En este artículo final de la serie de JSON Relational Duality Views, vamos a analizar las ventajas y casos de uso que nos ofrece esta nueva funcionalidad de Oracle Database 23c.


Algunas Ventajas de JSON Relational Duality Views


Alta Concurrencia

JSON Relational Duality Views introducen control de concurrencia sin bloqueo previo (concurrencia optimista), basado en versiones que contempla la actualización de datos tanto mediante el UPDATE de un documento JSON como la ejecución de sentencias UPDATE en forma directa a las tablas relacionales involucradas.


Eliminación de Datos Duplicados

Las bases de datos de documentos tienen (en general) el problema de mantener sus documentos independientes. Al no tener un buen mecanismo para declarar relaciones entre documentos, que genera duplicación de datos e inconsistencias. Los datos duplicados conducen a mayores costos de mantenimiento, errores en la toma de decisiones y oportunidades comerciales perdidas.

JSON Relational Duality elimina la duplicación de datos ya que la información de un único documento JSON es almacenada mediante transacciones ACID (atomicidad, consistencia, aislamiento, durabilidad) en diversas tablas relacionales, eliminando la duplicación de datos pero manteniendo la consistencia automáticamente.


Datos Accesibles en Múltiples Documentos

Las bases de datos de documentos no son flexibles cuando debemos tener información compartida entre distintas jerarquías. Si deseamos tener información de los productos en los documentos de Compras y Ventas, es probable que tengamos que duplicar la misma en ambas jerarquías, o que debamos usar una tercer jerarquía que se relacione a ambas pero sacrificando la integridad y generando un modelo mixto (documentos relacionados) que suma mas problemas que lo que soluciona.

Las vistas duales JSON Relational permiten definir multiples múltiples vistas de JSON en grupos de tablas superpuestas. Este modelado de datos flexible hace que la creación de múltiples aplicaciones con los mismos datos sea fácil y eficiente, permitiendo por ejemplo que la tabla relacional de productos sea incluida tanto en la vista de Compras como la de Ventas, siendo actualizable en ambas pero almacenada en forma única y consistente.


Eliminar Roundtrips

Al desarrollar una aplicación es necesario mapear entre objetos de nivel de aplicación (como clases, utilizados por lenguajes de programación) y las tablas relacionales. Es normal tener que modificar datos de varias tablas para implementar un caso de uso empresarial, lo cual genera un problema llamado "desajuste de impedancia relacional de objetos". Es frecuente el uso de frameworks (Object Relational Mapping - ORM) que facilitan las tareas de mapeo, pero en general es a expensas de performance ya que no utilizan las funciones avanzadas que ofrecen las bases de datos. Dado que una operación en un objeto de nivel de aplicación puede dar lugar a múltiples accesos o actualizaciones de las tablas participantes, esto ralentiza el rendimiento de la aplicación.

JSON Relational Duality Views elimina la necesidad de asignar objetos a tablas relacionales a nivel de la aplicación, proporcionando vistas JSON totalmente actualizables sobre los datos almacenados en forma relacional. Las aplicaciones pueden leer un documento, realizar los cambios necesarios y volver a escribir el documento sin preocuparse por la estructura de datos subyacente, el mapeo, la consistencia o el ajuste del rendimiento, permitiendo recuperar o almacenar todos los datos necesarios para una aplicación en una sola operación de base de datos.


Lo Mejor de Ambos Mundos

En definitiva, con JSON Relational Duality Views podemos unir las ventajas de ambos mundos!



JSON Relational Duality Views permite:
  • Eliminar el uso de frameworks de ORM para mapear información de las aplicaciones al modelo de datos relacional que la almacena.
  • Acceder a la información relacional mediante APIs de documentos como ser Oracle REST Data Services u Oracle Database API for MongoDB.

Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.

viernes, 19 de mayo de 2023

Oracle 23c & JSON - Relational Duality Views - Parte #2


En este segundo artículo sobre JSON Relational Duality Views vamos a analizar algunas características mas avanzadas de las mismas y vamos a analizar también las vistas del diccionario de datos que contienen información sobre esta nueva funcionalidad.


Control de Concurrencia Optimista

En el articulo anterior "Oracle 23c & JSON - Relational Duality Views - Parte #1" estuvimos actualizando documentos JSON mediante el uso de vistas duales, pero no contemplamos la posibilidad que los datos subyacentes hayan cambiado, algo que en la vida real ocurre frecuentemente.

Duality Views utiliza una nueva arquitectura de control de concurrencia optimista o sin bloqueo disponible en Oracle Database 23c que permite a los desarrolladores administrar sus datos de manera consistente en operaciones sin estado (como ser REST GET y PUT), ya que los bloqueos tienen estado y no se pueden retener en llamadas sin estado.

Este nuevo algoritmo posee las siguientes características:

  • Incorpora el concepto de etiqueta de entidad (ETAG) del protocolo HTTP a la base de datos, siendo una ETAG un hash o una firma para el contenido de una página web recuperada, pero esto aplicado a documentos.
  • Cuando se realiza un GET en Duality View, el documento JSON devuelto también contiene el ETAG del conjunto de filas utilizadas en la construcción del documento.
  • Cuando ese documento se modifica y luego se vuelve a poner en la base de datos, el ETAG proporcionado se compara con el ETAG actual de las filas. Si el ETAG difiere, el objeto debe haber sido modificado por otra sesión entre el momento en que se hizo el GET y el momento en que se envió el PUT, por lo que el PUT es rechazado. La aplicación puede volver a obtener la versión mas actualizada del documento (el cual contiene un nuevo ETAG) mediante un GET, aplicarle los cambios requeridos, y volver a intentar hacer un PUT.
  • Si el PUT tiene éxito, tenemos la garantía de que no se han producido cambios intermedios en el objeto y garantizamos la atomicidad y la coherencia a nivel de documento.

Este tipo de control de concurrencia sin bloqueo permite a los desarrolladores centrarse en su aplicación sin tener que implementar control de concurrencia dentro de la aplicación.


Control de Concurrencia Optimista en Funcionamiento

Vamos a ver como funciona el control de concurrencia en la práctica. Lo primero que vamos a hacer es consultar un documento en particular, usando los ejemplos del artículo "Oracle 23c & JSON - Relational Duality Views - Parte #1" donde comenzamos a explicar como funcionan las vistas duales.


Vamos a obtener el documento JSON asociado al comprobante #3, usando la siguiente sintaxis:

SELECT JSON_SERIALIZE(c.data PRETTY) FROM VDCOMPROBANTE c WHERE c.data.IDComprobante = 3;


Lo cual devuelve el siguiente documento:

JSON_SERIALIZE(C.DATAPRETTY) --------------------------------------------------------------------- { "_metadata" : { "etag" : "66C4365F9A178623F27913B947DF8C7C", "asof" : "00000000007844AF" }, "IDComprobante" : 3, "FechaComprobante" : "2023-05-16T00:00:00", "IDCliente" : 3, "Importe" : 14200, "Detalles" : [ { "IDDetalle" : 5, "IDProducto" : 5, "CantidadProducto" : 14, "ImporteUnitario" : 1000 } ] }

El valor resaltado en ROJO es el ETAG calculado para la versión actual del documento, el cual sirve para identificar a la misma.


A continuación, vamos a usar una sentencia UPDATE de SQL para actualizar en forma directa, en la tabla de detalles de comprobantes, el valor de la columna cantidad del registro de detalle #5, como vemos a continuación:

UPDATE tDetalleComprobante SET Cantidad = 7 WHERE ID_DetalleComprobante = 5;

1 row updated.


Volviendo al documento JSON que obtuvimos en el primer SELECT, asumamos ahora que la aplicación que lo obtuvo le hizo una modificación (por ejemplo, cambió el ID de cliente, como remarcamos en azul) y desea grabar dicho cambio, lo cual hace enviando el documento completo (incluyendo el ETAG que obtuvo al leer en documento) ejecutando la siguiente sentencia:

UPDATE vdComprobante c SET data = '{ "_metadata" : { "etag" : "66C4365F9A178623F27913B947DF8C7C", "asof" : "00000000007844AF" }, "IDComprobante" : 3, "FechaComprobante" : "2023-05-16T00:00:00", "IDCliente" : 500, "Importe" : 14200, "Detalles" : [ { "IDDetalle" : 5, "IDProducto" : 5, "CantidadProducto" : 14, "ImporteUnitario" : 1000 } ] }' WHERE c.data.IDComprobante = 3;


Al ejecutarla, obtendremos el siguiente mensaje de error:

Error report - ORA-42699: Cannot update JSON Relational Duality View 'VDCOMPROBANTE': The ETAG of document with ID 'FB03C10400' in the database did not match the ETAG passed in.


Esto se debe a que el ETAG del documento almacenado en la base de datos cambió al momento en que modificamos el detalle, lo cual podemos ver si volvemos a consultar el mismo:

SELECT JSON_SERIALIZE(c.data PRETTY) FROM VDCOMPROBANTE c WHERE c.data.IDComprobante = 3;


Obteniendo:

JSON_SERIALIZE(C.DATAPRETTY)
---------------------------------------------------------------- { "_metadata" : { "etag" : "5A94261D02F1232A3F126D7C36A961BD", "asof" : "00000000007848C6" }, "IDComprobante" : 3, "FechaComprobante" : "2023-05-16T00:00:00", "IDCliente" : 3, "Importe" : 14200, "Detalles" : [ { "IDDetalle" : 5, "IDProducto" : 5, "CantidadProducto" : 7, "ImporteUnitario" : 1000 } ] }


Con la versión "actualizada" del documento, la aplicación tiene que volver a aplicar el cambio individual deseado (actualizar el cliente) y volver a enviar todo el documento, con el ETAG que ahora si coincide con la versión almacenada en la base de datos.


Vistas del Diccionario de Datos

El diccionario de datos de Oracle 23 incorpora algunas nuevas vistas para acceder a la información de las vistas duales JSON Relacional. Veremos las mismas en detalle a continuación (analizaremos las visas que comienzan con USER_, que permiten acceder a los objetos del usuario conectado, existen versiones con prefijo ALL_ y DBA_ que ven todos los objetos sobre los que el usuario posee permisos y todos los objetos de la base de datos respectivamente).


USER_JSON_DUALITY_VIEWS

Esta vista contiene información general de cada JSON Relational Duality View definida en el esquema.

SELECT * FROM USER_JSON_DUALITY_VIEWS;


Las columnas ALLOW_INSERT, ALLOW_UPDATE y ALLOW_DELETE nos indican que tipo de acciones se pueden realizar sobre las mismas. La columna JSON_SCHEMA contiene el esquema JSON que describe al documento que esta disponible al utilizar cada una de las vistas.


USER_JSON_DUALITY_VIEW_TABS

Esta vista nos muestra la tabla principal y todas las tablas involucradas en cada una de las vistas duales:

SELECT * FROM USER_JSON_DUALITY_VIEW_TABS;


USER_JSON_DUALITY_VIEW_TAB_COLS

Esta vista muestra información sobre todas las columnas (atributos del documento JSON) definidas en una vista dual:

SELECT * FROM USER_JSON_DUALITY_VIEW_TAB_COLS;


La columna JSON_KEY_NAME nos indica el nombre de atributo en el documento JSON y las columnas PRIMARY_KEY_POSETAG_POS nos indica si forma parte de la clave primaria (y en que posición) y en que posición se encuentra dentro del ETAG.


USER_JSON_DUALITY_VIEW_LINKS

Esta vista nos permite entender las relaciones entre las distintas tablas en cada vista / documento JSON:

SELECT * FROM USER_JSON_DUALITY_VIEW_LINKS;


Conclusión

En este artículo hicimos un análisis del mecanismo de control de concurrencia optimista (sin lockeos) implementado en JSON Relational Duality Views y las vistas del diccionario de datos que contienen información


Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.

martes, 16 de mayo de 2023

Oracle 23c & JSON - Relational Duality Views - Parte #1

La version 23c de la base de datos Oracle introduce muchas mejoras para trabajar con documentos JSON, en este artículo vamos a comenzar por una de las mas importantes, que es la disponibilidad de vistas "duales" para ver e interactuar con la información almacenada en nuestra base de datos en tablas relacionales pero directamente en formato JSON.

En el artículo vamos a utilizar dos tablas las cuales pueden crear para probar esta nueva funcionalidad usando el código a continuación:

CREATE TABLE tComprobante ( ID_Comprobante NUMBER(10) CONSTRAINT PK_Comprobante PRIMARY KEY, Fecha_Comprobante DATE NOT NULL, ID_Cliente NUMBER(10) NOT NULL, Importe DECIMAL (10,2) NOT NULL); -- CREATE TABLE tDetalleComprobante ( ID_DetalleComprobante NUMBER(10)
CONSTRAINT PK_DetalleComprobante PRIMARY KEY, ID_Comprobante NUMBER(10) NOT NULL
CONSTRAINT FK_DetalleComprobante_ID_Comprobante REFERENCES tComprobante(ID_Comprobante), ID_Producto NUMBER(10) NOT NULL, Cantidad NUMBER(10) NOT NULL, ImporteUnitario DECIMAL (10,2) NOT NULL);


Y también vamos a insertar algunos datos en las mismas, de la siguiente manera (aprovechando la nueva funcionalidad "Oracle 23c - Constructor de Tabla a partir de Valores"):

INSERT INTO tComprobante VALUES (1, DATE '2023-05-15', 1, 20500), (2, DATE '2023-05-15', 2, 9800), (3, DATE '2023-05-16', 3, 14200); -- INSERT INTO tDetalleComprobante VALUES (1, 1, 1, 20, 1000), (2, 1, 2, 5, 100), (3, 2, 3, 9, 1000), (4, 2, 4, 8, 100), (5, 3, 5, 14, 1000), (6, 3, 6, 2, 100); -- COMMIT;


Creando una Vista Dual JSON - Relacional

Vamos a crear una vista dual, la cual nos permite ver la información de varias tablas de nuestro sistema como si fuera un objeto JSON, usando la siguiente sintaxis:

CREATE OR REPLACE JSON RELATIONAL DUALITY VIEW vdComprobante AS SELECT JSON {'IDComprobante' : c.ID_Comprobante, 'FechaComprobante' : c.Fecha_Comprobante, 'IDCliente' : c.ID_Cliente, 'Importe' : c.Importe , 'Detalles' : [ SELECT JSON {'IDDetalle' : d.ID_DetalleComprobante, 'IDProducto' : d.ID_Producto, 'CantidadProducto' : d.Cantidad, 'ImporteUnitario' : d.ImporteUnitario} FROM tDetalleComprobante d WITH INSERT UPDATE DELETE WHERE d.ID_Comprobante = c.ID_Comprobante ] } FROM tComprobante c WITH INSERT UPDATE DELETE;

La sintaxis es sencilla, en el ejemplo, estamos definiendo el mapeo entre las columnas de nuestras dos tablas (en verde) y los atributos del documento JSON (en naranja), y estamos permitiendo insertar, eliminar y actualizar registros de ambas tablas mediante la vista.


Usando una Vista Dual JSON - Relacional

Consultar Datos

Una vez que creamos la vista, podemos comenzar a usarla. Lo primero que vamos a hacer es consultar los datos disponibles en ella usando una sentencia SELECT:

SELECT * FROM vdComprobante;

Vemos que la vista posee una única columna, llamada DATA, la cual contiene un documento JSON representando a cada registro de nuestra tabla tComprobante y sus detalles asociados:


Tambien podemos usar la función JSON_SERIALIZE para obtener una representación mas "legible" del documento, usando la opción PRETTY de la misma, como mostramos a continuación:

SELECT JSON_SERIALIZE(c.data PRETTY) FROM vdComprobante c;


Nota: vemos que el documento contiene un elemento adicional llamado "_metadata" que vamos a explicar y analizar en un segundo artículo sobre JSON Relational Duality Views.


Podemos ver que la vista posee una única columna de tipo JSON si usamos el comando DESC para describir a la misma:

DESC vdComprobante

Name Null? Type ---- ----- ---- DATA JSON


Actualizar Datos

Las vistas duales no solo permiten ver los datos de nuestro modelo relacional como objetos JSON, sino que también podemos utilizarlas para actualizar datos tanto de la entidad principal (Comprobante) como de sus detalles usando un documento JSON. Probaremos de modificar la fecha del primer comprobante, y la cantidad y precio del primer item de dicho comprobante (nuevos valores resaltados en ROJO) , como vemos a continuación:

UPDATE vdComprobante c SET c.data = ( '{ "IDComprobante" : 1, "FechaComprobante" : "2023-05-14T00:00:00", "IDCliente" : 1, "Importe" : 20500, "Detalles" : [ { "IDDetalle" : 1, "IDProducto" : 1, "CantidadProducto" : 10, "ImporteUnitario" : 2000 }, { "IDDetalle" : 2, "IDProducto" : 2, "CantidadProducto" : 5, "ImporteUnitario" : 100 } ] }') WHERE c.data.IDComprobante = 1;

Nota: Usamos "dot notation" para referirnos a los elementos dentro del documento JSON almacenado en la columna data desde la sentencia SQL.


Tras ejecutar ese update sobre la vista, podemos ver que la fecha del comprobante fue actualizada y se ve reflejada así tanto en la table como en la vista dual:

SELECT ID_Comprobante, Fecha_Comprobante FROM tComprobante c WHERE c.ID_Comprobante = 1;
ID_COMPROBANTE FECHA_COMPROBANTE -------------- ------------------- 1 14-05-2023 00:00:00
SELECT c.data.IDComprobante, c.data.FechaComprobante
FROM vdComprobante c WHERE c.data.IDComprobante = 1;

IDCOMPROBANTE FECHACOMPROBANTE ------------------------------------------------------ ------------------------------------------------------ 1 "2023-05-14T00:00:00"


Lo mismo con las cantidades y precios del primer detalle:

SELECT d.ID_DetalleComprobante, d.Cantidad, d.ImporteUnitario FROM tDetalleComprobante d WHERE d.ID_DetalleComprobante = 1;

ID_DETALLECOMPROBANTE CANTIDAD IMPORTEUNITARIO --------------------- ---------- --------------- 1 10 2000


Eliminar Datos

Podemos eliminar datos usando directamente la vista, como se muestra en el ejemplo:

DELETE FROM vdComprobante c WHERE c.data.IDComprobante = 3;

1 row deleted.


La funcionalidad de DELETE sirve para eliminar todo el documento, si quisiéramos eliminar solamente un detalle de un comprobante tenemos que hacer un UPDATE sobre la vista enviando el documento JSON sin el registro de detalle que se desea eliminar, como podemos ver a continuación (previo a restaurar el comprobante #3 que eliminamos recién):

UPDATE vdComprobante c SET c.data = ( '{ "IDComprobante" : 3, "FechaComprobante" : "2023-05-16T00:00:00", "IDCliente" : 3, "Importe" : 14200, "Detalles" : [ { "IDDetalle" : 5, "IDProducto" : 5, "CantidadProducto" : 14, "ImporteUnitario" : 1000 } ] }') WHERE c.data.IDComprobante = 3;


Podemos validar que ahora el comprobante #3 posee un único item de la siguiente manera:

SELECT ID_DetalleComprobante, Cantidad, ImporteUnitario, ID_Producto FROM tDetalleComprobante d WHERE d.ID_Comprobante = 3;
ID_DETALLECOMPROBANTE CANTIDAD IMPORTEUNITARIO ID_PRODUCTO --------------------- ---------- --------------- ----------- 5 14 1000 5


Insertar Datos

Así como pudimos actualizar y eliminar, podemos insertar nuevos datos mediante las vistas duales. Si queremos insertar un detalle, en realidad debemos actualizar el documento con un detalle adicional, pero si queremos insertar un nuevo documento compuesto por un Comprobante y su detalle, usamos la sentencia SELECT como mostramos a continuación, y estaremos insertando datos en ambas tablas:

INSERT INTO vdComprobante c VALUES ( '{ "IDComprobante" : 4, "FechaComprobante" : "2023-05-16T00:00:00", "IDCliente" : 4, "Importe" : 6500, "Detalles" : [ { "IDDetalle" : 7, "IDProducto" : 7, "CantidadProducto" : 6, "ImporteUnitario" : 1000 } ] }');


Podemos validar el resultado de dicha operación con las siguientes consultas:

SELECT ID_Comprobante, Fecha_Comprobante FROM tComprobante c WHERE c.ID_Comprobante = 4;
ID_COMPROBANTE FECHA_COMPROBANTE -------------- ------------------- 4 16-05-2023 00:00:00

SELECT ID_DetalleComprobante, Cantidad, ImporteUnitario, ID_Producto FROM tDetalleComprobante d WHERE d.ID_Comprobante = 4;

ID_DETALLECOMPROBANTE CANTIDAD IMPORTEUNITARIO ID_PRODUCTO --------------------- ---------- --------------- ----------- 7 6 1000 7


Conclusión

En este artículo hicimos un análisis inicial de la funcionalidad de JSON - Relational Duality Views que seguiremos explorando en los próximos días, así como otras importantes mejoras a JSON disponibles en Oracle Database 23c 


Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.



lunes, 8 de mayo de 2023

Oracle 23c - Nueva funcionalidad DOMAIN

Oracle 23c introduce un nuevo concepto llamado DOMAIN que permite definir en un único lugar las propiedades y restricciones relacionados con un dato en particular (como ser un numero de telefono o una dirección de mail) y posteriormente utilizar el mismo en todas aquellas columnas que formen parte de ese "dominio".

En este artículo nos vamos a concentrar en la forma más básica de esta nueva funcionalidad, que son los dominios simples.


Creando un Dominio Simple (de una columna)

Existen diversos tipos de dominios, los cuales incluyen muchas funciones avanzadas. Vamos a comenzar con un ejemplo para crear un dominio sencillo, que nos permita definir, por ejemplo, las características de un número de CUIT (Clave Unica de Identificación Tributaria) utilizado en Argentina.

Vamos a crear el mismo usando el tipo de datos VARCHAR2, con un tamaño máximo de veinte caracteres, y usaremos una expresión regular para validar que el mismo cumpla con el formato válido, el cual consta once dígitos, los cuales se visualizan como sección de dos dígitos seguidos de ocho dígitos adicionales y un dígito de control, separados entre sí por dos guiones:

CREATE DOMAIN d_NRO_CUIT AS VARCHAR2(20)
CONSTRAINT chk_nro_cuit CHECK (REGEXP_LIKE (d_NRO_CUIT, '^[0-9]{11}$'))
DISPLAY(SUBSTR(d_NRO_CUIT,1,2)||'-'||SUBSTR(d_NRO_CUIT,3,8)||'-'||SUBSTR(d_NRO_CUIT,11,1));

Nota: Para crear un dominio se requiere el privilegio CREATE DOMAIN, el cual forma parte del nuevo rol DB_DEVELOPER_ROLE como explicamos en el artículo "Oracle 23c - Nuevo Rol DB_DEVELOPER_ROLE".


Una vez que creamos el dominio, podemos utilizarlo para definir columnas en tablas de nuestra base de datos, las cuales heredarán todas sus características (por el momento, sólo veremos el tipo de datos, tamaño, una función de check y un formato de visualización, pero hay algunas más).

CREATE TABLE tCliente ( ID_Cliente NUMBER(10) CONSTRAINT PK_Cliente PRIMARY KEY, Nombre_Cliente VARCHAR2(100) NOT NULL, CUIT_Cliente DOMAIN d_NRO_CUIT NOT NULL);


Paso siguiente, vamos a probar de insertar dos registros distintos en nuestra tabla usando el siguiente script:

INSERT INTO tCliente VALUES (1, 'Supermercado Genial', '34123456789'); INSERT INTO tCliente VALUES (2, 'Distribuidora MaxiPrecios', '3412345678A');

Obteniendo el siguiente resultado:


Podemos ver que la segunda fila no fue insertada ya que el valor provisto para el campo CUIT_Cliente no satisface las reglas expresadas en la restricción definida para el dominio, que debe ser de 11 dígitos.

Vamos a consultar los datos insertados, y vamos a usar la función DOMAIN_DISPLAY aplicada a la columna CUIT_Cliente para ver el contenido de la columna pero con el formato especificado en el dominio:

SELECT ID_Cliente, Nombre_Cliente, CUIT_Cliente, DOMAIN_DISPLAY(CUIT_Cliente) FROM tCliente;

Lo cual nos muestra lo siguiente:


Así como podemos definir un tipo de datos, tamaño y restricción de tipo CHECK, también podemos definir otras condiciones al crear un DOMAIN, como ser:

  • DISPLAY: Permite definir un formato en que se mostrará el dato. Por ejemplo, convertir la primer letra de cada palabra a mayúsculas y el resto en minúsculas usando la función INITCAP.
  • ORDER: Permite definir un formato para ordenar los datos. Por ejemplo, ordenar convirtiendo un texto todo a mayúsculas o minúsculas.
  • COLLATE: Si estamos usando tipode datos de texto extendido, podemos usar esta opción para indicar que la columna sea de tipo CASE INSENSITIVE en las búsquedas.

Funciones de Dominio

Ya vimos en el ejemplo anterior el uso de DOMAIN_DISPLAY, la cual nos permite ver la información de una columna con el formato especificado en el dominio. Existen otras funciones que nos devuelven información respecto a las columnas y los dominios al que están asociadas. En el ejemplo siguiente podemos ver algunas de ellas (el resto se pueden ver en la documentación).


La función DOMAIN_NAME nos muestra el dominio asociado a una columna:

SELECT DOMAIN_NAME(CUIT_Cliente) AS Dominio FROM tCliente;

DOMINIO ---------------------------------------- DEMO.D_NRO_CUIT


La función DOMAIN_CHECK nos permiten validar si un dato cumple con las condiciones especificadas en la restricción de tipo CHECK de un dominio, como vemos a continuación:

SELECT DOMAIN_CHECK('d_NRO_CUIT','00123456789') AS Sin_CAST, DOMAIN_CHECK('d_NRO_CUIT',CAST ('00123456789' AS VARCHAR2(100))) AS Con_CAST_OK, DOMAIN_CHECK('d_NRO_CUIT',CAST ('AA123456789' AS VARCHAR2(100))) AS Con_CAST_ERROR;

Lo cual nos muestra el siguiente resultado:


Nota: Si queremos validar un valor que cumple con la condición en forma directa, igualmente nos devuelve como resultado 0. Es necesario hacer un CAST al tipo de datos esperado por el dominio para que funciones correctamente, como se muestra en el ejemplo. Esto seguramente será corregido en el corto plazo para que el CAST no sea necesario.


Ver relación de columnas con Dominios usando DESC

Al describir una tabla con el comando DESC, ahora no sólo muestra el tipo de datos de las columnas sino que también muestra si la misma está asociada a un dominio, como vemos a continuación:

SQL> DESC tCliente
Name Null? Type ----------------------------------------- -------- ---------------------------- ID_CLIENTE NOT NULL NUMBER(10) NOMBRE_CLIENTE NOT NULL VARCHAR2(100) CUIT_CLIENTE NOT NULL VARCHAR2(20) DEMO.D_NRO_CUIT


Vistas de Diccionario de Datos

Los Dominios son un nuevo tipo de objetos en las bases de datos, y podemos verlos en las vistas del diccionario de datos como ser USER_OBJECTS:

SELECT object_name, object_type
FROM user_objects;

OBJECT_NAME OBJECT_TYPE -------------------- -------------------- TCLIENTE TABLE D_NRO_CUIT DOMAIN PK_CLIENTE INDEX


La vista USER_TAB_COLUMNS incorpora dos nuevas columnas para ver la información de dominios asociada a una columna, como podemos ver a continuación:

SELECT column_id, column_name, domain_owner, domain_name FROM user_tab_columns;

COLUMN_ID COLUMN_NAME DOMAIN_OWNER DOMAIN_NAME ---------- -------------------- -------------------- -------------------- 1 ID_CLIENTE 2 NOMBRE_CLIENTE 3 CUIT_CLIENTE DEMO D_NRO_CUIT


Y se incorporan tres nuevas vistas que muestran información específica de los dominios:

SELECT * FROM user_domains; SELECT * FROM user_domain_cols; SELECT * FROM user_domain_constraints;


Conclusión

Si bien analizamos sólo el tipo de DOMAIN mas sencillo, podemos ver que es una funcionalidad muy completa que puede facilitar el diseño de una base de datos al poder definir reglas y atributos comunes a muchas columnas bajo un único dominio, y luego crear las columnas especificando el dominio para que las mismas hereden automáticamente estas validaciones y atributos.


Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.



viernes, 5 de mayo de 2023

Oracle 23c - Soporte de tipo de datos DATE, TIMESTAMP e INTERVAL en funciones CEIL y FLOOR

Otra interesante mejora en Oracle 23c es la posibilidad de usar las funciones FLOOR y CEIL (las cuales devuelven el valor redondeado hacia abajo o hacia arriba) con los tipos de datos DATE, TIMESTAMP e INTERVAL, además de usarlas con datos numéricos como era posible hasta ahora.


Nota: para poder ver los resultados correctos de los ejemplos mostrados en el artículo, es recomendable configurar las variables de sesión de formato de fecha de la siguiente manera:

ALTER SESSION SET nls_date_format='DD-MM-YYYY HH24:MI:SS'; ALTER SESSION SET nls_timestamp_format='DD-MM-YYYY HH24:MI:SS.FF3';


FLOOR y CEIL con DATE

En el siguiente ejemplo, vamos a mostrar el uso de ambas funciones con el tipo de datos DATE. La sintaxis mas sencilla es la siguiente:

SELECT SYSDATE AS Fecha_Hora,
    FLOOR(SYSDATE) AS FLOOR_SYSDATE,
    CEIL(SYSDATE) AS CEILING_SYSDATE;

Y al ejecutar la misma obtenemos el siguiente resultado:


Ambas funciones permiten el uso de un modificador de formato, en el cual podemos especificar si queremos redondear (hacia arriba o hacia abajo) con una unidad de medida distinto al valor por defecto que, como toda la aritmética de fechas en Oracle, se basa en días. El ejemplo siguiente muestra como redondear hacia abajo en minutos y como redondear hacia arriba en horas:

SELECT SYSDATE AS Fecha_Hora,
    FLOOR(SYSDATE, 'MI') AS FLOOR_SYSDATE,
    CEIL(SYSDATE, 'HH24') AS CEILING_SYSDATE;

Podemos ver el resultado de ejecutar la sentencia:


Nota
: para ver los formatos aceptados por estas funciones ver el artículo CEIL, FLOOR, ROUND, and TRUNC Date Functions en la documentación oficial.


FLOOR y CEIL con TIMESTAMP

La sintaxis es exactamente igual, y podemos ver el mismo ejemplo usando modificadores de formato pero esta vez con el tipo de dato TIMESTAMP:

SELECT CURRENT_TIMESTAMP AS Fecha_Hora,
    FLOOR(CURRENT_TIMESTAMP,'MI') AS FLOOR_TIMESTAMP,
    CEIL(CURRENT_TIMESTAMP,'HH24') AS CEILING_TIMESTAMP;

Obteniendo estos resultados:


Nota: ambas funciones aceptan valores de entrada de tipo TIMESTAMP, pero devuelven valores de tipo DATE, como se ve en la imagen al no incluir fracciones de segundos.


FLOOR y CEIL con INTERVAL

Por último, vamos a aplicar las funciones a datos de tipo INTERVAL, para ellos usamos una CTE que simula una tabla con una columna de este tipo de datos, y le aplicamos ambas funciones con modificadores de formato para redondear a minutos hacia abajo y redondear a horas hacia arriba:

WITH vData AS (SELECT INTERVAL '0 05:23:14.333' DAY TO SECOND AS Intervalo) SELECT Intervalo,
    FLOOR(Intervalo,'MI') AS FLOOR_INTERVAL,
    CEIL(Intervalo,'HH24') AS CEILING_INTERVAL FROM vData;

Obteniendo el siguiente resultado:


Conclusión

Si bien la función FLOOR puede reemplazarse sin problemas por TRUNC (en todos los ejemplos mencionados devuelve el mismo resultado) la posibilidad de usar la misma así como usar CEILING con tipos de datos adicionales a los numéricos es una ventaja que facilita el desarrollo de código SQL a partir de Oracle 23c.


Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.


jueves, 4 de mayo de 2023

Oracle 23c - Constructor de Tabla a partir de Valores


Otra mejora interesante en el lenguaje SQL de Oracle Database 23c es la posibilidad de "convertir" un conjunto de valores en una tabla en forma muy rápida, sin necesidad de tener que usar sentencias SELECT con la tabla DUAL.

Esta mejora al constructor VALUES (el cual solemos utilizar en las sentencias INSERT para proveer una lista de valores) ahora acepta múltiple filas en una misma sentencia, y se puede usar no solo para insertar sino en diversas sentencias SQL, como mostraremos en este artículo.


Sintaxis

La sintaxis del constructor VALUES con múltiples filas es la siguiente:

VALUES ( <ValorA1>, <ValorB1>, <ValorC1> )
     [,( <ValorA2>, <ValorB2>, <ValorC2> )...]


Uso en sentencias INSERT

Podemos usarlo en una sentencia INSERT para insertar múltiples filas en una única sentencia. Primero vamos a crear una tabla vacía para poder insertar los datos en la misma:

CREATE TABLE tValues (ID NUMBER(10) NOT NULL CONSTRAINT PK_Values PRIMARY KEY, Valor VARCHAR2(20) NOT NULL);


Luego, usaremos VALUES para insertar tres filas en la nueva tabla en una única sentencia SELECT:

INSERT INTO tValues VALUES (1, 'Valor 1'), (2, 'Valor 2'), (3, 'Valor 3');

Y vemos el resultado de la operación:


Uso en sentencias SELECT

Así como lo usamos para insertar múltiples valores en una única sentencia, podemos usar el constructor para devolver múltiple filas en una sentencia SELECT. La única diferencia es que debemos usar el mismo en la cláusula FROM dentro de paréntesis y debemos proporcionar un alias tanto para la "tabla" como para cada una de las columnas:

SELECT * FROM ( VALUES (4, 'Valor 4'), (5, 'Valor 5'), (6, 'Valor 6') ) t (ID, Valor);

Esta sentencia devuelve el siguiente resultado:


Uso en Cláusulas WITH

Este constructor puede usarse también en cláusulas WITH, como mostramos a continuación:

WITH vDatos (ID, Valor) AS ( VALUES (7, 'Valor 7'), (8, 'Valor 8'), (9, 'Valor 9') ) SELECT * FROM vDatos;

Con el siguiente resultado:


Uso en Sentencias MERGE

Otro ejemplo de usabilidad de este constructor es en sentencias MERGE. En el ejemplo, vamos a actualizar la tabla creada en el primer ejemplo, usando la siguiente sentencia: 

MERGE INTO tValues t USING (VALUES (3, 'Valor 3 Modif.'), (4, 'Valor 4 Nuevo') ) s (ID, Valor) ON (s.ID = t.ID) WHEN MATCHED THEN UPDATE SET t.Valor = s.Valor WHEN NOT MATCHED THEN INSERT (t.ID, t.Valor) VALUES (s.ID, s.Valor);

Luego de ejecutar la sentencia MERGE, si consultamos el contenido de la tabla tValues veremos que la fila #3 ha sido modificada y la fila #4 ha sido insertada:



Conclusión

El uso del constructor VALUES simplifica la generación de múltiple filas en base a valores dados, para ser utilizados en diverso tipo de sentencias SQL.


Si desean conocer más sobre Oracle 23c, es recomendable que vean estos artículos en este blog como punto de partida:

Adicionalmente, pueden consultar todos los artículos relacionados a Oracle Database 23c agrupados en en el tag Database 23c.