SELECT value BEFORE max mysql - mysql

so, i have this table generated annually :
+----+------+
| id | name |
+----+------+
| 1 | 20162|
| 2 | 20162|
| 3 | 20171|
| 4 | 20171|<<<||| "how do i get this bfore max value"
| 5 | 20172|
| 6 | 20172|
+----+------+
If i query :
SELECT name FROM table WHERE where name=(SELECT max(name))
The result is 20172
How do i get the value before that (20171)?

This will get second max.
SELECT *
FROM table
WHERE name NOT IN (SELECT MAX(name) FROM table )
ORDER BY id DESC LIMIT 1

You can do the little trick:
SELECT name FROM table ORDER BY name DESC LIMIT 1,1
If you want a portable solution (since the above will only work in MySQL probably):
SELECT name FROM table t1 where
1 = (SELECT count(distinct name)
from table t2
where t2.name > t1.name )
This selects the name which has exactly one other name which is greater than it. It will have issues when e.g. all values are the same but that may actually be the intention sometimes.

Also you can try this:
SELECT name
FROM (SELECT DISTINCT name FROM yourtable) TMP
ORDER BY name DESC LIMIT 1,1

how about this:
select b.second_id,b.name from
(select id, max(name) as max from tbl_max) as a
LEFT JOIN
(select id as second_id, name from tbl_max ) as b
on a.id <> second_id where name < a.max order by name desc limit 1;
Another solution much better:
set #max= 0;
select max(name) into #max from tbl_max;
select * from tbl_max where name < #max ORDER BY id desc limit 1
it return a result of
4 20171

If you want Max(name)
SELECT id,MAX(name) FROM table
WHERE name < (SELECT MAX(name) FROM table )
or
SELECT id,MAX(name) FROM table
WHERE name <> (SELECT MAX(name) FROM table )
And there is an article for Find nth high value
(as Question changed later, this tricks is not useful for this problem)
can you use this simple order by ?
SELECT * FROM tablename
group by name
order by name desc
limit 1,1
if you have multiple equal values in MAX(name) you need group by, even this query is not general solution!

You can try below code.It will give you nth highest record.
SELECT NAME FROM table ORDER BY name DESC LIMIT n,1
Hope this will helps

SELECT DISTINCT name FROM table ORDER BY name DESC LIMIT 1,1;
The LIMIT clause accepts two values, an offset and a count. When only 1 value is provided, it assumes the offset is 0
DISTINCT is necessary as you have duplicate values for name. If this was unintentional, it's not needed.
Example at http://sqlfiddle.com/#!9/ea6e2/2

Related

How can I select the second-to-last rows in a mysql table, grouped by column?

Structure is:
CREATE TABLE current
(
id BIGINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
symbol VARCHAR(5),
UNIQUE (id), INDEX (symbol)
) ENGINE MyISAM;
id
symbol
1
A
2
B
3
C
4
C
5
B
6
A
7
C
8
C
9
A
10
B
I am using the following
SELECT *
FROM current
WHERE id
IN
(
SELECT MAX(id)
FROM current
GROUP BY symbol
)
to return the last records in a table.
id
symbol
8
C
9
A
10
B
How can I return the next-to-last results in a similar fashion?
I know that I need
ORDER BY id DESC LIMIT 1,1
somewhere, but my foo is weak.
I would want to return
id
symbol
5
B
6
A
7
C
For versions of MySql prior to 8.0, use a subquery in the WHERE clause to filter out the max id of each symbol and then aggregate:
SELECT MAX(id) id, symbol
FROM current
WHERE id NOT IN (SELECT MAX(id) FROM current GROUP BY symbol)
GROUP BY symbol
ORDER BY id;
See the demo.
SELECT *
FROM current
WHERE id IN (
SELECT DISTINCT T.id FROM current AS T
WHERE id=(
SELECT id FROM current
WHERE symbol=T.symbol
ORDER BY id DESC LIMIT 1,1
)
)
Easy if your MySql can use ROW_NUMBER. (MySql 8)
Just make it sort descending, then take the 2nd.
WITH CTE AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY id DESC) AS symbol_rn
FROM current
)
SELECT id, symbol
FROM CTE
WHERE symbol_rn = 2
ORDER BY id;
In MySql 7.5 you can simply self-join on the symbol, and group by.
Then the 2nd last will have 1 higher id.
SELECT c1.id, c1.symbol
FROM current c1
LEFT JOIN current c2
ON c2.symbol = c1.symbol
AND c2.id >= c1.id
GROUP BY c1.id, c1.symbol
HAVING COUNT(c2.id) = 2
ORDER BY c1.id;
id
symbol
5
B
6
A
7
C
db<>fiddle here
The performance will really benefit from an index on symbol.
You can try this;
SELECT *
FROM current
WHERE id
IN (SELECT MAX(id)
FROM current
GROUP BY symbol)
ORDER BY id DESC LIMIT 1,3
limit 1,3 says; get the last 3 results excluding the last result. You can change the numbers.

