I guess I don't understand the order in which subqueries work.
When I run this SQL statement I get 1 (out of 3 that exist in the table) random 'friend id' :
SELECT t1.sender_id AS Connections FROM
(SELECT DISTINCT sender_id
FROM connection
WHERE receiver_id = 'my_id'
AND status = 'Approved') t1
UNION ALL
SELECT t2.receiver_id FROM
(SELECT DISTINCT receiver_id
FROM connection
WHERE sender_id = 'my_id'
AND status = 'Approved') t2
ORDER BY RAND() LIMIT 1;
One random id is returned which is what I want.
BUT when I wrap the previous SQL statement within another SQL statement to get the friend's name and id (from the id in sub-query) the results come back randomly as either empty or 1 friend or 2 friends or all 3 friends :
SELECT id, name FROM profile
WHERE id = (
SELECT t1.sender_id AS Connections FROM
(SELECT DISTINCT sender_id
FROM connection
WHERE receiver_id = 'my_id'
AND status = 'Approved') t1
UNION ALL
SELECT t2.receiver_id FROM
(SELECT DISTINCT receiver_id
FROM connection
WHERE sender_id = 'my_id'
AND status = 'Approved') t2
ORDER BY RAND() LIMIT 1);
I want it to emit the same behaviour as the first code snippet.
The problem is that the subquery is being re-executed for every row being tested in profile. Each time it returns a different random ID; if that ID happens to match the current row of profile, the row is returned.
Instead of using WHERE id =, use a JOIN. This will just run the subquery once.
SELECT p.id, p.name
FROM profile AS p
JOIN (
SELECT t1.sender_id AS Connections FROM
(SELECT DISTINCT sender_id
FROM connection
WHERE receiver_id = 'my_id'
AND status = 'Approved') t1
UNION ALL
SELECT t2.receiver_id FROM
(SELECT DISTINCT receiver_id
FROM connection
WHERE sender_id = 'my_id'
AND status = 'Approved') t2
ORDER BY RAND() LIMIT 1) AS r
ON p.id = r.Connections
Are you really asking for all the sender_ids and one receiver_id?
It feels like you have an extra layer of SELECTs.
When in doubt, add extra parentheses:
( SELECT ... )
UNION ALL
( SELECT ... ORDER BY ...) -- this ORDER BY applies only to the select
ORDER BY ... -- This applies to the result of the UNION, not the second select
Related
I have three tables:
user: id, name
keyword: id, name
userkeyword: id, user_id, keyword_id
I want to execute query in following way:
Display those users whose keyword/s are matched with the login user's
keywords. In the order of maximum number of keyword matched user
should display first
e.g : If userA having 4 matched keywords, userB having 8, userC having 1, userD having 6 then the result should be in the order of,
userB
userD
userA
userC
For that I have done with this query (assume login user's id is 1):
select *
from user
where id IN (
select user_id
from userkeywords
where keyword_id IN (
select keyword_id
from userkeywords
where user_id=1)
group by user_id
order by count(keyword_id) desc)
AND id != 1
Here the result is getting perfect but the order is not correct. I have merged two queries in following manner"
select *
from user
where id IN (?)
AND id!=1
+
select user_id
from userkeywords
where keyword_id IN (
select keyword_id
from userkeywords
where user_id=1)
group by user_id
order by count(keyword_id) desc
Second query returns user_id in correct order but when I merged both queries, order was changed (wrong).
Hope I have mentioned my query properly with enough detail.
A subquery returns an unordered set, so the order by in a subquery only matters for its limit clause, if there is any. Any database other than MySQL would give an error message for a purely decorative sort order.
There's no way to sort on a column that only exists in the where clause. You'd have to rewrite the query. One option is to replace your in conditions with joins:
select uk2.name
from userkeywords uk1
join userkeywords uk2
on uk1.keyword_id = uk2.keyword_id
and uk1.user_id <> uk2.user_id
join user u2
on u2.id = uk2.user_id
where uk1.user_id = 1
group by
uk2.name
order by
count(*) desc
This should do it.
select uk.user_id, u.name
from userkeywords uk
left join user u on u.id = uk.user_id
where uk.keyword_id IN (
select keyword_id
from userkeywords
where user_id=1)
group by uk.user_id
order by count(uk.keyword_id) desc) AND uk.user_id != 1
Also, JOIN provides better performance.
I would use an inner join to select the correct rows:
SELECT *
FROM user
INNER JOIN (
SELECT * FROM userkeyword
WHERE keyword_id IN (
SELECT keyword_id
FROM userkeyword
WHERE user_id=1
)
) uk
ON user.id = uk.user_id
GROUP BY u.id
ORDER BY count(*) DESC;
I have a table in SQL that is a list of users checking in to a website. It looks much like this:
id | date | status
------------------
Status can be 0 for not checking in, 1 for checked in, 2 for covered, and 3 for absent.
I'm trying to build one single query that lists all rows with status = 0, but also has a COUNT on how many rows have status = 3 on each particular id.
Is this possible?
MySQL VERSION
just join a count that is joined by id.
SELECT t.*, COALESCE(t1.status_3_count, 0) as status_3_count
FROM yourtable t
LEFT JOIN
( SELECT id, SUM(status=3) as status_3_count
FROM yourtable
GROUP BY id
) t1 ON t1.id = t.id
WHERE t.status = 0
note: this is doing the boolean sum (aka count)..
the expression returns either true or false a 1 or a 0. so I sum those up to return the count of status = 3 for each id
SQL SERVER VERSION
SELECT id, SUM(CASE WHEN status = 3 THEN 1 ELSE 0 END) as status_3_count
FROM yourtable
GROUP BY id
or just use a WHERE status = 3 and a COUNT(id)
Try a dependent subquery:
SELECT t1.*,
( SELECT count(*)
FROM sometable t2
WHERE t2.id = t1.id
AND t2.status = 3
) As somecolumnname
FROM sometable t1
WHERE t1.status=0
You can use a join for this. Write one query that will get all rows with a status zero:
SELECT *
FROM myTable
WHERE status = 0;
Then, write a subquery to get counts for the status of 3 for each id, by grouping by id:
SELECT COUNT(*)
FROM myTable
WHERE status = 3
GROUP BY id;
Since you want all the rows from the first table (at least that's what I am picturing), you can use a LEFT JOIN with the second table like this:
SELECT m.id, m.status, IFNULL(t.numStatus3, 0)
FROM myTable m
LEFT JOIN (SELECT id, COUNT(*) AS numStatus3
FROM myTable
WHERE status = 3
GROUP BY id) t ON m.id = t.id
WHERE m.status = 0;
The above will only show the count for rows containing an id that has status 0. Hopefully this is what you are looking for. If it is not, please post some sample data and expected results and I will help you try to reach it. Here is an SQL Fiddle example.
My working query is below. However, the results from that query will produce duplicates AND non duplicates on name column. I want to be able to only show results where name columns from the two select queries are different
select t.*
from tbl_user_tmp t JOIN
(select activity, class, count(*) as NumDuplicates
from tbl_user_tmp
where user = 'bignadad2'
group by activity, class
having NumDuplicates > 1)
tsum ON t.activity = tsum.activity and t.class = tsum.class
columns are in this order
id, name, activity, class, activity_id
I only want to show these results where activity, class match and name does not.
2059 lg_lmk com.lge.lmk com.lge.lmk.activities.LmkMainActivity 48255
3668 task_manager com.lge.lmk com.lge.lmk.activities.LmkMainActivity 48255
These are the other results i do not want to see
2690 phone com.modoohut.dialer com.modoohut.dialer.DialActivity 54700
2694 phone com.modoohut.dialer com.modoohut.dialer.DialActivity 54700
I forgot that you needs only some results
SELECT * FROM tbl_user_tmp AS t1
INNER JOIN (
SELECT activity, class, COUNT(1) AS cnt FROM tbl_user_tmp
WHERE user = 'first'
GROUP BY activity, class
HAVING cnt > 1
) AS t2
ON t1.activity = t2.activity AND t1.class = t2.class
WHERE user = 'first' -- remove records of the other users
GROUP BY t1.name, t1.activity, t1.class -- select distinct records
SQLFiddle
If class is unique in the activity then you can remove activity from the GROUP BY-statement.
I would like to know if there is a way in mysql to check if the query produces some results and if no do execute query. Example:
(SELECT * FROM table WHERE id=2) IF NO RESULT (SELECT * FROM table WHERE id=4)
EDIT
I need only one query, basically first I check if there a result with a param (ex status=0) if there is no result I would like to execute the same query with the param changed to status=2.
Hope it can help
MORE EDIT
Basically I have a table with operatorators and departments and another one with all the users, first I check if there is an available operator in the first table and it's not on holiday, if there is no result I will lselect and admin from the second table, but only if there is no operator availbable
MORE MORE EDIT
This query check if there is an operator available but it doesn't select the admin
query = "SELECT b.id
FROM ".$SupportUserTable." b
INNER JOIN ".$SupportUserPerDepaTable." a
ON b.id=a.user_id
WHERE a.department_id=? AND b.holiday='0' AND a.user_id!=".$_SESSION['id']."
ORDER BY b.assigned_tickets,b.solved_tickets ASC LIMIT 1";
Lastest Solution
This is not exactly what I was looking for, but it works, I'm open to improvments to avoid the execution of two queries:
$query = "SELECT *
FROM(
(SELECT b.id
FROM ".$SupportUserTable." b
INNER JOIN ".$SupportUserPerDepaTable." a
ON b.id=a.user_id
WHERE a.department_id=? AND b.holiday='0' AND a.user_id!=".$_SESSION['id']."
ORDER BY b.assigned_tickets,b.solved_tickets ASC LIMIT 1)
UNION
(SELECT id
FROM ".$SupportUserTable."
WHERE status='2' AND id!=".$_SESSION['id']."
ORDER BY assigned_tickets,solved_tickets ASC LIMIT 1)
) tab
LIMIT 1
";
SELECT * FROM table WHERE id = (
SELECT id FROM table WHERE id IN (2,4) ORDER BY id LIMIT 1
)
You can do like this
Select IF(
(SELECT count(*) FROM table WHERE id=2), (SELECT * FROM table WHERE id=2),(SELECT * FROM table WHERE id=4))
select t.*
from
(SELECT * FROM table
WHERE id in (2,4)
LIMIT 1)t
order by t.id
I have the following MySQL statement
SELECT * FROM user_messages AS T WHERE user_id = '33' AND id = (SELECT Max(id) from user_messages AS TT WHERE T.from_userid = TT.from_userid) ORDER BY status, id DESC
The problem I seem to be having is when I only have one record. I would think that MySQL would return the single record associated with user_link = '33', but instead it returns nothing.
I need to use the "Max" function because I use it to pull the most recent entries. I am trying to avoid having multiple queries or having to use php to sort also. Any help much appreciated!
This is your query:
SELECT *
FROM user_messages AS T
WHERE user_id = '33' AND
id = (SELECT Max(id)
from user_messages AS TT
WHERE T.from_userid = TT.from_userid
)
ORDER BY status, id DESC
Here are three reasons it could be failing to return any rows. First, user_id = '33' may not exist in the table. Second from_userid may be NULL. Third, the id value may be NULL for all matching records.
Perhaps this simpler version would help:
select *
from user_messages um
where user_id = '33'
order by id desc
limit 1
Thanks for your answer Gordon, I checked the database, and the record exists. I did some more research, and what it turns out to be is that I needed to join the data. I was able to return the Min or Max record, but the corresponding/related fields weren't returned with it.
SELECT * FROM user_messages INNER JOIN(SELECT from_username, MAX(id) AS id FROM user_messages WHERE user_link = '33' GROUP BY from_username ORDER BY status, id DESC) t2 ON user_messages.id = t2.id AND user_messages.from_username = t2.from_username
The thread that answered the question was this one - Need To Pull Most Recent Record By Timestamp Per Unique ID