i have the table "user" (Primary key is id) and the table "user_meta" (Primary key is user_id and valid_from).
The user table contains basic user data e.g. username, password, etc.
The user_meta contains possible changing data e.g. lastname, gender(yea its 2018 :D) etc.
So i have a history on which day which data are valid.
My Problem ist that i try to select all user with the currently valid data, but i failed often...
How i can select the correct data ?
For one user i can simply use
"select * from user_meta
JOIN user on user_meta.user_id = user.id
ORDER BY valid_from DESC LIMIT 1"
but how its working with multiple/all users?
greetings,
False
you could use a join on a subselect for max_valid group by user
select * from user_meta
inner join (
select user.id, max(user_meta.valid_from) max_valid
from user_meta
JOIN user on user_meta.user_id = user.id
group by user.id
) t on t.id= user_meta.user_id and t.max_valid = user_meta.valid_from
or more simple
select * from user_meta
inner join (
select user_meta.user_id, max(user_meta.valid_from) max_valid
from user_meta
group by user_meta.user_id
) t on t.user_id= user_meta.user_id and t.max_valid = user_meta.valid_from
You probably want something along these lines:
SELECT u.*, um.*
FROM user u
INNER JOIN user_meta um
ON u.id = um.user_id
INNER JOIN
(
SELECT user_id, MAX(valid_from) AS max_valid_from
FROM user_meta
GROUP BY user_id
) t
ON um.user_id = t.user_id AND
um.valid_from = t.max_valid_from;
Not much to explain here, except that the subquery aliased as t will filter off all metadata records except for the latest one, for each user.
Related
I am trying to retrieve the 'Roles' sets of a given users.id on a query with INNER JOIN combined with a WHERE condition. But things go wrong.
My database has four tables:
t_users : id, username, userpass, status, ...
t_action: id, id_user, id_role, id_type_role, ...
t_role: id, libelle, status
t_type_role: id, libelle, status
My query:
SELECT U.id AS ID, R.libelle AS ROLE, T.libelle AS TYPE
FROM t_user U
JOIN t_action A ON A.id_user = U.id
JOIN t_type_role T ON T.id = A.id_type_role
JOIN t_role R ON R.id = A.id_role
WHERE A.id_user = '1' AND R.libelle = 'System'
But this query returns no data. (Tested on a phpmyadmin SQL board.)
Use:
SELECT u.id AS id,
r.libelle AS role,
t.libelle AS type
FROM users u
JOIN action a ON a.id_user = u.id
JOIN type_role t ON t.id = a.id_type_role
JOIN role r ON r.id = a.id_role
WHERE a.id_user =1
AND t.libelle = 'System';
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=20570938deb2bec281d5070dd28bf19d
Don't put single quotes on integers, change WHERE a.id_user ='1' to WHERE a.id_user = 1.
libelle = 'System' is in the type_role table not in the role table
as Akina has already mentioned in the comment section, there is no "libelle" value in the table "role" which equals 'Système' as you has mentioned it above. That is the reason why you do not get any output. Fix it to 'System' in the MySQL database and try it out again.
I have 3 tables like so
Table 1: UserInfo
user_id userName
123 userOne
Table 2: Post
user_id postContent
123 This is test message
Table 3: LikePost
user_id likesPostId
123 This is test message
I would like to run a query to get total number of post likes, posts, and user information from those 3 tables.
I can do this for each one such as in Post table:
SELECT COUNT(*) FROM Post WHERE Post.user_id = '123'
and SELECT * FROM UserInfo WHERE UserInfo.user_id = '123'
Is anyone have better solution in just 1 query? Thank you so much!
Use a structured query (with subqueries) something like this.
SELECT u.user_id, u.userName, p.num postcount, l.num likecount
FROM UserInfo u
LEFT JOIN (
SELECT COUNT(*) num,
user_id
FROM Post
GROUP BY user_id
) p ON u.user_id = p.user_id
LEFT JOIN (
SELECT COUNT(*) num,
user_id
FROM LikePost
GROUP BY user_id
) l ON u.user_id = l.user_id
What's going on here? The two subqueries, for example
SELECT COUNT(*) num,
user_id
FROM LikePost
GROUP BY user_id
each generate a virtual table with either zero or one row per user_id, showing a count for each user_id. You then join those virtual tables to your UserInfo table.
Use LEFT JOIN because ordinary innner JOIN will suppress users that lack either posts or likes.
Try This
SELECT ui.userName,Count(p.*),
Count(lp.*) as TotalPostLikes
FROM UserInfo ui
INNER JOIN Post p on p.user_id=ui.user_id
INNER JOIN LikePost lp on lp.user_id=ui.user_id
WHERE ui.user_id = '123'
GROUP BY ui.userName
If you want to select Username, Post and Likes on post, try the following
SELECT ui.userName,p.postContent as PostContent,
(SELECT COUNT(lp.user_id) FROM LikePost lp
WHERE lp.user_id=ui.user_id) as Likes,
(SELECT COUNT(_p .user_id) FROM Post _p
WHERE _p .user_id=ui.user_id) as TotalPosts
FROM UserInfo ui
INNER JOIN Post p on p.user_id=ui.user_id
WHERE ui.user_id = '123'
Yes you can do it within one query using leftjoin on Post and LikePost like below
SELECT COUNT(*),User.userName FROM UserInfo as User
leftjoin Post as Post on Post.user_id = User.user_id
leftjoin LikePost as LikePost on LikePost.user_id = User.user_id
where Post.user_id = 123
group by Post.user_id
I have the tables users and statuses . I want to select all the users, plus any statuses they might have, but only the most recent status from each user.
Here is the code that doesn't work:
SELECT users.id, alias, gender, login, logout, users.create_date, statustext as statustxt, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN statuses s ON users.id = s.user_id
WHERE s.ID = (
SELECT MAX(s2.ID)
FROM statuses s2
WHERE s2.user_id = s.user_id
)
This gets the users with the most recent statuses, but not the users from the users table as well. Maybe it can be fixed by some small adjustment?
I got the sub query by searching, but I don't understand how that code works. It seems to compare two versions of the same table (For example: WHERE s2.user_id = s.user_id ) . Where can I read about this sort of technique?
Is a sub query required in this case by the way?
If you can find a solution would be great, and some basic explanation of how it works would highly appreciated.
----------EDIT---------------
I took one of the responses (by maresa) and combined with the sub query of my initial code , and this works(!) It has 3 selects and looks a bit over complicated maybe?:
SELECT users.id, alias, gender, login, logout, users.create_date, statustext as statustxt, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN (
SELECT user_id, statustext FROM statuses s
WHERE s.ID = (
SELECT MAX(s2.ID)
FROM statuses s2
WHERE s2.user_id = s.user_id
)
) as s ON users.id = s.user_id
I've encountered similar problem. This post is relevant: http://www.microshell.com/database/sql/optimizing-sql-that-selects-the-maxminetc-from-a-group/.
Regarding your specific query, since you care only the latest status, you want to first get the latest status from each users. Assuming that the latest status has the latest id (based on your sample), the SQL would be below:
SELECT
MAX(ID), statustext, user_id
FROM
statuses
GROUP BY
user_id
What the above query does is, to get the latest status per user_id. Once you get that, you can think of it as if it's a table. Then simply join on this "table" (the query) instead of the real one (statuses table). Therefore, your query would be like below:
SELECT
users.id, alias, gender, login, logout, users.create_date, statustext as statustxt, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM
users
LEFT JOIN (
SELECT
MAX(ID), user_id
FROM
statuses
GROUP BY
user_id
) as s ON users.id = s.user_id
LEFT JOIN statuses ON statuses.ID = s.ID -- EDIT: Added this line.
You might use a subselect as the join, and limit to show only 1 row:
SELECT
users.id,
alias,
gender,
login,
logout,
users.create_date,
statustext as statustxt,
TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN (
SELECT user_id,statustext
FROM statuses s1
WHERE s1.user_id = users.id
ORDER BY status_date DESC
LIMIT 1
) s ON users.id = s.user_id
You could use a in clause with a tuple
SELECT
users.id
, alias
, gender
, login
, logout
, users.create_date
, statustext as statustxt
, TIMESTAMPDIFF(YEAR,birthdate,CURDATE()) AS age
FROM users
LEFT JOIN statuses s ON users.id = s.user_id
WHERE (s.user_id, s.ID) in (
SELECT user_id, MAX(s2.ID)
FROM statuses s2
group by user_id
)
There are three tables. user, like, comment. Table like and comment has rows associated to user. I need all users with their associated row count from table like and comment. It's easy to do when there is only one table associated. However, here is my query.
SELECT u.id as id, u.display_name as displayName,
COUNT(x.user_id) as likeCount,
COUNT(y.user_id) as commentCount
FROM `user` u
LEFT JOIN
`like` x ON x.user_id = u.id
LEFT JOIN
`comment` y ON y.user_id = u.id
GROUP BY u.id
Table relationships:
One user has many likes
One user has many comments
commentCount is giving correct rows count, but likeCount giving wrong rows count. Please don't post answer which uses sub queries. I want it with only ONE SELECT clause. I am using MySQL. TIA
You can get the user count per individual table, like this:
SELECT user, COUNT(*) AS t1Count
FROM table1
GROUP BY user;
SELECT user, COUNT(*) AS t2Count
FROM table2
GROUP BY user;
Then you can join those two to the Users table to get the count of each. You should use COALESCE() to return null values with 0:
SELECT u.id, COALESCE(t1.t1Count, 0), COALESCE(t2.t2Count, 0)
FROM users u
LEFT JOIN(
SELECT user, COUNT(*) AS t1Count
FROM table1
GROUP BY user) t1 ON u.id = t1.user
LEFT JOIN(
SELECT user, COUNT(*) AS t2Count
FROM table2
GROUP BY user) t2 ON u.id = t2.user;
Here is an SQL Fiddle example.
I have two tables:
users:
user_id user_name
data:
user_id user_data user_time
I wan to select the latest entry from the data table, but return the user_name, user_id, user_data and user_time.
I have tried the following query, but it returns the first entry, not the last for each user:
sql = "SELECT users.user_name, users.user_id, data.user_data, data.user_time
FROM users
INNER JOIN data ON data.user_id = users.user_id
GROUP BY users.user_name
ORDER BY data.user_time DESC";
Use GROUP BY and MAX, WHERE...IN:
SELECT u.user_id, u.user_name, d.user_data, d.user_time
FROM users u
INNER JOIN data d ON d.user_id = u.user_id
WHERE (d.user_id, d.user_time) =
(SELECT user_id, MAX(user_time) FROM data GROUP BY user_id)
I think you had better add data_id column to data table.
Unless data_id, both user_id and user_time are necessary for PRIMARY KEY(and user_time is not always unique, not reliable)
If there is data_id, it can be bitly simple:
SELECT u.user_id, u.user_name, d.user_data, d.user_time
FROM users u
INNER JOIN data d ON d.data_id =
(SELECT data_id FROM data
WHERE user_id = u.user_id ORDER BY data_time DESC LIMIT 1)