Select the top N rows from each group - sql-server-2008

Select the top N rows from each group, should return first row of the group.
[sample rows][1]
From the above attached image, display 1, 3 and 4 rows. How to get this, can you help

Try this. Replace MyTable with your table name. This query will return top 2. As you did not mention in which order you want to return Top N records, I used GROUP_Row_ID to order. You can change it as per your need.
DECLARE #n INT = 2;
SELECT *
FROM ( SELECT * ,
ROW_NUMBER() OVER ( PARTITION BY AlternativeId
ORDER BY GROUP_Row_ID ) AS rn
FROM MyTable ) t
WHERE t.rn <= #n;

Related

How to find the top three column totals using SQL

Trying to sum all columns in my table and to find the top 3 of them.
columns have only a value of 1 or 0. That's why I am trying to sum the all inputs to compare them with each other.
I've stucked with order by code integrated into sum()
SELECT (ID)
FROM Student
ORDER BY SUM(C1), SUMC(C2)...SUM(C10)
lIMIT 3
If I understand correctly, you can use union all to calculate the sum for each column and then order by and limit:
select c.*
from ((select 'col1', sum(col1) as s from t) union all
(select 'col2', sum(col2) as s from t) union all
. . .
(select 'col10', sum(col10) as s from t)
) c
order by s desc
limit 3;
try -
SELECT TOP 3 (ID)
FROM Student
GROUP BY (ID)
ORDER BY SUM(C1), SUMC(C2)...SUM(C10) DESC

Query to fetch second highest salary from table [duplicate]