SQL select maximum number of duplicates value in a column

Here I have this table:
Copies
nInv | Subject | LoanDate | BookCode |MemberCode|
1 |Storia |15/04/2019 00:00:00 |7844455544| 1 |
2 |Geografia |12/09/2020 00:00:00 |8004554785| 4 |
4 |Francese |17/05/2006 00:00:00 |8004894886| 3 |
5 |Matematica |17/06/2014 00:00:00 |8004575185| 3 |
I'm trying to find the value of the highest number of duplicates in the MemberCode column. So in this case I should get 3 as result, as its value appears two times in the table. Also, MemberCode is PK in another table, so ideally I should select all rows of the second table that match the MemberCode in both tables. For the second part I guess I should write something like SELECT * FROM Table2, Copies WHERE Copies.MemberCode = Table2.MemberCode but I'm missing out almost everything on the first part. Can you guys help me?
Use group by and limit:
select membercode, count(*) as num
from t
group by membercode
order by count(*) desc
limit 1;
SELECT MAX(counted) FROM
(SELECT COUNT(MemberCode) AS counted
FROM table_name GROUP BY MemberCode)
Using analytic functions, we can assign a rank to each member code based on its count. Then, we can figure out what its count is.
WITH cte AS (
SELECT t2.MemberCode, COUNT(*) AS cnt,
RANK() OVER (ORDER BY COUNT(*) DESC, t2.MemberCode) rnk
FROM Table2 t2
INNER JOIN Copies c ON c.MemberCode = t2.MemberCode
GROUP BY t2.MemberCode
)
SELECT cnt
FROM cte
WHERE rnk = 1;
Something like this
with top_dupe_member_cte as (
select top(1) MemberCode, Count(*)
from MemberTable
group by MemberCode
order by 2 desc)
select /* columns from your other table */
from OtherTable ot
join top_dupe_member_cte dmc on ot.MemberCode=dmc.MemberCode;

Mysql SELECT first row with value found or last row if no value

I would like to select row where first value found or last row if no value found.
id | customer | default_address
--------------------------------
1 John 0
2 Will 0
3 David null
4 Joe 0
In this case, i would like to have row with id = 4 because there's no default_address with value 1.
id | customer | default_address
--------------------------------
1 John 0
2 Will 1
3 David null
4 Joe 0
In this case, i need row 2 because i need default_address with value 1.
Default address can have values 0,1 and null.
Not so fancy, but I guess this should work and run pretty fast
select * from
(
(select * from your_table where default_address = 1 order by id limit 1)
union all
(select * from your_table order by id desc limit 1)
) tmp
limit 1
SQLFiddle demo
You can achieve this with a single query with conditional ordering using CASE EXPRESSION:
SELECT * FROM YourTable t
ORDER BY CASE WHEN t.default_address = 1 THEN t.id ELSE 999999999 end ASC,
t.id DESC
LIMIT 1;
This way, IF there is a record with default_address = 1 it will be ordered first, if not, the last ID will be ordered first, and then it will pick the first one using LIMIT 1.
Note that I used the value 999999999 in the case so it will choose the first one in case there is more then 1 row with default_address = 1, this number just have to be larger then your biggest ID in the table in order for this to work properly.
You can use this Sub-Query.
SELECT * FROM(
(SELECT * FROM your_table WHERE default_address = 1 ORDER BY id LIMIT 1)
UNION ALL
(SELECT * FROM your_table ORDER BY id DESC limit 1)
) tmp
limit 1

