MySQL Update a table by min of another table - mysql

I would like to update Table Lease from Table History
CREATE TABLE Lease
(`LeaseID` int, `Name` varchar(3), `Users` varchar(15), `WhoSignID` int, `NoteDate` date)
;
INSERT INTO Lease
(`LeaseID`, `Name`, `Users`, `WhoSignID`, `NoteDate`)
VALUES
(1, 'AAA', '1000,1001', NULL, NULL),
(2, 'BBB', '1002', NULL, NULL),
(3, 'CCC', '1003,1004', NULL, NULL),
(4, 'DDD', '1005,1006, 1007', NULL, NULL)
;
CREATE TABLE History
(`HistoryID` int, `LeaseID` int, `User` int, `SignDate` date)
;
INSERT INTO History
(`HistoryID`, `LeaseID`, `User`, `SignDate`)
VALUES
(1, 1, 1000, '2020-01-05'),
(2, 1, 1001, '2020-01-04'),
(3, 1, 1001, '2020-01-02'),
(4, 1, 1000, '2020-01-03'),
(6, 2, 1002, '2020-05-01'),
(7, 2, 1002, '2020-05-03')
;
I looking of a Mysql Update to update Table Lease :
NoteDate and WhoSignID based on SignDate and User
where Minimum of SignDate of User
Table Lease After Update
LeaseID | Name | Users | WhoSignID | NoteDate
1 | AAA | 1000,1001 | 1001 | 2020-01-02
2 | BBB | 1002 | 1002 | 2020-05-01
...
I appreciate any assist

Your Lease table has a serious design problem, because it is storing users as a CSV list. Instead, you should have each user value on a separate record. That being said, it appears that the CSV user list is immaterial to your current problem, which only required finding the earliest date for each lease. If so, then a simple update join should suffice:
UPDATE Lease l
INNER JOIN
(
SELECT h1.LeaseID, h1.User, h2.MinSignDate
FROM History h1
INNER JOIN
(
SELECT LeaseID, MIN(SignDate) AS MinSignDate
FROM History
GROUP BY LeaseID
) h2
ON h2.LeaseID = h1.LeaseID AND
h2.MinSignDate = h1.SignDate
) h
ON h.LeaseID = l.LeaseID
SET
WhoSignID = h.User,
NoteDate = h.MinSignDate;

Related

MySQL joining tables of different structure

