Select rows with maximum value where values in two columns are same - sql

I have a simple table like this
....................................
| hotelNo | roomType | totalBooking |
....................................
| 1 | single | 2 |
| 1 | family | 4 |
| 2 | single | 3 |
| 2 | family | 2 |
| 3 | single | 1 |
.....................................
Now I want to get the most commonly booked roomType for each hotels, i.e the following result
......................
| hotelNo | roomType |
......................
| 1 | family |
| 2 | single |
| 3 | single |
......................
P.S I use sub-query to get the first table

If you want the maximum, you can use window functions:
select hotelNo, roomType
from (select t.*, row_number() over (partition by hotelNo order by totalBooking desc) as seqnum
from table t
) t
where seqnum = 1;

Sample table
SELECT * INTO #TEMP
FROM
(
SELECT 1 HOTELNO ,'SINGLE' ROOMTYPE ,2 TOTALBOOKING
UNION ALL
SELECT 1,'FAMILY',4
UNION ALL
SELECT 2,'SINGLE',3
UNION ALL
SELECT 2,'FAMILY',2
UNION ALL
SELECT 3,'SINGLE',1
)TAB
Result query
;WITH CTE1 AS
(
SELECT HOTELNO,ROOMTYPE,TOTALBOOKING,
MAX(TOTALBOOKING) OVER (PARTITION BY HOTELNO) MAXX
FROM #TEMP
)
SELECT DISTINCT HOTELNO,ROOMTYPE,MAXX
FROM CTE1
WHERE TOTALBOOKING=MAXX

Another way of doing is by using Aggregate Function Max
SELECT A.hotelNo,
A.roomType,
A.totalBooking
FROM tablename A
JOIN (SELECT Max (totalBooking) totalBooking,
hotelNo
FROM tablename
group by hotelNo) B
ON a.totalBooking = b.totalBooking
AND a.hotelNo = b.hotelNo
SQL FIDDLE DEMO

Related

Select Except the duplicate Records from the table in SQL Server

I have a SQL Server table that has duplicate entries in one of the columns e.g.:
+----+-----------+------------+
| id | object_id | status_val |
+----+-----------+------------+
| 1 | 1 | 0 |
| 2 | 1 | 0 |
| 3 | 1 | 0 |
| 4 | 2 | 0 |
| 5 | 3 | 0 |
| 6 | 4 | 0 |
| 7 | 4 | 0 |
+----+-----------+------------+
I need the output to be like this:
+----+-----------+------------+
| id | object_id | status_val |
+----+-----------+------------+
| 4 | 2 | 0 |
| 5 | 3 | 0 |
+----+-----------+------------+
How to resolve this?
Is this what you are looking for?
SELECT * FROM <yourTable> t1
WHERE t1.object_id NOT IN
(
SELECT t2.object_id
FROM <yourTable> t2
GROUP BY t2.object_id
HAVING COUNT(object_id) > 1
)
Try this:
select min(id),
object_id,
min(status_val)
from table
group by object_id
having count(*) = 1
Use HAVING and GROUP BY
SELECT MIN(id) id, object_id, MIN(status_val) status_val
FROM yourtable
GROUP BY object_id
HAVING COUNT(object_id) = 1
Output
id object_id status_val
4 2 0
5 3 0
SQL Fiddle: http://sqlfiddle.com/#!6/7f643f/9/0
You can use group by for unique record like below :-
SELECT * from TABLENAME
group by TABLE_COLOUM_NAME
This query give you only unique value from your Table.
Give a row number for each row partitioned and ordered by the columns [object_id], [status_val]. Then from the result set select the rows which having maximum row number 1.
Query
;with cte as(
select [rn] = row_number() over(
partition by [object_id], [status_val]
order by [object_id], [status_val]
), *
from [your_table_name]
)
select min([id]) as [id], [object_id], [status_val]
from cte
group by [object_id], [status_val]
having max([rn]) = 1;
Find a demo here
SELECT COUNT(*)
FROM(
SELECT DISTINCT object_id
FROM object_table ) as row_count, status_val,id,object_id FROM object_table where row_count = 1;
I think you are looking for that

Conditional Group By in SQL

