query with several LEFT JOIN , not getting desired result - mysql

Here's a sql fiddle
Can anyone tell me how can I get this output using LEFT JOIN?
notification_recipient pm_sender msg modification_page_id
Peter Tom Hello NULL
notification_recipient pm_sender msg modification_page_id
Peter NULL NULL 2
Here's the query that I have tried:
SELECT u.name AS notification_recipient,us.name AS pm_sender,
p.msg,um.page_id AS modification_page_id
FROM notification n
LEFT JOIN pm p ON p.pm_id = n.pm_id
LEFT JOIN users u ON u.user_id = p.recipent_id
LEFT JOIN users us ON us.user_id = p.sender_id
LEFT JOIN user_modification um ON um.modification_id = n.modification_id
WHERE u.name = 'Peter'
AND n.is_read = '0'
I was looking for some sort conditional join, which means joining different tables based on whether a value exists in a field, but I couldn't find one that would work on my example. Any other efficient solution would also be appreciated.
Background:
I'm planning to make a notification system that sends out different types of messages to users (private messages among users, message that their modifications to an entry has been approved etc).
When a user logs in, I want to make a query to find out if there's any unread notifications for that user. If yes, a notification will be sent to him via Ajax.
To illustrate, suppose that Tom has sent a private message to Peter and his modification to an entry is approved, two triggers from table pm and user_modification will be invoked to add two new rows into notification. Column pm_id is referenced by pm and modification_id is by modification. is_read is defaulted 0 as Not Read.
Here's the table schema:
CREATE TABLE notification
(`id` int, `modification_id` int,`pm_id` int,`is_read` int)
;
INSERT INTO notification
(`id`,`modification_id`,`pm_id`,`is_read`)
VALUES
(1,1,NULL,0),
(2,NULL,1,0),
(3,2,NULL,0)
;
CREATE TABLE user_modification
(`modification_id` int, `user_id` int,`page_id` int, `is_approved` int)
;
INSERT INTO user_modification
(`modification_id`,`user_id`,`page_id`,`is_approved`)
VALUES
(1,1,5,1),
(2,2,2,1),
(3,3,3,0)
;
CREATE TABLE pm
(`pm_id` int, `sender_id` int,`recipent_id` int,`msg` varchar(200))
;
INSERT INTO pm
(`pm_id`,`sender_id`,`recipent_id`,`msg`)
VALUES
(1,1,2,'Hello');
CREATE TABLE users
(`user_id` int, `name`varchar(20))
;
INSERT INTO users
(`user_id`,`name`)
VALUES
(1,'Tom'),
(2,'Peter'),
(3,'David')
;
Here's the output I want for a notification if user David logs in. Each row for each message type.
notification_recipient pm_sender msg modification_page_id
Peter Tom Hello NULL
notification_recipient pm_sender msg modification_page_id
Peter NULL NULL 2
The notification to Peter will be like this:
`1.You have received a private message from Tom.
2.your modification on <a href='mysite.com/5'>that page</a> is approved`.

This query should do the job. Here's SQLFiddle
SELECT
n.id,
IF(pmu.name IS NULL, pmm.name, pmu.name) recipient,
pmus.name sender, pm.msg, m.modification_id
FROM
notification n
LEFT JOIN user_modification m ON (n.modification_id = m.modification_id)
LEFT JOIN pm ON (n.pm_id = pm.pm_id)
LEFT JOIN users pmu ON (pm.recipent_id = pmu.user_id)
LEFT JOIN users pmus ON (pm.sender_id = pmus.user_id)
LEFT JOIN users pmm ON (m.user_id = pmm.user_id)
WHERE
(pmu.name = 'Peter' OR
pmm.name = 'Peter') AND
n.is_read = 0;

Related

Mysql do not update if row does not exist

