Get Data From 2 Tables in One Query - mysql

I am trying to retrieve data for my notification system. I have three tables. One table (notifications) holds the actual information for the notification itself. I have two other tables to keep track of who gets certain notifications. There are two types of notifications, user and global. A global notification goes to all users while a user notification only goes to specific users. For this I have two tables, notifications_users and notifications_global. The table structures are below:
notifications (id, title, url, start, end)
notifications_users (notification_id, user_id, viewed)
notifications_global (notification_id, user_id, viewed)
What I want to do is to grab the notification title and url (from notifications table) along with the viewed value for all notifications that go to a specific user from both notifications_users and notifications_global tables. Would a UNION query be the best option here? I thought about just separating the queries but then I have two different arrays to loop through in my PHP script which I do not want. There has to be a way to grab all of this data with one query into one array. The following gives me an empty set:
SELECT notification.title, notification.url
FROM notifications
RIGHT JOIN notifications_users ON notifications.id = notifications_users.notification_id
RIGHT JOIN notifications_global ON notifications.id = notifications_global.notification_id
WHERE notifications_users.user_id = 11508 AND notifications_global.user_id = 11508;

SELECT a.title, a.url, b.viewed as 'User View', c.viewed as 'Global View'
FROM Notifications a
INNER JOIN Notifications_Users b
ON a.id = b.notification_id
INNER JOIN Notifications_Global c
ON b.notification_id = c.notification_id
WHERE b.user_id = 11508 and c.user_id = 11508

I think I might have been over-complicating this a bit. I just wrote the two queries for each table separate and then put UNION between them. If anyone is interested, here is what I ended up going with.
(SELECT notification_id, notification_title, notification_url, tbl_notifications_users_lookup.viewed
FROM tbl_notifications
INNER JOIN tbl_notifications_users_lookup ON tbl_notifications.id = tbl_notifications_users_lookup.notification_id
WHERE tbl_notifications_users_lookup.user_id = 11508)
UNION
(SELECT notification_id, notification_title, notification_url, tbl_notifications_global_lookup.viewed
FROM tbl_notifications
INNER JOIN tbl_notifications_global_lookup ON tbl_notifications.id = tbl_notifications_global_lookup.notification_id
WHERE tbl_notifications_global_lookup.user_id = 11508);

Related

MySQL join 2 tables and result should be IN third_table

I got 3 tables: requests, d_requests (delivery requests) and s_requests (send requests).
part of "d_requests" and "s_requests" is always the same (userID, ticket_creation_date and some other data). So it was chunked from these tables and put to "requests" upon each insert to db.
Now I need to do following: JOIN requests and d_requests selecting some data, and then I need to make sure that such selection is IN s_requests' column "send_before"
SELECT r.type, r.request_from, r.request_to, d.departure_date
FROM requests as r
JOIN d_requests as d ON r.request_id = d.requests_id
WHERE r.type='d' AND r.request_from='Beijing'
AND r.request_to='Tokyo' AND d.departure_date
IN (SELECT s.s_before from s_requests s where s.s_before<='user_defined_date')
ORDER BY d.departure_date
I have a result, but it's partial. As I see from the DB, it should give me some several rows of output while it only generates a table with 1 row. Even if I set "user_defined_date" to something like 2025-12-12, output is still 1 row (while all tickets are in 2017 and early 2018).
I think you might need something like this
SELECT r.type, r.request_from, r.request_to, d.departure_date
FROM requests as r
INNER JOIN d_requests as d ON r.request_id = d.requests_id
INNER JOIN s_requests as s ON r.request_id = s.requests_id
WHERE r.type='d' AND r.request_from='Beijing'
AND r.request_to='Tokyo' AND s.s_before<='user_defined_date'
ORDER BY d.departure_date
But it's quite difficult to make suggestions when I don't know the full schema of those table, and what it is you're trying to achieve.

SQL query that returns all the ids without a certain value

