How do I select items in range of a SQL query? - mysql

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)

Related

Sort the table in sequential conditions?

I have a users table with 5 columns, id, age, is_premium, is_male, is_customer where id is the primary key.
First statement I do is. This statement has the potential of returning 0 rows:
SELECT * FROM users
WHERE is_premium = 1 AND
is_name = 0 AND
is_customer = 0
Then ONLY from the rows I got from the above query, I want to find the person with the largest age.
SELECT * FROM <from the above query>
WHERE id = (SELECT MAX(ID) <from the above query>)
Question: How do make these 2 separate SQL statements into a single statement and what is the most efficient way of doing this?
why not directly:
SELECT *
FROM users
WHERE is_premium = 1
AND is_name = 0
AND is_customer = 0
ORDER BY age DESC, id ASC
LIMIT 1
for mysql version 8 and above you can also use common table expressions (CTE):
WITH D AS (
SELECT * FROM users
WHERE is_premium = 1
AND is_name = 0
AND is_customer = 0
)
SELECT *
FROM D
WHERE AGE = (SELECT MAX(AGE) FROM D)
ORDER BY ID
LIMIT 1
Assuming you have a primary key column called id, just move the query in a sub-query:
SELECT *
FROM users
WHERE id = (
SELECT id
FROM users
WHERE is_premium = 1 AND is_name = 0 AND is_customer = 0
ORDER BY age DESC
LIMIT 1
)
suppose you will have multiple users with same max() age
select * from users t1
inner join (
select max(age) maxage from users
where is_premium=1 and is_name=0 and is_customer=0) t2
on t2.maxage = t1.age
where is_premium=1 and is_name=0 and is_customer=0
or if you don't want to repeat your conditions.
select * from users t1
inner join(
select
(select max(t.age) from users t where t.id = t2.id) as maxage,
t2.id
from users t2
where is_premium=1 and is_name=0 and is_customer=0) t3 on t3.id = t1.id

How to remove duplicates from table using join in mysql

select *
from
(
SELECT id, imei1, status
FROM `owarranty_imei` mto
WHERE EXISTS
(
SELECT 1
FROM `owarranty_imei` mti
WHERE mto.imei1=mti.imei1
LIMIT 1, 1
)
) t1
left join `owarranty_warranty_activations` as t2 on t1.id=t2.imei_id
where t2.id is null
limit 100
this is my query. In owarranty_imei has more than 100000 records. i want to get duplicates from imei table which owarranty_imei not in owarranty_warranty_activation table. This query work for few records but when i run it for more than 1000000 records its not working
SELECT
mto.id,
mto.imei1,
mto.status
FROM
`owarranty_imei` mto
INNER JOIN
`owarranty_imei` mti ON mto.imei1=mti.imei1
LEFT JOIN
`owarranty_warranty_activations` as t2 ON mto.id=t2.imei_id
GROUP BY mto.id
HAVING COUNT(t2.id)=0

Select rows with most frequent values of one column MySQL [duplicate]

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

mysql query that has join and counts

I need help getting the top 5 results and their counts from columns from two different tables in a mysql database joined together.
table1 cols
-------
id, country, timestamp
table2 cols
--------
id, table1_id, reason
The results id like to get are the top 5 countries and their number of times found between two timestamps, and the top 5 reasons and their counts for all the rows used to generate the first count. There is a one to many relationship between table1 and table2. This is stumping me and I appreciate any insight you could give me.
It's not entirely clear what resultset you want to return.
This may be of some help to you:
SELECT t.country
, COUNT(DISTINCT t.id) AS count_table1_rows
, COUNT(r.id) AS count_table2_rows
, COUNT(*) AS count_total_rows
FROM table1 t
LEFT
JOIN table2 r
ON r.table1_id = t.id
WHERE t.timestamp >= NOW() - INTERVAL 7 DAY
AND t.timestamp < NOW()
GROUP BY t.country
ORDER BY COUNT(DISTINCT t.id) DESC
LIMIT 5
That will return a maximum of 5 rows, one row per country, with counts of rows in table1, counts of rows found in table2, and a count of the total rows returned.
The LEFT keyword specifies an "outer" join operation, such that rows from table1 are returned even if there are no matching rows found in table2.
To get the count for each "reason", associated with each country, you could do something like this:
SELECT t.country
, COUNT(DISTINCT t.id) AS count_table1_rows
FROM table1 t
LEFT
JOIN ( SELECT s.country
, r.reason
, COUNT(*) AS cnt_r
FROM table1 s
JOIN table2 r
ON s.table1_id = t.id
WHERE s.timestamp >= NOW() - INTERVAL 7 DAY
AND s.timestamp < NOW()
GROUP
BY s.country
, r.reason
) u
ON u.country = t.country
WHERE t.timestamp >= NOW() - INTERVAL 7 DAY
AND t.timestamp < NOW()
GROUP
BY t.country
, u.reason
ORDER
BY COUNT(DISTINCT t.id) DESC
, t.country DESC
, u.cnt_r DESC
, u.reason DESC
This query doesn't "limit" the rows being returned. It would be possible to modify the query to have only a subset of the rows returned, but that can get complex. And before we muck the complexity of adding "top 5 within top 5" type limits, we want to ensure that the rows returned by a query are a superset of the rows we actually want.
Is this what you want?
select t2.reason, count(*)
from (select t1.country, count(*)
from table1 t1
where timestamp between #STARTTIME and #ENDTIME
group by country
order by count(*) desc
limit 5
) c5 join
table1 t1
on c5.country = t1.country and
t1.timestamp between #STARTTIME and #ENDTIME join
table2 t2
on t2.table1_id = t1.id
group by t2.reason;
The c5 subquery gets the five countries. The other two bring back the data for the final aggregation.

SQL select the last 3 dates from a table

I have a table with lots of fields in mysql
I need a query to return (in the same raw!) the top last 3 dates (dates can have large gaps between them)
ie:
2012/01/20
2012/01/18
2012/01/12
2012/01/10
2012/01/04
etc...
Any help will be appreciated
I must get them in the same row!
This is the query I am trying to use with no success:
SELECT a.id, a.thedate, b.id AS id1, b.thedate AS thedate1, c.id AS id2, c.thedate as thedate2
FROM mytable AS a INNER JOIN mytable AS b ON a.id = b.id INNER JOIN mytable AS c ON b.id=c.id
WHERE c.thedate = SELECT MAX(thedate)
EDIT :
SELECT group_concat(date) FROM (SELECT date FROM my_table ORDER BY date DESC LIMIT 3) AS temp
Corrected-
SELECT group_concat(date) FROM ( select date from table_name order by date desc limit 3) as a
SELECT GROUP_CONCAT(a.date )
FROM (
SELECT date
FROM my_table
ORDER BY date DESC
LIMIT 3
) AS a