How to calculate rank with group by in sql? - sql

Suppose if I have table1 as following
Category Brand Value
A A1 4
B B1 7
C C1 8
A A2 3
B B2 4
C C2 6
A A3 9
B B3 10
C C3 1
A A4 5
Now if I want to calculate rank for each brand but grouped by category how do I go about it?
Something like
Select rank() (over value)
from table
group by category
Expected output is this:
Category Brand Value Rank
A A3 9 1
A A4 5 2
A A1 4 3
A A2 3 4
B B3 10 1
B B1 7 2
B B2 4 3
C C1 8 1
C C2 6 2
C C3 1 3

Maybe you are looking for something like this.
See this official documentation on DENSE_RANK for more details
select brand, category, dense_rank() over(partition by category order by value desc) as dr
from table

You may add a PARTITION BY clause to your RANK() call, specifying the category as the partition.
SELECT RANK() OVER (PARTITION BY category ORDER BY value) rnk
FROM yourTable
ORDER BY category, rnk;

Related

SQL Count, and distinct count(*) by grouping two columns [duplicate]

This question already has answers here:
Selecting COUNT(*) with DISTINCT
(7 answers)
Closed 2 years ago.
I have a table with 2 fields: where someid2 is unique filed
GID Name Some_ID1 Some_ID_2 (unique)
-- ------- ------- -------
1 A A1 K1
1 A A1 K2
1 A A1 K3
1 A A2 K4
2 B B1 K4
3 C C1 K5
3 C C2 K6
3 C C3 K7
3 C C4 K8
I want to group them by name and Gid, with 'count' of two columns
GID Name Count(Some_ID1) Count(Some_ID_2)
-- ------- ------- -------
1 A 2 4
2 B 1 1
3 C 4 4
what i hve tried is grouped the column gid and name but i cant figure out to get count of some_id1
SELECT
gid, name
count( Some_id1) as Some_id1_count , -- need to fix count here
count( Some_id2) as Some_id2_count
FROM table_a
GROUP BY gid, name
I think you just want count(distinct):
select gid, name, count(distinct some_id1) cnt1, count(*) cnt2
from table_a
group by gid, name

How to order the records from different records with matching data in two differents columns as continous rows in output

Need help with Oracle query which will provide the output in below the format.
Sample table
c1 c2 c3 c4
-- -- -- --
A 1 A1
B 2 B1 C1
D 6 E2 A1
A 2 A1
C 3 C1
D 4 D1 E1
I want to join same table where data in 3rd Column matches the data in 4th and expecting the data to be sorted as subsequent records as below
c1 c2 c3 c4
-- -- -- --
A 1 A1
A 2 A1
D 6 E2 A1
B 2 B1 C1
C 3 C1
That's not a grouping, it's a sorting that you need:
select *
from your_table
order by coalesce(col1,'ZZZ') desc,
col2 desc --coalesce will use 'ZZZ' to order if column is null

select rows from main table based on highest date in child table between a date range

Sorry for the confusing title.
I've this table:
ApplicantID Applicant Name
-------------------------------
1 Sandeep
2 Thomas
3 Philip
4 Jerin
ALong with this child table which is connected with the above table:
DetailsID ApplicantID CourseName Dt
---------------------------------------------------------------------
1 1 C1 10/5/2014
2 1 C2 10/18/2014
3 1 c3 7/3/2014
4 2 C1 3/2/2014
5 2 C2 10/18/2014
6 2 c3 1/1/2014
7 3 C1 1/5/2014
8 3 C2 4/18/2014
9 3 c3 2/23/2014
10 4 C1 3/15/2014
11 4 C2 2/20/2014
12 4 C2 2/20/2014
I want to get applicantsID, for example, when I specify a date range from
4/20/2014 to 3/5/2014 I should have:
ApplicantID Applicant Name
-------------------------------
3 Philip
4 Jerin
That means the applicants from the main table that must be in the second table and also the highest date of the second table must fall in the specified date range. Hope the scenario is clear.
you can use window analytic function row_number to get applicant with maximum date in the given time range.
select T1.[ApplicantID], [Applicant Name]
from Table1 T1
join ( select [ApplicantID],
ROW_NUMBER() over ( partition by [ApplicantID] order by Dt desc) as rn
from Table2
where Dt BETWEEN '3/5/2014' AND '4/20/2014'
) T
on T1.[ApplicantID] = T.[ApplicantID]
and T.rn =1
You will need to pull the MAX per ApplicantId with a GROUP BY in a sub-query, then JOIN to that result. This should work for you:
Select A.ApplicantId, A.[Applicant Name]
From ApplicantTableName A
Join
(
Select D.ApplicantId, Max(D.Dt) DT
From DetailsTableName D
Group By D.ApplicantId
) B On A.ApplicantId = B.ApplicantId
Where B.DT Between '03/05/2014' And '04/20/2014'

ORACLE Special JOIN

