SQL select multiple tables - mysql

I have a question with SQL.
I have 3 tables, messages, payments and reports.
I want to get all events from a user from every table in one query, so this is wat I like to get:
SELECT * FROM messages WHERE receiver_id = 10
SELECT * FROM payments WHERE client_id = 10
SELECT * FROM reports WHERE receiver_id = 10
But in 1 query.
Is this possible?
Table structures:
- messages
id
sender_id
receiver_id
type
text
created_at
updates_at
- payments
id
user_id
client_id
method
cost
status
text
created_at
updated_at
- reports
id
sender_id
receiver_id
text
created_at
updates_at

If there is no relationships between the 3 tables then you can just use UNION. It can be as simple as this:
SELECT receiver_id as event_id, event_name, event_description, event_timestamp FROM messages WHERE receiver_id = 10
UNION
SELECT client_id as event_id, event_name, event_description, event_timestamp FROM payments WHERE client_id = 10
UNION
SELECT receiver_id as event_id, event_name, event_description, event_timestamp FROM reports WHERE receiver_id = 10
This assumes there is a common set of columns across all tables that you are interested in. If however the columns have differing names but contain the same data (and datatypes), then you can use AS to rename the columns in the query to achieve the UNION
SELECT receiver_id as event_id, col1 as event_name, col2 as event_description, col3 event_timestamp FROM messages WHERE receiver_id = 10
UNION
SELECT client_id as event_id, col_a as event_name, col_b as event_description, col_c event_timestamp FROM payments WHERE client_id = 10
UNION
SELECT receiver_id as event_id, col_x as event_name, col_y as event_description, col_z event_timestamp FROM reports WHERE receiver_id = 10
Keep in mind that UNION will eliminate duplicate rows in your dataset. If this is not the behavior that you want then use UNION ALL instead (it will not eliminate duplicates).
Also keep in mind that if you column datatypes are not the same across the table then you will have to cast them to a common type. Casting is highly dependent of your DBMS so make sure you use the correct methods to do so.

Its fairly straight forward providing receiver_id and client_id hold the same values:
SELECT * FROM `messages` LEFT JOIN `payments` ON
`messages`.`receiver_id` = `payments`.`client_id` LEFT JOIN `reports`
ON `messages`.`receiver_id` = `reports`.`receiver_id`

I dont really understand what you want, do you want to get all the data in 1 query, or do you need joins with them?
If you want all the data in one table you can just do this i think:
SELECT * FROM messages as M, payments as P, reports as R
WHERE M.receiver_id, P.client_id, R.receiver_id = 10
Else you need to show us some more details about the relationship

You can use Union or Union All to concatenate the results sets of the 3 queries. However all 3 tables must have the same amount of columns for it to work. Post your tables structure for a more exact solution.
SELECT * FROM messages WHERE receiver_id = 10
Union All
SELECT * FROM payments WHERE client_id = 10
Union All
SELECT * FROM reports WHERE receiver_id = 10

Related

Get latest value of field using group by

I am making a query in which i want the job ids to be grouped but i want the latest timestamp row in the result which is not happening
Here is the SQL fiddle
http://sqlfiddle.com/#!9/de8769
The normal view for table is
The output after using this query i made
SELECT
DISTINCT(user_id),
job_id,
message,
receiver_id,
parent,
type,
id as id FROM ai_ms_messages
WHERE (receiver_id = '7' OR user_id = '7') AND type<>0 AND type<>2 group by job_id
ORDER BY max(timestamp) DESC
But as you can see its taking the value of id as 3 for job_id 11 but it should have taken the value 5 (as that is latest for job_id 11) and also the order is wrong. Since job_id 11 is latest not job_id 12. Is there any way to achieve this ?
The query would be:
select
distinct(m1.user_id),
m1.job_id,
m1.message,
m1.receiver_id,
m1.parent,
m1.type,
m1.id as id from ai_ms_messages as m1
where m1.type<>0 and m1.type<>2
and m1.timestampt = (select max(m2.timestamp) from ai_ms_messages as m2 where m2.job_id = m1.job_id)
As per your query you are looking for data for receiver_id = '7' and for id =5 , receiver_id = '6' , so this is not in your query output.
Just remove where condition, or check data as per condition only.
GROUP BY groups on the first matching result it hits.
So, its preferable this method as the subquery.
SELECT *
FROM (
SELECT DISTINCT (
user_id
), job_id, message, receiver_id, parent,
TYPE , id AS id
FROM ai_ms_messages
WHERE (
receiver_id = '7'
OR user_id = '7'
)
AND TYPE <>0
AND TYPE <>2
ORDER BY TIMESTAMP DESC
) AS sub
GROUP BY job_id