I am having trouble either getting any result or a correct result in the following problem - http://www.sqlfiddle.com/#!9/696ed2/4
Overall goal is to list all transactions of users who are linked together as 'Customers'. So if John is looking at his dashboard, he will see which books Alice (his customer) has rented (including book title), and which books were sold (he won't be able to see the title of that book).
When two multiple tables are joined to the parent table, where both depending tables have an 'active' flag set against each row, I can't seem to get only active rows.
# USERS
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`active` boolean DEFAULT NULL
);
INSERT INTO `users` (`id`, `name`, `active`) VALUES
(1, 'John', 1),
(2, 'Alice', 1),
(3, 'Jess', 1),
(4, 'Bob', 1);
# BOOKS
CREATE TABLE `books` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`active` boolean DEFAULT NULL
);
INSERT INTO `books` (`id`, `name`, `active`) VALUES
(1, 'On the Road', 1),
(2, 'Neuromancer', 0),
(3, 'Modern History', 1),
(4, 'Red Mars', 1);
# TRANSACTIONS
CREATE TABLE `transactions` (
`id` int(11) NOT NULL,
`user_1_id` int(11) NOT NULL,
`user_2_id` int(11) DEFAULT NULL,
`book_id` int(11) DEFAULT NULL,
`timestamp` varchar(20) DEFAULT NULL,
`type` enum('Rent', 'Sold') NOT NULL
);
INSERT INTO `transactions` (`id`, `user_1_id`, `user_2_id`, `book_id`, `timestamp`, `type`) VALUES
(1, 1, 2, 1, '1465238591', 'Rent'),
(2, 2, 1, 2, '1465238592', 'Rent'),
(3, 2, 3, 3, '1465238593', 'Rent'),
(4, 3, 4, NULL, '1465238594', 'Sold'),
(5, 2, 3, NULL, '1465238595', 'Sold'),
(6, 3, 4, NULL, '1465238596', 'Sold'),
(7, 2, 2, 4, '1465238597', 'Rent'),
(8, 1, 3, 1, '1465238598', 'Rent'),
(9, 2, 4, 2, '1465238598', 'Rent');
# RELATIONSHIPS
CREATE TABLE `relationships` (
`id` int(11) NOT NULL,
`user_1_id` int(11) DEFAULT NULL,
`user_2_id` int(11) NOT NULL,
`type` enum('Customer', 'Supplier') NOT NULL
);
INSERT INTO `relationships` (`id`, `user_1_id`, `user_2_id`, `type`) VALUES
(1, 1, 2, 'Customer'),
(2, 2, 1, 'Customer'),
(3, 2, 4, 'Customer'),
(3, 1, 4, 'Supplier'),
(3, 3, 1, 'Customer');
Query:
SELECT DISTINCT
t.id,
t.type,
t.timestamp,
u1.name as user_1_name,
u2.name as user_2_name,
b.name as book_name
FROM transactions t
LEFT JOIN relationships r ON (r.user_1_id = 1 AND r.type = 'Customer')
LEFT JOIN books b ON (b.id = t.book_id AND b.active)
LEFT JOIN users u1 ON (u1.id = t.user_1_id) # AND u1.active
LEFT JOIN users u2 ON (u2.id = t.user_2_id) # AND u2.active
WHERE (r.user_2_id = t.user_1_id OR t.user_2_id = 1 AND t.user_1_id != 1)
# AND b.active AND u1.active AND u2.active
[Result]:
| id | type | timestamp | user_1_name | user_2_name | book_name |
|----|------|------------|-------------|-------------|----------------|
| 3 | Rent | 1465238593 | Alice | Jess | Modern History |
| 2 | Rent | 1465238592 | Alice | John | (null) | <<< Should not be here
| 7 | Rent | 1465238597 | Alice | Alice | Red Mars |
| 5 | Sold | 1465238595 | Alice | Jess | (null) | <<< Correct
| 9 | Rent | 1465238598 | Alice | Bob | (null) | <<< Should not be here
In the above result, the problem is that book Neuromancer has flag 'active' set to 0, so should not appear in the result. I have played with placing AND b.active at different places, but the results are always wrong. (See http://www.sqlfiddle.com/#!9/696ed2/5)
Looking at the mess above, I am not even sure my approach is any good, any suggestions are welcome.
As D. Smania mentioned in comments you need to make sure b.active is either 1 or NULL but based on your LEFT JOIN condition b.active will always be 1 so you need to do the join only on id and rely on the WHERE condition for comparison. This should yield the results you asked for:
SELECT DISTINCT
t.id,
t.type,
t.timestamp,
u1.name AS user_1_name,
u2.name AS user_2_name,
b.name AS book_name
FROM transactions t
LEFT JOIN relationships r ON (r.user_1_id = 1 AND r.type = 'Customer')
LEFT JOIN books b ON (b.id = t.book_id)
LEFT JOIN users u1 ON (u1.id = t.user_1_id)
LEFT JOIN users u2 ON (u2.id = t.user_2_id)
WHERE (r.user_2_id = t.user_1_id OR t.user_2_id = 1 AND t.user_1_id != 1)
AND (b.active OR b.active IS NULL)
AND u1.active AND u2.active
SQL Fiddle
One note - in your first WHERE condition it's not clear to me whether you mean:
(r.user_2_id = t.user_1_id OR (t.user_2_id = 1 AND t.user_1_id != 1))
or
((r.user_2_id = t.user_1_id OR t.user_2_id = 1) AND t.user_1_id != 1)
It's always best to be explicit with your logic grouping when you have adjacent AND and OR conditions.

How to join two mysql table and retrieve latest results from joined table?

I have two tables. I need to join these two tables and retrieve latest status from execution table. How can I retrieve?
My schema and data:
CREATE TABLE test
(`id` serial primary key, `ref_id` int, `ref_name` varchar(7))
;
INSERT INTO test
(`id`, `ref_id`, `ref_name`)
VALUES
(1, 1, 'trial'),
(2, 3, 'test'),
(3, 7, 'testing')
;
CREATE TABLE execution
(`id` serial primary key, `ref_id` int, `status` varchar(11))
;
INSERT INTO execution
(`id`, `ref_id`, `status`)
VALUES
(1, 1, 'Completed'),
(2, 2, 'Completed'),
(3, 1, 'Completed'),
(4, 3, 'In progress'),
(5, 3, 'To do'),
(6, 2, 'In progress'),
(7, 1, 'Completed'),
(7, 1, 'To do')
;
Expected result is here below.
ref_id | ref_name | status |
3 | testing | In progress |
2 | test | To do |
1 | trial | To do |
I have tried with below query:
SELECT
ref_id,
ref_name,
status
FROM
test
JOIN execution ON test.ref_id = execution.ref_id
GROUP BY `ref_id`
ORDER BY `ref_id` DESC;
This query retrieves the status, but the retrieved status is not a latest one. How can retrieve the latest status by joining these two tables.
you can use below query
select T2.ref_id,T2.ref_name,OE.status from
(
select t1.ref_id,t1.ref_name,e.id from test t1 inner join
(select max(id) as id,ref_id from execution group by ref_id) as e
on
t1.ref_id=e.ref_id
) as T2
inner join execution OE on T2.id=OE.id
https://www.db-fiddle.com/f/rvnm8APX27dmW9a84JkCsS/1
It seems you have given in-correct data as an example as ref_id 7 not found in
execution table. However this might help you
SELECT b.ref_id,
b.ref_name,
a.status
FROM execution a
JOIN (SELECT MAX(id) id ,ref_id
FROM execution
GROUP BY ref_id) a1
USING(id,ref_id)
JOIN test b ON a.ref_id = b.ref_id ORDER BY ref_id DESC;

Joining 2 tables with Group By

poll_opts table for storing options and poll_voted for storing vote result, pid stands for poll id(unique) and oid stands for option id(unique for individual poll only)
poll_voted [Primary Key: pid.oid.emp]
+-----+-----+-------+
| pid | oid | emp |
+-----+-----+-------+
poll_opts [Primary Key: pid.oid]
+-----+-----+---------+
| pid | oid | opt |
+-----+-----+---------+
pid & oid type: int , opt type: text
If you need the "not existent" results as well you need a left outer join preserves all results from poll_opts even if no match in poll_votes is found.
MySql 5.7 Join Syntax
Query:
select opt, count(vo.oid)
from poll_opts po
left outer join poll_voted vo on vo.oid = po.oid and po.pid=vo.pid
where po.pid = 3 -- 3
group by opt
Output:
opt count(vo.oid)
Chrome 0
Firefox 0
IE 0
MS Edge 0
Opera 1
Testdata:
CREATE TABLE poll_voted (`pid` int, `oid` int, `emp` int);
INSERT INTO poll_voted (`pid`, `oid`, `emp`)
VALUES
(1, 0, 1989),
(1, 2, 1989),
(1, 4, 1989),
(1, 6, 1989),
(3, 2, 1989)
;
CREATE TABLE poll_opts (`pid` int, `oid` int, `opt` varchar(15));
INSERT INTO poll_opts (`pid`, `oid`, `opt`)
VALUES
(1, 0, 'WinXP'),
(1, 2, 'wIN7'),
(1, 4, 'wIN 10'),
(1, 6, 'Ubuntu'),
(3, 0, 'IE'),
(3, 1, 'MS Edge'),
(3, 2, 'Opera'),
(3, 3, 'Chrome'),
(3, 4, 'Firefox')
;

Mysql: how to find users that currently in the room?

I have table entrances that logs times when users came into the room and came out of the room. Something like that:
user | action | time
-------------------------------------------
Ivan | in | 2016-08-28 12:00:00
John | in | 2016-08-28 12:00:01
Ann | in | 2016-08-28 12:00:02
Ivan | out | 2016-08-28 12:00:03
Ivan | in | 2016-08-28 12:00:04
Ann | out | 2016-08-28 12:00:05
Ivan | out | 2016-08-28 12:00:06
Mike | in | 2016-08-28 12:00:07
John | out | 2016-08-28 12:00:08
Ann | out | 2016-08-18 12:00:09
John | in | 2016-08-18 12:00:10
John | out | 2016-08-18 12:00:11
Ann | in | 2016-08-18 12:00:12
Users actions are independent. Only is known is that first action is always in and user cannot in twice witout out (and reverse).
My goal is to find all users that currently in room.
I have two ideas:
select users that have no out after latest in
select users that has count in more that count out
How to implement this on mysql? Or any other ideas?
SQL for testing:
CREATE TABLE `entrances` (
`id` int(11) NOT NULL,
`user` varchar(10) COLLATE utf8_bin NOT NULL,
`action` varchar(3) COLLATE utf8_bin NOT NULL,
`time` datetime NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
INSERT INTO `entrances` (`id`, `user`, `action`, `time`) VALUES
(1, 'Ivan', 'in', '2016-08-28 12:00:00'),
(2, 'John', 'in', '2016-08-28 12:00:01'),
(3, 'Ann', 'in', '2016-08-28 12:00:02'),
(4, 'Ivan', 'out', '2016-08-28 12:00:03'),
(5, 'Ivan', 'in', '2016-08-28 12:00:04'),
(6, 'Ann', 'out', '2016-08-28 12:00:05'),
(7, 'Ivan', 'out', '2016-08-28 12:00:06'),
(8, 'Mike', 'in', '2016-08-28 12:00:07'),
(9, 'John', 'out', '2016-08-28 12:00:08'),
(10, 'Ann', 'out', '2016-08-28 12:00:09'),
(11, 'John', 'in', '2016-08-28 12:00:10'),
(12, 'John', 'out', '2016-08-28 12:00:11'),
(13, 'Ann', 'in', '2016-08-28 12:00:12');
ALTER TABLE `entrances` ADD PRIMARY KEY (`id`);
First get the last action time from each user in the subquery
Then join against that subquery to have only the last record for each user
Then take only those records having the action = in with the where clause
Like this
select e.*
from entrances e
join
(
select user, max(time) as mtime
from entrances
group by user
) t on t.user = e.user
and t.mtime = e.time
where e.action = 'in'
Join all row with the action out and select the ones that have no out action or the in action is newer
SELECT * FROM entrances e
LEFT JOIN entrances e1 ON e.user = e1.user AND e1.action = 'out'
WHERE e.action = 'in' AND (e1.user IS NULL OR e.time > e1.time)
GROUP BY e.user, e.action
I will try something like this however it's better to test on real data.
select user from entrances e inner join
(select user, count(*) cnt from entrances where action='in') as e_in
on e.user=e_in.user left join
(select user, count(*) cnt from entrances where action='out') as e_out
on e.user=e_out.user
where e_out.user is null or (e_in.cnt - e_out.cnt) = 1
Mysql implementation of my idea number 1:
SELECT e.* FROM entrances e
WHERE NOT EXISTS (
SELECT * FROM entrances WHERE e.user=user AND action="out" AND time > (
SELECT MAX(time) FROM entrances WHERE e.user=user AND action="in"
)
)
This works literally as written: find users that have no out after last in. Utilizes NOT EXISTS feature. Advantage is readability without magic, plain algorythm.

Subquery on JOIN to pulling all ID/Names

* user
user_id
name
* client
client_id
name
* user_client
user_client_id
user_id
client_id
* message
message_id
client_id
description
Sample Table Rows
user_id
1
2
3
client_id name
10 John
11 James
12 David
13 Richard
14 Bob
user_client
user_id client_id
1 11
1 13
3 14
3 10
message
message_id client_id message
1 11 Hello Word
2 12 MySQL is awesome
3 14 I like StackOverflow
4 13 This is very cool
What it's not working is when I use that query as a subquery on a LEFT JOIN to pull the messages only for those clients pertinent to the user.
Any ideas?
Thanks!
The DDL to set up the example (in MySQL) and the query I believe you are looking for are shown below.
/*
-- DDL TO SET UP EXAMPLE
create schema example;
use example;
create table user (
user_id int,
name varchar(64)
);
create table client(
client_id int,
name varchar(64)
);
create table user_client (
user_client_id int,
user_id int,
client_id int
);
create table message(
message_id int,
client_id int,
message varchar(64)
);
insert into user values (1, 'Peter');
insert into user values (2, 'Paul');
insert into user values (3, 'Mary');
insert into client values (10, 'John');
insert into client values (11, 'James');
insert into client values (12, 'David');
insert into client values (13, 'Richard');
insert into client values (14, 'Bob');
insert into user_client values (1, 1, 11);
insert into user_client values (2, 1, 11);
insert into user_client values (3, 3, 14);
insert into user_client values (4, 3, 10);
insert into message values (1, 11, 'Hello World');
insert into message values (2, 12, 'MySQL is awesome');
insert into message values (3, 14, 'I like StackOverflow');
insert into message values (4, 13, 'This is very cool');
*/
-- query to get all messages for all clients of a given user
select
*
from
user_client uc
join user u on uc.user_id = u.user_id
join client c on uc.client_id = c.client_id
join message m on m.client_id = c.client_id
where
u.user_id = 1;
-- query to get all messages for a given client
select
*
from
user_client uc
join user u on uc.user_id = u.user_id
join client c on uc.client_id = c.client_id
join message m on m.client_id = c.client_id
where
c.client_id = 11;
This should really be done as three separate queries as three different questions are being asked based on the comments:
So you want a query that will return messages from related clients if
there is a record in client, and all messages if there is no record in
client for a give user? – John
...
Hi #John that is correct. And that's why I was using the subquery,
because it pulls exactly that, but for some reason the client_id and
name are coming as NULL for all but one. – Kitara
The first question (query) is find all of the messages for all of the clients of a given user.
The second question (query) is: Does a user have "authorization" to view all messages. If the user has no clients then that user is "authorized" to view all messages.
The third question (query) is: If the user is authorized to view all messages get all of the messages.
These are very simple straight forward queries to write, execute, and understand. Trying to conflate all of this into a single query will add complexity and represents poor separation of concerns. If executing three very simple queries represents a performance issue the architecture of the application needs to be reconsidered.
There was a mistake in my original ddl in one of the inserts (fixed below). In the sql below I've also added a user with no messages. I believe the query at the end of what is posted below is what you are looking for.
-- DDL TO SET UP EXAMPLE
drop schema example;
create schema example;
use example;
create table user (
user_id int,
name varchar(64)
);
create table client(
client_id int,
name varchar(64)
);
create table user_client (
user_client_id int,
user_id int,
client_id int
);
create table message(
message_id int,
client_id int,
message varchar(64)
);
insert into user values (1, 'Peter');
insert into user values (2, 'Paul');
insert into user values (3, 'Mary');
insert into client values (10, 'John');
insert into client values (11, 'James');
insert into client values (12, 'David');
insert into client values (13, 'Richard');
insert into client values (14, 'Bob');
insert into client values (15, 'Quiet Client');
insert into user_client values (1, 1, 11);
insert into user_client values (2, 1, 13);
insert into user_client values (3, 3, 14);
insert into user_client values (4, 3, 10);
insert into user_client values (5, 1, 15);
insert into message values (1, 11, 'Hello World');
insert into message values (2, 12, 'MySQL is awesome');
insert into message values (4, 13, 'This is very cool');
insert into message values (3, 14, 'I like StackOverflow');
-- query to get all messages for all clients of a given user
select
u.user_id,
u.name user_name,
c.client_id,
c.name client_name,
m.message
from
user_client uc
join user u on uc.user_id = u.user_id
join client c on uc.client_id = c.client_id
left outer join message m on m.client_id = c.client_id
where
u.user_id = 1;
Output:
+ ------------ + -------------- + -------------- + ---------------- + ------------ +
| user_id | user_name | client_id | client_name | message |
+ ------------ + -------------- + -------------- + ---------------- + ------------ +
| 1 | Peter | 11 | James | Hello World |
| 1 | Peter | 13 | Richard | This is very cool |
| 1 | Peter | 15 | Quiet Client | |
+ ------------ + -------------- + -------------- + ---------------- + ------------ +
3 rows