SQL query about to bind multiple elements to a specific one - json

I am going to convert my current PostgreSQL database into a MongoDB version. For example, I have a table to record tweets, and another table to record multiple hashtags used by a specific tweet. What I wanna do is to use SQL to get a table like below and then export it as a .csv file so that I could import it to MongoDB.
Example:
2018-04-02 18:12:32 This plane has no outlet for me to charge my p... [{'tag': 'GucciGarden', 'airline': 'American A...
The problem that I met is that I can get a .csv file contains json array like "[{'tag': 'GucciGarden', 'airline': 'American A...", but it is a String type! And when I import it into MongoDB. The quote will be kept, which makes sth wrong.
And here is my SQL code:
SELECT tweets.tweet_id,tweets.text,
(SELECT array_to_json(array_agg(row_to_json(d)))
from (
SELECT tags.tag
FROM
tags
WHERE tags.tweet_id=tweets.tweet_id
) d
) as Tags
from tweets
Here is the result that I import into MongoDB:
{
"_id" : ObjectId("5ac59c272221ade1185ec241"),
"tweet_id" : 9.80869021435351e+17.0,
"created_at" : "2018-04-02 18:06:13",
"text" : "RT #MiraSorvino: Brad Myles shares #Delta that awareness is working- 9,000 #humantrafficking cases identified by #polarisproject National H��",
"screen_name" : "MMexville",
"favorite_count" : 0.0,
"retweet_count" : 40.0,
"source" : "the public",
"tags" : "[{'tag': 'humantrafficking', 'airline': 'Delta Air Lines'}]"}

this is because [{'tag': is not a valid json - you should have used double quotes and cast to json, eg:
let's say smth like your sample:
t=# create table c (i int, t text, j text);
CREATE TABLE
t=# insert into c values(1,'text',$$[{'tag': 'GucciGarden'}]$$);
INSERT 0 1
t=# select * from c;
i | t | j
---+------+--------------------------
1 | text | [{'tag': 'GucciGarden'}]
(1 row)
so then smth like your qry:
t=# select to_json(c) from (select i,t,replace(j,$$'$$,'"')::json j from c) c;
to_json
-------------------------------------------------
{"i":1,"t":"text","j":[{"tag": "GucciGarden"}]}
(1 row)
of course you will have positive false replacements of single quotes, eg 'tag': 'Gucci's Garden' will break the query logic, so you will have to make a more sophisticated replacement. probably with regular expressions to be neater.

Related

How to search within MySQL JSON object array?

Consider the following JSON object,
[
{
"id": 5964460916832,
"name": "Size",
"value": "Small",
"position": 1,
"product_id": 4588516409440
},
{
"id": 5964460916833,
"name": "Size",
"value": "Medium",
"position": 2,
"product_id": 4588516409440
},
{
"id": 5964460916834,
"name": "Size",
"value": "Large",
"position": 3,
"product_id": 4588516409440
}
]
This is a value present in a table field called custom_attrs of JSON data type in a MySQL 8.0 table. I wanted to search the JSON data to match with multiple fields in the same object.
For example,
I wanted to see if there's a match for name "Size" and value "Medium" within the same object. It should not match the name in the first object and the value in the second object.
While we can always use JSON table, I don't prefer that due to the complexities it brings during the JOINs.
JSON_SEARCH supports LIKE operator, but it cannot ensure if it's from the same object
JSON_CONTAINS supports multiple fields but not LIKE as follows,
SET #doc = CAST('[{"id":5964460916832,"name":"Size","value":"Small","position":1,"product_id":4588516409440},{"id":5964460916833,"name":"Size","value":"Medium","position":2,"product_id":4588516409440},{"id":5964460916834,"name":"Size","value":"Large","position":3,"product_id":4588516409440}]' AS JSON);
SELECT JSON_CONTAINS(#doc, '{"name":"Size", "value":"Small"}')
Is there any way to get the same JSON_CONTAINS like functionality with partial search like, {"name":"Size", "value":"%sma%"}
Any help on this would be greatly helpful.
JSON_CONTAINS() only works with equality, not with pattern matching.
The JSON_TABLE() function is the solution intended to address the task you are trying to do. But you said you don't want to use it.
You can simulate JSON_TABLE() using other functions.
select * from (
select
json_unquote(json_extract(col, concat('$[',n.i,'].id'))) as `id`,
json_unquote(json_extract(col, concat('$[',n.i,'].name'))) as `name`,
json_unquote(json_extract(col, concat('$[',n.i,'].value'))) as `value`
from (select #doc as col) j
cross join (select 0 as i union select 1 union select 2 union select 3 union select 4 union select 5 ...) as n
) as t
where t.`id` is not null
order by id, `name`;
Output:
+---------------+------+--------+
| id | name | value |
+---------------+------+--------+
| 5964460916832 | Size | Small |
| 5964460916833 | Size | Medium |
| 5964460916834 | Size | Large |
+---------------+------+--------+
You could then easily add a condition like AND value LIKE '%sma%'.
As you can see, this query is even more complex than if you had used JSON_TABLE().
Really, any solution is going to be complex when you store your data in JSON format, then try to use SQL expressions and relational operations to query them as if they are normalized data. This is because you're practically implementing a mini-database within the functions of a real database. This is sometimes called the Inner-Platform Effect:
The inner-platform effect is the tendency of software architects to create a system so customizable as to become a replica, and often a poor replica, of the software development platform they are using. This is generally inefficient and such systems are often considered to be examples of an anti-pattern.
If you want simple queries, you should store data in normal rows and columns, not in JSON. Then you could get your result using quite ordinary SQL:
SELECT id, name, value FROM MyTable WHERE name = 'Size' AND value LIKE '%sma%';

How to search JSON data in MySQL by key and value?

I am inserting my data in a database with json_encoded. Now I want to search in "feature", but the result is not exactly true.
MySQL query:
select *
from `modul_69`
where `extmod` like '%"68":%'
and `extmod` like '%"4"%'
and `extmod` not like '%"4":%'
Results:
row1 data:
{"68":["1","4","7"],"67":["14"],"75":["28"]} - true
row2 data:
{"68":["59"],"67":["1","11","13"],"75":["3","4","5","27"]} - false
I want select only row1 by key:68 and value:4
Please help
Here is one way to do it using MySQL JSON functions, available since version 5.7:
select *
from t
where json_search(js -> '$."68"', 'one', '4') is not null
What this does is get the array that correspond to outer key '68' (using ->, which is a syntactic sugar for json_extract()), and then search its content with json_search(); if a non-null value is returned, we have a match.
To find if the value '"4"' is contained in the member '"68"', you can first extract the array using JSON_EXTRACT() :
SELECT JSON_EXTRACT(m.extmod, '$."68"')
FROM modul_69 m;
This outputs
["1", "4", "7"]
["59"]
To search in a JSON array if it contains a specific value, you can use JSON_CONTAINS() :
SELECT JSON_CONTAINS('["1", "4", "7"]', '"4"', '$'); -- output is 1
SELECT JSON_CONTAINS('["59"]', '"4"', '$'); -- output is 0
Now you can combine both functions to get the rows that contains the expected value :
Schema (MySQL v5.7)
CREATE TABLE modul_69
(
extmod JSON
);
INSERT INTO modul_69 VALUES ('{"68":["1","4","7"],"67":["14"],"75":["28"]}'), ('{"68":["59"],"67":["1","11","13"],"75":["3","4","5","27"]}');
Query #1
SELECT *
FROM modul_69 m
WHERE JSON_CONTAINS(JSON_EXTRACT(m.extmod, '$."68"'),
'"4"',
'$') = 1;
Output
| extmod |
| --------------------------------------------------- |
| {"67": ["14"], "68": ["1", "4", "7"], "75": ["28"]} |
View on DB Fiddle

SQL Is there a way to link two tables with the same value but different formats?

So I have two columns from two different databases that I would like to link.
Problem is that my first column outputs the numbers with this format "1 789 987" and my second column outputs the data "0000000001789987"
How can I write my WHERE sql forumla to idententify these as matching?
Ok so I pulled out the qrys to excel to provide you with more information.
Here are the different tables.
Looks like Tbl2 has NUM column set to text. And even though the QRY in the program gave spaces to the numbers in Tbl1 it looks like the qry removed them shrug
SELECT *
FROM "Tbl1","Tbl2"
WHERE "Tbl1"."num" = "Tbl2"."num"
AND "Tbl1"."Date" BETWEEN '2019-01-21' AND '2019-01-25'
I hope the information became abit clearer. I'm new to SQL and Stackoverflow, i'll try and improve my questions information in the future.
Well, to transform format 1 to format 2 you can try something like this :
set #format1 = "1 789 987";
set #format2 = "0000000001789987";
select LPAD(REPLACE(#format1, ' ', ''), 16, "0") as format1, #format2 as format2
Output is :
====================================
format1 | format2
====================================
0000000001789987 | 0000000001789987
This way format1 looks like format2 if you test it. The REPLACE remove the ' ' and the LPAD will fill the string with 0 untill the string is 16 char length like the format2.
So you can use this in you WHERE condition :
...WHERE LPAD(REPLACE(your_first_column, ' ', ''), 16, "0") = your_other_column
Now you can try to transform both column in int too, you didn't provide lot of information about those format so hard to find the best solution !
This cast may fit for you:
NOTE: tbl1 contains ids like: 1 789 987
select *
from tbl1 join tbl2 on (
cast( -- casts to integer
replace(tbl1.text_id, ' ', '') -- removes whitespaces
as int) =
tbl2.numeric_id -- join on second table
)
In any case please provide sample data and a testable example what you did and the results you need

insert and fetch strings and matrices to/from MySQL with Matlab

I need to store data in a database. I have installed and configured a MySQL database (and an SQLite database) in Matlab. However I cannot store and retrieve anything other than scalar numeric values.
% create an empty database called test_data base with MySQL workbench.
% connect to it in Matlab
conn=database('test_database','root','XXXXXX','Vendor','MySQL');
% create a table to store values
create_test_table=['CREATE TABLE test_table (testID NUMERIC PRIMARY KEY, test_string VARCHAR(255), test_vector BLOB, test_scalar NUMERIC)'];
curs=exec(conn,create_test_table)
Result is good so far (curs.Message is an empty string)
% create a new record
datainsert(conn,'test_table',{'testID','test_string','test_vector','test_scalar'},{1,'string1',[1,2],1})
% try to read out the new record
sqlquery='SELECT * FROM test_table8';
data_to_view=fetch(conn,sqlquery)
Result is bad:
data_to_view =
1 NaN NaN 1
From the documentation for "fetch" I would expect:
data_to_view =
1×4 table
testID test_string test_vector test_scalar
_____________ ___________ ______________ ________
1 'string1' 1x2 double 1
Until I learn how to read blobs I'd even be willing to accept:
data_to_view =
1×4 table
testID test_string test_vector test_scalar
_____________ ___________ ______________ ________
1 'string1' NaN 1
I get the same thing with an sqlite database. How can I store and then read out strings and blobs and why isn't the data returned in table format?
Matlab does not document that the default options for SQLite and MySQL database retrieval are to attempt to return everything as a numeric array. One only needs this line:
setdbprefs('DataReturnFormat','cellarray')
or
setdbprefs('DataReturnFormat','table')
in order to get results with differing datatypes. However! now my result is:
data_to_view =
1×4 cell array
{[2]} {'string1'} {11×1 int8} {[1]}
If instead I input:
datainsert(conn,'test_table',{'testID','test_string','test_vector','test_scalar'},{1,'string1',typecast([1,2],'int8'),1})
Then I get:
data_to_view =
1×4 cell array
{[2]} {'string1'} {16×1 int8} {[1]}
which I can convert like so:
typecast(data_to_view{3},'double')
ans =
1 2
Unfortunately this does not work for SQLite. I get:
data_to_view =
1×4 cell array
{[2]} {'string1'} {' �? #'} {[1]}
and I can't convert the third part correctly:
typecast(unicode2native(data_to_view{1,3}),'double')
ans =
0.0001 2.0000
So I still need to learn how to read an SQLite blob in Matlab but that is a different question.

How to search multiple items in JSON array in Postgres 9.3

I have scenario where i need to search multiple values in a JSON array. Below is my schema.
ID DATA
1 {"bookIds" : [1,2,3,5], "storeIds": [2,3]}
2 {"bookIds" : [1,2], "storeIds": [1,3]}
3 {"bookIds" : [11,12,10,9], "storeIds": [4,3]}
I want all the rows with value 1,2. Below is query i am using (This is query is written by one of fellow stackoverflow user Mr. klin credit to him).
select t.*
from JSONTest t, json_array_elements(data->'bookIds') books
where books::text::int in (1, 2);
However output I am duplicate rows in output, below is my output.
id data
1 {"bookIds" : [1,2,3,5], "storeIds": [2,3]}
1 {"bookIds" : [1,2,3,5], "storeIds": [2,3]}
2 {"bookIds" : [1,2], "storeIds": [1,3]}
2 {"bookIds" : [1,2], "storeIds": [1,3]}
I want only two rows in output that is id 1,2. How can i do that? I don't want use Distinct due to other constraints,
SQL Fiddle : http://sqlfiddle.com/#!15/6457a/2
Unfortunately there is no direct conversion function from a JSON array to a "real" Postgres array. (data ->'bookIds')::text returns something that is nearly a Postgres array literal: e.g. [1,2,3,5]. If you replace the [] with {} the value can be cast to an integer array. Once we have a proper integer array we can use the #> to test if it contains another array:
select *
from jsontest
where translate((data ->'bookIds')::text, '[]', '{}')::int[] #> array[1,2];
translate((data ->'bookIds')::text, '[]', '{}') will convert [1,2,3,5] to {1,2,3,5} which then is converted to an array using ::int[]
SQLFiddle: http://sqlfiddle.com/#!15/6457a/4