SQL count without group by

I have a table that has user_id and purchase_id. I want to filter the table so that only users with more than 2 purchases (i.e. there are more than 2 rows for that user in the table). I used count and group by, but it does not work in a way I want.
create view myview as
select user_Id, purchase_id, count(*) as count from mytable group by user_Id;
select user_id, purchase_id from myview where count >2;
But it gives me only users (only one user_id) that has more than 2 purchase and it does not give me all of their purchases. For example if the table looks like this:
user_id purchase_id
1 1212
1 1312
2 1232
1 1321
3 1545
3 4234
My query gives me this :
1 1212
3 1545
But I want this:
1 1212
1 1312
1 1321
3 1545
3 4234
change your last sql like this
select mt.user_id, mt.purchase_id
from myview mv
inner join mytable mt
on mt.user_id=mv.user_id where mv.count >5;
SELECT
*
FROM
mytable mt,
(SELECT user_id, count(*) AS purchase_count FROM mytable GROUP BY user_id) ct
WHERE
mt.user_id = ct.user_id AND ct.purchase_count > 5;
SELECT *
FROM MYTABLE
WHERE USER_ID IN (SELECT USER_ID
FROM MYTABLE
GROUP BY USER_ID
HAVING COUNT(*)>=2)
I tested in my netezza,it works. hopefully, it's also working in mysql
Try GROUP BY with HAVING comment.
SELECT user_Id, purchase_id
FROM mytable
GROUP BY user_Id
HAVING count( * ) >5
As far as I can tell you want to list the user id's and purchase id's of all users that have over 5 purchases.
In order to do this you could do a join on two queries.
For example:
SELECT tblLeft.user_id,
tblLeft.purchase_id
FROM myview tblLeft
JOIN (SELECT user_id,
Count(*) AS purchases
FROM myview
GROUP BY user_id
HAVING purchases > 1) tblRight
ON tblLeft.user_id = tblRight.user_id
The tblRight is essentially a table containing the user_id's of all users with over 5 purchases.
We then do a select (tblLeft) and join it on the tbl right, ensuring only customers with over 5 purchases remain.

What SQL statement have to use to get list of non duplicate id's from 1 table, but 2 columns

Have a table for chat where I collect user_from, user_to and other non important stuffs for the example.
Want to get user_id's for all the users related in conversation (with where clause for current user_id).
In the example below I'm trying to search all the users where user_id=1 exist, but I know that doesn't work and don't know how to make it works.
SELECT *
FROM messages M
WHERE M.user_from="1" OR M.user_to="1"
GROUP by m.user_from, m.user_to
If in table are rows (below). Actually I want to receive finally array([0]=>2, [1]=>4), because user_id=1 have conversation only with user_id=2 and user_id=4
user_from | user_to
1 2
1 2
2 1
1 4
3 2
Probably I have to use Select in the Select or use anykind of Case When Then, but have no idea how to loop it correctly
RESOLVED WITH ORDERING BY LAST MSG_ID!!!
SELECT (IF( user_from = 1, user_to, user_from )) as user_id
FROM messages
WHERE `user_from` = 1 OR `user_to` = 1
GROUP BY user_id
ORDER BY MAX(msg_id) DESC
select user_to as user_id
from messages
where user_from = 1
union
(select user_from as user_id
from messages
where user_to = 1)
edited on suggestion from #Анжел Миленов - (I do not personally know why the brackets make a difference in mySql)

SELECT COUNT(*) for unique pairs of IDs

