This question already has answers here:
Get top n records for each group of grouped results
(12 answers)
Closed 6 years ago.
I'd like to select all rows from a table which contain 50 most frequent values of the column. I tried to use such a join, but it seems my choice of LEFT JOIN is wrong. The inner part of the statement seems fine. What should I change in my statement?
SELECT col1, col2
FROM tbl as t1
LEFT JOIN (
SELECT id
FROM tbl
WHERE id > 123
AND id < 987654
GROUP BY col1
ORDER BY COUNT(id) DESC
LIMIT 50
) AS t2
ON t1.id = t2.id
Rather than a JOIN, have you tried using an IN operator as part of your WHERE clause?
For example...
SELECT col1, col2
FROM tbl as t1
WHERE t1.id IN (
SELECT id
FROM tbl
WHERE id > 123
AND id < 987654
GROUP BY col1
ORDER BY COUNT(id) DESC
LIMIT 50
)
Right join should work if your subquery is correct, returning any matching rows of t1 to t2 (as opposed to all rows of t1 and the matching t2's or nulls):
SELECT col1, col2
FROM tbl as t1
RIGHT JOIN (
SELECT id
FROM tbl
WHERE id > 123
AND id < 987654
GROUP BY col1
ORDER BY COUNT(id) DESC
LIMIT 50
) AS t2
ON t1.id = t2.id
I realize my query was OK, I just wanted to join on col1, not id :)
SELECT col1, col2
FROM tbl as t1
LEFT JOIN (
SELECT col1
FROM tbl
WHERE id > 123
AND id < 987654
GROUP BY col1
ORDER BY COUNT(id) DESC
LIMIT 50
) AS t2
ON t1.col1 = t2.col1
Related
Here are 2 tables.
Table 1
id value
1 3
2 2
3 3
4 1
5 4
6 3
Table 2
id
1
3
4
How do I get the ids that are in Table 2 which have the max value in Table 1?
Output:
id
1
3
I already tried the following to get the max value, but I cannot figure out how to use it in a single query to get the matching rows. Because I think I need to select from the same table I just inner joined.
select max(table1.value)
from table2
inner join table1 on table1.id = table2.id;
Here is one method:
select t2.id
from (select t2.*, rank() over (order by value desc) as seqnum
from table2 t2 join
table1 t1
on t2.id = t1.id
) t
where seqnum = 1;
Or, an alternative that puts all the ids on one row:
select group_concat(t2.id) as ids
from table2 t2 join
table1 t1
on t2.id = t1.id
group by t1.value
order by t1.value desc
limit 1;
You have a couple of options available without using window functions:
You can use a WHERE clause to select only id values that have a value equal to the MAX(value) from your query and an id that is in Table2:
SELECT t1.id
FROM Table1 t1
WHERE value = (
SELECT MAX(t1.value)
FROM Table2 t2
JOIN Table1 t1 ON t1.id = t2.id
)
AND id IN (SELECT id FROM Table2)
You can JOIN your query to Table1 and Table2 again, matching the value in Table1 and the id in Table2:
SELECT t1.id
FROM (
SELECT MAX(t1.value) AS max_value
FROM Table2 t2
JOIN Table1 t1 ON t1.id = t2.id
) t
JOIN Table1 t1 ON t1.value = t.max_value
JOIN Table2 t2 ON t2.id = t1.id
In both cases the output is
id
1
3
Demo on SQLFiddle
Too low to comment but from the SQL statement you gave, you just need to add the tableid in your select parameters.
select table2.id, max(table1.value)
from table2
inner join table1 on table1.id = table2.id;
Is it possible to convert this SQL into a JOIN?
SELECT (SELECT t2.id
FROM items t2
WHERE t2.user_id = items.user_id
ORDER BY [a list of cols that aren't stated here]
LIMIT 1) AS id
FROM items
WHERE company_name = '....'
GROUP BY user_id
Why both? Just use FIRST_VALUE():
SELECT DISTINCT col3,
FIRST_VALUE(col4) OVER (PARTITION BY col3 ORDER BY col1)
FROM tbl
WHERE col2;
table looks like this:
id group name
1 1 A
2 1 A
3 2 A
4 2 B
5 3 A
I want to select the rows with more than one distinct names in the same group. The result should be the following:
id group name
3 2 A
4 2 B
Any idea how do achieve this?
You can get the groups with aggregation:
select group
from t
group by group
having min(name) <> max(name);
You can get the original rows using join, in, or exists:
select t.*
from t
where t.group in (select group
from t
group by group
having min(name) <> max(name)
);
Note: group is a lousy name for a column because it is a SQL keyword and a MySQL reserved word.
You could do it with a correlated subquery:
SELECT t1.id, t1.group, t1.name
FROM mytable AS t1
WHERE EXISTS (
SELECT * FROM mytable t2
WHERE t2.group=t1.group AND t2.name <> t1.name
);
Or you could do it by counting distinct names in the group:
SELECT t1.id, t1.group, t2.name
FROM mytable AS t1
INNER JOIN (
SELECT t2.group FROM mytable AS t2
GROUP BY t2.group HAVING COUNT(DISTINCT t2.name) > 1
) AS t2 USING (group);
Right now my sql query gives back a valid array of id's and distance pairs.
I want to change my query to just give back id's. Not sure how to rearrange the subquery in my query.
SELECT distinct t2.id, ( 5 * distance_given1 ) AS distance_calculated
FROM table1 t1
inner join table2 t2 on t1.id = t2.m_id
HAVING distance_calculated < distance_given2
ORDER BY distance_calculated LIMIT 0 , 20
Just move the expression into the WHERE clause.
SELECT DISTINCT t2.id
FROM table1 t1
INNER JION table2 t2 ON t1.id = t2.m_id
WHERE (5 * distance_given1) < distance_given2
ORDER BY (5 * distance_given1)
LIMIT 0, 20
If the expression is more complicated and you don't want to repeat it, you can use a subquery:
SELECT id
FROM (SELECT distinct t2.id, ( 5 * distance_given1 ) AS distance_calculated
FROM table1 t1
inner join table2 t2 on t1.id = t2.m_id
HAVING distance_calculated < distance_given2
ORDER BY distance_calculated
LIMIT 0 , 20) AS x
My database is in MySQL.
Assume I have the following table:
id number
1 45
2 25
3 66
4 43
......
......
......
30 54
31 21
etc ... etc.
I want to have a query like so:
select * from myTable where number = 25
but I want to also include 2 more items, one above it and one below it (based on ID).
the result set of my query would turn up with the following result set: 1, 2, 3.
If I selected the number 66, then the result set would include 2, 3, 4. Etc, etc.
The idea would be to range the query by saying, hey, I want anything that has an ID equal to 1 minus this queries' id, and also one plus this queries' id.
I hope this makes sense.
Any help would be great.
Thanks!
P.S.
The point of this is to capture events in a log so that I can see what happened before and after a certain event happened
SELECT t.*
FROM
myTable AS t
JOIN
( SELECT id
FROM myTable
WHERE number = 25
) AS my
ON t.id BETWEEN my.id - 1 AND my.id + 1 ;
Notice that this will not show 3 rows if your ids have gaps.
Also, if the number you choose (25 in the example) appears more than once (but k times), the result will be 3*k rows.
If there are gaps, as expected, in the id column, you can use this:
SELECT *
FROM
( SELECT t.*
FROM
myTable AS t
JOIN
( SELECT MIN(id) AS id
FROM myTable
WHERE number = 25
) AS my
ON t.id <= my.id
ORDER BY t.id DESC
LIMIT 2
) AS a
UNION ALL
SELECT *
FROM
( SELECT t.*
FROM
myTable AS t
JOIN
( SELECT MIN(id) AS id
FROM myTable
WHERE number = 25
) AS my
ON t.id > my.id
ORDER BY t.id ASC
LIMIT 1
) AS b ;
If there are gaps in the id column and the number is not unique so the parameter (25) can appear more than once (but say k times), you can have a query that returns 3*k rows (almost all the times):
SELECT t.*
FROM
myTable AS t
JOIN
( SELECT id
FROM myTable
WHERE number = 25
) AS ti
ON t.id =
( SELECT tt.id
FROM myTable AS tt
WHERE tt.id < ti.id
ORDER BY tt.id DESC
LIMIT 1
)
OR t.id = ti.id
OR t.id =
( SELECT tt.id
FROM myTable AS tt
WHERE tt.id > ti.id
ORDER BY tt.id ASC
LIMIT 1
) ;
Not sure if it will work (I remember there are some limitations on ORDER BY and LIMIT in UNIONed queries, but don't have mysql instance to check it), but what if you try:
(SELECT t2.id
FROM myTable t1
INNER JOIN myTable t2 ON t2.id > t1.id
WHERE t1.number = 25
ORDER BY t2.id
LIMIT 1)
UNION ALL
(SELECT t2.id
FROM myTable t1
INNER JOIN myTable t2 ON t2.id < t1.id
WHERE t1.number = 25
ORDER BY t2.id DESC
LIMIT 1)
SELECT * FROM table WHERE id IN (SELECT id, id-1, id+1 FROM table WHERE number=25)