MYSQL query that orders data by timestamps contained in two tables - mysql

I'm trying to build a query that will order results by date and time, but the date and time upon which I want to base the ordering is contained in two tables. Additionally, I'd like to group the data by loc_id for rows having the same date.
For example, if I had this data:
Notes:
- parent_id links to the id in TABLE A.
- there are two other tables USER and LOC which are used to get the user name and location name.
- comments are optional in TABLE A, but will always exist in TABLE B.
I've found a lot of info on this site that's close to what I need, but nothing that's quite helped me solve this issue.
Thanks.

SELECT a.`date`
, a.`time`
, l.loc_name AS l_name
, a.rating
, a.comment
, u.user_name
FROM table_a AS a
LEFT JOIN loc AS l
ON l.loc_id = a.loc_id
LEFT JOIN `user` AS u
ON u.user_id = a.user_id
UNION ALL
SELECT b.`date`
, b.`time`
, l.loc_name AS l_name
, NULL AS rating
, b.comment
, u.user_name
FROM table_b AS b
LEFT JOIN table_a AS a
ON a.id = b.parent_id
LEFT JOIN loc AS l
ON l.loc_id = a.loc_id
LEFT JOIN `user` AS u
ON u.user_id = b.user_id
ORDER BY `date`
, l_name
, `time`

Something like this?
(Assumed your users table was called users and your locations table locations.)
SELECT a.date, a.time, l.loc_name, a.rating, b.comment, u.user_name
FROM table_a a
JOIN locations l ON l.loc_id = a.loc_id
LEFT JOIN table_b b ON b.parent_id = a.id
RIGHT JOIN users u ON a.user_id = u.user_id
GROUP BY a.loc_id
ORDER BY a.date DESC, a.time DESC

Related

mysql query takes to much time