I have a table like the following, named matches:
match_id ( AUTO INCREMENT )
user_id ( INT 11 )
opponent_id ( INT 11 )
date ( TIMESTAMP )
What I have to do is to SELECT the count of the rows where user_id and opponent_id are a unique pair. The goal is to see the count of total matches started between different users.
So if we have:
user_id = 10 and opponent_id = 11
user_id = 20 and opponent_id = 22
user_id = 10 and opponent_id = 11
user_id = 11 and opponent_id = 10
The result of the query should be 2.
In fact we only have 2 matches that have been started by a couple of different users. Match 1 - 3 - 4 are the same matches, because played by the same couple of user IDs.
Can anyone help me with this?
I have done similar queries but never on pairs of IDs, always on a single ID.
FancyPants answer is correct, but I prefer to use DISTINCT when no aggregate function is used:
SELECT COUNT(DISTINCT
LEAST(user_id, opponent_id),
GREATEST(user_id, opponent_id)
)
FROM yourtable;
is sufficient.
SELECT COUNT(*) AS nr_of_matches FROM (
SELECT
LEAST(user_id, opponent_id) AS pl1,
GREATEST(user_id, opponent_id) AS pl2
FROM yourtable
GROUP BY pl1, pl2
) sq
see it working in an sqlfiddle

very difficult mysql query - random order on two tables

Consider this classical setup:
entry table:
id (int, PK)
title (varchar 255)
entry_category table:
entry_id (int)
category_id (int)
category table:
id (int, PK)
title (varchar 255)
Which basically means entries can be in one or more categories (the entry_category table is used as MM/join table)
Now I need to query 6 unique categorys along with 1 unique entries from these categories by RANDOM!
EDIT: To clarify: the purpose of this is to display 6 random categories with 1 random entry per category.
A correct result set would look like this:
category_id entry_id
10 200
20 300
30 400
40 500
50 600
60 700
This would be incorrect as there are duplicates in the category_id column:
category_id entry_id
10 300
20 300
...
And this is incorrect as there are duplicates in the member_id column:
category_id entry_id
20 300
20 400
...
How can I query this?
If I use this simple query with order by rand, the result contains duplicated rows:
select c.id, e.id
from category c
inner join entry_category ec on ec.category_id = c.id
inner join entry e on e.id = ec.entry_id
group by c.id
order by rand()
Performance is at the moment not the most important factor, but I would need a reliably working query for this, and the above is pretty much useless and does not do what I want at all.
EDIT: as an aside, the above query is no better when using select distinct ... and leaving out the group by. This includes duplicate rows as distinct only makes sure that the combinations of c.id and e.id are unique.
EDIT: one solution I found, but probably slow as hell on larger datasets:
select t1.e_id, t2.c_id
from (select e.id as e_id from entry e order by rand()) t1
inner join (select ec.entry_id as e_id, ec.category_id as c_id from entry_category ec group by e_id order by rand()) t2 on t2.e_id = t1.e_id
group by t2.c_id
order by rand()
SELECT category_id, entity_id
FROM (
SELECT category_id,
#ce :=
(
SELECT entity_id
FROM category_entity cei
WHERE cei.category_id = ced.category_id
AND NOT FIND_IN_SET(entity_id, #r)
ORDER BY
RAND()
LIMIT 1
) AS entity_id,
(
SELECT #r := CAST(CONCAT_WS(',', #r, #ce) AS CHAR)
)
FROM (
SELECT #r := ''
) vars,
(
SELECT DISTINCT category_id
FROM category_entity
ORDER BY
RAND()
LIMIT 15
) ced
) q
WHERE entity_id IS NOT NULL
LIMIT 6
This solution is not a piece of code I'd be proud of, since it relies on black magic of session variables in MySQL to keep the recursion stack. However, it works.
Also it's not perfectly random and can in fact yield less than 6 values (if entity_id's duplicate across the categories too often). In this case, you can increase the value of 15 in the innermost query.
Create a unique index or a PRIMARY KEY on category_entity (category_id, entity_id) for this to work fast.
Seems to me that the good way to do this is to pick 6 distinct values from each set, shuffle each list of values (each list individually), and then glue the lists together into a two-column result.
To randomize which six you get, shuffle the entire list of each type of value, and grab the first six.