SELECT statement testing max values - mysql

I'm trying to get the ID where the Upper value is less than/equal to a given value.
myTable
(`ID`, `Lower`, `Upper`)
(1, 1, 9),
(2, 10, 49),
(3, 50, 99),
(4, 100, 499),
(5, 500, 999),
(6, 1000, 4999),
(7, 5000, 9999);
I've tried:
SELECT ID
FROM myTable
WHERE Lower>=3 AND Upper<=3;
and
SELECT ID
FROM myTable
WHERE Upper<=3
ORDER BY ID DESC;
and
SELECT ID
FROM myTable
GROUP BY ID HAVING MAX(Upper)<=3
ORDER BY MAX(Upper);
and
SELECT *
FROM myTable t1
WHERE t1.Upper <= (
SELECT (MAX(t2.Upper))
FROM myTable t2
);
all of which return empty rows.
The option:
SELECT ID
FROM myTable
WHERE Upper<=10
ORDER BY ID DESC;
works where the test value is greater than 9...
Can anyone suggest a solution that might work?

The syntax on
SELECT ID
FROM myTable
WHERE Upper<=3
ORDER BY ID DESC;
Works just fine on MYSQL? double check for spelling errors or syntactical errors perhaps?

If you want to return NULL where there is no match instead of "no rows", you can try something like:
SELECT max(ID)
FROM myTable
WHERE Upper<=3;

I think I get it now - you want the id of the maximum Upper value, as long as it is less than or equal to your "test" value. Is that correct?
select id
from myTable
where Upper <= X
order by Upper desc
LIMIT 1;
So if X = 9 you would get ID: 1
If X = 10 you would also get ID: 1
If X = 163 you would get ID: 3

Related

MySQL and using SELECT from custom position

I have a MySQL problem that I can't figure out.
I run a query:
SELECT id, totalsum FROM table ORDER BY totalsum DESC
This could give me the following result:
1, 10000
4, 90000
8, 80000
3, 50000
5, 40000
++++
What is need is a code that should work something like this:
SELECT id, totalsum
FROM table ORDER BY totalsum DESC
START LISTING FROM id=8 AND CONTINUE TO THE END OF RESULT / LIMIT
Resulting in someting like this
8, 80000
3, 50000
5, 40000
++++
I can not use this query:
SELECT id, totalsum
FROM table
WHERE id>=8
ORDER BY totalsum DESC
Because the id could be both < and >.
Have tried using LIMIT AND OFFSET but that resulting in very slow speed.
Any advice pointing me in the right direction will be appreciated!
Here's a way to do it:
Assign each row a row_num based on totalsum in descending order (CTE)
Select from the above where row_num >= the row_num of id=8
create table a_table (
id int,
total int);
insert into a_table values
(1, 100000),
(4, 90000),
(8, 80000),
(3, 50000),
(5, 40000);
with cte as (
select id,
total,
row_number() over (order by total desc) as row_num
from a_table)
select *
from cte
where row_num >= (select row_num from cte where id=8);
Result:
id|total|row_num|
--+-----+-------+
8|80000| 3|
3|50000| 4|
5|40000| 5|
EDIT:
The above query may return wrong result if other rows have the same total. A comment said it well, just use the following query can do the job:
select id, total
from a_table
where total <= (select total from a_table where id=8)
order by total desc;

How to select rows that have the same value in a column, without knowing that value in advance?

I have a table of records
Table records(id, docId, title)
Given an id, I would like to select all rows equal to or less than the id, that share the same docId. I do not know the docId in advance.
Here is some sample data:
insert into records (id, docId, title) values
(1, 1, 'a'),
(2, 1, 'b'),
(3, 2, 'c'),
(4, 1, 'd')
I can do this with two selects by doing something like so...
select #docId := docId from records where id = 4;
select id, title from records where docId = #docId and id <= 4;
...resulting in...
[{ id: 4, title: 'd'},{id: 2, title: 'b'},{id: 1, title: 'a'}]
I am wondering: Is it possible to do this in a single query?
You could join both queries:
SELECT id, title
FROM records a
JOIN records b ON a.docId = b.docId AND a.id < b.id
WHERE b.id = 4;
I do not know if I understood your question, but if you need to search records with only one query, it may be that subselect helps you ...
select r.id, r.title
from records r
where r.docId in (
select r2.docId
from records r2
where r2.id = 4
)
and r.id <= 4
I hope I've helped. Best whises.

SQL query to implement multiple WHERE condition on same column