I want to optimize this query becouse it takes to much time to return records
SELECT
u.*,
s.legal_name AS structure_name,
ui.id AS userinfo_id,
ui.structure_id AS structure_id,
ui.lrn_user,
ui.gender,
ui.fiscal_code,
ui.prov,
ui.phone,
ui.school_name,
ui.school_codice_meccanografico,
us.status, us.date AS status_date,
CONCAT(u.lastname,' ',u.firstname) AS fullname,
CONCAT(u.firstname,' ',u.lastname) AS display_name,
uu.username AS created_by_name,
g.group_names,
IF(u.website_id = 0,'Sito Web principale', w.name) AS website_name
FROM fcf_users AS u
LEFT JOIN (
SELECT
gu.user_id,
GROUP_CONCAT(gg.name SEPARATOR ', ') AS group_names
FROM fcf_user_user_groups gu
JOIN fcf_user_groups gg ON gg.id = gu.group_id
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN fcf_users_userinfo AS ui ON ui.user_id = u.id
LEFT JOIN fcf_users_user_statuses AS us ON us.user_id = u.id
LEFT JOIN fcf_structures_structures AS s ON s.id = ui.structure_id
LEFT JOIN fcf_users AS uu ON uu.id = u.created_by
LEFT JOIN fcf_websites AS w ON w.id = u.website_id
WHERE
u.id IN (SELECT user_id FROM fcf_user_user_groups WHERE group_id = '8')
AND u.id IN (SELECT user_id FROM fcf_user_user_groups WHERE group_id = '8')
AND ui.lrn_user = '0'
ORDER BY fullname ASC
LIMIT 0,25
If anyone can help, thanks
Turn it inside-out. That is, first use a 'derived' table to locate 25 users you want. Then gather the rest of the info.
What you have gathers all the info (including all the JOIN work) for all the users, then sorts and peels off 25.
It will be something like:
SELECT -- lots of stuff
FROM ( SELECT u.id,
CONCAT(u.lastname,' ',u.firstname) AS fullname
FROM fcf_users AS u
JOIN fcf_user_user_groups AS ug ON ...
JOIN fcf_users_userinfo AS ui ON ui.user_id = u.id
WHERE ug.group_id = '8'
AND ui.lrn_user = '0'
ORDER BY u.lastname, u.firstname -- now sargeable
LIMIT 25
) AS u25
JOIN .... -- whatever tables are needed to get the rest of the columns
ORDER BY u25.fullname -- yes, again, but now using the CONCAT
-- no limit here
Also:
u: INDEX(lastname, firstname, id)
user_user_group is a "many-t0=many mapping" table? If so, follow the indexing advice here: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
Ditto for any other many:many tables.
Note how I put into the derived table only the tables needed to achieve the LIMIT.

How to run nested left join in mysql

I have three tables, likes, users and statuses. So I am returning from likes tables and joining likes table with user table to see who liked it and joining likes table with statuses table to see who posted it.
Now want to get the user information from status table to see who wrote the status. Here comes the problem.
SQL Fiddle http://sqlfiddle.com/#!9/d0707b/2
My current query
select l.*, s.* , a.id as aid, a.userName from likes l
left join
statuses s on l.source_id = s.id
left join
users a on l.user_id = a.id
where
l.user_id in (5,7)
or
(s.privacy='Public' and s.interest in ('mobile', 'andriod') )
order by l.id desc
Here s.user_id=a.id I want to join the statuses table with user table.
[If question is not clear please comment, will try to edit]
Thank you.
You have to make a join to the user table again. Take a look here:
SELECT
l.*, s.*,
a.id AS aid,
a.userName,
b.userName
FROM
likes l
LEFT JOIN statuses s ON l.source_id = s.id
LEFT JOIN users a ON l.user_id = a.id
LEFT JOIN users b ON s.user_id = b.id
WHERE
l.user_id IN (5, 7)
OR (
s.privacy = 'Public'
AND s.interest IN ('mobile', 'andriod')
)
ORDER BY
l.id DESC

MYSQL: Handling Multiple LEFT JOINS

I have a query with one LEFT JOIN that works fine. When I add a second LEFT JOIN to a table with multiple records per field in the first table, however, I am getting the product of the results in the two tables ie books x publishers returned. How can I prevent this from happening?
SELECT a.*,b.*,p.*, group_concat(b.id as `bids`)
FROM authors `a`
LEFT JOIN books `b`
ON b.authorid = a.id
LEFT JOIN publishers `p`
on p.authorid = a.id
GROUP by a.id
EDIT:
Figured it out. The way to do this is to use subqueries as in this answer:
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;
If you do multiple LEFT Joins, your query will return a cartesian product of the results. To avoid this and get only one copy of fields you desire, do a subquery for each table you wish to join as below. Hope this helps someone in the future.
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;

Selecting All Rows in SQL, With Specific Criteria

I have a fairly complicated SQL statement I am working on. Here is where I am at:
SELECT c.category
FROM master_cat as c
LEFT JOIN
(
SELECT cat_id, user_id COUNT(cat_id) favoriteCat
FROM ratings
GROUP BY user_id
) a ON a.cat_id= c.cat_id
LEFT JOIN users AS u
ON u.user_id AND a.user_id
WHERE u.username = '{$user}' LIMIT 1
This statement is incomplete. I am missing a middle table here. cat_id is not actually in ratings. But items_id is from a table called items and cat_id is also in that table as well.
So what I am trying to do is this:
SELECT rating FROM ??? GROUP BY cat_id where u.user=$user
The only thing I can think of doing maybe is another LEFT join with items inside favoriteCat but I am not sure if that is allowed.
I was overthinking this, here is my final solution:
SELECT c.category, count(r.rating) AS totalCount
FROM ratings as r
LEFT JOIN items AS i
ON i.items_id = r.item_id
LEFT JOIN users AS u
ON u.user_id = r.user_id
LEFT JOIN master_cat AS c
ON c.cat_id = i.cat_id
WHERE r.user_id = '{$user_id}'
GROUP BY c.category
ORDER BY totalCount DESC

MySQL query optimization

Just wondering what's a better way to write this query. Cheers.
SELECT r.user_id AS ID, m.prenom, m.nom
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id
LEFT JOIN `0_user` AS m ON r.user_id = m.id
WHERE r.section_id = $section_id
AND l.rank = '$rank_name' AND depart_id IN
(SELECT depart_id FROM 0_depart WHERE user_id = $user_id AND section_id = $section_id)
GROUP BY r.user_id
Here are the table structures:
0_rank: id | section_id | rank_name |
other_stuffs
0_user: id | prenom | nom | other_stuffs
0_right: id | section_id | user_id |
rank_id | other_stuffs
0_depart: id | section_id | user_id | depart_id
| other_stuffs
The idea is to use the same in a function like:
public function usergroup($section_id,$rank_name,$user_id) {
// mysql query goes here to get a list of appropriate users
}
Update: I think I have not been able to express myself clearly earlier. Here is the most recent query that seems to be working.
SELECT m.id, m.prenom, m.nom,
CAST( GROUP_CONCAT( DISTINCT d.depart ) AS char ) AS deps,
CAST( GROUP_CONCAT( DISTINCT x.depart ) AS char ) AS depx
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id
LEFT JOIN `0_member` AS m ON r.user_id = m.id
LEFT JOIN `0_depart` AS d ON m.id = d.user_id
LEFT JOIN `0_depart` AS x ON x.user_id = $user_id
WHERE r.section = $section_id
AND l.rank = '$rank_name'
GROUP BY r.user_id ORDER BY prenom, nom
Now I want to get only those result, where all entries of deps are present in entries in depx.
In other term, every user is associated with some departs. $user_id is also an user is associated with some departs.
I want to get those users whose departs are common to the departs of $user_id.
Cheers.
Update
I'm not sure without being able to see the data but I believe this query will give you the results you want the fastest.
SELECT m.id, m.prenom, m.nom,
CAST( GROUP_CONCAT( DISTINCT d.depart ) AS char ) AS deps,
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id and r.user_id = $user_id
LEFT JOIN `0_member` AS m ON r.user_id = m.id
LEFT JOIN `0_depart` AS d ON m.id = d.user_id
WHERE r.section = $section_id
AND l.rank = '$rank_name'
GROUP BY r.user_id ORDER BY prenom, nom
Let me know if this works.
Try this:
(By converting the functionality of the IN (SELECT...) to an inner join, you get exactly the same results but it might be the optimizer will make better choices.)
SELECT r.user_id AS ID, m.prenom, m.nom
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id and r.section_id = 2
LEFT JOIN `0_user` AS m ON r.user_id = m.id
INNER JOIN `0_depart` AS x ON l.section_id = x.section_id and x.user_id = $user_id AND x.section_id = $section_id
WHERE l.rank = 'mod'
GROUP BY r.user_id
I also moved the constraints on 0_right to the join statement because I think that is clearer -- presumably this change won't matter to the optimizer.
I know nothing about your DB structure but your subselect looks like it can be replaced with a simple INNER JOIN against whatever table has the depart column. MySQL is well known for its poor subquery optimization.
Without knowing the structures or indexes, I would first add "STRAIGHT_JOIN" if the critical criteria is in-fact from the 0-rank table. Then, ensure 0_rank has an index on "rank". Next, ensure the 0_right has an index on rank_id at a minimum, but rank_id, section to take advantage of BOTH your criteria. Index on 0_member on id.
Additionally, do you mean left-join (ie: record only required in the 0_rank or 0_member) on the respective 0_right and 0_member tables instead of a normal join (where BOTH tables must match on their IDs).
Finally, ensure index on the depart table on user_id.
SELECT STRAIGHT_JOIN
r.user_id AS ID,
m.prenom,
m.nom
FROM
0_rank AS l
LEFT JOIN `0_right` AS r
ON l.id = r.rank_id
AND r.section = 2
LEFT JOIN `0_member` AS m
ON r.user_id = m.id
WHERE
l.rank = 'mod'
AND depart IN (SELECT depart
FROM 0_depart
WHERE user_id = 2
AND user_sec = 2)
GROUP BY
r.user_id
---- revised post from feedback.
From the parameters you are listing, you are always including the User ID... If so, I would completely restructure it to get whatever info is for that user. Each user should apparently can be associated to multiple departments and may or may NOT match the given rank / department / section you are looking for... I would START the query with the ONE USER because THAT will guarantee a single entry, THEN tune-down to the other elements...
select STRAIGHT_JOIN
u.id,
u.prenom,
u.nom,
u.other_stuffs,
rank.rank_name
from
0_user u
left join 0_right r
on u.id = r.user_id
AND r.section_id = $section_id
join 0_rank rank
on r.rank_id = rank.id
AND rank.rank_name = '$rank_name'
left join 0_dept dept
on u.id = dept.user_id
where
u.id = $user_id
Additionally, I have concern about your table relationships and don't see a legit join to the department table...
0_user
0_right by User_ID
0_rank by right.rank_id
0_dept has section which could join to rank or right, but nothing to user_id directly
Run explain on the query - it will help you find where the caveats are:
EXPLAIN SELECT r.user_id AS ID, m.prenom, m.nom
FROM 0_rank AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id
LEFT JOIN `0_member` AS m ON r.user_id = m.id
WHERE r.section = 2
AND l.rank = 'mod' AND depart IN
(SELECT depart FROM 0_depart WHERE user_id = 2 AND user_sec = 2)
GROUP BY r.user_id\G