I have the query to search for ids in jsonb column, the array can contain many id's.
Say I have data like this
id | act | act_id | from_ids | object_ids | post_date
2 post 1 {"2":"1494308197","3":"1494308198","4":"1494308199"} {"items":["104564"]} 1494308197
And a query like this
SELECT an.*
FROM activity_network an
WHERE an.from_ids ?| ARRAY['2','3'];
That query will return the row because it finds 2 and 3. But how can I return what it finds in it's own column. So that it returns 2,3 in text or json format or something like that in the results as well.
I tried this
SELECT an.*, jsonb_each_text(from_ids) b
FROM activity_network an
WHERE an.from_ids ?| ARRAY['2','3'];
But that creates 3 rows with a b column each one with the value 2, 3 and 4. I want 1 row with b column containing both 2 and 3 which is what I searched on.
Is that possible?
example result that I'm looking for. notice the last column. I put it as column delimited for demo purpose. it can be any format I can use.
2 | post | 1 | {"2":"1494308197","3":"1494308198","4":"1494308199} | {"items":["104564"]} | 1494308197 | 2,3}
here I explode/implode it. Quite ugly way.
t=# with p as (
with c as (
select '{"2":"1494308197","3":"1494308198","4":"1494308199"}'::json j
)
select json_object_keys(j),j->json_object_keys(j) v
from c
)
select concat('{',string_agg(concat('"',json_object_keys,'"',':',v)::text,','),'}')::json
from p
where json_object_keys::int = ANY (ARRAY [2,4]);
concat
-------------------------------------
{"2":"1494308197","4":"1494308199"}
(1 row)
Time: 0.348 ms
The function jsonb_exists_all sounds like what you want. It requires that all the elements in the array exist as top-level keys in the object.
You can find that and other undocumented functions for jsonb using the \df *jsonb* command in psql.
example paste:
test=# SELECT * from twj WHERE jsonb_exists_any(from_ids, ARRAY['2','3']);
id | act | from_ids
----+------+-----------------------------------------------------------
1 | post | {"2": "1494308197"}
3 | post | {"2": "1494308197", "3": "1494308198", "4": "1494308199"}
(2 rows)
test=# SELECT * from twj WHERE jsonb_exists_all(from_ids, ARRAY['2','3']);
id | act | from_ids
----+------+-----------------------------------------------------------
3 | post | {"2": "1494308197", "3": "1494308198", "4": "1494308199"}
(1 row)
The ?| operator you are using calls the jsonb_exists_any function.
Related
2-column MySQL Table:
| id| class |
|---|---------|
| 1 | A,B |
| 2 | B,C,D |
| 3 | C,D,A,G |
| 4 | E,F,G |
| 5 | A,F,G |
| 6 | E,F,G,B |
Requirement is to generate a report/output which tells which individual CSV value of class column is in how many rows.
For example, A is present in 3 rows (with id 1,3,5), and C is present in 2 rows (with id 2,3), and G is in 4 rows (3,4,5,6) so the output report should be
A - 3
B - 3
C - 2
...
...
G - 4
Essentially, column id can be ignored.
The draft that I can think of - first all the values of class column need to picked, split on comma, then create a distinct list of each unique value (A,B,C...), and then count how many rows contain the unique value from that distinct list.
While I know basic SQL queries, this is way too complex for me. Am unable to match it with some CSV split function in MySQL. (Am new to SQL so don't know much).
An alternative approach I made it to work - Download class column values in a file, feed it to a perl script which will create a distinct array of A,B,C, then read the downloaded CSV file again foreach element in distinct array and increase the count, and finally publish the report. But this is in perl which will be a separate execution, while the client needs it in SQL report.
Help will be appreciated.
Thanks
You may try split-string-into-rows function to get distinct values and use COUNT function to find number of occurrences. Specifically check here
I'm still pretty new to handling JSON fields in MySQL. All the solutions I've come across deal with objects that have key/values; unable to find one that handles JSON arrays.
Anyways, what I want to do is to be able to select all rows where the interestIds contain 2 in them. How do I do that? Thanks.
Users table
+----+-------------+
| id | interestIds |
+----+-------------+
| 1 | [1, 2] |
| 2 | [3, 2] |
| 3 | [2, 4] |
+----+-------------+
Sample test query:
SET #userId = 2;
SELECT * FROM Users
WHERE #userId IN JSON_CONTAINS(#user, interestIds, '$[1]');
I am confused as how to use the JSON_* functions; not sure what to put for the 3rd parameter...
You can use the following solution, using JSON_CONTAINS:
SELECT *
FROM Users
WHERE JSON_CONTAINS(interestIds, '2') = 1;
The third (optional) paramater path gives you the posibility to use this function only on a specific part of your JSON value. So the following example checks if 2 is the second value of the array:
SELECT *
FROM test
WHERE JSON_CONTAINS(interestIds, '2', '$[1]') = 1;
demo on dbfiddle.uk
Use JSON_SEARCH which returns path to element you are searching, or null if not found:
SELECT *
FROM users
WHERE JSON_SEARCH(interestids, 'one', '2') IS NOT NULL
Live Demo
If you're storing many-to-many relationship using simple JSON array, there are better ways to do it. Consider creating user_interest table and doing it the right and simpler way. That is if your JSON actually looks like you have shown us and does not contain dynamic key-value pairs.
SQL> select id
from users
where JSON_CONTAINS(interestIds, "2","$");
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.0015 sec)
Wrap your select to JSON_ARRAYAGG
Like:
SELECT JSON_ARRAYAGG(JSON_OBJECT(....)) FROM table....
I'm storing a object / data structure like this inside a MySql (actually a MariaDb) database:
{
idx: 7,
a: "content A",
b: "content B",
c: ["entry c1", "entry c2", "entry c3"]
}
And to store it I'm using 2 tables, very similar to the method described in this answer: https://stackoverflow.com/a/17371729/3958875
i.e.
Table 1:
+-----+---+---+
| idx | a | b |
+-----+---+---+
Table 2:
+------------+-------+
| owning_obj | entry |
+------------+-------+
And then made a view that joins them together, so I get this:
+-----+------------+------------+-----------+
| idx | a | b | c |
+-----+------------+------------+-----------+
| 7 | content A1 | content B1 | entry c11 |
| 7 | content A1 | content B1 | entry c21 |
| 7 | content A1 | content B1 | entry c31 |
| 8 | content A2 | content B2 | entry c12 |
| 8 | content A2 | content B2 | entry c22 |
| 8 | content A2 | content B2 | entry c32 |
+-----+------------+------------+-----------+
My question is what is the best way I can get it back to my object form? (e.g. I want an array of the object type specified above of all entries with idx between 5 and 20)
There are 2 ways I can think of, but both seem to be not very efficient.
Firstly we can just send this whole table back to the server, and it can make a hashmap with the keys being the primary key or some other unique index, and collect up the different c columns, and rebuild it that way, but that means it has to send a lot of duplicate data, and take a bit more memory and processing time to rebuild on the server. This method also won't be very pleasant to scale if we have multiple arrays, or have arrays within arrays.
Second method would be to do multiple queries, filter Table 1 and get back the list of idx's you want, and then for each idx, send a query for Table 2 where owning_obj = current idx. This would mean sending a whole lot more queries.
Neither of these options seems very good, so I'm wondering if there is a better way. Currently I'm thinking it can be something that utilizes JSON_OBJECT(), but I'm not sure how.
This seems like a common situation, but I can't seem to find the exact wording to search for to get the answer.
PS: The server interfacing with MySql/MariaDb is written in Rust, don't think this is relevant in this question though
You can use GROUP_CONCAT to combine all the c values into a comma-separated string.
SELECT t1.idx, t1.a, t1.b, GROUP_CONCAT(entry) AS c
FROM table1 AS t1
LEFT JOIN table2 AS t2 ON t1.idx = t2.owning_obj
GROUP BY t1.idx
Then explode the string in PHP:
$result_array = [];
while ($row = $result->fetch_assoc()) {
$row['c'] = explode(',', $row['c']);
$result_array[] = $row;
}
However, if the entries can be long, make sure you increase group_concat_max_len.
If you're using MySQL 8.0 you can also use JSON_ARRAYAGG(). This will create a JSON array of the entry values, which you can convert to a PHP array using json_decode(). This is a little safer, since GROUP_CONCAT() will mess up if any of the values contain comma. You can change the separator, but you need a separator that will never be in any values. Unfortunately, this isn't in MariaDB.
I understand that I can use the value in insert into to insert multiple rows by columns but my case is a bit different:
I have a string that looks like this: "2,5,14,25,30".
And a connection table that has 2 columns: (INT UserId, INT GroupId).
Let's say I want to connect userId = 1 with all the IDs in the string, how should I do that?
I am familiar with the FIND_IN_SET() function that helps me search for those IDs, but I can't figure how to use it in this case.
In the end, I want my table to look like this:
UserId | GroupId
-----------------
1 | 2
1 | 5
1 | 14
1 | 25
1 | 30
We have a table which contains card_no information. containing data like:
-----------------------------------------
| id [int(11)] | card_no [varchar(16)] |
-----------------------------------------
| 1 | 0124578965874563 |
| 2 | 1245789658478596 |
| 3 | 8471452369587458 |
-----------------------------------------
Now we need a query to find card number(s) which contains 7 in 6th position. Or which contains 4 in 2nd position.
This is actually needed when we printed card numbers and find some numbers unreadable. so we need to identify the card with rest of the numbers. For example we have data like:
1245_896584_8596
Now we need to identify the card with this data.
Thanks in advance.
You can use function SUBSTRING:
SELECT id, card_no
FROM mytable
WHERE SUBSTRING(card_no, 6, 1) = '7' OR SUBSTRING(card_no, 2, 1) = '4'
Demo here
Use SUBSTR string function
SELECT *
FROM yourtable
WHERE SUBSTR(card_no,2,1) = 4
OR SUBSTR(card_no,6,1) = 7
Use like in where clause and wildcard for exactly one symbol _
Something like
select * from table where card_no like '_____7℅'