Select Query to get last 3 calls and mails - mysql

I have 2 tables. One of tables has all mails from users and other table has all calls from users.
Table 1
call_id, | call_date | user_id
1 | 10/01/12| 3
2 | 9/01/12 | 3
Table 2
mail_id, | mail_date | user_id
1 | 8/01/12 | 3
2 7/01/12 | 3
I need to get last last 3 calls and mails :
10/01/12 - call
9/01/12 - call
8/01/12 - mail

Assuming MySQL, and for just one User_ID...
SELECT
*
FROM
(
SELECT 'call' AS type, id, call_date AS event_date, user_id FROM table_1
UNION ALL
SELECT 'mail' AS type, id, mail_date AS event_date, user_id FROM table_2
)
data
WHERE
user_id = 3
ORDER BY
event_date DESC
LIMIT
3
EDIT: Ooops, forgot to specify DESC in the ORDER BY, sorry.

Declare #userID int;
SET #userID=3;
select call_id FROM table_1
where user_id=#userID
order by call_date desc limit 2
UNION ALL
select mail_id FROM table_2
where user_id=#userID
order by mail_date desc limit 1

Related

ORDER BY after GROUP BY does not working

SELECT *
FROM (SELECT id, user, MAX(score) FROM table_1 GROUP BY user) AS sub
ORDER BY 'sub.score' ASC;
This SQL query should select from a table only a score per user, and for accuracy, the highest.
The table structure is this:
+-----------------------+
| id | score | username |
+-----------------------+
| 1 | 15 | mike |
| 2 | 23 | tom |
| 3 | 16 | mike |
| 4 | 22 | jack |
etc..
The result should be like:
3 mike 16
2 tom 23
4 jack 22
And then reordered:
3 mike 16
4 jack 22
2 tom 23
But the query does not reorder the subquery by score. How to do so?
Let's look at what you are doing step by step:
SELECT id, user, MAX(score) FROM table_1 GROUP BY user
Here you are grouping by user name, so you get one result row per user name. In this result row you select the user name, the maximum score found for this user name (which is 16 for 'mike') and one of the IDs found for the user name (which can be 1 or 3 for 'mike', the DBMS is free to choose one). This is probably not what you want.
SELECT * FROM (...) AS sub ORDER BY 'sub.score' ASC;
'sub.score' is a string (single quotes). You want to order by the max score from your subquery instead. So first give the max(score) a name, e.g. max(score) as max_score, and then access that: ORDER BY sub.max_score ASC.
Anyway, if you want the record with the maximum score for a user name (so as to get the according ID, too), you could look for records for which not exists a record with the same user name and a higher score. Sorting is easy then: as there is no aggregation, you simply order by score:
select * from table_1 t1 where not exists
(select * from table_1 higher where higher.name = t1.name and higher.score > t1.score)
order by score;
Assuming user|score is unique..:
SELECT x.*
FROM table_1 x
JOIN ( SELECT user, MAX(score) score FROM table_1 GROUP BY user) y
ON y.user = x.user
AND y.score = x.score
ORDER BY x.score
No need to write sub queries.
Simply you can use this way:
SELECT id, `user`, MAX(score) FROM table_1 GROUP BY `user`
ORDER BY MAX(score);
If you want query with sub query:
SELECT * FROM (SELECT id, `user`, MAX(score) as max_score FROM table_1
GROUP BY `user`) AS sub ORDER BY max_score;

SQL get rows based on two ids

I really don't know how to title this problem properly.
Heres the table structure:
ID | CLIENT_ID | …
ID is primary and auto increment. CLIENT_ID on the other hand can occur multiple times.
What i want is to fetch the rows by CLIENT_ID with highest ID ... Heres a example
ID | CLIENT_ID
1 | 1
2 | 1
3 | 2
4 | 3
5 | 2
So here CLIENT_ID 1 and 2 occurs multiple times (because there is a newer version).
After the query i want the following IDs in the results: 2,4,5 (Because the highest ID in rows with CLIENT_ID 1 is the row with ID 2 and so on)
If you need all the columns you can use a select in
select * from my_table
where (id, client_id) in ( select max(id), client_id
from my_table
group by client_id);
but if you need only the id
select id from my_table
where (id, client_id) in ( select max(id), client_id
from my_table
group by client_id);
or more simple
select max(id)
from my_table
group by client_id;
SELECT * FROM table GROUP BY client_id HAVING max(id)
this should be more efficient than a sub select

Unique combination of two columns in mysql or postgres

