MySQL Select From Multiple Tables + Last row from somewhere else - mysql

Hello I have these 4 tables with the structure
Table: Users [ id, username, password, bouquet_id ]
Table: Bouquets [ id, bouquet_name, stream_ids = serialized array ]
Table: Streams [ id, channel_name ]
Table: activity [ id, user_id, stream_id ]
I want to select ALL users but with their info as well from other tables + THE LAST ROW from table activity per user
For example the following query:
SELECT t1.*,t2.`bouquet_name`
FROM `users` t1,`bouquets` t2
WHERE t1.`bouquet_id ` = t2.`id`
ORDER BY t1.id DESC
Takes the data from the first 2 tables and assigned the bouquet_id to its bouquet name.
Now i want to have in query the last ROW from activity table WITH it's stream name [based on stream_id]
The following query does the job i want[ PER USER]
SELECT t1.channel_name
FROM `streams` t1,`activity` t2
WHERE t2.user_id = **'%d'** AND t1.id = t2.stream_id
ORDER BY t2.id DESC
LIMIT 1
But its a kind slow since for every user in the table "users" i run 2 queries.
I want the 2 queries above to be embed together as one so that i will be able to select the data from the first two tables BUT WITH the last row from table activity based on user_id.
Hope you understand me
thank you

You should not depend on "last row" = "row with greatest id". Since MySQL provides the possibility to setup master-master replications, which then offers the possibility to assign auto_increment values in more or less arbitrary order, this assumption is not always true. An additional timestamp column would be better.
You can select the most recent activity per user for example with:
SELECT user_id, MAX(id) FROM activity GROUP BY user_id
You then have to join this into your query and join in the basic activity table to retrieve the data you actually want. You may want to replace your joins with left joins too, so you will always retrieve all users regardless if there exists rows in bouquets or activity:
SELECT t1.*, t2.`bouquet_name`, activity.*
FROM `users` t1
LEFT JOIN `bouquets` t2
ON t1.`bouquet_id ` = t2.`id`
LEFT JOIN (
SELECT user_id, MAX(id) AS maxid FROM activity GROUP BY user_id
) mostrecent
ON t1.user_id = mostrecent.user_id
LEFT JOIN activity
ON t1.user_id = activity.user_id AND activity.id = mostrecent.maxid
ORDER BY t1.id DESC
And then you can join in the stream data too like
SELECT t1.*, t2.`bouquet_name`, activity.stream_id, streams.channel_name
FROM `users` t1
LEFT JOIN `bouquets` t2
ON t1.`bouquet_id ` = t2.`id`
LEFT JOIN (
SELECT user_id, MAX(id) AS maxid FROM activity GROUP BY user_id
) mostrecent
ON t1.user_id = mostrecent.user_id
LEFT JOIN activity
ON t1.user_id = activity.user_id AND activity.id = mostrecent.maxid
LEFT JOIN streams
ON activity.stream_id = streams.id
ORDER BY t1.id DESC

Related

Selecting Counts from Different Tables with a Subquery

I'm new to MySQL, and I'd like some help in setting up a MySQL query to pull some data from a few tables (~100,000 rows) in a particular output format.
This problem involves three SQL tables:
allusers : This one contains user information. The columns of interest are userid and vip
table1 and table2 contain data, but they also have a userid column, which matches the userid column in allusers.
What I'd like to do:
I'd like to create a query which searches through allusers, finds the userid of those that are VIP, and then count the number of records in each of table1 and table2 grouped by the userid. So, my desired output is:
userid | Count in Table1 | Count in Table2
1 | 5 | 21
5 | 16 | 31
8 | 21 | 12
What I've done so far:
I've created this statement:
SELECT userid, count(1)
FROM table1
WHERE userid IN (SELECT userid FROM allusers WHERE vip IS NOT NULL)
GROUP BY userid
This gets me close to what I want. But now, I want to add another column with the respective counts from table2
I also tried using joins like this:
select A.userid, count(T1.userid), count(T2.userid) from allusers A
left join table1 T1 on T1.userid = A.userid
left join table2 T2 on T2.userid = A.userid
where A.vip is not null
group by A.userid
However, this query took a very long time and I had to kill the query. I'm assuming this is because using Joins for such large tables is very inefficient.
Similar Questions
This one is looking for a similar result as I am, but doesn't need nearly as much filtering with subqueries
This one sums up the counts across tables, while I need the counts separated into columns
Could someone help me set up the query to generate the data I need?
Thanks!
You need to pre-aggregate first, then join, otherwise the results will not be what you expect if a user has several rows in both table1 and table2. Besides, pre-aggregation is usually more efficient than outer aggregation in a situation such as yours.
Consider:
select a.userid, t1.cnt cnt1, t2.cnt cnt2
from allusers a
left join (select userid, count(*) cnt from table1 group by userid) t1
on t1.userid = a.userid
left join (select userid, count(*) cnt from table2 group by userid) t2
on t2.userid = a.userid
where a.vip is not null
This is a case where I would recommend correlated subqueries:
select a.userid,
(select count(*) from table1 t1 where t1.userid = a.userid) as cnt1,
(select count(*) from table2 t2 where t2.userid = a.userid) as cnt2
from allusers a
where a.vip is not null;
The reason that I recommend this approach is because you are filtering the alllusers table. That means that the pre-aggregation approach may be doing additional, unnecessary work.

Generating SQL Query