Order mysql query in the same order I provide the OR statements in

Here's a query:
SELECT *
FROM table
WHERE id = 1
OR id = 100
OR id = 50
Note that I provided the ids in this order: 1,100,50.
I want the rows to come back in that order: 1,100,50.
Currently, i comes back 1,50,100 - basically in ascending order. Assume the rows in the table were inserted in ascending order also.
Use the MySQL specific FIND_IN_SET function:
SELECT t.*
FROM table t
WHERE t.id IN (1, 100, 50)
ORDER BY FIND_IN_SET(CAST(t.id AS VARCHAR(8)), '1,100,50')
Another way to approach this would put the list in a subquery:
select table.*
from table join
(select 1 as id, 1 as ordering union all
select 100 as id, 2 as ordering union all
select 50 as id, 3 as ordering
) list
on table.id = list.id
order by list.ordering
You can just do this with ORDER BY:
ORDER BY
id = 1 DESC, id = 100 DESC, id = 50 DESC
0 is before 1 in ORDER BY.
Try this
SELECT *
FROM new
WHERE ID =1
OR ID =100
OR ID =50
ORDER BY ID=1 DESC,ID=100 DESC,ID=50 DESC ;
http://www.sqlfiddle.com/#!2/796e2/5
... WHERE id IN (x,y,x) ORDER BY FIELD (id,x,y,z)

MySQL sorting by date with GROUP BY