I have to get unique combinations of two columns.
Eg if the values are :
sender_id recipient_id created_at
1 2 10/11/2014
2 1 10/12/2014
1 2 10/13/2014
1 3 10/14/2014
I want the output to be :
sender_id recipient_id created_at
1 3 10/14/2014
1 2 10/13/2014
I wrote this query :
SELECT DISTINCT ON (sender_id, recipient_id) *
FROM "messages"
WHERE ((recipient_id = 1 and recipient_delete = false)
or (sender_id = 1 and sender_delete = false))
ORDER BY sender_id, recipient_id, created_at DESC
But it outputs this:
sender_id recipient_id created_at
1 3 10/14/2014
2 1 10/12/2014
1 2 10/13/2014
One option to get all pairs, regardless of whether they are forward or backward (for example (1, 2) == (2, 1)) is to select the LEAST() and GREATEST() from each row, and then select distinct values. Using this query:
SELECT DISTINCT LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id)
FROM myTable;
You will get the following output:
| 1 | 2 |
| 1 | 3 |
Once you have that, you can GROUP by these to get the maximum date for each pair:
SELECT LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id), MAX(created_at)
FROM myTable
GROUP BY LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id);
This query will give you the data you need for each pair, but it won't return the actual row from your original table. If there is a row of format | 2 | 1 | 2014-10-15 | this query will return | 1 | 2 | 2014-10-15.
To get the original row from your table, you need to JOIN on the condition that all of the necessary columns match:
SELECT m.*
FROM myTable m
JOIN(
SELECT LEAST(sender_id, recipient_id) AS least,
GREATEST(sender_id, recipient_id) AS greatest,
MAX(created_at) AS maxDate
FROM myTable
GROUP BY LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id)) tmp
ON tmp.least = LEAST(m.sender_id, m.recipient_id) AND tmp.greatest = GREATEST(m.sender_id, m.recipient_id) AND tmp.maxDate = m.created_at;
Here is an SQL Fiddle example that matches your expected results.
The initial idea with DISTINCT ON is good, but:
it works with postgres but not with mysql, DISTINCT ON() being a PostgreSQL non-standard extension.
the ON() has to be applied to an expression where (1,2) and (2,1) are equivalent.
So a close query that should work and be efficient for postgres is:
SELECT DISTINCT ON (pair) *,
CASE WHEN sender_id<recipient_id
THEN (sender_id,recipient_id)
ELSE (recipient_id,sender_id)
END AS pair
FROM messages
ORDER BY pair, created_at DESC ;

How can I select adjacent row in sql when ordered by a different field?

I've got a table of data with the following structure:
id | likes
1 | 2
2 | 5
3 | 2
4 | 6
5 | 2
If want to find the row next to #3 I can use :
SELECT * FROM table WHERE id >= 3 ORDER BY id
However what I want to do is order by table by likes. When the data is ordered by likes it looks like this
id | likes
1 | 2
3 | 2
5 | 2
2 | 5
4 | 6
How can I select the rows before or after a certain id when ordered by likes?
e.g. for id 5, my result would be row id 3 before and row id 2 after.
If likes are unique numbers, following should work.
previous:
SELECT * FROM table WHERE likes < (SELECT likes FROM table WHERE id = ID) ORDER BY likes DESC LIMIT 1
next:
SELECT * FROM table WHERE likes > (SELECT likes FROM table WHERE id = ID) ORDER BY likes ASC LIMIT 1
You may change 1 of them to <= or >= and add WHERE id != ID
Your second table shows wrong ids for the first two rows, by the way.
It should be:
id likes
1 2
3 2
This works in MySQL for me:
Select id, likes from (SELECT id, #rownum:=#rownum+1 AS rownum, likes
FROM table u, (SELECT #rownum:=0) r ORDER BY likes) as derived where
rownum >= 2 and rownum <= 4;
(SELECT id, #rownum:=#rownum+1 AS rownum, likes FROM table u, (SELECT
#rownum:=0) r ORDER BY likes);
The last part tries to simulate the row number, which is missing in MySQL, but available in MSSQL, Oracle and others.

SQL get all rows sorted without duplicates

I have a table that looks something like this:
________________________
|id|value|date|approved|
-----------------------
What I need to be able to do is get each row where approved = 1. That part is obvious. For each occurrence of value, I only want the most recent row (sorted by date).
Meaning that with a table like this:
________________________
|id|value|date|approved|
-----------------------
|1 |Foo | 5 | 1 |
|2 |Bar | 6 | 1 |
|3 |Foo | 8 | 1 |
-----------------------
I only want the rows with id 2 and 3.
I assume I need to use DISTINCT somehow, but I'm not sure how. Could anyone help me out here?
SELECT m.*
FROM (
SELECT DISTINCT value
FROM mytable
) md
JOIN mytable m
ON m.id =
(
SELECT id
FROM mytable mi
WHERE mi.value = md.value
AND mi.approved = 1
ORDER BY
mi.value DESC, mi.date DESC, mi.id DESC
LIMIT 1
)
Create an index on (value, date, id) for this to work fast.
You need:
select id, value, date, approved where (value, date) in (
select value, max(date)
from your_table
group by value
);
Actually using GROUP BY will yeld better results. try something like this:
SELECT id, value, date, approved FROM table WHERE approved = 1 GROUP BY value ORDER BY date;
select id, value, date, approved
from mytable a
where approved = 1
and date =
(select max(b.date)
from mytable b
where b.approved = 1
and b.value = a.value)
select
id,
value,
date
from
( select
value, max( date ) as LastInstance
from
YourTable
where
approved = 1
group by
value ) PreQuery
join YourTable
on PreQuery.value = YourTable.value
and PreQuery.LastInstance = YourTable.LastInstance
and YourTable.approved = 1
order by
date
Try with this:
SELECT * DISTINCT FROM TABLA WHERE APPROVED = 1 ORDER BY DATE DESC
Hope this helps you.