I have two tables, tbl1 and tbl2 as below:
CREATE TABLE tbl1 (`uid` int);
INSERT INTO tbl1 (`uid`)
VALUES
(100),
(200),
(300),
(400);
CREATE TABLE tbl2 (`id` int, `uid` int, `status` int);
INSERT INTO tbl2 (`id`, `uid`, `status`)
VALUES
(1, 100, 0),
(2, 100, 1),
(3, 100, 2),
(4, 100, 4),
(5, 200, 0),
(6, 200, 1),
(7, 300, 0),
(8, 300, 3),
(9, 300, 4),
(10, 400, 1),
(11, 400, 2);
SQLFIDDLE:
http://sqlfiddle.com/#!2/1a6c20/13
Problem:
I want to join these two tables.
The result should show the rows having tbl2.status = 0 but not having tbl2.status = 1.
This is the SQL query which I'm trying to run:
SELECT DISTINCT tbl1.uid, tbl2.id, tbl2.status
FROM tbl1
INNER JOIN tbl2 ON (tbl1.uid = tbl2.uid)
WHERE tbl2.status = 0
OR tbl2.status <> 1;
CORRECT expected result is: 7, 300, 0.
Here, uid=300 has a row with status=0 and this uid=0 has no row with status=1. So this is the expected result that I want.
uid=100 has both status=0 and status=1, so this is not the required result.
uid=200 also has both status=0 and status=1 so this is not the required result.
uid=400 does not have status=0, this is not the required result.
Help please!!!
You need to select uids with status=0 but not the ones that appear in your table also with status=1. So you need to exclude them from the result set. You need one more condition in your where clause to have the expected result. This can be done by using NOT IN.
Try the following query
SELECT tbl1.uid, tbl2.id, tbl2.status
FROM tbl1
INNER JOIN tbl2 ON (tbl1.uid = tbl2.uid)
WHERE tbl2.status = 0
and tbl2.uid NOT IN (SELECT uid from tbl2 where status=1);
you can use NOT EXISTS clause
SELECT DISTINCT T1.uid, T2.id, T2.status
FROM tbl1 T1
INNER JOIN tbl2 T2 ON (T1.uid = T2.uid)
WHERE T2.status = 0
AND NOT EXISTS ( SELECT 1 FROM tbl2 T22
where T2.uid = T22.uid
and T22.status =1 )
There is a logic error with the OR in the WHERE clause.
The clause WHERE tbl2.status = 0 produces the desired result:
(7, 300, 0)
The clause OR tbl2.status <> 1 produces
(7, 300, 0)
(8, 300, 3)
(9, 300, 4)
Since this is an OR the union is taken, and you get all three tuples.
People new to SQL often find OR to be tricky. I used to keep truth tables near me when an unexpected result confused me.
Remove
OR tbl2.status <> 1
It is illogical (if the column equals 0 then it can't equal 1) and confuses the query.
If you want all unique uids where status equals 0, but never equals 1, then use a subquery, with the AND logic;
WHERE tbl2.status = 0
AND tbl2.uid NOT IN (SELECT uid FROM tbl2 WHERE status=1)
This selects all the rows where status equals 0, and then removes the rows where the same uid has a status that equals 1. This will give you the expected result you gave in the question.
If you want all rows except those where status equals 1, use;
WHERE tbl2.status <> 1
Which gives you exactly the same result as the current query, the tbl2.status = 0 is irrelevant.
Putting OR doesn't make sense, just remove the OR part and Try this:
SELECT DISTINCT tbl1.uid, tbl2.id, tbl2.status
FROM tbl1
INNER JOIN tbl2 ON (tbl1.uid = tbl2.uid)
WHERE status = 0;

SQL select top 3 or more if there are more items sharing the 3rd highest value?

Any SQL experts out there who can explain how I select say the top 3 items. When the 3rd item is equal to the 4th and 5th I'd like those included too but only in that situation. e.g. with the following list
40,
30,
15,
15,
15,
10
it would return 40,30,15,15,15 not 40,30,15.
If you want to output only the score field, you can do it like this:
SET #ranks = 3;
PREPARE stmt_top3 FROM
'SELECT score FROM (
SELECT score, (#row := #row + 1), IF (#row = ?, #min_score := score, NULL)
FROM user_score
WHERE score IN (
SELECT top_score.score FROM (
SELECT DISTINCT score,
(#row := 0), (#min_score := 0)
FROM user_score
ORDER BY score DESC
LIMIT ?
) AS top_score
)
ORDER BY score DESC
) AS score_rows
WHERE score >= #min_score
ORDER BY score DESC';
EXECUTE stmt_top3 USING #ranks, #ranks;
DEMO # SQL Fiddle
I used a prepared statement, so the number of ranks to show is flexible. If you don't want that, just hardcode a 3 instead of the 2 ?.
Otherwise, if you also need the rank and the user_id, i.e., here's a more extensive approach:
Highscore-Like Ranking (Shared Ranks)
Selects all rows that share the first rank, no matter how many there are.
As long as the number of these rows is not greater than or equal to the maximum of ranks allowed (3 in your example) the next rank is the number of rows + 1.
And everything begins from the start and so on ...
SET #ranks = 3;
PREPARE stmt_top3 FROM
'SELECT user_score.user_id, score_rank.rank, score_rank.score
FROM user_score
INNER JOIN (
SELECT (#last_rank := #last_rank + #last_equal_score) AS rank,
score, (#last_equal_score := count(score)) AS equal_score
FROM user_score
WHERE score IN (
SELECT top_score.score FROM (
SELECT DISTINCT score,
(#last_rank := 1), (#last_equal_score := 0)
FROM user_score
ORDER BY score DESC
LIMIT ?
) AS top_score
)
GROUP BY score
ORDER BY score DESC
) AS score_rank
ON user_score.score = score_rank.score
WHERE score_rank.rank <= ?
ORDER BY score_rank.rank ASC';
EXECUTE stmt_top3 USING #ranks, #ranks;
DEMO # SQL Fiddle
Due to the fact that MySQL does not support LIMIT in subqueries for certain subquery operators such as IN, you have to wrap your subquery that contains a LIMIT clause in another simple subquery to avoid the following error:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT
& IN/ALL/ANY/SOME subquery'
Restrictions on Subqueries
I used the following setup for testing:
CREATE TABLE user (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
) ENGINE=InnoDB;
INSERT INTO user (id) VALUES
(NULL),
(NULL),
(NULL),
(NULL),
(NULL),
(NULL);
CREATE TABLE user_score (
user_id INT UNSIGNED NOT NULL UNIQUE,
score INT NOT NULL DEFAULT 0,
INDEX (score),
FOREIGN KEY (user_id) REFERENCES user (id)
) ENGINE=InnoDB;
INSERT INTO user_score (user_id, score) VALUES
(1, 40),
(2, 30),
(3, 15),
(4, 15),
(5, 15),
(6, 10);
SELECT *
FROM myTable
WHERE Field1 IN(
SELECT *
FROM (
SELECT DISTINCT Field1
FROM myTable
ORDER BY Field1 DESC
LIMIT 3)
) t
The only issue you might have from your example is if you specifically DON'T want to include duplicates of 40 & 30?

SQL: how to select randomly and order by higest

i have a schema look like this:
CREATE TABLE users
(
id int auto_increment primary key,
name varchar(20),
point int(255)
);
INSERT INTO users
(name, point)
VALUES
('Jack', 1),
('Rick', 5),
('Danny', 11),
('Anthony', 24),
('Barla', 3),
('James', 15),
('Melvin', 12),
('Orthon', 5),
('Kenny', 2),
('Smith', 30),
('Steven', 27),
('Darly', 45),
('Peter', 44),
('Parker', 66),
('Lola', 78),
('Jennifer', 94),
('Smart', 87),
('Jin', 64),
('David', 31),
('Jill', 78),
('Ken', 48),
('Martin', 19),
('Adrian', 20),
('Oliver', 16),
('Ben', 100);
and my sql is:
select id, name, point from users Order by point desc, rand() LIMIT 5
problem is, my query does not select 5 row randomly and order them by point. Any idea, how to solve it? here is sqlfiddle:
http://sqlfiddle.com/#!2/18f15/1
select id,name,point from
(select id, name, point from users Order by rand()
LIMIT 5) abc
order by point desc;
SQLFIDDLE
problem is, my query does not select 5 row randomly and order them by point.
It's because in your given Query you are using ORDER BY clause.
select id, name, point from users Order by point desc, rand() LIMIT 5
Try with removing point desc, in ORDER BY clause
select id, name, point from users Order by rand() LIMIT 5
SQL FIDDLE
Edit
select id,name,pont from
(select id, name, point from users Order by rand() LIMIT 5)temp
order by point desc
Note: There should be no Table with name temp in you DB (i.e. since you are using it in you alias)