Update column values with dynamically created JSON MySql - mysql

I have a Relationships table that looks something like this
ID | FromID | ToID | JsonPair
1 10 8 null
2 11 13 null
3 15 21 null
4 26 22 null
And 2 tables From and To
FromID | FromName ToID | ToName
1 'A' 1 'Z'
2 'B' 2 'Y'
... ...
10 'E' 8 'M'
11 'I' ...
... 13 'N'
15 'O' ...
... 21 'F'
26 'U' 22 'H'
I'm trying to update the JsonPair column with Json objects in the form {FromName: ToName}. So the resulting table would look like
ID | FromID | ToID | JsonPair
1 10 8 {'E':'M'}
2 11 13 {'I':'N'}
3 15 21 {'O':'F'}
4 26 22 {'U':'H'}
I'm a novice at SQL. I'm thinking that I should SELECT the names first, then use the result to put in the UPDATE statement.
So far I got this to return FromName and ToName
SELECT F.FromName FROM Relationships AS R
JOIN From as F
ON R.FromID = F.FromID
and
SELECT T.ToName FROM Relationships AS R
JOIN To as T
ON R.FromID = T.FromID;
Then I think I should use the result of this to do
UPDATE Relationships
SET JsonPair = (combine result above and format to json)
WHERE JsonPair IS NULL;
I'm stuck at the combining and format step. Can I get help with this please?
I'm using MySql

You can use the update/join syntax, along with json_object().
Consider:
update relationships r
inner join t_from f on f.fromid = r.fromid
inner join t_to t on t.to_id = r.to_id
set r.jsonpair = json_object(f.fromname, t.toname)
Note: from and to are reserved words in MySQL, hence bad choices for table names. I renamed them to t_from and t_to in the query.

Related

SQL : Record Existance Check in another table

My requirement is below .
I have two tables let's call them Table A and Table B :
PARTNER_ID PARTNER_Registration Partner_PANNUMBER
----------
1 11 AB1
2 22 AB2
3 33 AB3
4 44 AB4
5 55 AB5
6 66 AB6
7 77 AB5
8 88 AB8
i Will have another table B which contains PID , Preg, Ppan as follows
PID PREG PPAN
----------
1 11 AB1
2 22 AB2
3 33 AB3
4 44
5 AB5
66 AB6
Now I should create a column Output in table A and have output as follows
PARTNER_ID PARTNER_Registration Partner_PANNUMBER Output
----------
1 11 AB1 All three Found
2 22 AB2 All three Found
3 33 AB3 All three found
4 44 AB4 PPAN NOT FOUND
5 55 AB5 PARTNER_Registration Not Found in TABLE B
6 66 AB6 PARTNER_ID Not found in Table B
7 77 AB5 PARTNER_ID, PARTNER_Registration Not found in Table B
8 88 AB8 None of them Found in Table B
Can some one help me find an easy way to acheive this in SQL,
I would like to populate which values of 3 columns are not present in another and update output column accordingly..
Thanks
I would just add up the number of matches:
select a.*,
( exists (select 1 from b where b.PID = a.PARTNER_ID) +
exists (select 1 from b where b.PREG = a.PARTNER_Registration) +
exists (select 1 from b where b.PPAN = a.Partner_PANNUMBER)
) as num_matches
from a;
You can use multiple LEFT JOIN with table B, and test which ones produce NULL values.
SELECT a.*,
CASE WHEN b1.pid IS NOT NULL AND b2.preg IS NOT NULL AND b3.ppan IS NOT NULL
THEN 'All three found'
WHEN b1.pid IS NULL AND b2.preg IS NULL AND p3.ppan IS NULL
THEN 'None of them found in Table B'
ELSE CONCAT(CONCAT_WS(', '
IF(b1.pid IS NULL, 'Partner_ID', NULL),
IF(b2.preg IS NULL, 'Partner_Registration', NULL),
IF(b3.ppan IS NULL, 'PPAN', NULL)),
' not found in Table B') AS Output
FROM TableA AS a
LEFT JOIN TableB AS b1 ON a.partner_id = b1.pid
LEFT JOIN TableB AS b2 ON a.partner_registration = b2.preg
LEFT JOIN TableB AS b3 ON a.partner_pannumber = b.ppan
CONCAT_WS() will ignore null values, so with the IF statements inverting NULL with the names of the missing values, you get the list of results you want.
I would use multiple LEFT JOIN because of the use case of OP where we can manipulate the null values means missing in table2 to achieve the exact output you want.
The query looks big but it is just manipulating the string using specific string functions to get the final string output.
select a.partner_id
,a.partner_registration
,a.partner_pannumber
,case
when chk = ''
then 'All three found'
else
concat(case (length(chk) - length(replace(chk,',','')))
when 3
then 'None of them found'
when 1
then replace(chk,',',' not found')
else
regexp_replace(chk,'[,]',' not found',1,2)
end
,' in table2'
)
end Remarks
from
(
select a.*
,concat(case when pi.pid is null then 'Partner Id,' else '' end
,case when pr.preg is null then 'Partner Registration,' else '' end
,case when pp.ppan is null then 'PPAN,' else '' end
) chk
from table1 a
left join table2 pi
on a.partner_id = pi.pid
left join table2 pr
on a.partner_registration = pr.preg
left join table2 pp
on a.partner_pannumber = pp.ppan
) a
P.S. I personally like the answer with usage of concat_ws as it has less code but you need a little modification for non of them match in ... case.

