I'm having a bit of an issue regarding multi-querying with PHP. My database has the following tables, with their schema shown below:
First table - Users
(PK) UserID,
UserFullName
UserID FullName
1000 Arthur Whitney
2000 Alex Schwartz
3000 Eva Kilpatrick
Second table - Invites
(PK) RowID,
InvName, InvSender, InvSenderTotal, InvReceiver, InvReceiverTotal, InvStatus
RowID InvName InvSenderID InvSenderTotal InvReceiverID InvReceiverTotal InvStatus
1 Fair 1000 10 2000 10 Sent
2 Party 2000 45 1000 45 Sent
3 Cinema 2000 12 1000 12 Sent
4 Vacation 3000 15 1000 5 Expired
Say for example when user Alex Schwartz, with unique ID 2000, is logged in, I would like to have it that he can see the name of the invite, the invite sender's name (if he is the invite receiver), the invite receiver's name (if he is the invite sender) and both the invite sender and receiver's totals.
I have devised the following two queries, which yield the results that I intended:
QUERY1
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvReceiverID
WHERE i.InvStatus = 'Sent' AND i.InvSenderID = 2000
This shows me the Invite Name, Invite Sender and Receivers' totals and the Invite Receiver's name when Alex Schwartz was sending the invite. Expected output here would be specific details of Row IDs 2 and 3.
The following query shows the inverse:
QUERY2
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvSenderID
WHERE i.InvStatus = 'Sent' AND i.InvReceiverID = 2000
So this enables Alex Schwartz to see the name of the person who sent the invite, along with the same data. Expected output here would be specific details of Row ID 1.
Hence, I am able to get the data I want, but I require two different queries in order to get it. This has presented problems in PHP whereby only one query seems to carry out. So my question is essentially, does anyone know how I could combine these two queries into one? I am also aware of multiQuerying and have tried to implement this, but have not had much success with that either.
Thanks in advance of your help.
Why not use UNION.
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvReceiverID
WHERE i.InvStatus = 'Sent' AND i.InvSenderID = 2000
UNION
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName FROM Invites as i INNER JOIN Users as u ON u.UserID = i.InvSenderID
WHERE i.InvStatus = 'Sent' AND i.InvReceiverID = 2000
As a note, you can do this without union all (which is preferred over union unless you intentionally need to remove duplicates):
select i.InvName, i.InvSenderTotal, i.InvReceiverTotal, u.FullName
FROM Invites i INNER JOIN
Users u
ON u.UserID IN (i.InvReceiverID, i.InvSenderID)
WHERE i.InvStatus = 'Sent' AND i.InvSenderID = 2000;
Related
I have users table and also articles tables. Article table contains articles submitted by users. I am working on a sql query to display random 4 users with more than 5 articles. user_id is stored in articles table. I have searched around in stackoverflow and google even though there are some similar questions, i couldn't find anything specific to mine.
Can anyone let me know if this question has been answered before and give me a link if yes otherwise I have the following query:
SELECT *
FROM users WHERE type = 3
INNER JOIN articles ON
users.user_id = articles.user_id HAVING COUNT(user_id) > 5
This doesn't seem to work. I will appreciate any help to improve this query.
Database table is as follows:
USERS:
user_id
username
email
type
ARTICLES:
id
user_id
title
For example, total user count is 100. User with user_id 49 has 10 articles, and another user with user_id 50 has 20 articles and the rest of the users have less than 5 articles. So the query should return only the user 49 and 50.
Hope this makes sense.
regards
I've mocked up some table data to test my query. WHERE clauses must be positioned after JOINs. You are also a little ambiguous about the comparison of COUNT AND 5 -- if you want more than 5 then >5, if you want 5 or more then >=5.
SQL: (SQLFiddle Demo)
SELECT a.user_id,a.username,COUNT(b.user_id)
FROM users a
INNER JOIN articles b ON a.user_id=b.user_id
WHERE a.type=3
GROUP BY a.user_id
HAVING COUNT(b.user_id)>5
ORDER BY RAND()
LIMIT 4
You have where join and having in wrong position, you missed group by for a correct functioning of having
and you need an order by rand and limit 4
SELECT u.user_id
FROM users u
INNER JOIN articles a ON u.user_id = a.user_id
WHERE a.type = 3
group by u.user_id
HAVING COUNT(a.user_id)>= 5
order by rand() limit 4
I have search but i dont solve my problems.
I have a many to many relationship.
3 tables:
groups_mail | groups_mail_j (joint table) | mails
id, name | mail_id, groupe_id | id, name
I dont know how select all the emails for my groupe ID=1
Thx.
Your question is not clear. Anyways this is what I have understood based on the question.
Your having 3 tables mails, groups_mail, groups_mail_j
You want to join all the tables and get all the records based on mails-> id column.
If above is the required on than you can do that by the following
SELECT m.id mailid, m.name AS email, gm.name AS groupname, gm.id AS groupmailid
FROM mails AS m
INNER JOIN groups_mail_j AS gmj ON gmj.mail_id = m.id
INNER JOIN groups_mail AS gm ON gm.id = gmj.groupe_id
WHERE m.id = 1
Here I have used the custom selectors. You can use any selector ie specific column of tables by using the alias of that table.
Okay so in-case you want to show all the mail-ids for the selected group then use the following query. No need to put the mail_id because if you knew mail id then you wouldn't have to query again.
SELECT m.name AS email
FROM mails AS m
INNER JOIN groups_mail_j AS gmj ON gmj.mail_id = m.id
INNER JOIN groups_mail AS gm ON gm.id = gmj.groupe_id
WHERE gmj.groupe_id = 1
Here 1 in hard coded in case you can user $group_id
Please check your spelling whether is group_id or groupe_id
I'm not a database professional, but currently working on one query (PHP->MySQL):
I have 3 tables:
'Items': id, name, link
'ItemsToUsers': id, item_id, user_id
'Users': id, email
Each 'Item' availability is submitted to regular changes which I check on fly by some algorithm.
My goal is to
1) SELECT all Items and check on fly if they are available
2) If Item is available, notify users who are monitoring it by email. For that I need to SELECT users from 'ItemsToUsers' and then get their emails from Users table.
I know how to do it in a straightforward way, but I feel that I will fall into running to many queries. (individual SELECT for every user...)
Is there a way to do it more efficiently: in one query or by changing the algorithm?
Thank you for your time!
There's not enough information to determine how an item is available. This severely impedes the ability to query item 2.
That said, let's suppose we add a "available" column to the Items table that is a tinyint of 0 for not available, 1 for available.
A query, then, which would get all email addresses for persons watching items that are available is:
SELECT u.email FROM Users AS u JOIN ItemsToUsers AS k ON k.user_id = u.id JOIN Items AS i on i.id = k.item_id WHERE i.available = 1;
Alternatively, you could use a subquery and IN.
Let's suppose you have a different table called Availability with the columns id, item_id and available, which again is a tinyint containing a 1 for available and 0 for not available.
SELECT u.email FROM Users AS u JOIN ItemsToUsers AS k ON k.user_id = u.id WHERE k.item_id IN (SELECT a.item_id FROM Availability AS a WHERE a.available = 1);
Again, without an idea of how you are getting a list of available products, it is impossible to optimize your queries for retrieving a list of email addresses.
Your steps allude to doing this in n+1 queries (where n = number of entries in the Items table):
SELECT * FROM Items; -- This is the +1 part
While iterating over that result set, you intend to determine if it's available and, if it is, to notify users who are watching it. Assuming you have a given item id and you want to select all users' email if that product id is active, then you could do this:
SELECT email FROM Users u
INNER JOIN ItemsToUsers iu ON iu.user_id = u.id
INNER JOIN Items i ON iu.item_id = i.id
WHERE i.id = {your item id}
You would be running this query for every item in your table. This is the n part.
In general you could instead generate a list of emails for all users who are watching all products that are active, after you have already determined which ones should be active:
SELECT DISTINCT email FROM Users u
INNER JOIN ItemsToUsers iu ON iu.user_id = u.id
INNER JOIN Items i ON iu.item_id = i.id
WHERE i.is_active = 1
This will get the job done in a total of 2 queries, regardless of how many users or items you have. As a bonus, this one can give you distinct emails, whereas the first solution would still need application-level code to remove duplicates returned by the multiple queries.
SELECT Items.id, Items.name, Items.link FROM Items
INNER JOIN ItemsToUsers ON ItemsToUsers.item_id = Items.items.id
INNER JOIN Users ON ItemsToUsers.user_id = Users.id ;
//USER TABLE
user_id name
1 ben
2 alex
3 john
//CONVERSION TABLE
c_id user_one user_2
1 2(alex) 1(ben)
2 2(alex) 3(john)
3 1(ben) 3(john)
//MESSAGE TABLE
m_id c_id send receive message
1 1 2(alex) 1(ben) hi ben
2 1 2(alex) 1(ben) ben, u there?
3 2 1(ben) 3(john) whatever...
//QUERY 1
SELECT * FROM conversion WHERE user_one=1(ben)
OR user_two=1(ben)
So now i know ben have 2 conversations (one with alex another with john)
my question is
how to join 3 tables and fetch out like this
conversation_1 - Alex(id=2) - Last message in cv_1(ben, u there?)
conversationi_3 - John(id=3) - Last message in cv_3(whatever...)
like facebook message
The main idea is that you have to use joins. Standard JOIN syntax will be of no help here because you cannot have OR statement in JOIN .. ON. But something like this will do the trick
SELECT c.c_id, u.user_id, u.name, MAX(m_id), message FROM message m, conversation c, user u
WHERE m.c_id = c.c_id
AND
(
c.user_one = u.user_id
OR
c.user_2 = u.user_id
)
GROUP BY c.c_id
Here we join 3 tables together, getting maximum message ID (I assume ID is auto incremental so it is safe to assume that the higher ID the older the message) and group by conversation id. This is how we will have the oldest message and conversation details of messages where Ben (logged in user for instance) was involved
A nice article I saw somewhere on stack overflow before is http://www.khankennels.com/blog/index.php/archives/2007/04/20/getting-joins/. The current approach is INNER JOIN.
SQL Fiddle - http://sqlfiddle.com/#!2/ae6e6/14
I have two table
User
userID username email
1 abc abc#abc.com
2 def def#def.com
3 ghi ghi#ghi.com
Referral
refID userID invEmail
1 1 abc#def.com
2 1 omg#mog.com
3 1 def#def.com
So what i plan is to give a invited user 5 point meanwhile a inviter 10 point, so basically userID 1 gained 30 meanwhile userID 2 gained 5. The point i can do in PHP but one part i faced difficulty is when a invEmail to be identified. I don't mind separating into multiple queries if its will work.
How do i show this in sql?
I tried something like
SELECT *, count(r.userID) FROM user u, referral r WHERE u.userID = r.userID OR u.email = r.invEmail GROUP BY r.userID
It returned wrong value.
What i would like it to return, how much count is there inviter and invitee(matched email who has registered based on inviter invitation)
How should i do it?
Thank you.
Edit: i forgot to add something into question, what if i wanted the inviter to receive 10 points only if invitee registered? what i meant is that, only if invEmail exists in u.email then only userID received 10 point. Sorry for my mistake.
You might be able to do something like this:
select points.userId, points.username, points.email, sum(points.points)
FROM
(select u.*, count(*)*10 as points
from user u
join referral on u.userID = r.userID
join user verify_user on r.invEmail = verify_user.email
group by u.userID, u.username, u.email
UNION
select u.*, count(*)*5 as points
from user u
join referral on u.email = r.invEmail and u.userID != r.userID
group by u.userID, u.username, u.email
) as points
group by points.userId, points.username, points.email
I think you need two separate selects to get the points for each type of registration, combined with a union statement.