What is the simplest SQL query to find the second largest integer value in a specific column?
There are maybe duplicate values in the column.
SELECT MAX( col )
FROM table
WHERE col < ( SELECT MAX( col )
FROM table )
SELECT MAX(col)
FROM table
WHERE col NOT IN ( SELECT MAX(col)
FROM table
);
In T-Sql there are two ways:
--filter out the max
select max( col )
from [table]
where col < (
select max( col )
from [table] )
--sort top two then bottom one
select top 1 col
from (
select top 2 col
from [table]
order by col) topTwo
order by col desc
In Microsoft SQL the first way is twice as fast as the second, even if the column in question is clustered.
This is because the sort operation is relatively slow compared to the table or index scan that the max aggregation uses.
Alternatively, in Microsoft SQL 2005 and above you can use the ROW_NUMBER() function:
select col
from (
select ROW_NUMBER() over (order by col asc) as 'rowNum', col
from [table] ) withRowNum
where rowNum = 2
I see both some SQL Server specific and some MySQL specific solutions here, so you might want to clarify which database you need. Though if I had to guess I'd say SQL Server since this is trivial in MySQL.
I also see some solutions that won't work because they fail to take into account the possibility for duplicates, so be careful which ones you accept. Finally, I see a few that will work but that will make two complete scans of the table. You want to make sure the 2nd scan is only looking at 2 values.
SQL Server (pre-2012):
SELECT MIN([column]) AS [column]
FROM (
SELECT TOP 2 [column]
FROM [Table]
GROUP BY [column]
ORDER BY [column] DESC
) a
MySQL:
SELECT `column`
FROM `table`
GROUP BY `column`
ORDER BY `column` DESC
LIMIT 1,1
Update:
SQL Server 2012 now supports a much cleaner (and standard) OFFSET/FETCH syntax:
SELECT [column]
FROM [Table]
GROUP BY [column]
ORDER BY [column] DESC
OFFSET 1 ROWS
FETCH NEXT 1 ROWS ONLY;
I suppose you can do something like:
SELECT *
FROM Table
ORDER BY NumericalColumn DESC
LIMIT 1 OFFSET 1
or
SELECT *
FROM Table ORDER BY NumericalColumn DESC
LIMIT (1, 1)
depending on your database server. Hint: SQL Server doesn't do LIMIT.
The easiest would be to get the second value from this result set in the application:
SELECT DISTINCT value
FROM Table
ORDER BY value DESC
LIMIT 2
But if you must select the second value using SQL, how about:
SELECT MIN(value)
FROM ( SELECT DISTINCT value
FROM Table
ORDER BY value DESC
LIMIT 2
) AS t
you can find the second largest value of column by using the following query
SELECT *
FROM TableName a
WHERE
2 = (SELECT count(DISTINCT(b.ColumnName))
FROM TableName b WHERE
a.ColumnName <= b.ColumnName);
you can find more details on the following link
http://www.abhishekbpatel.com/2012/12/how-to-get-nth-maximum-and-minimun.html
MSSQL
SELECT *
FROM [Users]
order by UserId desc OFFSET 1 ROW
FETCH NEXT 1 ROW ONLY;
MySQL
SELECT *
FROM Users
order by UserId desc LIMIT 1 OFFSET 1
No need of sub queries ... just skip one row and select second rows after order by descending
A very simple query to find the second largest value
SELECT `Column`
FROM `Table`
ORDER BY `Column` DESC
LIMIT 1,1;
SELECT MAX(Salary)
FROM Employee
WHERE Salary NOT IN ( SELECT MAX(Salary)
FROM Employee
)
This query will return the maximum salary, from the result - which not contains maximum salary from overall table.
Old question I know, but this gave me a better exec plan:
SELECT TOP 1 LEAD(MAX (column)) OVER (ORDER BY column desc)
FROM TABLE
GROUP BY column
This is very simple code, you can try this :-
ex :
Table name = test
salary
1000
1500
1450
7500
MSSQL Code to get 2nd largest value
select salary from test order by salary desc offset 1 rows fetch next 1 rows only;
here 'offset 1 rows' means 2nd row of table and 'fetch next 1 rows only' is for show only that 1 row. if you dont use 'fetch next 1 rows only' then it shows all the rows from the second row.
Simplest of all
select sal
from salary
order by sal desc
limit 1 offset 1
select * from (select ROW_NUMBER() over (Order by Col_x desc) as Row, Col_1
from table_1)as table_new tn inner join table_1 t1
on tn.col_1 = t1.col_1
where row = 2
Hope this help to get the value for any row.....
Use this query.
SELECT MAX( colname )
FROM Tablename
where colname < (
SELECT MAX( colname )
FROM Tablename)
select min(sal) from emp where sal in
(select TOP 2 (sal) from emp order by sal desc)
Note
sal is col name
emp is table name
select col_name
from (
select dense_rank() over (order by col_name desc) as 'rank', col_name
from table_name ) withrank
where rank = 2
SELECT
*
FROM
table
WHERE
column < (SELECT max(columnq) FROM table)
ORDER BY
column DESC LIMIT 1
It is the most esiest way:
SELECT
Column name
FROM
Table name
ORDER BY
Column name DESC
LIMIT 1,1
As you mentioned duplicate values . In such case you may use DISTINCT and GROUP BY to find out second highest value
Here is a table
salary
:
GROUP BY
SELECT amount FROM salary
GROUP by amount
ORDER BY amount DESC
LIMIT 1 , 1
DISTINCT
SELECT DISTINCT amount
FROM salary
ORDER BY amount DESC
LIMIT 1 , 1
First portion of LIMIT = starting index
Second portion of LIMIT = how many value
Tom, believe this will fail when there is more than one value returned in select max([COLUMN_NAME]) from [TABLE_NAME] section. i.e. where there are more than 2 values in the data set.
Slight modification to your query will work -
select max([COLUMN_NAME])
from [TABLE_NAME]
where [COLUMN_NAME] IN ( select max([COLUMN_NAME])
from [TABLE_NAME]
)
select max(COL_NAME)
from TABLE_NAME
where COL_NAME in ( select COL_NAME
from TABLE_NAME
where COL_NAME < ( select max(COL_NAME)
from TABLE_NAME
)
);
subquery returns all values other than the largest.
select the max value from the returned list.
This is an another way to find the second largest value of a column.Consider the table 'Student' and column 'Age'.Then the query is,
select top 1 Age
from Student
where Age in ( select distinct top 2 Age
from Student order by Age desc
) order by Age asc
select age
from student
group by id having age< ( select max(age)
from student
)
order by age
limit 1
SELECT MAX(sal)
FROM emp
WHERE sal NOT IN ( SELECT top 3 sal
FROM emp order by sal desc
)
this will return the third highest sal of emp table
select max(column_name)
from table_name
where column_name not in ( select max(column_name)
from table_name
);
not in is a condition that exclude the highest value of column_name.
Reference : programmer interview
Something like this? I haven't tested it, though:
select top 1 x
from (
select top 2 distinct x
from y
order by x desc
) z
order by x
See How to select the nth row in a SQL database table?.
Sybase SQL Anywhere supports:
SELECT TOP 1 START AT 2 value from table ORDER BY value
Using a correlated query:
Select * from x x1 where 1 = (select count(*) from x where x1.a < a)
select * from emp e where 3>=(select count(distinct salary)
from emp where s.salary<=salary)
This query selects the maximum three salaries. If two emp get the same salary this does not affect the query.