I have the following table that I want to group by type. When there are multiple rows with the same type (e.g., A & B type), I want to preserve the 'value' from the row with the highest rank (i.e., primary > secondary > tertiary..)
rowid | type | rank | value
1 | A | primary | 1
2 | A | secondary | 2
3 | B | secondary | 3
4 | B | tertiary | 4
5 | C | primary | 5
So the resulting table should look like
rowid | type | rank | value
1 | A | primary | 1
3 | B | secondary | 3
5 | C | primary | 5
Any suggestions will be highly appreciated!
p.s., I'm working in MS SQL Server.
You can use row_number(). Here is a simple'ish method:
select t.*
from (select t.*,
row_number() over (partition by type
order by charindex(rank, 'primary,secondary,tertiary')
) as seqnum
from t
) t
where seqnum = 1;
This uses charindex() as a simple method of ordering the ranks.
try this,
;WITH CTE
AS (
SELECT *
,row_number() OVER (
PARTITION BY [type] ORDER BY value
) rn
FROM #t
)
SELECT *
FROM cte
WHERE rn = 1
Another way of doing is with Row_Number and an Order By specifying your rule with CASE.
Schema:
CREATE TABLE #TAB(rowid INT, [type] VARCHAR(1), rankS VARCHAR(50) , value INT)
INSERT INTO #TAB
SELECT 1 , 'A' , 'primary' , 1
UNION ALL
SELECT 2 , 'A' , 'secondary', 2
UNION ALL
SELECT 3 , 'B' , 'secondary' , 3
UNION ALL
SELECT 4 , 'B' , 'tertiary' , 4
UNION ALL
SELECT 5 , 'C' , 'primary' , 5
Now apply rank rule with Row_Number
SELECT * FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY [type] ORDER BY (CASE rankS
WHEN 'primary' THEN 1
WHEN 'secondary' THEN 2
WHEN 'tertiary' THEN 3 END )) AS SNO, * FROM #TAB
)A
WHERE SNO =1
Result:
+-----+-------+------+-----------+-------+
| SNO | rowid | type | rankS | value |
+-----+-------+------+-----------+-------+
| 1 | 1 | A | primary | 1 |
| 1 | 3 | B | secondary | 3 |
| 1 | 5 | C | primary | 5 |
+-----+-------+------+-----------+-------+

Querying latest date for a particluar attribute where it is not in date format

