I am currently assigned to a task to transform our active non-normalized table to a normalized one. We decided to use database triggers to facilitate the bulk migration and succeeding data changes until we discontinue the old table.
Below are the structure and sample of our old table:
SELECT * FROM TabHmIds;
ID EntitlementID TabId HmId
1 101 201 301
2 102 202 302
The required structure and sample of our new table should look like:
SELECT * FROM tab_integration;
id tab_id integration_id metadata
1 201 1 { "paid_id": {"entitlement_id": 101, "id": 301} }
2 202 1 { "paid_id": {"entitlement_id": 202, "id": 302} }
The following is what I have done in my INSERT trigger so far:
CREATE TRIGGER tab_integration_after_insert AFTER INSERT ON `TabHmIds`
FOR EACH ROW
BEGIN
DECLARE var_metadata JSON;
DECLARE var_new_metadata JSON;
DECLARE var_hm_metadata JSON;
DECLARE var_integration_id INT(11);
SELECT
metadata,
integration_id INTO var_metadata,
var_integration_id
FROM
`go`.`tab_integration` gti
WHERE
gti.`tab_id` = NEW.`TabId`;
SET var_hm_metadata = JSON_OBJECT('entitlement_id', NEW.`EntitlementId`, 'id', NEW.`HmId`);
IF var_integration_id = 1 THEN
IF var_metadata IS NULL THEN
SET var_new_metadata = JSON_OBJECT('paid_id', var_hm_metadata);
ELSE
SET #paid_id = JSON_UNQUOTE(JSON_EXTRACT(var_metadata, '$.paid_id'));
SET var_new_metadata = JSON_ARRAY_APPEND(var_metadata, '$.paid_id', var_hm_metadata);
END IF;
END IF;
UPDATE `tab_integration` gti SET `metadata` = var_new_metadata WHERE `tab_id` = NEW.`TabId`;
END
However, what I get is this:
SELECT * FROM tab_integration;
id tab_id integration_id metadata
1 201 1 { "paid_id": "{\"entitlement_id\": 101, \"id\": 301}" }
2 202 1 { "paid_id": "{\"entitlement_id\": 202, \"id\": 302}" }
From the table above, the JSON object is parsed into STRING. I am aware that the JSON_OBJECT parses the passed value to a string. SO I used the JSON_UNQUOTE(JSON_EXTRACT(…)) to convert the paid_id path value to JSON, but it does not get parsed into JSON. I also tried JSON_MERGE_PRESERVE to put the JSON object under the paid_id path but I end up getting:
{“paid_id”: [], “entitlement_id”: 101, “id”: 301 }
I also tried to put the JSON array into a temporary table using JSON_TABLE and modify the values in the temporary and convert that temporary table to JSON using JSONARRAYAGG. But Workbench keeps saying I have an error in my syntax even though I directly copied examples from the web.
I also tried CASTing a well-formed string to JSON, but Workbench also throws a syntax error.
I have spent a week into resolving this data structure in MySQL.
Database scripting is not my strong suit and I am new to the JSON functions in MySQL. Thank you in advance to those who will reply.
If in case needed, my MySQL Workbench is version 10.4.13-MariaDB. But the script should work in MySQL 5.7.
I found the answer to my problem.
Before inserting the new JSON data, I parsed it to CHAR and REPLACEd the characters making it a string. I did the following and it worked!
# After performing the needed JSON manipulations, cleanse the JSON string to JSON.
SET var_new_metadata = CAST(var_new_metadata AS CHAR);
SELECT REPLACE(REPLACE(REPLACE(var_new_metadata, '\\', ''), '\"\{', '{'), '\}\"', '}') INTO var_new_metadata;
After cleansing the data, the UPDATE call still worked and tried to do some JSON manipulations after and, yes, still works!
Related
I have used a table for storing JSON data. For a specific client I create records for 12 months and store the data of the 12 months in JSONArray format. In that way I create one row per client.
The approved field can be true, false or null. What I need to do is, check whether the approved value is true and that the data is not null. I tried to search for queries JSON data but couldn't find solution.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<ClientData> clientQuery = cb.createQuery(ClientData.class);
Root<ClientData> clientMaster = clientQuery.from(ClientData.class);
..
..
..
predicates.add(cb.isTrue(cb.function("json_contains", Boolean.class,
clientMaster.get("Data"), cb.literal("approved"))));
predicates.add(cb.isNotNull(cb.function("json_contains", Double.class,
clientMaster.get("Data"), cb.literal("data"))));
But this gave me error:-
com.mysql.jdbc.MysqlDataTruncation: Data truncation: Invalid JSON text in argument 2 to function json_contains: "Invalid value." at position 1.
I'm using MySQL version 5.7.31. Any help would be appreciated.
try this way
if(!CommonUtils.checkIsNullOrEmpty(filterDto.getProduct()) ) {
String a=(String.join(",",filterDto.getProduct()));
predicates.add(cb.isTrue(cb.function("json_contains", Boolean.class,
userRoot.get("product"), cb.literal("\"" + a + "\""))));
// predicates.add(userRoot.get("product").in(a));
}
I have a table in Bigquery which has 2 columns - job_id and json_column(string which is in JSON format). When I tried to read the data and identify some objects it gives me error as below:
SyntaxError:Unexpected end of JSON input at undefined line XXXX, columns xx-xx
It Always gives me line 5931 and second time I execute again it gives line 6215.
If it's related to JSON structure issue, how can I know which row/job_id that number 5931 corresponds to? If I subset for a specific job_id, it returns the values but when I tried to execute on the complete table, I got this error. I tried looking at the job_id at the row_numbers mentioned and code works fine for those job_ids.
Do you think its JSON structure issue and how to identify which row/job_id has this Issue?
Table Structure:
Code:
CREATE TEMPORARY FUNCTION CUSTOM_JSON_EXTRACT(json STRING, json_path STRING)
RETURNS ARRAY<STRING>
LANGUAGE js AS """
var result = jsonPath(JSON.parse(json), json_path);
if(result){return result;}
else {return [];}
"""
OPTIONS (
library="gs://json_temp/jsonpath-0.8.0.js"
);
SELECT job_id,dist,gm,sub_gm
FROM lz_fdp_op.fdp_json_file,
UNNEST(CUSTOM_JSON_EXTRACT(trim(conv_column), '$.Project.OpsLocationInfo.iDistrictId')) dist ,
UNNEST(CUSTOM_JSON_EXTRACT(trim(conv_column), '$.Project.GeoMarketInfo.Geo')) gm,
UNNEST(CUSTOM_JSON_EXTRACT(trim(conv_column), '$.Project.GeoMarketInfo.SubGeo')) sub_gm
Would this work for you?
WITH
T AS (
SELECT
'1000149.04.14' AS job_id,
'{"Project":{"OpsLocationInfo":{"iDistrictId":"A"},"GeoMarketInfo":{"Geo":"B","SubGeo":"C"}}}' AS conv_column
)
SELECT
JSON_EXTRACT_SCALAR(conv_column, '$.Project.OpsLocationInfo.iDistrictId') AS dist,
JSON_EXTRACT_SCALAR(conv_column, '$.Project.GeoMarketInfo.Geo') AS gm,
JSON_EXTRACT_SCALAR(conv_column, '$.Project.GeoMarketInfo.SubGeo') AS sub_gm
FROM
T
BigQuery JSON Functions docs:
https://cloud.google.com/bigquery/docs/reference/standard-sql/json_functions
how can I read multiple arrays in an object in JSON without using
unnest?
Can you explain better with an input sample your comment?
JSON_MODIFY does not support many base types in the 3rd argument. How fix it?
DECLARE #info NVARCHAR(200)='{"name":"John","skills":["C#","SQL"]}'
SELECT JSON_MODIFY(#info, '$.code', NEWID())
SELECT JSON_MODIFY(#info, '$.date', GETDATE())
SELECT JSON_MODIFY(#info, '$.cost', CAST(1 AS money))
Msg 8116, Level 16, State 1, Line 3
Argument data type uniqueidentifier is invalid for argument 3 of
json_modify function.
Msg 8116, Level 16, State 1, Line 5
Argument data type datetime is invalid for argument 3 of json_modify
function.
Msg 8116, Level 16, State 1, Line 8
Argument data type money is invalid for argument 3 of json_modify
function.
Try use my dbo.JSON_MODIFY function that solved problem
CREATE FUNCTION dbo.JSON_MODIFY(#expression nvarchar(max), #path nvarchar(max), #newValue sql_variant)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #tempJson nvarchar(max) = (SELECT #newValue AS col1 FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
RETURN JSON_MODIFY(#expression, #path, JSON_VALUE(#tempJson, '$.col1'))
END
GO
DECLARE #info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
PRINT dbo.JSON_MODIFY(#info, '$.code', NEWID())
PRINT dbo.JSON_MODIFY(#info, '$.date', GETDATE())
PRINT dbo.JSON_MODIFY(#info, '$.cost', CAST(1 AS money))
GO
Output results:
{"name":"John","skills":["C#","SQL"],"code":"85543240-38C5-4647-B555-4388ADAD71F0"}
{"name":"John","skills":["C#","SQL"],"date":"2019-10-03T16:16:10.600"}
{"name":"John","skills":["C#","SQL"],"cost":"1.0000"}
I can think of a few reasons why updating JSON in SQL is a bad idea starting with the configuration options of those that consume the json. If you use need to be able to round-trip the JSON and you use something like NewtonSoft default settings you would need to cast/ format a date string in a particular way
select format(GetUtcDate(),'yyyy-MM-dd''T''HH:mm:ss.fff')+'Z'
in your sample, you should probably use if you use .net to read the data when it leaves your database... in any way you need to make sure you align your database with those that consume the data and, in my use-case use format to make sure the data does not generate corrupt JSON.
DECLARE #info NVARCHAR(100)='{"name":"John","skills":["C#","SQL"]}'
in your modify this would look like this
SELECT JSON_MODIFY(#info, '$.date', format(GetUtcDate(),'yyyy-MM-dd''T''HH:mm:ss.fff')+'Z')
I am building a testcafe web automation project with a data-driven framework. I am using NPM/node.jS/ with Visual Studio Code. I have a JSON File and a SQL query, I want to point the where clause of the SQL query to the JSON data. This is very new to me, any suggestions are welcome.
I saw that there is an NPM JSON-SQL Node.JS library, but I have not used it, any example will help me.
Json file:
[
{
venid='ABC'
status='Open'
},
{
venid='IGH'
status='Closed'
},
SQL query:
// running the query and query will take data from the json file
Select *
From table
Where VendorId = <jsondata>
and Inventorystatus = <jsonData>
I'm not sure if I understand your question correctly, but if you want to parse a JSON data using SQL Server, you should use JSON capabilities, which are introduced in SQL Server 2016. Most of the scripting languages have enough capabilities to wotk with JSON format, so you should pass JSON data directly to SQL Server.
Next is a basic example, which you may use as a starting point (note, that your JSON is not valid):
Table with data:
CREATE TABLE #Data (
VendorId nvarchar(3),
InventoryStatus nvarchar(10)
)
INSERT INTO #Data
(VendorId, InventoryStatus)
VALUES
(N'ABC', N'Open'),
(N'ABC', N'Closed'),
(N'IGH', N'Open'),
(N'IGH', N'Closed')
T-SQL:
-- Valid JSON
-- [{"venid":"ABC", "status":"Open"}, {"venid":"IGH", "status":"Closed"}]
DECLARE #json nvarchar(max)
-- Read JSON from file. Try to send JSON data directly to SQL Server.
-- Additional permissions are needed to execute next statement.
--SELECT #json = BulkColumn
--FROM OPENROWSET (BULK '<Path>\JsonData.json', SINGLE_CLOB) as j
-- Read JSON from variable
SELECT #json = N'[{"venid":"ABC", "status":"Open"}, {"venid":"IGH", "status":"Closed"}]'
-- Use JSON data in a statement
SELECT d.*
FROM #Data d
JOIN OPENJSON(#json) WITH (
VendorId nvarchar(3) '$.venid',
InventoryStatus nvarchar(10) '$.status'
) j ON (d.VendorId = j.VendorId) AND (d.InventoryStatus = j.InventoryStatus)
Output:
---------------------------
VendorId InventoryStatus
---------------------------
ABC Open
IGH Closed
Very similar to this question MySQL Dynamic Query Statement in Python
However what I am looking to do instead of two lists is to use a dictionary
Let's say i have this dictionary
instance_insert = {
# sql column variable value
'instance_id' : 'instnace.id',
'customer_id' : 'customer.id',
'os' : 'instance.platform',
}
And I want to populate a mysql database with an insert statement using sql column as the sql column name and the variable name as the variable that will hold the value that is to be inserted into the mysql table.
Kind of lost because I don't understand exactly what this statement does, but was pulled from the question that I posted where he was using two lists to do what he wanted.
sql = "INSERT INTO instance_info_test VALUES (%s);" % ', '.join('?' for _ in instance_insert)
cur.execute (sql, instance_insert)
Also I would like it to be dynamic in the sense that I can add/remove columns to the dictionary
Before you post, you might want to try searching for something more specific to your question. For instance, when I Googled "python mysqldb insert dictionary", I found a good answer on the first page, at http://mail.python.org/pipermail/tutor/2010-December/080701.html. Relevant part:
Here's what I came up with when I tried to make a generalized version
of the above:
def add_row(cursor, tablename, rowdict):
# XXX tablename not sanitized
# XXX test for allowed keys is case-sensitive
# filter out keys that are not column names
cursor.execute("describe %s" % tablename)
allowed_keys = set(row[0] for row in cursor.fetchall())
keys = allowed_keys.intersection(rowdict)
if len(rowdict) > len(keys):
unknown_keys = set(rowdict) - allowed_keys
print >> sys.stderr, "skipping keys:", ", ".join(unknown_keys)
columns = ", ".join(keys)
values_template = ", ".join(["%s"] * len(keys))
sql = "insert into %s (%s) values (%s)" % (
tablename, columns, values_template)
values = tuple(rowdict[key] for key in keys)
cursor.execute(sql, values)
filename = ...
tablename = ...
db = MySQLdb.connect(...)
cursor = db.cursor()
with open(filename) as instream:
row = json.load(instream)
add_row(cursor, tablename, row)
Peter
If you know your inputs will always be valid (table name is valid, columns are present in the table), and you're not importing from a JSON file as the example is, you can simplify this function. But it'll accomplish what you want to accomplish. While it may initially seem like DictCursor would be helpful, it looks like DictCursor is useful for returning a dictionary of values, but it can't execute from a dict.