MYSQL: Return results ONLY for the first true encountered SELECT expression

Desired result:
Return results ONLY for the first true encountered SELECT expression.
Explanation:
So, I have three different SELECT expresions:
SELECT * FROM table WHERE column1 = 'sometext' AND column2='1'
SELECT * FROM table WHERE column1 = 'someothertext' AND column2='2'
SELECT * FROM table WHERE column1 = 'somethirdtext' AND column2='3'
I want to have the results from 1. If 1. is returning NULL, I would like result from select number 2. If Select number 2. is returning NULL, I would like to use select number 3. and so on.
Please note that I am expecting more than one row to be returned for each condition that is true - and I only want the result from either SELECT 1) 2) or 3) (in that order)
It is important to only return results from the one single SELECT expression, so even if 2. and 3. would return something, I would only like results from 1.
The code I have right now is following that expected logic BUT when a I have more than one rows being returned by some of the below SELECTS, it gives me error:
1242 - Subquery returns more than 1 row
The code right now:
SELECT IFNULL( (SELECT * FROM table WHERE column = 'sometext'), IFNULL( (SELECT * FROM table WHERE column = 'someothertext'), IFNULL( (SELECT * FROM table WHERE column = 'somethirdtext'), 0 ) ) )
You're looking for COALESCE function.
SELECT COALESCE(
(SELECT col FROM t WHERE `column` = 'sometext'),
(SELECT col FROM t WHERE `column` = 'someothertext'),
(SELECT col FROM t WHERE `column` = 'somethirdtext')
);
-please, note that subquery should not return more than 1 row/column.
I would approach this slightly differently, since you can only return one row per condition anyway, I would use the following to limit the number of selects done:
SELECT *
FROM table
WHERE column IN ('sometext', 'someothertext', 'somethirdtext')
ORDER BY CASE column
WHEN 'sometext' THEN 1
WHEN 'someothertext' THEN 2
WHEN 'somethirdtext' THEN 3
END
LIMIT 1;
As pointed out in the comments, you can use FIELD for the sort too:
SELECT *
FROM table
WHERE column IN ('sometext', 'someothertext', 'somethirdtext')
ORDER BY FIELD(column, 'sometext', 'someothertext', 'somethirdtext')
LIMIT 1;
I think you can get multiple rows per condition using the following:
SELECT T.*
FROM Table T
INNER JOIN
( SELECT Column
FROM Table
WHERE column IN ('sometext', 'someothertext', 'somethirdtext')
ORDER BY FIELD(column, 'sometext', 'someothertext', 'somethirdtext')
LIMIT 1
) MinT
ON MinT.Column = T.Column;
Basically the subquery MinT does the same as before, ordering by whichever condition matches. Then gets the value for the column of the first match and limits the whole table to this value.
Example on SQL Fiddle
SELECT t.*
FROM
( SELECT o.column1, o.column2
FROM
( SELECT 1 AS ord, 'sometext' AS column1, '1' AS column2 UNION ALL
SELECT 2, 'someothertext', '2' UNION ALL
SELECT 3, 'somethirdtext', '3'
) AS o
WHERE EXISTS
( SELECT 1
FROM table AS td
WHERE td.column1 = o.column1
AND td.column2 = o.column2
)
ORDER BY o.ord
LIMIT 1
) AS d
JOIN
table AS t
ON t.column1 = d.column1
AND t.column2 = d.column2 ;
MySQL isn't my daily db so I might be off on this, but can't you just use LIMIT 1 on your subqueries?

MYSQL select rows starting from a random number

