I am trying to get some data into my postgres DB from a CSV file containing a json dump. As long as it is just strings it is alright, but I want my strings containing timestamps to be stored as timestamps in postgres. Soo I need to do some conversion of the two fields:registerdate and dateofbirth. The below code works except for the date conversion lines...
Any clue on how to successfully convert the two strings to timestamps below:
CREATE TABLE users (
id SERIAL,
mongo_id TEXT,
password VARCHAR(128),
firstname VARCHAR(200),
lastname VARCHAR(200),
dateofbirth TIMESTAMP,
registerdate TIMESTAMP,
displayname VARCHAR(200),
language VARCHAR(200),
country VARCHAR(200),
profilepicture VARCHAR(200),
backgroundpicture VARCHAR(200),
type VARCHAR(200),
sex VARCHAR(6),
offlinemode BOOLEAN,
email VARCHAR(200),
friends VARCHAR(255)[]
);
INSERT INTO users (mongo_id, password,firstname,lastname, dateofbirth, registerdate, displayname, language)
SELECT data->>'_id',
data->>'password',
data->>'firstName',
data->>'secondName',
to_timestamp(data->'dateOfBirth'->>'$date'), /*<------*/
to_timestamp(data->'registerDate'->>'$date'), /*<-------*/
data->>'displayName',
data->>'language'
FROM import.mongo_users;
The data format in mongo_users:
{ "_id" : "1164", "password" : "aaa123123", "firstName" : "Adam", "secondName" : "Kowlalski", "dateOfBirth" : { "$date" : "2014-05-18T07:41:09.202+0200" }, "registerDate" : { "$date" : "2016-06-01T12:59:53.941+0200" }, "displayName" : "Adam Kowlalski", "language" : "nb", "country" : null, "profilePicture" : null, "backgroundPicture" : null, "type" : "USER", "sex" : "MALE", "offlineMode" : true, "email" : "bk_1164#test.email", "friends" : [ "KUE" ] }
The to_timestamp function requries two parameters: date_time in text format, and the formatting template.
You don't need to use to_timestamp since your date-time values are already formatted with a valid timestamp, and PostgreSQL understands json-formatted timestamps well enough. The following works well:
SELECT data->>'_id',
data->>'password',
data->>'firstName',
data->>'secondName',
(data->'dateOfBirth'->>'$date')::timestamp, --<< simply cast to timestamp
(data->'registerDate'->>'$date')::timestamp, --<< simply cast to timestamp
data->>'displayName',
data->>'language'
FROM (SELECT
'{ "_id" : "1164", "password" : "aaa123123", "firstName" : "Adam", "secondName" : "Kowlalski", "dateOfBirth" : { "$date" : "2014-05-18T07:41:09.202+0200" },
"registerDate" : { "$date" : "2016-06-01T12:59:53.941+0200" }, "displayName" : "Adam Kowlalski", "language" : "nb", "country" : null, "profilePicture" : null,
"backgroundPicture" : null, "type" : "USER", "sex" : "MALE", "offlineMode" : true, "email" : "bk_1164#test.email", "friends" : [ "KUE" ] }'::jsonb as data) d
Your JSON Date format looks like ISO 8601 (https://en.wikipedia.org/wiki/ISO_8601). For transforming the input string to a date variable you should use the to_date function.
e.g.
to_date(data->'dateOfBirth'->>'$date','YYYY-MM-DD"T"HH24:MI:SS')
Be ware that you have to check if Timezone differences play a role. Postgresql has an option OF: https://www.postgresql.org/docs/current/static/functions-formatting.html
For me this is what worked.
SELECT to_timestamp(nullif(LEFT(dates_json->>'date_prop',10), '')::numeric) as date_extracted FROM table_name
First shrink the value to 10 symbols (if the timestamp include miliseconds), then check if it is null, convert to numeric, then pass it to function to_timestamp().
This way I fixed another error "date/time field value out of range".
Related
I have the json block modeled below. I want to selectively delete individual blocks from my_items based on the id which is AAA and BBB in my sample. ie if I tried to delete the AAA block under my_items I would want tojust delete the {"id" : "AAA"} but if wanted to delete the BBB block it would delete the larger {"name" : "TestRZ", "id" : "BBB", "description" : ""} block.
I know I can use the #- to remove whole blocks like SELECT '{sample_json}'::jsonb #- '{my_items}' would purge out the whole my_items block. But I dont know how to use this to conditionally delete children under a parent block of json. I have also used code similar to this example to append data inside a nested structure by reading in the node of the nested structure cat-ing new data to it and rewriting it. UPDATE data SET value= jsonb_set(value, '{my_items}', value->'items' || (:'json_to_adds'), true) where id='testnofeed'.
But I dont know how to apply either of these methods to: 1)Delete data in nested structure using #- or 2)Do the same using `jsonb_set. Anyone have any guidance for how to do this using either of these(or another method).
{
"urlName" : "testurl",
"countryside" : "",
"description" : "",
"my_items" : [
{
"id" : "AAA"
},
{
"name" : "TestRZ",
"id" : "BBB",
"description" : ""
},
],
"name" : "TheName"
}
Data is stored in value jsonb. when I update I will be able to pass in a unique kind so that it only updates this json in one row in db.
-- Table Definition
CREATE TABLE "public"."data" (
"id" varchar(100) NOT NULL,
"kind" varchar(100) NOT NULL,
"revision" int4 NOT NULL,
"value" jsonb
);
This works in PostgreSQL 12 and later with jsonpath support. If you do not have jsonpath, then please leave a comment.
with data as (
select '{
"urlName" : "testurl",
"countryside" : "",
"description" : "",
"my_items" : [
{
"id" : "AAA"
},
{
"name" : "TestRZ",
"id" : "BBB",
"description" : ""
}
],
"name" : "TheName"
}'::jsonb as stuff
)
select jsonb_set(stuff, '{my_items}',
jsonb_path_query_array(stuff->'my_items', '$ ? (#."id" <> "AAA")'))
from data;
jsonb_set
---------------------------------------------------------------------------------------------------------------------------------------------------
{"name": "TheName", "urlName": "testurl", "my_items": [{"id": "BBB", "name": "TestRZ", "description": ""}], "countryside": "", "description": ""}
(1 row)
To update the table directly, the statement would be:
update data
set value = jsonb_set(value, '{my_items}',
jsonb_path_query_array(value->'my_items',
'$ ? (#."id" <> "AAA")'));
This works for versions before PostgreSQL 12:
with data as (
select 1 as id, '{
"urlName" : "testurl",
"countryside" : "",
"description" : "",
"my_items" : [
{
"id" : "AAA"
},
{
"name" : "TestRZ",
"id" : "BBB",
"description" : ""
}
],
"name" : "TheName"
}'::jsonb as stuff
), expand as (
select d.id, d.stuff, e.item, e.rn
from data d
cross join lateral jsonb_array_elements(stuff->'my_items') with ordinality as e(item, rn)
)
select id, jsonb_set(stuff, '{my_items}', jsonb_agg(item order by rn)) as new_stuff
from expand
where item->>'id' != 'AAA'
group by id, stuff;
id | new_stuff
----+---------------------------------------------------------------------------------------------------------------------------------------------------
1 | {"name": "TheName", "urlName": "testurl", "my_items": [{"id": "BBB", "name": "TestRZ", "description": ""}], "countryside": "", "description": ""}
(1 row)
The direct update for this is a little more involved:
with expand as (
select d.id, d.value, e.item, e.rn
from data d
cross join lateral jsonb_array_elements(value->'my_items')
with ordinality as e(item, rn)
), agg as (
select id, jsonb_set(value, '{my_items}', jsonb_agg(item order by rn)) as new_value
from expand
where item->>'id' != 'AAA'
group by id, value
)
update data
set value = agg.new_value
from agg
where agg.id = data.id;
I have below JSON. I was trying to construct a query to fetch the FirstName, LastName, Iphone Number and Home Number. I am trying to use JSON Path filter expression. Its not working for me.
{
"firstName": "John",
"lastName" : "doe",
"age" : 26,
"address" : {
"streetAddress": "naist street",
"city" : "Nara",
"postalCode" : "630-0192"
},
"phoneNumbers": [
{
"type" : "iPhone",
"number": "0123-4567-8888"
},
{
"type" : "home",
"number": "0123-4567-8910"
}
]
}
Query used
DECLARE #jsonInfo NVARCHAR(MAX)
SET #jsonInfo=N'{
"firstName": "John",
"lastName" : "doe",
"age" : 26,
"address" : {
"streetAddress": "naist street",
"city" : "Nara",
"postalCode" : "630-0192"
},
"phoneNumbers": [
{
"type" : "iPhone",
"number": "0123-4567-8888"
},
{
"type" : "home",
"number": "0123-4567-8910"
}
]
}'
SELECT
JSON_VALUE(#jsonInfo,'$.firstName') AS FirstName,
JSON_VALUE(#jsonInfo,'$.lastName') AS LastName
--JSON_VALUE(#jsonInfo,'$.phoneNumbers[?(#.type=="iPhone")].number') AS IPhoneNumber,
--JSON_VALUE(#jsonInfo,'$.phoneNumbers[?(#.type=="home")].number') AS HomeNumber
Regards
Amirtharaj
One way to parse this JSON is with OPENJSON() and explicit schema. phoneNumbers is a JSON array, so you need an additional OPENSJON() call.
SELECT
j1.firtstName, j1.lastName, j1.streetAddress, j1.city, j1.postalCode,
j2.*
FROM OPENJSON(#jsonInfo) WITH (
firtstName varchar(100) '$.firstName',
lastName varchar(100) '$.lastName',
streetAddress varchar(100) '$.address.streetAddress',
city varchar(100) '$.address.city',
postalCode varchar(100) '$.address.postalCode',
phoneNumbers nvarchar(max) '$.phoneNumbers' AS JSON
) j1
CROSS APPLY (
SELECT
MAX(CASE WHEN [type] = 'iPhone' THEN [number] END) AS iPhone,
MAX(CASE WHEN [type] = 'home' THEN [number] END) AS home
FROM OPENJSON(j1.phoneNumbers) WITH (
type varchar(10) '$.type',
number varchar(20) '$.number'
)
) j2
Result:
firtstName lastName streetAddress city postalCode iPhone ome
John doe naist street Nara 630-0192 0123-4567-8888 0123-4567-8910
Of course, you can extract each phone number from $.phoneNumbers JSON array using $.phoneNumbers[x].number as path expression (x is zero-based index):
SELECT
j1.firtstName, j1.lastName, j1.streetAddress, j1.city, j1.postalCode,
j1.number1, j1.number2
FROM OPENJSON(#jsonInfo) WITH (
firtstName varchar(100) '$.firstName',
lastName varchar(100) '$.lastName',
streetAddress varchar(100) '$.address.streetAddress',
city varchar(100) '$.address.city',
postalCode varchar(100) '$.address.postalCode',
number1 varchar(100) '$.phoneNumbers[0].number',
number2 varchar(100) '$.phoneNumbers[1].number'
) j1
I want to Insert "middle_name" : "k" in between first_name and last_name
{
"first_name" : "Sammy",
"last_name" : "Shark"
}
I expect the key to be inserted like this :
{
"first_name" : "Sammy",
"Middle_name":"k",
"last_name" : "Shark"
}
but, when using json_insert() or Json_set() result is
{
"first_name" : "Sammy",
"last_name" : "Shark",
"Middle_name":"k"
}
I have a MySQL database and one of the tables is called 'my_table'. In this table, one of the columns is called 'my_json_column' and this column is stored as a JSON object in MySQL. The JSON object has about 17 key:value pairs (see below). I simply want to return a "slimmed-down" JSON Object from a MySQL query that returns 4 of the 17 fields.
I have tried many different MySQL queries, see below, but I can't seem to get a returned subset JSON Object. I am sure it is simple, but I have been unsuccessful.
Something like this:
SELECT
json_extract(my_json_column, '$.X'),
json_extract(my_json_column, '$.Y'),
json_extract(my_json_column, '$.KB'),
json_extract(my_json_column, '$.Name')
FROM my_table;
yields:
5990.510000 90313.550000 5990.510000 "Operator 1"
I want to get this result instead (a returned JSON Object) with key value pairs:
[ { X: 5990.510000, Y: 90313.550, KB: 2105, Name: "Well 1" } ]
Sample data:
{
"Comment" : "No Comment",
"Country" : "USA",
"County" : "County 1",
"Field" : "Field 1",
"GroundElevation" : "5400",
"Identifier" : "11435358700000",
"Interpreter" : "Interpreter 1",
"KB" : 2105,
"Name" : "Well 1",
"Operator" : "Operator 1",
"Owner" : "me",
"SpudDate" : "NA",
"State" : "MI",
"Status" : "ACTIVE",
"TotalDepth" : 5678,
"X" : 5990.510000,
"Y" : 90313.550
}
Thank you in advance.
Use JSON_OBJECT(), available since MySQL 5.6:
Evaluates a (possibly empty) list of key-value pairs and returns a JSON object containing those pairs
SELECT
JSON_OBJECT(
'X', json_extract(my_json_column, '$.X'),
'Y', json_extract(my_json_column, '$.Y'),
'KB', json_extract(my_json_column, '$.KB'),
'Name', json_extract(my_json_column, '$.Name')
) my_new_json
FROM my_table;
This demo on DB Fiddle with your sample data returns:
| my_new_json |
| ----------------------------------------------------------- |
| {"X": 5990.51, "Y": 90313.55, "KB": 2105, "Name": "Well 1"} |
I am having a table which is storing the JSON values. Within these JSONs, the JSON is having null attributes like below :
{
"name" : "AAAA",
"department" : "BBBB",
"countryCode" : null,
"languageCode" : null,
"region" : "AP"
}
I would like to write a query so that all the null attributes are removed from the output. For e.g. for the above-mentioned JSON, the resultant output JSON should be like this.
{
"name" : "AAAA",
"department" : "BBBB",
"region" : "AP"
}
I would like to have a generic query which I can apply to any JSON to get rid of null attributes in MySQL (v5.7).
In case you don't know all the keys in advance:
WITH j AS (SELECT CAST('{"a": 1, "b": "null", "c": null}' AS JSON) o)
SELECT j.o, (SELECT JSON_OBJECTAGG(k, JSON_EXTRACT(j.o, CONCAT('$."', jt.k, '"')))
FROM JSON_TABLE(JSON_KEYS(o), '$[*]' COLUMNS (k VARCHAR(200) PATH '$')) jt
WHERE JSON_EXTRACT(j.o, CONCAT('$."', jt.k, '"')) != CAST('null' AS JSON)) removed
FROM j;
Outputs:
o
removed
{"a": 1, "b": "null", "c": null}
{"a": 1, "b": "null"}
And this will keep your keys with string value "null", which is different from json null.
The following query will work for removing a single key value pair, where the value is NULL:
SELECT JSON_REMOVE(col, '$.countryCode')
FROM yourTable
WHERE CAST(col->"$.countryCode" AS CHAR(50)) = 'null';
But, I don't see a clean way of doing multiple removals in a single update. We could try to chain the updates together, but that would be ugly and non readable.
Also, to check for your JSON null, I had to cast the value to text first.
Demo
How you can remove null keys using JSON_REMOVE function. $.dummy is used if the condition is false.
select json_remove(abc,
case when json_unquote(abc->'$.name') = 'null' then '$.name' else '$.dummy' end,
case when json_unquote(abc->'$.department') = 'null' then '$.department' else '$.dummy' end,
case when json_unquote(abc->'$.countryCode') = 'null' then '$.countryCode' else '$.dummy' end,
case when json_unquote(abc->'$.languageCode') = 'null' then '$.languageCode' else '$.dummy' end,
case when json_unquote(abc->'$.region') = 'null' then '$.region' else '$.dummy' end)
from (
select cast('{
"name" : "AAAA",
"department" : "BBBB",
"countryCode" : null,
"languageCode" : null,
"region" : "AP"
}' as json) as abc ) a
Output:
{"name": "AAAA", "region": "AP", "department": "BBBB"}