Let me try to explain the scenario. I have two tables A (Columns - A1, A2, A3) & B (Columns - B1, B2, B3). I need to join table A with table B on A1.B2. For every join, table B has one or two records with different values for B3(X or Y). I wanna write one query where the JOIN query needs to pick the row with B3=X(if there's no other row with B3=Y); If two rows exists (B3=X & B3=Y), then the query needs to pick only the row with B3=Y (ignoring the row with B3=X).
Let me try to give some values to the tables & explain a little bit more.
Table A
********
A1 A2 A3
1 11 111
2 22 222
3 33 333
4 44 444
Table B
********
B1 B2 B3
6 1 X
7 1 Y
8 2 X
9 3 X
10 3 Y
11 4 X
Again.. JOIN is on A1.B2. The result should be as following,
JOIN Results
*************
A1 A2 A3 B1 B2 B3
1 11 111 7 1 Y
2 22 222 8 2 X
3 33 333 10 3 Y
4 44 444 11 4 X
Let me know if you guys have any clarification about my question.
Thanks in advance.
Yogi
You can pick the rows from table B with the ROW_NUMBER function if you partition by the join column and order by your "picking order" column:
SELECT b1, b2, b3,
ROW_NUMBER() OVER (PARTITION BY b2 ORDER BY b3 DESC) as rn
FROM b;
1 Y 1
1 X 2
2 X 1
3 Y 1
3 X 2
4 X 1
Then you can filter the first row, the one with rn=1:
SELECT b1, b2, b3
FROM (SELECT b1, b2, b3,
ROW_NUMBER() OVER (PARTITION BY b2 ORDER BY b3 DESC) as rn
FROM b)
WHERE rn=1;
7 1 Y
8 2 X
10 3 Y
11 4 X
The filtered rows can then be joined to table a:
SELECT *
FROM a
JOIN (
SELECT b1, b2, b3
FROM (SELECT b1, b2, b3,
ROW_NUMBER() OVER (PARTITION BY b2 ORDER BY b3 DESC) as rn
FROM b
)
WHERE rn=1
) bfilter ON a.a1 = bfilter.b2;
1 11 111 7 1 Y
2 22 222 8 2 X
3 33 333 10 3 Y
4 44 444 11 4 X
If 'X' and 'Y' are not actual values, you can extend the ORDER clause with a CASE statement to allow for general values:
ROW_NUMBER() OVER (PARTITION BY b2 ORDER BY
CASE b3 WHEN 'Y' THEN 1
WHEN 'X' THEN 2
...
END ASC)
Edit:
SELECT a1, a2, a3, b1, b2, b3
FROM (
SELECT a1, a2, a3, b1, b2, b3,
ROWNUMBER() OVER (PARTITION BY a1 ORDER BY
CASE WHEN a2=... AND b3=... THEN 1
WHEN a2=... AND b3=... THEN 2
...
END ASC)
FROM a JOIN b ON a.a1 = b.b2
)
WHERE rn = 1;
You can use left outer joins as follows
select A.A1, A.A2, A.A3,
nvl(BT1.B1, BT2.B1),
nvl(BT1.B2, BT2.B2),
nvl(BT1.B3, BT2.B3) from A
left outer join B BT1 on A.A1 = BT1.B2 and BT1.B3 = 'Y'
left outer join B BT2 on A.A1 = BT2.B2 and BT2.B3 = 'X'
A good explanation of the various joins is at http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
Here is, how I would do it:
Make the join
group by B2
take the max(B3)
That way you ensure that X is only picked, when there is no alphabetically higher value (Y) available
With UNION
select a.*,b.* from a,b
where a.a1=b.b2
and b.b3='Y'
union
select a.*,b.* from a,b
where a.a1=b.b2
and not exists (select bb.br from b bb where bb.b2=a.a1 and bb.b3='Y')
Without UNION
select a.*,b.* from a,b
where a.a1=b.b2
and (b.b3='Y'
or not exists (select bb.b3 from b bb where bb.b2=a.a1 and bb.b3='Y'))
The constraint here is that B has exactly 1 or 2 rows for each A's row

SELECT statement with multiple WHERE criteria (MS-Access)

Below is the sample data:
c1 c2 c3 c4 c5
1 a1 a 1 1
2 a2 a 2 1
3 a3 a 3 1
4 a4 a 4 1
5 b1 b 1 1
6 b2 b 2 1
7 b3 b 3 1
8 b4 b 4 1
9 a1 c 3 1
I want to get the the below details:
c1 c2 c3 c4 c5
1 a1 a 1 1
5 b1 b 1 1
9 a1 c 3 1
C1 is primary key, the criteria is for any given unique(c2) where c4 is the lowest, I want to return the contents(all the 5 columns) of the row.
Try this:
SELECT t1.*
FROM Table1 t1
INNER JOIN
(
SELECT c3, MIN(c4) c4
FROM Table1
GROUP BY c3
) t2 ON t1.c3 = t2.c3 ANd t1.c4 = t2.c4
SQL Fiddle Demo
Update:1 In SQL the returned results is a set set(unless you specify an ORDER BY clause, it is a cursor in this case), wherein the order is not guaranteed. This is a standard. You should use an ORDER BY clause if you want to guarantee a specific order. In your case , the results is not guaranteed to be ordered like 1 5 9. Add ORDER BY c1 instead.
The ORDER BY clause might be crucial in some cases, for example, if want to get the top three rows, or the maximum one, in this case you have to specify an ORDER BY clause.
So if you wants to persist a specific order the you have specify an ORDER BY.
1 As noted by #Fahim Parker, see the comments below.
select c1,c2,c3,c4,c5
from table
where c4= (select min(c4) from table as f where f.c4 = table.c4);
i hope that helps