I want to select 30 rows (in serial order) from anywhere in the middle of the table.
I am trying to do it by adding a column 'serial' having unique auto_increment serial numbers.
My code is:
SELECT * FROM A2
WHERE serial>(
SELECT ROUND(RAND()*(
SELECT COUNT(*) FROM A2))
) LIMIT 30
The part (SELECT ROUND(RAND()*(SELECT COUNT(*) FROM A2))) is working perfectly i.e. generates a random number from 1 till the number of rows in the table, but the complete query is not working. It throws rows that are not in serial progression, and it almost always throws first row having serial below 30.
Can someone help me to write this query.
Thanks
This should work, assuming your serial starts at 0; if it starts at 1, just replace >= with >.
SELECT A2.*
FROM A2
JOIN (SELECT FLOOR(RAND() * (SELECT COUNT(*)-29 FROM A2)) serial) b
WHERE a2.serial >= b.serial
ORDER BY a2.serial
LIMIT 30;
An SQLfiddle to test with.
Try this
SELECT * FROM A2 ORDER BY RAND() LIMIT 30
This will pick 30 random rows.
Append ORDER BY serial like this:
SELECT *
FROM A2
WHERE serial > (
SELECT ROUND(RAND() * (
SELECT COUNT(*) FROM A2)))
ORDER BY serial
LIMIT 30;
Also you probably need to use FLOOR instead of ROUND and subtract 30 from upper limit like this:
WHERE serial > (
SELECT FLOOR(RAND() * ((SELECT COUNT(*) FROM A2) - 30)))
Otherwise you may get less then 30 rows when random is close to the end of the table.
Sorry. That answer was wrong. Here is what works for me:
SELECT a2.*
FROM a2,
(SELECT FLOOR(RAND() * ((SELECT COUNT(*) FROM a2) - 30)) AS r) AS r
WHERE a2.serial BETWEEN r.r AND r.r + 29;
I would just do this
SELECT * FROM A2
WHERE serial > (
SELECT ROUND(RAND()*(SELECT COUNT(*) FROM A2))
)
ORDER BY serial
LIMIT 30

retrieving multiple random rows from MySQL query result set - without using order by rand()

I have a query which aims to retrieve a random row from a result set. I do not want to use ORDER BY Rand() as it seems to be rather inefficient.
My method is as follows:
generate a single random number between [0,1)
give each row of the result query a unique 'rank' number. i.e. give the first row a value 1, second row a value 2, and so forth
use the random number to get a number between 1 and the number of rows in the result
return the row where rank == the number generated from the random number
example query:
SELECT * FROM(
(SELECT #rand := RAND(), #rank := 0) r1
CROSS JOIN
(SELECT (#rank:=#rank+1) as num, A.id FROM
A JOIN B
ON A.id = B.id
WHERE B.number = 42
)
WHERE num = FLOOR(1 + #rand * #rank) LIMIT 1
This works for retrieving one row, but I instead want 10 random rows. Changing LIMIT 1 to LIMIT 10 doesn't work, because if num + 10 > number of rows the query doesn't return 10 rows.
The only solution I can think of it to either generate 10 random numbers in the sql query, check they are all different from each other and have several WHERE num = random_number_1 lines. Alternatively, I could call the query 10 times, checking that the rows selected are unique. I wouldn't know how to do the former, and the latter seems like it is rather inefficient. Unless there is likely to be some wonderful cache that would make running the same query extremely fast?
Does anyone have any ideas? thank you
You could try the following:
select sq2.c1
from ( select *
from (select #count := 0) sq0
cross join
(select t1.c1, #count := #count+1
from t t1
join t t2
using(c1)
where t2.c2 = 42
) sq1
) sq2
--use a probability to pick random rows
where if(#count <= 5, 1, floor(1 + rand() * (#count-1))) <= ceiling(log(pow(#count,2)))+1
limit 5;
The results will be random unless the result set is smaller (or the same size as) the limit. If this is a problem, you can wrap the whole thing:
select sq3.* from ( select ... limit 5 ) sq3
order by rand().
This will only randomize the small number of output rows (at most 5) which is efficient.
Of course, you can always use a temporary table:
create temporary table rset (row_key int auto_increment, key(row_key))
as ( select .... where c2 = 42 ) engine=myisam;
set #count := select count(*) from rset;
select rset.c1
from rset
where row_key in ( (floor(1 + rand() * (#count-1))),
(floor(1 + rand() * (#count-1))),
(floor(1 + rand() * (#count-1))),
(floor(1 + rand() * (#count-1))),
(floor(1 + rand() * (#count-1))) );
drop table rset;
If you want to guarantee that you get five unique rows, then you can use a second temporary table:
create temporary table row_keys ( row_key int not null primary key );
-- do this successful five times. if you get a unique key error try again
insert into row_keys values (floor(1 + rand() * (#count-1));
select rset.c1
from rset
join row_keys
using(row_key);