I have a table that maps User and Feature. Basically what features are enabled for each user. The table is |userId|featureId| with one(user) to many(feature) relationship.
I would like to create a query that takes a list of userIds and returns the list of userIds that are missing a specific feature.
Meaning I need to make sure that every id has a specific featureId.
userId featureId
1 A
1 B
2 A
3 C
4 D
3 A
So in this example, I'll get the list of ids (1, 2, 3, 4) and a featureId A and the query will return one row with userId 4 since it's the only userId with the feature A enabled.
To find a list of users that don't have feature X I would left join to the list of users that has that feature and return the ones not there. Like this:
SELECT *
FROM table_you_did_not_name as base
LEFT JOIN (
SELECT DISTINCT userID
FROM table_you_did_not_name
WHERE feature = 'X'
) as sub ON base.userID = sub.userID
WHERE sub.userID is null
I think I may have answered a different question: this doesn't address your data; but I'm unsure how you determine it is user 4 you want returned. as each user is missing some of the features the others have. Perhaps we just need to add a where clause below for the specific feature(A) in your example?
Think of data in terms of sets
You need
a set of data for all users (User or something)
a set of data for all features (feature)
and what features a user has (User_Feature)
Then you need to
Generate a set of every feature to every users (cross join)
Identify which of those the user has identified. (left join in user_feature)
and then only keep those where no feature has been identified (where no record in user_feature)
One method: This basically says return the features for each user that exist in a feature list, but have not been associated to a user.
SELECT U.userID, F.FeatureID as FeatureIDMissing
FROM USER U
CROSS JOIN FEATURE F
LEFT JOIN UserFeature UF
on U.UserID = UF.UserID
and F.FeatureID = UF.FeatureID
WHERE UF.UserID is null
-- and F.FeatureID = 'A' --maybe add this?
Alternate method: (combine two steps (2,3) by simply excluding those features which already exist for the user.
In english this says, return all the features for each user for which a user has not been associated
SELECT U.userID, F.FeatureID as FeatureIDMissing
FROM USER U
CROSS JOIN FEATURE F
WHERE not exists (SELECT *
FROM userFeature UF
WHERE U.UserID = UF.UserID
and F.FeatureID = UF.FeatureID)
--and F.FeatureID = 'A' --maybe add this?
Either answer should return the same results. It's a matter of preference database and performance .. Look at the execution plans to help decide which is best for you and your data.
Now maybe you mean you give a list of userID's you want to generate a unique set of features for all those users, and then return users w/o those features. If so instead of a cross join to feature you just need to use (Select distinct FeatureID from userFeatures where UserID IN ('yourListHere') this will generate a unique set of features for those users and identify which users are missing certain features shared with that set of users.
So...
SELECT U.userID, F.FeatureID as FeatureIDMissing
FROM USER U
CROSS JOIN (SELECT distinct FeatureID
FROM userFeatures
WHERE UserID IN ('yourListHere')F
LEFT JOIN UserFeature UF
on U.UserID = UF.UserID
and F.FeatureID = UF.FeatureID
WHERE UF.UserID is null
-- and F.FeatureID = 'A' --maybe add this?
as an example.

SQL - How would I select data involving multiple tables?

RDMS: MySQL
I'm designing a private messaging thread system and have the following schema laid out for the messaging system (simplified). I need to select every conversation that an account with a known ID (we'll say '1') is in, the last message that was sent in the conversation, and the username of the other account that account '1' is chatting with. Grouped by the conversation id. (imagine an inbox page like Facebook has)
My SQL knowledge is limited (I'm going to spend the weekend advancing it) but it the meantime does anyone have a query that would get the job done?
Thanks in advance. Let me know if you need more info.
Schema
EDIT: I believe I may have found a solution (still needs proper testing).
I'm still accepting answers as I'm not sure this is the best route to go yet. I've reviewed the current answers posted and they are getting me in the right direction but so far aren't returning the right results.
Here's what I've come up with.
SELECT account_has_conversations.account_id, messages.*, account.username FROM messages, account, account_has_conversations WHERE account_has_conversations.conversation_id IN
(
SELECT account_has_conversations.conversation_id FROM account_has_conversations WHERE account_has_conversations.account_id = '1'
)
AND account_has_conversations.account_id != '1' AND account_has_conversations.account_id = account.account_id
AND account_has_conversations.conversation_id = messages.conversation_id
GROUP BY account_has_conversations.conversation_id
(I probably should have created some aliases haha)
First, you don't have any text fields in any of the tables in your pictured schema - so I don't know where you're storing the actual message content or how to retrieve it. That said, this query should work for you and you can just add the field that you need.
This will get you the username, the last message received, and the sending username for all of the selected user's conversations. I will ASSUME the message content is in the messages table and is stored in a field called "message_text":
DECLARE #AccountID int = 1 //For testing, but this should be parameterized
SELECT A.username as [User],
A2.username as [SendingUser]
M.message_text as [LastMessage]
FROM account A
JOIN account_has_conversations AHC
ON A.account_id = AHC.account_id
JOIN conversation C
ON AHC.conversation_id = C.conversation_id
JOIN
(
Select
sender_account_id,
conversation_id,
message_text,
row_number() over(partition by conversation_id order by time_sent desc) as rn
FROM messages
) as M
ON C.conversation_id = M.conversation_id
AND M.sender_account_id <> #AccountID //This prevents a circular join in case the last message was sent by the user. You may not need this, but it's impossible to tell based on the information you provided.
JOIN account A2
ON M.sender_account_id = A2.account_ID
WHERE rn = 1
AND A.account_id = #AccountID
I have tested this, but maybe this will get you in the right direction.
Select m.*
from account a
inner join account_has_conversation ahc
on (a.account_id = account_id)
inner join messages m
on (ahc.conversation_id = m.conversation_id)
where a.account_id = 1
I would add:
Your conversation table doesn't really seem to have any value.
Group By is great, but you're going to need to squeeze all your results down as well. By that I mean that if you want to squish ten rows into one (grouping by one of the columns where all the rows have the same value in that column), you'll need to have all the other columns in an aggregate function.
Based on the new schema as below.
messages(msg_id, con_id, message, time_sent, status)
acc(acc_id, username)
con(con_id)
conrel(con_id, acc_id)
(typing from phone so ignore any smaller mistakes).
SELECT distinct msg.message, conrel.con_id, acc.username FROM messages AS msg INNER JOIN conrel ON conrel.con_id = msg.con_id INNER JOIN acc ON conrel.acc_id=acc.acc_id where conrel.con_id IN (SELECT DISTINCT con_id FROM connrel WHERE acc_id=1) ORDER BY msg.time_sent DESC
The solution can further be improved or changed according to needs. Plus remember to use coma separated result of INNER query above instead as INNER queries are slower but when they are passed with IDs like 1,4,5 they become fast.
SELECT a.username, m.*, c.* FROM account a, messages m, conversation c
WHERE a.account_id = m.sender_account_id AND m.conversation_id = c.conversation_id

query that will pull data where a certain document type is not found

I am writing a query for a health organization. The query is to pull patient data, where an encounter/appointment was completed but a chart note was not generated. I have the query pulling patients and their appointments; is there a way to basically say "only show the patients where 'master_im' document was not generated"?
I am using Microsoft SQL Server Management Studio.
Without seeing your table structures, etc. your could do a check to see if the master_im IS NULL.
SELECT *
FROM yourTable
WHERE appointment = 'completed'
AND master_im IS NULL
I would advise posting some additional details on your tables.
If the data is stored in separate tables, then you will want to JOIN the tables together to get the results you want.
EDIT #1 based on your comment you could do something like this:
select *
from person p
inner join appointments a
on p.enc_id = a.encid
left join patient_documents pd
on p.enc_id = pd.enc_id
where a.status = 'completed'
and pd.document_desc != 'master_im'

MySQL - Join a table twice with a main table

I'm not sure if this can be done. But I just wanted to check with the experts out here.
My case is:
I have a table tbl_campaign which basically stores a campaigns which has a one to many relation with a table called tbl_campaign_user where the users that were selected during the campaign are stored along with the campaign id (tbl_campagin_user.cu_campaign_id = tbl_campaign.campaign_id ).
The second table (tbl_campaign_user) has a status field which is either 0 / 1 denoting unsent/sent. I wanted to write a single sql query which would read the campaign data as well as the number of sent and unsent campaign users (which is why I'm joining twice on the second table).
I tried this below, but I get the same number of count as sent and unsent.
SELECT `tbl_campaign`.*,
COUNT(sent.cu_id) as numsent,
COUNT(unsent.cu_id) as num_unsent FROM (`tbl_campaign`)
LEFT JOIN tbl_campaign_user as sent on (sent.cu_campaign_id = tbl_campaign.campaign_id and sent.cu_status='1')
LEFT JOIN tbl_campaign_user as unsent on (unsent.cu_campaign_id = tbl_campaign.campaign_id and unsent.cu_status='0')
WHERE `tbl_campaign`.`campaign_id` = '19'
I tried debugging by breaking the query into two parts:
=>
SELECT `tbl_campaign`.*,
COUNT(unsent.cu_id) as num_unsent FROM (`tbl_campaign`)
Left join tbl_campaign_user as unsent on (unsent.cu_campaign_id = tbl_campaign.campaign_id and unsent.cu_status='0')
WHERE `tbl_campaign`.`campaign_id` = '19'
The above works exactly as wanted. And so does the one below:
=>
SELECT `tbl_campaign`.*,
COUNT(sent.cu_id) as numsent FROM (`tbl_campaign`)
Left join tbl_campaign_user as sent on (sent.cu_campaign_id = tbl_campaign.campaign_id and sent.cu_status='1')
WHERE `tbl_campaign`.`campaign_id` = '19'
I am not sure what I've been doing wrong while merging the two. I know I don't know much about joins so possibly a conceptual error? Please could anyone help me?
Thx in advance!
You only need to join tbl_campaign_user once and
count (sum, whatever) how many times cu_status was zero/one.
SELECT `tbl_campaign`.id,
count(u.id) as num_all_campaign_users
sum(u.cu_status) as num_sentcampaign_users,
count(u.id) - sum(u.cu_status) as num_unsent_campaign_users
FROM `tbl_campaign` c
LEFT JOIN tbl_campaign_user as u on (u.cu_campaign_id = c.campaign_id)
WHERE `tbl_campaign`.`campaign_id` = '19'
group by `tbl_campaign`.id
Note that this is sort of pseudo code, you may have to elaborate
the sum/count in the select clause and the group by clause as well.