SQL Server select first instance of ranked data with a twist - sql

I have a Table1 that look like this:
Wallet No Status Rank
01774076563 Scanned 1
01765653275 Registered 3
01774076563 Accepted 2
01768836816 Rejected 4
01967866981 Accepted 2
01967866981 Rejected 4
I want it to look like this (Table2):
Wallet No Status Rank
01774076563 Scanned 1
01765653275 Registered 3
01768836816 Rejected 4
01967866981 Accepted 2
I have used following code but it shows Table1 instead of Table2:
SELECT MIN([Rank]) AS [Rank], STATUS_, walletno into table2
FROM table1
GROUP BY STATUS_, walletno
What I want is only the highest ranking wallet no and status, nothing else. But the Table2 gives me exact copy of Table1. What am I doing wrong here?

You are looking for the complete row, rather than just the minimum value. For this, the window functions are handy.
Because your logic uses min(), here is a method using that function:
SELECT [Rank], STATUS_, walletno into table2
FROM (select t.*,
MIN([rank]) over (partition by walletno) as minrank
from table1 t
) t
where [rank] = minrank;
The subquery calculates the minimum rank for each wallet. The outer filter than keeps only that row.
I would normally write this with row_number() as:
SELECT [Rank], STATUS_, walletno into table2
FROM (select t.*,
row_number() over (partition by walletno order by [rank]) as seqnum
from table1 t
) t
where seqnum = 1;
But the two should be pretty similar in terms of performance. The major difference is when two rows have the same minimum rank. The first version will return both rows. The second will arbitrarily choose one of them.

You can try this:
;WITH CTE as
(
SELECT
WalletNo,
Status,
ROW_NUMBER() OVER(PARTITION BY Status ORDER BY Rank) AS Row,
Rank
FROM
Item
)
SELECT WalletNo, Status, Row, Rank From CTE Where Row = 1
Fiddle here: http://sqlfiddle.com/#!3/14fa5/6

Related

How to group and pick only certain values based on a field using select query SQL

I have a table as follow
ID
ORDERNO
1
123
1
123
2
456
2
456
During every select query done via application using JDBC, only the grouped records based on ORDERNO should be picked.
That means, for example, during first select query only details related to ID = 1, but we cannot specify the ID number in where clause because we do not know how many number of IDs will be there in future. So the query should yield only one set of records; application will delete those records after picking, hence next select query will result in picking other set of records. How to achieve it?
You can use TOP WITH TIES for this
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID;
If you want to select and delete at the same time you could delete using an OUTPUT clause
WITH cte AS (
SELECT TOP (1) WITH TIES
t.ID,
t.ORDERNO
FROM YourTable t
ORDER BY
t.ID
)
DELETE cte
OUTPUT deleted.*;
As one option you could select on the MIN(ID) like:
SELECT *
FROM yourtable
WHERE ID = (SELECT MIN(ID) FROM yourtable);
You could also use window functions to do this:
SELECT ID, ORDERNO
FROM
(
SELECT ID, ORDERNO
DENSE_RANK() OVER (ORDER BY ID ASC) AS dr
FROM yourtable
)dt
WHERE dr = 1;
order your rows and select top n number of rows that you want :
select top (1) with ties ID, ORDERNO
from tablename
order by ID asc

Distinct rows in a table in sql