I need to set up a query that allows me to pick the most recent updated record within a group. If two records have the latest update, then the one with the longest update history should be picked. If both are null, or both have the same length of history, then neither should be chosen. The fields are varchar2 format. The last two digits in first record and last record correspond to the years those records were taken. The letters in the history length correspond to codes for what type of data was taken. Below is a sample table, with the expected results:
| group_id | id | First Record | Last Record | History Length |
---------------------------------------------------------------------------------
| a | 1 | record98 | record16 | SNDAWEDSPSEDSYSEAOE |
| a | 2 | record97 | record14 | AVNDAWEDSPSEDSYS |
| b | 3 | record96 | record15 | BVNDAWEDSPSEDSYSEAOE |
| b | 4 | record98 | record16 | UNDAWEDSPSEDSYSEAOP |
| b | 5 | record95 | record16 | UNDAWEDSPSEDSYSEAOPHYE|
| c | 6 | record96 | record12 | BVNDAWEDSPSEDSYSE |
| c | 7 | record10 | record15 | HUSIKD |
| d | 8 | null | null | null |
| d | 9 | null | null | null |
| e | 10 | record11 | record16 | ASIKSO |
| e | 11 | record11 | record16 | SIXLLO |
-------------------------------------------------------------------------------------------------------------------
Output
| group_id | id | First Record | Last Record | History Length |
---------------------------------------------------------------------------------
| a | 1 | record98 | record16 | SNDAWEDSPSEDSYSEAOE |
| b | 5 | record95 | record16 | UNDAWEDSPSEDSYSEAOPHYE|
| c | 7 | record10 | record15 | HUSIKD |
The history isn't as important as the latest record, so if that is too difficult to implement, I just need the one row with the latest record. Thank you.
Let me know if the below query works for your requirement.
SELECT group_id,ID,first_record,last_record,history_length
FROM (
SELECT group_id,ID,first_record,last_record,history_length,diff,
MAX(LENGTH(history_length)) OVER (PARTITION BY group_id) max_len,
count(1) OVER (PARTITION BY group_id,LENGTH(history_length)) cnt
FROM (
SELECT group_id,ID,first_record,last_record,history_length,
count(1) OVER (PARTITION BY group_id,LENGTH(history_length)) cnt,
MAX(to_date(to_number(substr(last_record, 7,2)),'RR')-to_date(to_number(substr(first_record, 7,2)),'RR')) OVER (PARTITION BY group_id) diff
FROM (
SELECT group_id,ID,first_record,last_record,history_length,
MAX(last_record) OVER (PARTITION BY group_id) max_last_record
FROM t
WHERE nvl(first_record,last_record) IS NOT NULL
)
WHERE last_record=max_last_record
)
WHERE (to_date(to_number(substr(last_record, 7,2)),'RR')-to_date(to_number(substr(first_record, 7,2)),'RR'))=diff
)
WHERE cnt=1
AND LENGTH(history_length)=max_len;
Personally I find hemalp108's answer hard to follow; I prefer to break each step down.
Below is how I did this using CTEs, where each subsequent CTE is the next step with a descriptive name i.e.
Add Max LastRecord
then Search by Max LastRecord
then Add HistoryTally
then Add Max HistoryTally
then Search by Max HistoryTally
then Add HistoryTally Frequency
then Search by HistoryTally Frequency
then return the result
P.S. SQLFiddle wasn't working so I had to so this in local SQL Server (don't have local Oracle) and tried to translate it back!
WITH YourTable AS
( SELECT *
FROM ( VALUES ( 'a',1,'record98','record16','SNDAWEDSPSEDSYSEAOE' ),
( 'a',2,'record97','record14','AVNDAWEDSPSEDSYS' ),
( 'b',3,'record96','record15','BVNDAWEDSPSEDSYSEAOE' ),
( 'b',4,'record98','record16','UNDAWEDSPSEDSYSEAOP' ),
( 'b',5,'record95','record16','UNDAWEDSPSEDSYSEAOPHYE' ),
( 'c',6,'record96','record12','BVNDAWEDSPSEDSYSE' ),
( 'c',7,'record10','record15','HUSIKD' ),
( 'd',8,null,null,null),
( 'd',9,null,null,null),
( 'e',10,'record11','record16','ASIKSO' ),
( 'e',11,'record11','record16','SIXLLO' )
) AS T ( group_id, id, FirstRecord, LastRecord, HistoryLength ) ),
AddMaxLastRecord AS
( SELECT *, MAX( LastRecord ) OVER ( PARTITION BY group_id ) MaxLastRecord
FROM YourTable ),
SearchByMaxLastRecord AS
( SELECT group_id, id, FirstRecord, LastRecord, HistoryLength
FROM AddMaxLastRecord
WHERE LastRecord = MaxLastRecord ),
AddHistoryTally AS
( SELECT *, LEN( HistoryLength ) AS HistoryTally
FROM SearchByMaxLastRecord ),
AddMaxHistoryTally AS
( SELECT *, MAX( HistoryTally ) OVER ( PARTITION BY group_id ) MaxHistoryTally
FROM AddHistoryTally ),
SearchByMaxHistoryTally AS
( SELECT group_id, id, FirstRecord, LastRecord, HistoryLength, HistoryTally
FROM AddMaxHistoryTally
WHERE HistoryTally = MaxHistoryTally ),
AddHistoryTallyFrequency AS
( SELECT *, COUNT( HistoryTally ) OVER ( PARTITION BY group_id ) AS HistoryTallyFreq
FROM SearchByMaxHistoryTally ),
SearchByHistoryTallyFrequency AS
( SELECT group_id, id, FirstRecord, LastRecord, HistoryLength
FROM AddHistoryTallyFrequency
WHERE HistoryTallyFreq = 1 )
SELECT *
FROM SearchByHistoryTallyFrequency;

select top 1 with max 2 fields