checking multiple values in between two columns mysql

I am trying for a MySQL query to check multiple values between two columns values.
For example, for one value here is my query which works
SELECT column3
FROM table
WHERE (12 between minvaluecol AND maxvaluecol) AND
id = 123;
I would like to check multiple values like (12,13,14,67,68) and should return the values that are in between minvaluecol and maxvaluecol columns. In this case, only 12,13,14 are in between minvaluecol and maxvaluecol columns whereas 67,68 are not.
my table looks like this,
id | minvaluecol | maxvaluecol
---- | ----------- | ------------
121 | 23 | 35
123 | 10 | 20
125 | 40 | 50
output for id 123 should look like,
12 | true
13 | true
14 | true
67 | false
68 | false
Please help me with this query in MySQL. Thank you.
Update
Completely revamped the answer based on updated question.
As you need all the values as different rows, you need to SELECT all of them with UNION and LEFT JOIN it with the original table, e.g.:
SELECT a.val, IF(a.val BETWEEN tv.minvaluecol AND maxvaluecol, 'true', 'false') AS result
FROM (
SELECT 12 AS val
UNION
SELECT 13 AS val
UNION
SELECT 14 AS val
UNION
SELECT 67 AS val
UNION
SELECT 68 AS val) a
JOIN test_values tv
WHERE tv.id = 123;
Here's the SQL Fiddle.
The easiest way to get your result is to Insert those values into a table and then join like this:
SELECT value,
CASE WHEN value between minvaluecol AND maxvaluecol THEN 'true ELSE 'false' END
FROM table
CROSS JOIN table_with_values
WHERE id = 123
ORDER BY value

Find duplicates from same table and constraint them from another table in sql