I've created a web app that lets you send money to other people (like paypal) for an University project.
For the send money webpage there's a form where an user can choose the money receiver's ID and the amount to send.
The system works with these queries:
SET #moneytosend= ? ; //amount to send
START TRANSACTION;
UPDATE users SET balance= balance- #moneytosend WHERE id = ?; //sender's ID
UPDATE users SET balance= balance+ #moneytosend WHERE id = ?; //receiver's ID
COMMIT WORK;
The problem is when the user inserts a non existing ID and the query is still executed, effectively removing money from the sender.
I'm sorry if my english is not perfect, and sorry again if there are any formatting errors.
One method is to check that the users exist in the update queries:
UPDATE users JOIN
(SELECT ? as sender_id, ? as receiver_id
) uu
ON u.id IN (uu.sender_id, uu.receiver_id)
SET balance = balance + (CASE WHEN u.id = uu.receiver_id THEN #moneytosend ELSE - #moneytosend)
WHERE EXISTS (SELECT 1 FROM users u2 WHERE u2.id = uu.sender_id) AND
EXISTS (SELECT 1 FROM users u2 WHERE u2.id = uu.receiver_id);
This logic combines the queries into a single query and still allows you to input only two parameters.
The problem is caused in the 1st update statement and it can be solved by adding EXISTS as a condition:
update users
set balance = balance - #moneytosend
where
balance >= #moneytosend
and
id = ? <-- sender id
and
exists (
select 1 from (select * from users where id = ? <-- receiver id
) t);
See the demo
As suggested by Raymond Nijland, do an extra check so that balance does not get a negative value.

return values of table 1 based on single column in table 2

I have 3 tables that I am using and need to make a query to return data from one table based on the value of a single column in the second table.
tbl_user
ID
login
pass
active
mscID
tbl_master
ID
name
training_date
MSCUnit
Active
tbl_msc
mscID
mscName
my current SQL statement:
SELECT
tbl_master.ID,
tbl_master.name,
tbl_master.training_date,
tbl_master.MSCUnit,
tbl_master.active,
tbl_user.mscID
FROM
tbl_master,
tbl_user
WHERE
tbl_master.active = 1 AND tbl_master.MSCUnit = tbl_user.mscID
The values stored in tbl_msc.mscID is a varchar(11) and it contains a string similar to A00 or A19. This is also the Primary key in the table.
The values stored in tbl_user.mscID matches that of tbl_msc.mscID. The values stored in tbl_master.UnitMSC also matches that of tbl_msc.mscID.
My goal is to return all records from tbl_master where the currently logged in user has the same mscID. The problem I am having is the statement returns all records in tbl_master.
I have tried several different join statements and for some reason, I cannot get this to filter correctly.
I am missing something. Any assistance in the SQL statement would be appreciated.
Thanks,
Will
You should be writing this using joins. I don't know how you know who the current user is, but the idea is to join the three tables together:
SELECT m.ID, m.name, m.training_date, m.MSCUnit, m.active,
u.mscID
FROM tbl_master m JOIN
tbl_user u
ON m.MSCUnit = u.mscID JOIN
tbl_msc msc
ON msc.mscID = u.msc_ID
WHERE m.active = 1 AND msc.mscName = ?;
Notice the use of proper, explicit, standard JOIN syntax and table aliases.
Select a.*, b.userid from
table_master a, table_user b where
a.mscunit in (select mscid from
table_user where active=1)
This should point you in the right direction.

MySQL Update using data from same table

Started off using this query to update data in the same table:
update INVENTORY
set billtoorg_id =
(
select
dist.BillToOrg_id
from
INVENTORY as inv,
contacts as cntc,
distributors as dist
where
inv.f_contact_id=cntc.contact_id
and cntc.f_dist_id=dist.dist_id
)
where
f_contact_id is not null
and f_product_id = 7
and BillToOrg_id is null
I found out that this won't work because in MySQL I can't use the same table in the query that I use in Update. So, I created a sub-query (see below) but now I'm getting error that indicates that the subquery returns more then 1 row.
update inventory
set billtoorg_id =
(
select
BillToOrg_id
from
(
SELECT
dist.BillToOrg_id
from
inventory as inv,
contacts as cntc,
distributors as dist
where
inv.f_contact_id=cntc.contact_id
and cntc.f_dist_id=dist.dist_id
) as I2
)
where
f_contact_id is not null
and `f_product_id` = 7
and `BillToOrg_id` is null
Can someone suggest a way to get this done? I'm new to SQL and I'm not sure I'm constructing the whole thing properly.
Looks like you want to se the billtoorg_id to be what is in the distributors table matched through contacts table. If you are trying to update more than 1 row at a time, your technique won't work.
Try:
update inventory as inv inner join contacts as cntc on inv.f_contact_id= cntc.contact_id
inner join distributors as dist on cntc.f_dist_id=dist.dist_id
set inv.billtoorg_id = dist.billtoorg_id
where
inv.f_contact_id is not null
and `inv.f_product_id` = 7
and `inv.BillToOrg_id` is null
edited as I think inner join will be better.

Build MySql Query

I have a database table called private_message. I run the following query on it to get a result of all the user id's that one user (47762) sent a private message to:
SELECT `to_id` FROM `private_message` WHERE `from_id` = 47762
I now want to use this query or some other way on another table called users to get the email address of all the users who received the email address from 47762.
I tried the following:
SELECT * FROM `users` WHERE `sid` = (SELECT `to_id` FROM `private_message` WHERE `from_id` = 47762)
In the above query, users.sid is the same as private_messages.to_id
I got the error #1242 - Subquery returns more than 1 row.
What I want is to get the email addresses of users that were sent a private message from user 47762 but the users table.
I'm a novice with MYSQL so would appreciate some help here.
Thanks.
You should use IN instead od = since you can't compate (=) sid to multiple values.
SELECT * FROM `users` WHERE `sid` IN (SELECT `to_id` FROM `private_message` WHERE `from_id` = 47762)
Anyway you can do a JOIN here instead:
SELECT u.* FROM `users` AS u
JOIN `private_message` AS pm ON u.`sid` = pm.`to_id`
WHERE pm.`from_id` = 47762
It will be more efficient in most cases.

field in subquery based on age of row instead of "group by"

I can't seem to get this query right. I have tables like this (simplified):
person: PersonID, ...other stuff...
contact: ContactID, PersonID, ContactDate, ContactTypeID, Description
I want to get a list of all the people who had a contact of a certain type (or types) but none of another type(s) that occurred later. An easy-to-understand example: Checking for records of gifts received without having sent a thank-you card afterward. There might have been other previous thank-you cards sent (pertaining to other gifts), but if the most recent occurrence of a Gift Received (we'll say that's ContactTypeID=12) was not followed by a Thank You Sent (ContactTypeID=11), the PersonID should be in the result set. Another example: A mailing list would be made up of everyone who has opted in (12) without having opted out (11) more recently.
My attempt at a query is this:
SELECT person.PersonID FROM person
INNER JOIN (SELECT PersonID,ContactTypeID,MAX(ContactDate) FROM contact
WHERE ContactTypeID IN (12,11) GROUP BY PersonID) AS seq
ON person.PersonID=seq.PersonID
WHERE seq.ContactTypeID IN (12)`
It seems that the ContactTypeID returned in the subquery is for the last record entered in the table, regardless of which record has the max date. But I can't figure out how to fix it. Sorry if this has been asked before (almost everything has!), but I don't know what terms to search for.
Wow. A system to check who has been good and sent thank yous. I think I would be in your list...
Anyway. Give this a go. The idea is to create two views: the first with personId and the time of the most recently received gift and the second with personId and the most recently sent thanks. Join them together using a left outer join to ensure that people who have never sent a thank you are included and then add in a comparison between the most recently received time and the most recent thanks time to find impolite people:
select g.personId,
g.mostRecentGiftReceivedTime,
t.mostRecentThankYouTime
from
(
select p.personId,
max(ContactDate) as mostRecentGiftReceivedTime
from person p inner join contact c on p.personId = c.personId
where c.ContactTypeId = 12
group by p.personId
) g
left outer join
(
select p.personId,
max(ContactDate) as mostRecentThankYouTime
from person p inner join contact c on p.personId = c.personId
where c.ContactTypeId = 11
group by p.personId
) t on g.personId = t.personId
where t.mostRecentThankYouTime is null
or t.mostRecentThankYouTime < g.mostRecentGiftReceivedTime;
Here is the test data I used:
create table person (PersonID int unsigned not null primary key);
create table contact (
ContactID int unsigned not null primary key,
PersonID int unsigned not null,
ContactDate datetime not null,
ContactTypeId int unsigned not null,
Description varchar(50) default null
);
insert into person values (1);
insert into person values (2);
insert into person values (3);
insert into person values (4);
insert into contact values (1,1,'2013-05-01',12,'Person 1 Got a present');
insert into contact values (2,1,'2013-05-03',11,'Person 1 said "Thanks"');
insert into contact values (3,1,'2013-05-05',12,'Person 1 got another present. Lucky person 1.');
insert into contact values (4,2,'2013-05-01',11,'Person 2 said "Thanks". Not sure what for.');
insert into contact values (5,2,'2013-05-08',12,'Person 2 got a present.');
insert into contact values (6,3,'2013-04-25',12,'Person 3 Got a present');
insert into contact values (7,3,'2013-04-30',11,'Person 3 said "Thanks"');
insert into contact values (8,3,'2013-05-02',12,'Person 3 got another present. Lucky person 3.');
insert into contact values (9,3,'2013-05-05',11,'Person 3 said "Thanks" again.');
insert into contact values (10,4,'2013-04-30',12,'Person 4 got his first present');