I have a table with multiple rows of the same member id. I need only distinct rows based on 2 unique columns
Ex: there are 100 different customers, the table has 1000 rows because every customer has multiple cities and segments assigned to him.
I need 100 distinct rows for these customers depending on a unique segment and city combination. There is no specific requirement for this combination, just the first from the table is fine.
So, currently the table is somewhat like this,
Hope this helps.
use row_number()
select * from (select *,row_number() over(partition by memberid order by sales) rn
from table_name
) a where a.rn=1
Handy sql-server top(1) with ties syntax for that
select top(1) with ties t.*
from table_name t
order by row_number() over(partition by memberid order by sales)
As you have no paticular requirement for which exactly row to select, any column will do at order by, it can be null as well
select top(1) with ties t.*
from table_name t
order by row_number() over(partition by memberid order by (select null))
The simplest way to do this is to use the ROW_NUMBER() OVER(GROUP BY...) syntax. You have no need to use an order by, since you want an arbitrary row, but only one, for each member.
Since you need only the expected data, and not the Row_Number value, make sure that you detail the fields returned, like below:
SELECT
MemberId,
city,
segment,
sales
FROM (
SELECT *
ROW_NUMBER() OVER (GROUP BY MemberId) as Seq
FROM [Status]
) src
WHERE Seq = 1

Selecting Second row if available if not first in SQL

I have a table that looks like below:
i want to Select Operator entry that corresponds to RowNumber 2 of InstanceNumber 1 and rownumber 1 of other instances. This is one example where only instancenumber 1 has two rownumber's but I want to able to pick last rownumber of each intances. Any help is appreciated.
One method uses window functions:
select t.*
from (select t.*, max(rownumber) over (partition by instancenumber) as maxrn
from t
) t
where rownumber = maxrn;
That said, if you have the right indexes, the following might be a tad faster:
select t.*
from t
where t.rownumber = (select max(t2.rownumber) from t t2 where t2.instancenumber = t.instancenumber);

Aggregate function like MAX for most common cell in column?

Group by the highest Number in a column worked great with MAX(), but what if I would like to get the cell that is at most common.
As example:
ID
100
250
250
300
200
250
So I would like to group by ID and instead of get the lowest (MIN) or highest (MAX) number, I would like to get the most common one (that would be 250, because there 3x).
Is there an easy way in SQL Server 2012 or am I forced to add a second SELECT where I COUNT(DISTINCT ID) and add that somehow to my first SELECT statement?
You can use dense_rank to return all the id's with the highest counts. This would handle cases when there are ties for the highest counts as well.
select id from
(select id, dense_rank() over(order by count(*) desc) as rnk from tablename group by id) t
where rnk = 1
A simple way to do what you want uses top and order by:
SELECT top 1 id
FROM t
GROUP BY id
ORDER BY COUNT(*) DESC;
This is a statistic called the mode. Getting the mode and max is a bit challenging in SQL Server. I would approach it as:
WITH cte AS (
SELECT t.id, COUNT(*) AS cnt,
row_number() OVER (ORDER BY COUNT(*) DESC) AS seqnum
FROM t
GROUP BY id
)
SELECT MAX(id) AS themax, MAX(CASE WHEN seqnum = 1 THEN id END) AS MODE
FROM cte;

Selecting type(s) of account with 2nd maximum number of accounts

Suppose we have an accounts table along with the already given values
I want to find the type of account with second highest number of accounts. In this case, result should be 'FD'. In case their is a contention for second highest count I need all those types in the result.
I'm not getting any idea of how to do it. I've found numerous posts for finding second highest values, say salary, in a table. But not for second highest COUNT.
This can be done using cte's. Get the counts for each type as the first step. Then use dense_rank (to get multiple rows with same counts in case of ties) to get the rank of rows by type based on counts. Finally, select the second ranked row.
with counts as (
select type, count(*) cnt
from yourtable
group by type)
, ranks as (
select type, dense_rank() over(order by cnt desc) rnk
from counts)
select type
from ranks
where rnk = 2;
One option is to use row_number() (or dense_rank(), depending on what "second" means when there are ties):
select a.*
from (select a.type, count(*) as cnt,
row_number() over (order by count(*) desc) as seqnum
from accounta a
group by a.type
) a
where seqnum = 2;
In Oracle 12c+, you can use offset/fetch:
select a.type, count(*) as cnt
from accounta a
group by a.type
order by count(*) desc
offset 1
fetch first 1 row only