How can you select JSON object key values using SQL - sql

I have some records in my database in a column called Swatch, that are JSON to describe a colour swatch. Each record looks something like this:
{"colors":{"color-1":"transparent","color-2":"transparent"}}
Basically it's one big colour object with lots of key value pairs. I would like to manipulate this JSON, using SQL, to achieve something like this instead to allow customisable colour names.
{"colors":[{"name": "Red", "color":"#00000"},{"name": "Green", "color":"#00000"}]
(Hex codes are just for demo purposes, I know they're not legit).
How can I achieve this? It is possible using SQL Server?

It may be possible, but a lot depends on the data and what you are trying to achieve.
There is a set of JSON functions available, most of them added in SQL 2016, and a pair more should be available in SQL Server 2022.
If you want to erase the current structure and data in favor of a new one that's easy.
If you want to convert your current structure to a different one, that might be pretty hard, but it's highly data related.
Let's consider your samples as the actual data you want to convert
{"colors":{"color-1":"transparent","color-2":"transparent"}}
{"colors":[{"name": "Red", "color":"#00000"},{"name": "Green", "color":"#00000"}]}
Here are the issues and complications I see
how can you say what color-1 is? what name does it have? that's human operation unless you have some ColorCode-ColorName table
Do you have a fixed/known amount of color-N keys? to get the property value cleanly you will need to reference them explicitly. (but I'm sure you can find other ways... without using JSON functions tho)
to change the structure you need to extract the data and build the new JSON string, basically a lot of string concatenation that will require quite some time to build and test
JSON function samples
I've played around with the JSON data and created some sample queries, I hope trying them will help you understand what it means to manipulate JSON data, and choose if SQL Server is a fitting tool for your task
-- key-values property
DECLARE #jdata2 as varchar(200) = '{"colors":{"color-1":"transparent","color-2":"transparent"}}'
SELECT
JSON_VALUE(#jdata2,'$."colors"."color-1"') AS Color1
,JSON_VALUE(#jdata2,'$."colors"."color-2"') AS Color2
,JSON_VALUE(#jdata2,'$."colors"."color-3"') AS Color3
GO
-- objects and arrays
DECLARE #jdata as varchar(200) = '{"company":"Contoso","colors":[{"name": "Red", "color":"#00000"},{"name": "Green", "color":"#00000"}]}'
SELECT JSON_QUERY(#jdata,'$.colors[0]')
SELECT JSON_VALUE(#jdata,'$.colors[0].name')
SELECT JSON_VALUE(#jdata,'$.company')
--Update property
SELECT JSON_MODIFY(#jdata,'$.company', 'Northwind')
--Add property
SELECT JSON_MODIFY(#jdata,'$.country', 'Italy')
--Add new Object
SELECT JSON_MODIFY(#jdata,'$.salesman', JSON_QUERY('{"name":"Mario","surname":"Rossi"}'))
--Append new Object in an Object array
SELECT JSON_MODIFY(#jdata,'append $.colors', JSON_QUERY('{"name":"Yellow", "color":"#00000"}','$'))
------ About DELETING
--Delete (whole) Property works fine
SELECT JSON_MODIFY(#jdata,'$.colors', NULL)
-- deleting sometihng inside an array is not fine at all
-- Should delete 1 value/object from the array... but no, 'null,' is left instead
SELECT JSON_MODIFY(#jdata,'$.colors[1]', NULL)
-- To "delete" properly pass the whole array or object array omitting the deleted value...
SELECT JSON_MODIFY(#jdata,'$.colors', JSON_QUERY('[{"name": "Green", "color":"#00000"}]'))

Related

SSRS - Number format expression not working

I have a table in my database called systemconfig which has some configs that I'll use on my reports. The idea is, instead of adjusting the 'number formats' directly in the textboxes properties of the report, I just change a value in this table, and then through a custom expression in the format property, it gets the value from this table
The query of the dataset 'ds_DecimalValues' is like this:
DECLARE #DecimalValue Nvarchar(500)
SELECT #DecimalValue =
( SELECT Value as 'DecimalValue' FROM SystemConfig WHERE Key = Decimal_Value )
SELECT
DecimalValue = #DecimalValue
ok, the result of this query is ##
In the textbox properties I have this expression in the Format line:
=First(Fields!DecimalValue.Value, "ds_DecimalValue")
But the report is showing 2 decimal values instead of none. I'm not sure if the decimal values are correct on the systemconfig table, I assume that '##' is correct to show no decimal values but I'm not sure about it. Any ideas guys??
Regards.
Would something like this work for you? Should round it to the nearest integer
=Floor(First(Fields!DecimalValue.Value, "ds_DecimalValue"))
When I have done this in the past I would typically use someting like f0 or n0 as the format code.
Try using this instead of ##.
If this does not work then a couple of things to debug.
Add a textbox that contains the same expression as you are using in your format property expression, make sure it is returning what you expect
Type the format code directly in and make sure that it formats as you expected.
remember that you don't need to use quotes when using codes like f0 etc.

Execute SQL Task -Full Result Set Datatype Mismatch Error

I am creating an SSIS package which has an execute SQL task and it passes result set variable to a for each loop container.
My Sql Query is:
Select distinct code from house where active=1 and campus='W'
I want the execute sql task to run this query and assign its results to a variable which is passed to a for each loop container which should loop through all the values in the result set.
But my execute sql task fails with error:
The type of the value (DBNull) being assigned to variable
"User::house" differs from the current variable type (String)
Now i have done my research and i have tried assigning the variable datatype Object but did not work. I tried using cast in my sql query and that also did not work.
Since my query returns multiple rows and one column, i am not sure how i can assign a datatype to the whole query?
Sample:
Code
AR
BN
CN
It sounds like you have a variety of issues in here.
Result Set
The first is in your Execute SQL Task and the need for agreement between the Result Set specification and the data type of the Variable(s) specified in the Result Set tab. If you specify Full Resultset, then the receiving object must be of type System::Object and you will only have 1 result set. The type of Connection Manager (ODBC/OLE/ADO) used will determine how you specify it but it's infinitely searchable on these fine forums.
The other two options are Single Row and XML. In 13 years of working with SSIS, I've never had cause to specify XML. That leaves us with Single Row. For a Single Row Result Set, you need to provide a variable for each column returned and it needs to be correctly typed.
To correct your issue, you need to declare a second variable. I usually call my rsObject (record set object) and then specify the data type as System.Object.
For Each Loop Container
Your For Each Loop Container will then be set with an Enumerator of "Foreach ADO Enumerator" and then the ADO object source variable will become "User::rsObject"
In the Variable Mappings, you'll specify your variable User::house to index 0.
Testing
Given a sample set of source source data, you can verify that you have your Execute SQL Task correctly assigning a result set to our object and the Foreach Loop Container is properly populating our variable.
SELECT DISTINCT
code
FROM
(
VALUES
('ABC', 1, 'w')
, ('BCD', 1, 'w')
, ('CDE', 0, 'w')
, ('DEF', 1, 'w')
, ('EFG', 1, 'x')
) house(code, active, campus)
WHERE
active = 1
AND campus = 'w';
If you change the value of campus from w to something that doesn't exist, like f then things will continue to work.
However, the error you're receiving can only be generated if the code is a NULL
Add one more entry to the VALUES collection like
, (NULL, 1, 'w')
and when the For Each Loop Container hits that value, you will encounter the error you indicate
The type of the value (DBNull) being assigned to variable "User::house" differs from the current variable type (String)
Now what?
SSIS variables cannot change their data type, unless they're of type Object (but that's not the solution here). The "problem" is that you cannot store a NULL value in an SSIS variable (unless it's of type object). Therefore you need to either exclude the rows that return a NULL (AND code IS NOT NULL) or you need to cast the NULL into sentinel/placeholder value as a substitute (SELECT DISTINCT ISNULL(code, '') AS code). If an empty string is a valid value, then you need to find something that isn't - "billinkcisthegreatestever10123432" is unlikely to exist in your set of codes but that might be a bit excessive.
Finally, think about renaming your SSIS variable from house to code. You might be able to keep things straight but some day you'll hand this code over to someone else for maintenance and you don't want to confuse them.
A picturesque answer https://stackoverflow.com/a/13976990/181965
the variable "User::house" is string , so , did you use it in result set?
you need declare son "object" var for result set
result set
then declare a string variable for every single Code from your result
For Each Loop Container
good luck

Compare parameter in WHERE clause stored procedure

I have a stored procedures which receives a list of ItemId's. If the list only contain one item this works:
AND (#OrgItemIds = -1 OR ...)
AND (#OrgItemIds = -1 AND...)
but if the list contains more than one item it crashes.
Anybody knows how to fix this? Can I check the size of the list somehow?
Or can I check just the first element of the list like #OrgItemIds[0] or something like that?
There is no "comma-separated set of values" or "array" datatypes in SQL SERVER. You handle the parameter as a scalar variable in your code. Thus when you provide a single value in that list, server implicitly converts it to int and your sp succeeds.When you provide string with commas - it becomes impossible to convert it to int.You have to manually parse your argument, put it into a table variable and then use this table in WHERE clause. Or change handling of this argument to support a string of values instead of scalar value:
... where #OrgItemIds like '%,' + cast(t.OrgItemIds as varchar(10)) + ',%'
which is much worse for performance than filtering by id or list of ids.

Checking existence of value inside JSON array

I've found the solution for more complex than this but I'm not able to make this work...
I've a column called data of type json. The JSON structure is as follows:
{"actions": ["action1","action2","action3", ..., "actionN"]}
So, let's say I've 3 rows with the following data:
{"actions": ["work","run"]}
{"actions": ["run","eat","sleep", 'walk']}
{"actions": ["eat","run","work"]}
I want to retrieve the rows where work is included in actions array.
I've tried something similar to what is posted here:
Query for element of array in JSON column, but since each element inside the array is just a json string, I got stuck there.
Then, I tried something like:
SELECT * from table t WHERE 'work' in ...
but this also failed to get the values as an string array to put it there.
Using PostgreSql 9.3.
Since you are using the json type, which is just a string internally, the simplest solution is:
SELECT * FROM table WHERE strpos(data::text, 'work') > 0;
It defeats the purpose of using json in the first place, but it is (very likely) faster than the json parser in this simple case.
A json solution would be:
SELECT * FROM (
SELECT *, unnest(data->"actions")::text AS action FROM table) AS foo
WHERE action = 'work';

Return binary as string, NOT convert or cast

Is there a simple way (like convert or cast) to return a binary field as a string without actually converting it or casting it?
The data I want looks like this in the database:
0x24FC40460F58D64394A06684E9749F30
When using convert(varchar(max), binary_field), it comes back like this:
$ü#FXÖC” f„étŸ0
cast(binary_field as varchar(max)) had the same results.
I'm trying to modify a datasource in an old vb.net application, so I want it to be a string like the existing values, but still look like the original data. I don't have to do it in the query, but I'd like to if possible to avoid modifying the old vb code too much.
Here is my select statement:
SELECT strName, strDesc, binUUID
FROM MyTableName
where intFamily = '1234'
order by strName
Alternatively, if I do cast/convert it, would it be safe to compare "$ü#FXÖC” f„étŸ0" stored as a string at a later time to another value gathered in the same way?
There is a built in function to generate hex strings from binary values
SELECT sys.fn_varbintohexstr (0x24FC40460F58D64394A06684E9749F30)