I have this table :
+------+-------+------------------------------------+
| id | rev | class |
+------+-------+------------------------------------+
| 1 | 10 | 2 |
| 1 | 10 | 5 |
| 2 | 40 | 6 |
| 2 | 50 | 6 |
| 2 | 52 | 1 |
| 3 | 33 | 3 |
| 3 | 63 | 5 |
+------+-------+------------------------------------+
I only need the rows where rev AND then class columns have max value.
+------+-------+------------------------------------+
| id | rev | class |
+------+-------+------------------------------------+
| 1 | 10 | 5 |
| 2 | 52 | 1 |
| 3 | 63 | 5 |
+------+-------+------------------------------------+
Query cost is important for me.
Just the rows that satisfy the condition that it has both max values?
Here's an SQL Fiddle;
SELECT h.id, h.rev, h.class
FROM ( SELECT id,
MAX( rev ) rev,
MAX( class ) class
FROM Herp
GROUP BY id ) derp
INNER JOIN Herp h
ON h.rev = derp.rev
AND h.class = derp.class;
The fastest way might be to have an index on t(id, rev) and t(id, class) and then do:
select t.*
from table t
where not exists (select 1
from table t2
where t2.id = t.id and t2.rev > t.rev
) and
not exists (select 1
from table t2
where t2.id = t.id and t2.class > t.class
);
SQL Server is pretty smart in terms of optimization, so the aggregation approach might be just as good. However, in terms of performance, this is just a bunch of index lookups.
Here is a SQL 2012 example. Very straight forward with the implied table and the PARTITION function.
Basically, with each ID as a partition/group, sort the values of the other fields in a descending order assigning each one an incrementing RowId, then only take the first one.
select id, rev, [class]
from
(
SELECT id, rev, [class],
ROW_NUMBER() OVER(PARTITION BY id ORDER BY rev DESC, [class] desc) AS RowId
FROM sample
) t
where RowId = 1
Here is the SQL Fiddle
Keep in mind, this works with the criteria in the example dataset, and not the MAX of two fields as stated in the question's title.
I guess you mean: the max of rev and the max of class. If not, please clarify what to do when there is no row where both fields have the highest value.
select id
, max(rev)
, max(class)
from table
group
by id
If you mean total value of rev and class use this:
select id
, max
, rev
from table
where id in
( select id
, max(rev + class)
from table
group
by id
)

SELECT only latest record of an ID from given rows

I have this table shown below...How do I select only the latest data of the id based on changeno?
+----+--------------+------------+--------+
| id | data | changeno | |
+----+--------------+------------+--------+
| 1 | Yes | 1 | |
| 2 | Yes | 2 | |
| 2 | Maybe | 3 | |
| 3 | Yes | 4 | |
| 3 | Yes | 5 | |
| 3 | No | 6 | |
| 4 | No | 7 | |
| 5 | Maybe | 8 | |
| 5 | Yes | 9 | |
+----+---------+------------+-------------+
I would want this result...
+----+--------------+------------+--------+
| id | data | changeno | |
+----+--------------+------------+--------+
| 1 | Yes | 1 | |
| 2 | Maybe | 3 | |
| 3 | No | 6 | |
| 4 | No | 7 | |
| 5 | Yes | 9 | |
+----+---------+------------+-------------+
I currently have this SQL statement...
SELECT id, data, MAX(changeno) as changeno FROM Table1 GROUP BY id;
and clearly it doesn't return what I want. This should return an error because of the aggrerate function. If I added fields under the GROUP BY clause it works but it doesn't return what I want. The SQL statement is by far the closest I could think of. I'd appreciate it if anybody could help me on this. Thank you in advance :)
This is typically referred to as the "greatest-n-per-group" problem. One way to solve this in SQL Server 2005 and higher is to use a CTE with a calculated ROW_NUMBER() based on the grouping of the id column, and sorting those by largest changeno first:
;WITH cte AS
(
SELECT id, data, changeno,
rn = ROW_NUMBER() OVER (PARTITION BY id ORDER BY changeno DESC)
FROM dbo.Table1
)
SELECT id, data, changeno
FROM cte
WHERE rn = 1
ORDER BY id;
You want to use row_number() for this:
select id, data, changeno
from (SELECT t.*,
row_number() over (partition by id order by changeno desc) as seqnum
FROM Table1 t
) t
where seqnum = 1;
Not a well formed or performance optimized query but for small tasks it works fine.
SELECT * FROM TEST
WHERE changeno IN (SELECT MAX(changeno)
FROM TEST
GROUP BY id)
for other alternatives :
DECLARE #Table1 TABLE
(
id INT, data VARCHAR(5), changeno INT
);
INSERT INTO #Table1
SELECT 1,'Yes',1
UNION ALL
SELECT 2,'Yes',2
UNION ALL
SELECT 2,'Maybe',3
UNION ALL
SELECT 3,'Yes',4
UNION ALL
SELECT 3,'Yes',5
UNION ALL
SELECT 3,'No',6
UNION ALL
SELECT 4,'No',7
UNION ALL
SELECT 5,'Maybe',8
UNION ALL
SELECT 5,'Yes',9
SELECT Y.id, Y.data, Y.changeno
FROM #Table1 Y
INNER JOIN (
SELECT id, changeno = MAX(changeno)
FROM #Table1
GROUP BY id
) X ON X.id = Y.id
WHERE X.changeno = Y.changeno
ORDER BY Y.id