Oh, my title is not the best one and as English is not my main language maybe someone can fix that instead of downvoting if they've understood the issue here.
Basically i have two tables - tourneyplayers and results. Tourneyplayers is like a side table which gathers together tournament information across multiple tables - results, tournaments, players etc. I want to check duplicates from the results table over column day1_best, from single tournament and return all the tourneyplayers who have duplicates.
Tourneyplayers contain rows:
Tourneyplayers
tp_id | resultid | tourneyid
1 | 2 | 91
2 | 21 | 91
3 | 29 | 91
4 | 1 | 91
5 | 3 | 92
Results contains rows:
Results:
r_id | day1_best
1 | 3
2 | 1
3 | 4
.. | ..
21 | 1
.. | ..
29 | 2
Now tourney with id = 91 has in total 4 results, with id's 1,2,21 and 29. I want to return values which have duplicates, so currently the result would be
Result
tp_id | resultid | day1_best
1 | 2 | 1
2 | 21 | 1
I tried writing something like this:
SELECT *
FROM tourneyplayers
WHERE resultid
IN (
SELECT r1.r_id
FROM results AS r1
INNER JOIN results AS r2 ON ( r1.day1_best = r2.day1_best )
AND (
r1.r_id <> r2.r_id
)
)
AND tourneyid =91
But in addition to values which had the same day1_best it chose two more which did not have the same. How could i improve my SQL or rewrite it?
First you JOIN both tables, so you know how the data looks like.
SELECT *
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`;
Then using the same query you GROUP to see what tourneyid, day1_best combination has multiple rows
SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`;
Finally you use the base JOIN and perform a LEFT JOIN to see what rows has a match and show only those rows.
SELECT t.`tp_id`, r.`r_id`, r.`day1_best`
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
LEFT JOIN (SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`
HAVING count(*) > 1) as filter
ON t.`tourneyid` = filter.`tourneyid`
AND r.`day1_best` = filter.`day1_best`
WHERE filter.`tourneyid` IS NOT NULL;
SQL DEMO
OUTPUT
Please try this :
Select tp.tp_id , tp.resultid ,r.day1_best from (Select * from Tourneyplayers
where tourneyid = 91)as tp inner join (select * from Result day1_best in(select
day1_best from result group by day1_best having count(*)>1 ) )as r on tp.resultid
= r.r_id ;

How can you select unique values with MySQL GROUP BY

I have a table full of messages between users, and I want to select the last message from each user where the userFrom is not my user(4), however if the last message to another user(userTo) is the last message between my user(4) and another user then that should be the value for that msg in the return records.
TABLE messages
id|userFrom|userTo|msg
-------------------------
1 | 4 | 9 |msg 1
2 | 9 | 4 |msg 2
3 | 4 | 63 |msg 1
4 | 63 | 4 |msg 2
5 | 4 | 9 |msg 3
6 | 9 | 4 |msg 4
7 | 9 | 4 |msg 5
8 | 63 | 4 |msg 3
My end goal is to use the data to show a list of messages from unique users where each row is a different user and it shows the last message between my user and that user(for visual reference i'm trying to create something like Facebook messages)
How i would like the above table data returned
id|userFrom|msg
-------------------------
7 | 9 |msg 5
8 | 63 |msg 3
i need the userFrom to be unique so i can extend the query to do additional joins to get the actual varchar username from the users table where the userFrom is some user but not my own user.
Here's one option with least and greatest:
select id, userfrom, userto, msg
from messages m join (
select max(id) maxid
from messages
group by least(userfrom, userto), greatest(userfrom, userto)
) t on m.id = t.maxid
SQL Fiddle Demo
BTW -- I assume your expected results are incorrect. You don't have id = 8 in your sample data.
Try this one:
SELECT messages.id, messages.userFrom, messages.msg
FROM messages INNER JOIN
(SELECT userFrom, max(id) AS mxid
FROM messages
GROUP BY userFrom) sub
ON messages.id = sub.mxid
WHERE messages.UserFrom <> 4
I think this is what you are looking for
CREATE TABLE [dbo].[UserMessage](
[id] [int] NOT NULL,
[userFrom] [int] NOT NULL,
[userTo] [int] NOT NULL,
[msg] [varchar](50) NOT NULL
) ON [PRIMARY]
insert into UserMessage values(1,4,9,'msg 1')
insert into UserMessage values(2,9,4,'msg 2')
insert into UserMessage values(3,4,63,'msg 1')
insert into UserMessage values(4,63,4,'msg 2')
insert into UserMessage values(5,4,9,'msg 3')
insert into UserMessage values(6,9,4,'msg 4')
insert into UserMessage values(7,9,4,'msg 5')
insert into UserMessage values(8,63,4,'msg 3')
SELECT lastMesageFrom.*, um.msg
FROM
(
select max(id) as id,UserFrom from UserMessage
where UserTo = 4
Group by UserFrom
)as lastMesageFrom
Left Outer join UserMessage um on um.id = lastMesageFrom.id
EDIT: Verified with MySQL: http://sqlfiddle.com/#!9/1045b/1
You must read the difference between where and having clause.
First, filter your records by WHERE clause (where userFrom!=4)
Then use the group by
And finally filter the messages that were sent to you by HAVING clause (having userTo=4)

Select those that HAVE NEVER had certain entry

I have a table of services that have been provided to clients. I'm trying to make a query that selects all the clients who received a service that WEREN'T provided by a certain user.
So consider this table...
id_client | id_service | id_user
--------------------------------
5 | 3 | 2
7 | 4 | 2
7 | 4 | 1
9 | 4 | 2
8 | 4 | 1
If I write the query like this:
SELECT id_client FROM table WHERE id_service=4 AND id_user<>1
I still end up getting id_client 7. But I don't want to get client 7 because that client HAS received that service from user 1. (They're showing up because they've also received that service from user 2)
In the example above I would only want to be returned with client 9
How can I write the query to make sure that clients that have EVER received service 4 from user 1 don't show up?
Try this:
SELECT DISTINCT id_client
FROM yourtable t
WHERE id_service = 4 AND id_client NOT IN
(SELECT DISTINCT id_client
FROM yourtable t
WHERE id_user = 1
)
I'd write it like this:
SELECT DISTINCT id_client
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON t1.id_client = t2.id_client AND t2.id_user = 1
WHERE t2.id_client IS NULL
When the conditions of a left outer join are not met, the row on the left side is still returned, and all the columns for the row on the right side are null. So if you search for cases of null in a column that would be certain to be non-null if there were a match, you know the outer join found no match.
SELECT id_client
FROM table
WHERE id_service = 4
GROUP BY id_client
HAVING MAX(CASE
WHEN id_user = 1 THEN 2
ELSE 1
END) = 1