have the following DB structure in MySQL:
Table1: ORDER
With Attributes
ID (PRIMARY KEY)
DELIVERY_DATE
CUSTOMER_ID
RESTAURANT_ID
ORDER_VALUE
Table2 : CUSTOMER
With Attributes
ID (PRIMARY KEY)
NAME
LOGIN
Table 3: Restaurant
ID (PRIMARY KEY)
NAME
CITY
I want to generate a db query where I select the customer_ID, login, Delivery_date, Sequence number, And the number of days from the last order to the second to last order.
I am having trouble using Inner join and generating the last two columns of my query:
Sequence: Each user may have ordered any number of dishes, I want to show a number three if it is the third time he orders, I should be able to get it from his customer_ID and the Order_ID
DateDiff from last order: I am having trouble getting the last and the second to last order and doing a dateDIFF and populating the column
I have so far constructed the following query:
SELECT customer_id,
login,
delivery_date,
sequence,
Datediff(second_to_last_order, last_order)
FROM order AS t1
INNER JOIN customer AS t2
ON t1.customer_id = t2.id
INNER JOIN restaurant AS t3
ON t1.restaurant_id = t3.id;
But it is obviously incomplete but i am stuck, any suggestions?
If this is MySQL Server query as you mentioned then I'd suggest you give the following a go;
SELECT CUSTOMER_ID, LOGIN, DELIVERY_DATE, SEQUENCE, DATEDIFF((SELECT MAX(z.ORDER_DATE) FROM ORDER AS z WHERE z.CUSTOMER_ID = t1.CUSTOMER_ID), (SELECT MAX(x.ORDER_DATE) FROM ORDER AS x WHERE x.CUSTOMER_ID = t1.CUSTOMER_ID AND x.ID != (SELECT MAX(x.ID) FROM ORDER AS x2 WHERE x2.CUSTOMER_ID = t1.CUSTOMER_ID)))
FROM ORDER AS a
INNER JOIN CUSTOMER AS t2 ON t1.CUSTOMER_ID = t2.ID
INNER JOIN RESTAURANT AS t3 ON t1.RESTAURANT_ID = t3.ID
ORDER BY t1;
I would advice that it's good practise to not call a table "ORDER" as this is a keyword used by MySQL, you could instead simple call the table "ORDERS" to avoid the issue.
This should do what you are asking for, if you have any issue please post any error text so we can better understand what you are facing.

MySQL Select Queries Explain

I have a question regarding a select mysql query. Lets say that we have 2 tables
users: [ id, username ]
logs [ id, user_id, action ]
I want to selecr all users from table users with the count(id) of table logs based on user_id
I tried to do the following:
SELECT t1.*,count(t2.`id`)
FROM `users` t1
LEFT JOIN `logs` t2
ON t1.id = t2.user_id;
But that only prints the users who have count logs > 0. I want to select all the users with the count of the logs.
SELECT t1.*,count(t2.`id`)
FROM `users` t1
LEFT JOIN `logs` t2
ON t1.id = t2.user_id
GROUP BY t1.id
Try this query :
SELECT *, (SELECT count(id) FROM `logs` WHERE `logs`.`id`=`id`) scount FROM `users`;
The count value you want will be in a field called scount.

MySQL merging two queries one with group by

I have two tables, one holds user info (id, name, etc) and another table that holds user tickets and ticket status (ticket_id, user_id, ticket_status, etc).
I want to produce a list of ALL the users for example: ( SELECT * FROM user_table )
And for each user I need a count of their tickets for example:
(SELECT t1.user_id, COUNT(*) FROM user_tickets t1 WHERE t1.ticket_status = 15 GROUP BY t1.ticket_status, t1.user_id )
I can do this query to achieve what I’m looking for but it takes 5sec. to run the query on 50000 tickets, while each query running separately only takes fraction of a second.
SELECT t1.user_id, COUNT(*)
FROM user_tickets t1
LEFT JOIN user_table t2 ON t1.user_id = t2.id
WHERE t2.group_id = 20 AND t1.status_id = 15
GROUP BY t1.status_id, user_id
Any idea how to write the query to get same performance as each separately?
An indexing where clause fixed the problem.

SQL - joining data

Is it possible to get 1 result where I require data from 3 tables.
First table: I will need to grab all the fields (1 row found by a primary key)
Second table: I will need to grab the field 'username' (connected to first table by 'master_id')
Third table: I will need to grab the latest added row with the associated master_id key (table has 'date', 'master_id', 'previous_name').
select top 1 first.*, second.username, third.*
from first
inner join second on first.id = second.master_id
inner join third on first.id = third.master_id
order by
third.date desc
As always there are dozens of ways to skin a cat, I'm not sure if this is optimized as the subquery methods, but it should work.
You can join the three tables together. Then, you can use a "filter" join to keep only the latest Table3 row:
select *
from Table1 t1
join Table2 t2
on t2.master_id = t1.master_id
join Table3 t3
on t3.master_id = t1.master_id
join (
select master_id
, max(date) as max_date
from Table3
group by
master_id
) as filter
on t3.master_id = filter.master_id
and t3.date = filter.max_date
You'll need a correlated subquery for that third table.
SELECT t1.*, username, date, previous_name
FROM FirstTable t1
INNER JOIN SecondTable t2 ON t1.master_id=t2.master_id
INNER JOIN
(SELECT master_id, date, previous_name
FROM ThirdTable AS t3_1
WHERE date = (
SELECT MAX(date)
FROM ThirdTable AS t3_2
WHERE t3_2.master_id=t3_1.master_id)) q1 ON q1.master_id=t1.master_id;
NOTE: Untested.