My table titles looks like this
id |group|date |title
---+-----+--------------------+--------
1 |1 |2012-07-26 18:59:30 | Title 1
2 |1 |2012-07-26 19:01:20 | Title 2
3 |2 |2012-07-26 19:18:15 | Title 3
4 |2 |2012-07-26 20:09:28 | Title 4
5 |2 |2012-07-26 23:59:52 | Title 5
I need latest result from each group ordered by date in descending order. Something like this
id |group|date |title
---+-----+--------------------+--------
5 |2 |2012-07-26 23:59:52 | Title 5
2 |1 |2012-07-26 19:01:20 | Title 2
I tried
SELECT *
FROM `titles`
GROUP BY `group`
ORDER BY MAX( `date` ) DESC
but I'm geting first results from groups. Like this
id |group|date |title
---+-----+--------------------+--------
3 |2 |2012-07-26 18:59:30 | Title 3
1 |1 |2012-07-26 19:18:15 | Title 1
What am I doing wrong?
Is this query going to be more complicated if I use LEFT JOIN?
This page was very helpful to me; it taught me how to use self-joins to get the max/min/something-n rows per group.
In your situation, it can be applied to the effect you want like so:
SELECT * FROM
(SELECT group, MAX(date) AS date FROM titles GROUP BY group)
AS x JOIN titles USING (group, date);
I found this topic via Google, looked like I had the same issue.
Here's my own solution if, like me, you don't like subqueries :
-- Create a temporary table like the output
CREATE TEMPORARY TABLE titles_tmp LIKE titles;
-- Add a unique key on where you want to GROUP BY
ALTER TABLE titles_tmp ADD UNIQUE KEY `group` (`group`);
-- Read the result into the tmp_table. Duplicates won't be inserted.
INSERT IGNORE INTO titles_tmp
SELECT *
FROM `titles`
ORDER BY `date` DESC;
-- Read the temporary table as output
SELECT *
FROM titles_tmp
ORDER BY `group`;
It has a way better performance. Here's how to increase speed if the date_column has the same order as the auto_increment_one (you then don't need an ORDER BY statement) :
-- Create a temporary table like the output
CREATE TEMPORARY TABLE titles_tmp LIKE titles;
-- Add a unique key on where you want to GROUP BY
ALTER TABLE titles_tmp ADD UNIQUE KEY `group` (`group`);
-- Read the result into the tmp_table, in the natural order. Duplicates will update the temporary table with the freshest information.
INSERT INTO titles_tmp
SELECT *
FROM `titles`
ON DUPLICATE KEY
UPDATE `id` = VALUES(`id`),
`date` = VALUES(`date`),
`title` = VALUES(`title`);
-- Read the temporary table as output
SELECT *
FROM titles_tmp
ORDER BY `group`;
Result :
+----+-------+---------------------+---------+
| id | group | date | title |
+----+-------+---------------------+---------+
| 2 | 1 | 2012-07-26 19:01:20 | Title 2 |
| 5 | 2 | 2012-07-26 23:59:52 | Title 5 |
+----+-------+---------------------+---------+
On large tables this method makes a significant point in terms of performance.
Well, if dates are unique in a group this would work (if not, you'll see several rows that match the max date in a group). (Also, bad naming of columns, 'group', 'date' might give you syntax errors and such specially 'group')
select t1.* from titles t1, (select group, max(date) date from titles group by group) t2
where t2.date = t1.date
and t1.group = t2.group
order by date desc
Another approach is to make use of MySQL user variables to identify a "control break" in the group values.
If you can live with an extra column being returned, something like this will work:
SELECT IF(s.group = #prev_group,0,1) AS latest_in_group
, s.id
, #prev_group := s.group AS `group`
, s.date
, s.title
FROM (SELECT t.id,t.group,t.date,t.title
FROM titles t
ORDER BY t.group DESC, t.date DESC, t.id DESC
) s
JOIN (SELECT #prev_group := NULL) p
HAVING latest_in_group = 1
ORDER BY s.group DESC
What this is doing is ordering all the rows by group and by date in descending order. (We specify DESC on all the columns in the ORDER BY, in case there is an index on (group,date,id) that MySQL can do a "reverse scan" on. The inclusion of the id column gets us deterministic (repeatable) behavior, in the case when there are more than one row with the latest date value.) That's the inline view aliased as s.
The "trick" we use is to compare the group value to the group value from the previous row. Whenever we have a different value, we know that we are starting a "new" group, and that this row is the "latest" row (we have the IF function return a 1). Otherwise (when the group values match), it's not the latest row (and we have the IF function returns a 0).
Then, we filter out all the rows that don't have that latest_in_group set as a 1.
It's possible to remove that extra column by wrapping that query (as an inline view) in another query:
SELECT r.id
, r.group
, r.date
, r.title
FROM ( SELECT IF(s.group = #prev_group,0,1) AS latest_in_group
, s.id
, #prev_group := s.group AS `group`
, s.date
, s.title
FROM (SELECT t.id,t.group,t.date,t.title
FROM titles t
ORDER BY t.group DESC, t.date DESC, t.id DESC
) s
JOIN (SELECT #prev_group := NULL) p
HAVING latest_in_group = 1
) r
ORDER BY r.group DESC
If your id field is an auto-incrementing field, and it's safe to say that the highest value of the id field is also the highest value for the date of any group, then this is a simple solution:
SELECT b.*
FROM (SELECT MAX(id) AS maxid FROM titles GROUP BY group) a
JOIN titles b ON a.maxid = b.id
ORDER BY b.date DESC
Use the below mysql query to get latest updated/inserted record from table.
SELECT * FROM
(
select * from `titles` order by `date` desc
) as tmp_table
group by `group`
order by `date` desc
Use the following query to get the most recent record from each group
SELECT
T1.* FROM
(SELECT
MAX(ID) AS maxID
FROM
T2
GROUP BY Type) AS aux
INNER JOIN
T2 AS T2 ON T1.ID = aux.maxID ;
Where ID is your auto increment field and Type is the type of records, you wanted to group by.
MySQL uses an dumb extension of GROUP BY which is not reliable if you want to get such results therefore, you could use
select id, group, date, title from titles as t where id =
(select id from titles where group = a.group order by date desc limit 1);
In this query, each time the table is scanned full for each group so it can find the most recent date. I could not find any better alternate for this. Hope this will help someone.