Query: Select and display next highest value as new column - mysql

I have this query which lists out IDs from "pages" on our site.
SELECT mdl_page.id
FROM mdl_page, mdl_log, mdl_user
WHERE mdl_log.module = "page"
AND mdl_log.action = "view"
AND mdl_user.id = mdl_log.userid
AND mdl_log.info = mdl_page.id
AND mdl_log.course = 178
The result is simple:
| ID |
|-----|
| 3 |
| 4 |
| 7 |
| 11 |
Notice the jumps in the count. I'm trying to get something like this:
| ID | NEXT ID |
|-----|---------|
| 3 | 4 |
| 4 | 7 |
| 7 | 11 |
| 11 | 12 |
Can anyone point in me in the right direction for this?
UPDATE
One twist, the system (not my own) I have to run the query through only allows queries that begin with 'SELECT'.

Two ways i can think of use a co-related subquery,in your sub query compare the value from main query and sorts it in ascending manner and limit the result to one
SELECT
p.id ,
(SELECT
p1.id
FROM mdl_page p1
JOIN mdl_log l1 ON (l1.info = p1.id)
JOIN mdl_user u1 ON (u1.id = l1.userid)
WHERE l1.module = "page"
AND l1.action = "view"
AND l1.course = 178
AND p1.id > p.id
ORDER BY p1.id ASC LIMIT 1) NEXT_ID
FROM mdl_page p
JOIN mdl_log l ON (l.info = p.id)
JOIN mdl_user u ON (u.id = l.userid)
WHERE l.module = "page" AND l.action = "view" AND l.course = 178
ORDER BY p.id
and use a rank query, in rank query i am left joining the same query with the less than condition ON (t.id< t1.id) so it will result in multiple rows like (3,4),(3,7),(3,11) so i need to pick the first combination of 3,4 for this i have used a rank query to give the rank to the items that belong to same group, in parent where i am just restricting the result set to show the first pair for each group
SELECT t3.id,t3.NEXT_ID FROM (
SELECT t.id id, t1.id NEXT_ID ,
#r:= CASE WHEN #g = t.id THEN #r +1 ELSE 1 END rownum,
#g:= t.id
FROM
(SELECT
p.id
FROM
mdl_page p
JOIN mdl_log l ON (l.info = p.id)
JOIN mdl_user u ON (u.id = l.userid)
WHERE l.module = "page"
AND l.action = "view"
AND l.course = 178
ORDER BY p.id
) t
LEFT JOIN
(SELECT
p.id
FROM
mdl_page p
JOIN mdl_log l ON (l.info = p.id)
JOIN mdl_user u ON (u.id = l.userid)
WHERE l.module = "page"
AND l.action = "view"
AND l.course = 178
ORDER BY p.id ) t1 ON (t.`id` < t1.id)
CROSS JOIN (SELECT #g:=0,#r:=0) t2
ORDER BY t.`ID` , t1.ID
) t3
WHERE t3.rownum = 1
resutset you will get as null for 11 if there is no more record exist which have an id greater than 11 ,or in other words the last record will have a null in next_id column
ID NEXT_ID
3 4
4 7
7 11
11 NULL

Perhaps you should create a temporary table, which is pretty much the same as the query you're running and erase the first line?
Then run your query and join it with the temporary table?

Related

MySQL query for on user to list of users

My message system is based on message, threads, and participant
a message created by a user is linked to a thread
two participants (or more) are linked to a thread
each participant is linked to one user
I got this request. It give me the number of message a user has received (= number of message where the user is not the creator)
I count all messages created in a list of threads where the user is not the creator.
I got the list of threads by find all thread_id where my user participant is linked
SELECT COUNT(m.id)
FROM message m
INNER JOIN message_thread t ON m.thread_id = t.id
WHERE m.created_by_id != :userId
AND t.id IN (
SELECT t2.id
FROM message_thread t2
INNER JOIN message_participants p ON p.thread_id = t2.id
WHERE p.user_id = :userId
)
So that is for one uniq user.
+-------------+
| count(m.id) |
+-------------+
| 7 |
+-------------+
I'm trying to do the same, but with severals users, and get for each of them, the number of message they received
+---------+-------------+
| User id | count(m.id) |
+---------+-------------+
| 12645 | 1 |
| 985 | 5 |
| 8956 | 15 |
| 37856 | 2 |
+---------+-------------+
I tried to do that with more JOIN instead of t.id IN ( and group by the result, but without success...
One method is a brute force method, where you bring in all the users you care about and use that information for the processing:
SELECT u.userid, COUNT(m.id)
FROM message m INNER JOIN
message_thread t
ON m.thread_id = t.id CROSS JOIN
(SELECT 12645 as userid UNION ALL
SELECT 985 UNION ALL
SELECT 8956 UNION ALL
SELECT 37856
) u
WHERE m.created_by_id <> u.userId AND
t.id IN (SELECT t2.id
FROM message_thread t2 INNER JOIN
message_participants p
ON p.thread_id = t2.id
WHERE p.user_id = u.userId
)
GROUP BY u.userId;
If you wanted to do this for all users, I would recommend a different approach: count all the messages a user participates in and subtract out the ones where s/he is the creator:
SELECT p.userid,
COUNT(DISTINCT CASE m.created_by_id <> p.userId THEN m.id END) as numMessages
FROM message m INNER JOIN
message_thread t
ON m.thread_id = t.id INNER JOIN
message_participants p
ON p.thread_id = t.id
GROUP BY p.userid;
You should use a group by
SELECT message_thread.your_user_id COUNT(m.id)
FROM message m
INNER JOIN message_thread t ON m.thread_id = t.id
WHERE m.created_by_id != :userId
AND t.id IN (
SELECT t2.id
FROM message_thread t2
INNER JOIN message_participants p ON p.thread_id = t2.id
)
group by message_thread.your_user_id

How to mention two aggregation functions in the same query?

I have an sql query to get information from three different tables as following:
select users.username, users.id, users.avatar, users.daily_tahmin, users.alert, f1.comments_no, f2.tahmins_no, f3.monthly_tahmins_no from users LEFT join
(SELECT count(comments) AS comments_no, user_id
FROM comments
Where user_id = 12
) AS f1 on users.id = f1.user_id left join
(
SELECT count(tahmin) AS tahmins_no, user_id
FROM tahminler
Where user_id = 12
) AS f2 on users.id = f2.user_id left join
(
SELECT count(tahmin) AS monthly_tahmins_no, user_id, matches_of_comments.match_id
FROM tahminler
INNER JOIN matches_of_comments on tahminler.match_id = matches_of_comments.match_id
Where user_id = 12 AND (MONTH( STR_TO_DATE( matches_of_comments.match_date, '%d.%m.%Y' ) ) = '01' AND YEAR( STR_TO_DATE( matches_of_comments.match_date, '%d.%m.%Y' ) ) = '2014')
) AS f3 on users.id = f3.user_id
where users.id = 12
and it gives the following result :
+------------+----+----------------+--------------+-------+-------------+------------+--------------------+
| username | id | avatar | daily_tahmin | alert | comments_no | tahmins_no | monthly_tahmins_no |
+------------+----+----------------+--------------+-------+-------------+------------+--------------------+
| cold heart | 12 | 1389002263.jpg | 0 | 0 | 65 | 258 | 10 |
+------------+----+----------------+--------------+-------+-------------+------------+--------------------+
The previous code was not optimized after i do some EXPLIAN and I tried to optimized it and I got the following query:
SELECT m.*,count(comments.id)
FROM comments
JOIN
(SELECT users.username, users.id, users.avatar, users.daily_tahmin, users.alert
FROM users
WHERE id=12)as m ON m.id = comments.user_id
My problem is that I can not get (tahmins_no,monthly_tahmins_no) every time i add them to the query it gives wrong result I can not find a way to add them correctly to the query to be in optimized way?? can I have any advice from anybody here?
Your simplified query is:
select m.*, count(c.id)
from comments c join
users m
on m.id = c.user_id
where m.id = 12
group by m.id;
You should be able to add in the monthly number:
select m.*, count(c.id), f3.*
from comments c join
users m
on m.id = c.user_id join
(select count(tahmin) AS monthly_tahmins_no, user_id, moc.match_id
from tahminler t join
matches_of_comments moc
on t.match_id = moc.match_id
Where user_id = 12 AND
MONTH( STR_TO_DATE( moc.match_date, '%d.%m.%Y' ) ) = 1 AND
YEAR( STR_TO_DATE( moc.match_date, '%d.%m.%Y' ) ) = 2014
) f3
on f3.user_id = m.id
where m.id = 12
group by m.id;
The month() and year() function return numbers, not strings. I don't understand why the field match_date would be stored as a string -- seems like a silly choice for a column whose name contains date.

MYSQL:SQL query to return a row only if all the rows satisfy a condition

Consider three tables -
users
id | type
-----------|------------
1 | a
2 | b
3 | c
types
id | type
-----------|------------
a | X
a | Y
b | X
c | X
c | Y
c | Z
training_status
id | training| status
-----------|-----------|-------------
1 | X | F
2 | X | S
2 | Y | S
3 | X | F
3 | Y | S
Each user has a type, and types defines the trainings that each user of a particular type have to complete.
training_status contains status of all the trainings that a user has taken and its result (S,F). It a user is yet to take a training, there won't be any row for that training.
I would like to find out all users that have successfully completed all the trainings that they have to take.
Here's the direction that I am thinking in:
select
id
from users
join types
using (type)
left join training_status
using (id,type)
where status NOT IN(None, F);
Obviously this is not the right query because even if the user has completed one of the trainings, we get that row. In the aforementioned example, I'd like to get id = 2 because he has completed both trainings of its type.
Try
SELECT DISTINCT u.id
FROM users u JOIN types t
ON u.type = t.type LEFT JOIN training_status s
ON u.id = s.id AND t.training = s.training
WHERE s.status IS NOT NULL
GROUP BY u.id
HAVING COUNT(t.type) = SUM(CASE WHEN s.status = 'S' THEN 1 ELSE 0 END)
or
SELECT DISTINCT u.id
FROM users u JOIN types t
ON u.type = t.type LEFT JOIN training_status s
ON u.id = s.id AND t.training = s.training
GROUP BY u.id
HAVING MAX(s.status IS NULL OR s.status = 'F') = 0
Output:
+------+
| id |
+------+
| 2 |
+------+
Here is SQLFiddle demo
Try this
SELECT *
FROM users
WHERE id NOT IN (
SELECT u.id
FROM users u
JOIN types t
ON u.type = t.type
LEFT JOIN training_status s
ON u.id = s.id AND t.training = s.training
WHERE s.status IS NULL OR s.status = 'F')
Try this
select distinct
u.id
from users u
left join types t
on u.type = t.type
left join training_status ts
on ts.training = t.training
where ts.status is not null
and ts.status != 'F'
This should work:
SELECT id
FROM users
WHERE id not in (SELECT x.id FROM (SELECT u.id AS id, t.type AS type, 'S' AS status
FROM users u, types t
WHERE u.type = t.id
EXCEPT
SELECT id, training, status
FROM training_status
) AS x (id, type, status)
)
Note that it uses set difference operator EXCEPT which gives difference of rows of all possible users and their successful training combinations and current training status. If the current status is complete for all users, the difference should yield no rows. Non zero result means there are users who did not complete required training. The outermost select gives list of users who are not in the list of users who did not complete the training, i.e, list of users who completed!
When executed in MS SQL server for your data it gives answer 2 who is the only one who completed training successfully.
Here is MySQL Version:
SELECT id
FROM users
WHERE id not in (SELECT x.id
FROM (SELECT u.id AS id, t.type AS type, 'S' AS status
FROM users u, types t
WHERE u.type = t.id
) x
WHERE NOT EXISTS
(SELECT ts.id, ts.training, ts.status
FROM training_status ts
WHERE ts.id = x.id AND
ts.training = x.type AND
ts.status = x.status
)
)
+------+
| id |
+------+
| 2 |
+------+
1 row in set (0.00 sec)

how to write the sql statement?

I have 3 tables as follows in my database. this is used to a application just like foursqure. i need help with the problem of writing the sql statement i have asked in the bottom of this.
thank you
user_details
user_id | fname
----------------
1 | Losh
8 | Dush
9 | Rosh
10 | NELL
friends
user_idf |user_idff
----------------
1 | 8
8 | 9
10 | 1
Check_in
check_in_id |user_id | place | date
--------------------------------------------
1 | 8 | Hotel | 01/01/2012
2 | 9 | Home | 05/01/2012
3 | 1 | Junction | 08/01/2012
4 | 1 | Rest | 11/01/2012
5 | 9 | Hotel | 15/01/2012
6 | 8 | Home | 15/01/2012
i get the user's who are friends with 8 and user 8 details AND the check in places as follows
SELECT a.`user_id`, a.`fname` , b.*
FROM `user_details` a, `check_in` b
WHERE (b.user_id = 8
OR b.user_id in (select user_idf from friends where user_idff = '8' union select user_idff from friends where user_idf = '8')) AND b.user_id = a.user_id
how do i write the sql to select who are friends with 8 and user 8 details AND the last check in place of those users
explanation::
i seeks for a answer such as
user id name place date
1 LOSH Rest 11/01/2012
8 DUSH HOME 15/01/2012
9 ROSH HOTEL 15/01/2012
Join it to the table returned by:
(SELECT `user_id`, `place` FROM Check_in GROUP BY user_id ORDER BY `date` DESC)
That should give you one entry per user, and since it's sorted in reverse by date, that entry should be the most recent.
But when i group by it gives me the first dates not the latest date
How about this:
(SELECT user_id, place
FROM (SELECT * FROM Check_in ORDER BY `date` DESC) tmp
GROUP BY user_id)
SELECT user_id, fname, c.place
FROM user_details u
INNER JOIN (SELECT IF(user_idff = 8, user_idf, user_idff) AS user_id
FROM friends
WHERE (user_idff = 8 OR user_idf = 8)
) f
ON u.user_id = f.user_id
LEFT JOIN (SELECT c1.user_id, place
FROM Check_in c1
LEFT JOIN Check_in c2
ON c1.user_id = c2.user_id AND
c1.date < c2.date
WHERE c2.date IS NULL
) c
ON u.user_id = c.user_id;
This doesn't break ties but it's a straighforward way of answering your question.
EDIT
I just re-read you question and I see that you want user 8 info also. It's not clear whether you want user 8 as a separate row or with info in line with the friends' rows.
select *
from
friends as f inner join check_in as ci on ci.user_id = f.user_idff
inner join user_details as ud on ud.user_id = f.user_idff
inner join user_details as ud8 on ud8.user_id = f.user_idf
where
f.user_idf = 8
and date = (
select max(date)
from friends as f2 inner join check_in as ci on ci.user_id = f2.user_idff
where f2.user_idf = f.user_idf
)
EDIT 2
You request may be a small bit unclear about determining which check-in location to return. Use this option if you want the latest location of each friend individually. The first query finds the most recent location among all friends. Obviously these are two variations on an identical theme.
select *
from
friends as f inner join check_in as ci on ci.user_id = f.user_idff
inner join user_details as ud on ud.user_id = f.user_idff
inner join user_details as ud8 on ud8.user_id = f.user_idf
where
f.user_idf = 8
and date = (
select max(date)
from check_in as ci
where ci.user_id = f.user_idff
)
(SELECT a.user_id, a.place, b.fname, a.date, a.time, a.check_in_id
FROM (SELECT * FROM check_in ORDER BY date DESC) as a, user_details as b
WHERE a.user_id = b.user_id AND (a.user_id in (select user_idf from friends where user_idff = '8' union select user_idff from friends where user_idf = '8') OR a.user_id = 8)
GROUP BY a.user_id)
above query gave me the required answer.
thank you all for the help given

mysql mixing counts from several tables

I am trying to achieve total number of topics, total number of posts, and last post for given section, please find db structures and query as following...
fcats
| id | title | section |
+----+--------+---------+
| 1 | test | gd |
+----+--------+---------+
ftopics
| id | title | cat_id |
+----+--------+---------+
| 1 | test1 | 1 |
+----+--------+---------+
fposts
| id | post | topic_id | posted_by
+----+-------+----------+---------+
| 1 | post | 1 | user |
+----+-------+----------+---------+
current query
SELECT id,
title ,
(SELECT count(id)
FROM ftopics
WHERE cat_id = id) AS total_topics
FROM fcats
WHERE section = "gd"
by using above query, i could only get total_topics for given section, but i am confused about how to get total number of posts, and last post for given section. please help, thanks.
SELECT A.id section_id,
IFNULL(COUNT(DISTINCT B.id),0) topics_count,
IFNULL(COUNT(C.id),0) post_count,
(SELECT post from fposts where id = MAX(C.id)) last_post
FROM fsections A LEFT JOIN ftopics B
ON A.id = B.cat_id
LEFT JOIN fposts C
ON C.topic_id = B.id
WHERE A.section = "gd"
GROUP BY A.id
Also include the null case if the section doesnot have any post
Maybe something like this:
SELECT
id,
title ,
(
SELECT
count(ftopics.id)
FROM
ftopics
WHERE
ftopics.cat_id = fcats.id
) AS total_topics,
(
SELECT
COUNT(distinct fposts.id)
FROM
ftopics
JOIN fposts
ON ftopics.id=fposts.topic_id
WHERE
ftopics.cat_id = fcats.id
),
(
select
fposts.id
from fposts
inner join ftopics on fposts.topic_id = ftopics.id
inner join fcats c2 on c2.id = ftopics.cat_id
where fcats.id = c2.id
order by fposts.id desc
limit 1
) as last_post_id
FROM fcats
WHERE section = "gd"
For first part of your answer you should use count distinct, and for second part a subquery:
SELECT c.id,
c.title ,
count( distinct t.cat_id) AS total_topics ,
count( distinct p.id) AS total_posts ,
(select p2.id
from ne_forum_posts p2
inner join ne_forum_topics t2 on p2.topic_id = t2.id
inner join ne_forum_sub_cats c2 on c2.id = t2.cat_id
where c2.id = c.id
order by p2.id desc
limit 1) as last_post_id
FROM ne_forum_sub_cats c LEFT OUTER JOIN
ne_forum_topics t on c.id = t.cat_id LEFT OUTER JOIN
ne_forum_posts p on p.topic_id = t.id
WHERE section = "gd"
all typos fixed and tested.