En mi artículo Almacenamiento de datos formateados en JSON en SQL Server 2016, dimos un vistazo a las opciones T-SQL disponibles para convertir datos JSON a filas y columnas para propósitos de poblar un SQL Server basado en el almacenamiento de datos. La incrementada popularidad de JSON en aplicaciones web modernas puede crear un requerimiento para que los equipos de datos expongan algunos de sus datos a aplicaciones cliente (por ejemplo, herramientas de reportes, servicios web, etc.) en un formato JSON. En este artículo dimos un vistazo a cómo tal requerimiento puede ser implementado por equipos de datos usando la cláusula de SQL Server 2016 FOR JSON.
SQL Server a Tipos de Datos Soportados JSON
Como muchas otras características en SQL Server, hay términos y condiciones para usarlos y JSON no es diferente. Por tanto, es importante que tomemos nota de los tipos de datos soportados. Los datos SQL Server almacenados en los siguientes tiempos de datos no pueden ser convertidos a JSON:
Un desglose de los tipos de datos soportados es mostrado en la Tabla 1.
SQL Server Data Type | JSON Data Type |
---|---|
char, nchar, varchar, nvarchar, date, datetime, datetime2, time, datetimeoffset, uniqueidentifier, money | string |
int, bigint, float, decimal, numeric | number |
Bit | Boolean |
varbinary, binary, image, timestamp, rowversion | BASE64-encoded string |
Cláusula T-SQL FOR JSON
Aunque el soporte de SQL Server para XML permitió la representación gráfica de datos vía un editor (mostrado en la Figura 1), intentar ver datos JSON vía un editor puede ser frustrante, ya que los datos JSON son mostrados en una sola fila sin formato.
Es por tanto recomendable que mientras usted aprende JSON en SQL Server que encuentre un editor de JSON. Para los propósitos de esta discusión, estaré usando JSONFormatter from curiousconcept.com. Como puede verse en la Figura 3, la salida JSON de la Figura 2 es ahora apropiadamente formateada.
Hay dos maneras de que los resultados relacionales puedan ser convertidos a JSON: las opciones AUTO y PATH.
Convertir los Resultados Usando el Modo AUTO
Esta es la manera más simple de convertir datos relacionales a formato JSON, ya que todo lo que tiene que hacer es añadir la cláusula FOR JSON AUTO al final de su sentencia SELECT. En este modo, la estructura de la salida JSON es deterinada por una combinación del orden de las columnas en su sentencia SELECT, así como las tablas que son referenciadas por la sentencia SELECT. La Figura 4 muestra una sentencia T-SQL que convierte los resultados de nuestros datos ficticios de Fruit Sales a JSON.
12345SELECTsales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesFOR JSON AUTOFigura 4 Los resultados del script de arriba son mostrados a continuación. En estos se muestra un solo arreglo (representado por un corchete) fue retornado con filas mantenidas como objetos (representados por llaves).
Figura 5 El script de muestra provisto en la Figura 4 no demuestra completamente el rol del ordenamiento en la columna y la tabla en la cláusula FOR JSON AUTO, ya que una sola tabla fue usada. La Figura 6 muestra el script revisado con una unión a otra dimensión de búsqueda de cliente ficticio respecto de los clientes que han comprado frutas.
12345678SELECTsales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number],cust.[Name],cust.[DOB],cust.[Gender]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesLEFT JOIN [selectSIFISOBlogs].[DIM].[Customer] custON sales.[Customer] = cust.[Customer]FOR JSON AUTOFigura 6 La ejecución del script anterior resulta en una salida que es mostrada en la Figura 7. Usted notará que otro arreglo hijo (con sus propios objetos) etiquetado cust aparece en la salida. El arreglo hijo representa la información recuperada de la dimensión de cliente.
Figura 7 De todas maneras, cuando cambiamos el orden de la columna de la sentencia SELECT de tal manera que comience con una columna de la dimensión de cliente como se muestra en la Figura 8, obtenemos una salida diferente que la que tenemos en la Figura 7, en la cual el arreglo hijo está ahora basado en la dimensión FruitSales.
1234567SELECTcust.[Name],sales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number] ,cust.[DOB],cust.[Gender]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesLEFT JOIN [selectSIFISOBlogs].[DIM].[Customer] custON sales.[Customer] = cust.[Customer]FOR JSON AUTOFigura 8
Figura 9 Por tanto, aunque usar el modo AUTO es conveniente, a menudo retorna una salida inconsistente, cuyo orden es sujeto a cambios basados en el orden de la columna en la sentencia SELECT. Para asegurarse que los resultados de su JSON son consistentes, usted tendrá que hacer uso del modo PATH.
- Convertir Resultados Usando el Modo Path
El modo PATH puede ser usado en dos maneras:
Sin una sintaxis de punto
Con una sintaxis de punto
Cuando usted está usando sin una sintaxis de punto, trabaja de manera similar al modo AUTO en que generará una salida JSON basada en el orden de las columnas en su sentencia SELECT.
12345678SELECTsales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number],cust.[Name],cust.[DOB],cust.[Gender]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesLEFT JOIN [selectSIFISOBlogs].[DIM].[Customer] custON sales.[Customer] = cust.[Customer]FOR JSON PATHFigura 10 De todos modos, como puede ser visto en la Figura 11, el modo PATH no agrupa automáticamente la información de tablas unidas a arreglos hijo. De hecho, todas las columnas de las dos tablas son mostradas en el mismo nivel raíz.
Figura 11 Para organizar la salida JSON en arreglos hijo, usted tendrá que usar la sintaxis de punto como se muestra en la Figura 12. La etiqueta antes del punto representa el nombre del objeto – en este caso, tenemos dos objetos llamados Sales y Cust.
1234567891011SELECTsales.[Item Nr] AS [Sales.Item Nr],sales.[Transaction Date] AS [Sales.Transaction Date],sales.[Fruit] AS [Sales.Fruit],sales.[Quantity] AS [Sales.Quantity],sales.[Customer] AS [Sales.Customer],sales.[MOP] AS [Sales.MOP],sales.[Account Number] AS [Sales.Account Number],cust.[Name] AS [Cust.Name],cust.[DOB] AS [Cust.DOB],cust.[Gender] AS [Cust.Gender]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesLEFT JOIN [selectSIFISOBlogs].[DIM].[Customer] custON sales.[Customer] = cust.[Customer]FOR JSON PATHFigura 12 The execution results of Figure 12 are shown below.
Figura 13 Usted aún puede organizar su salida JSON PATH a arreglos hijo convirtiendo su script a una unión anidada, como se muestra en la Figura 14.
1234567891011SELECT(SELECT cust.[Name],cust.[DOB],cust.[Gender]FROM [selectSIFISOBlogs].[DIM].[Customer] custWHERE cust.[Customer] = sales.[Customer]FOR JSON PATH) cust,sales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesFOR JSON PATHFigura 14
Figura 15 Opciones de la cláusula FOR JSON
Los modos AUTO y BOTH le permiten especificar opciones adicionales como ROOT, INCLUDE_NULL_VALUES and WITHOUT_ARRAY_WRAPPER.
ROOT
La opción ROOT es usada para asignar una etiqueta al arreglo de nivel superior. La Figura 16 muestra la aplicación de la opción ROOT.
12345678910SELECTsales.[Item Nr] AS [Sales.Item Nr],sales.[Transaction Date] AS [Sales.Transaction Date],sales.[Fruit] AS [Sales.Fruit],sales.[Quantity] AS [Sales.Quantity],sales.[Customer] AS [Sales.Customer],sales.[MOP] AS [Sales.MOP],sales.[Account Number] AS [Sales.Account Number],cust.[Name] AS [Cust.Name],cust.[DOB] AS [Cust.DOB],cust.[Gender] AS [Cust.Gender]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesLEFT JOIN [selectSIFISOBlogs].[DIM].[Customer] custON sales.[Customer] = cust.[Customer]FOR JSON PATH, ROOT ('TOP_LEVEL')Figura 16 Como puede verse en la Figura 17, nuestro arreglo principal es ahora titulado TOP_LEVEL.
Figure 17 INCLUDE_NULL_VALUES
El comportamiento por defecto cuando se usa la cláusula FOR JSON es que los valores NULL no serán incluidos en su salida JSON. Esto puede ser sobrescrito al especificar la opción INCLUDE_NULL_VALUES. Para ilustrar este punto, he añadido una transacción falsa de frutas en mi sentencia SELECT, como se muestra en la Figura 18. Usted notará que los últimos cuatro valores de mi transacción falsa son NULL.
1234567SELECTsales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesUNIONSELECT 12,20990101,'Dummy Fruit',NULL,NULL,NULL,NULLFOR JSON AUTOFigura 18 La salida de esta sentencia es mostrada en la Figura 19, en la cual los últimos 4 valores que eran NULL no son incluidos en la salida JSON.
Figura 19 Para sobrescribir este comportamiento, usted sólo necesita incluir la opción INCLUDE_NULL_VALUES como se muestra en la Figura 20.
1234567SELECTsales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesUNIONSELECT 12,20990101,'Dummy Fruit',NULL,NULL,NULL,NULLFOR JSON AUTO, INCLUDE_NULL_VALUESFigura 20 Como puede verse ahora en la Figura 21, después de incluir la opción INCLUDE_NULL_VALUES, nuestra salida JSON incluye valores NULL.
Figura 21 WITHOUT_ARRAY_WRAPPER
Por defecto, cada cláusula FOR JSON retorna datos JSON entre corchetes – también conocidos como arreglo. Hay instancias donde usted desea que los corchetes sean excluidos de la salida porque puede que desee concatenar dos o más datos JSON. La Figura 22 muestra la aplicación de la opción WITHOUT_ARRAY_WRAPPER.
12345SELECTsales.[Item Nr],sales.[Transaction Date],sales.[Fruit],sales.[Quantity],sales.[Customer],sales.[MOP],sales.[Account Number]FROM [selectSIFISOBlogs].[DIM].[FruitSales] salesFOR JSON AUTO, WITHOUT_ARRAY_WRAPPERFigura 22 Los resultados del script anterior son mostrados abajo.
Figura 23
Finalmente, usted habría notado en la Figura 22 que usé una cláusula TOP para limitar mi selección a una sola fila. Esto es porque cuando usted usa la opción WITHOUT_ARRAY_WRAPPER contra un dataset que tiene más de una fila, usted se encontrará con un error de validación JSON (mostrado abajo) causado por los corchetes faltantes. Por tanto, sea cuidadoso en la manera en que usa la opción WITHOUT_ARRAY_WRAPPER, ya que puede llevar a errores de validación no intencionales.
Figura 24 Descargas
Base de datos de muestra selectSIFISOBlogs
Reference
Últimas entradas de Sifiso Ndlovu (ver todo)- Entendiendo el Impacto de las sugerencias NOLOCK y WITH NOLOCK en SQL Server - May 28, 2018
- Cómo reemplazar caracteres especiales ASCII en SQL Server - May 25, 2018
- Convertir resultados de SQL Server a JSON - June 2, 2017