Takes 1 row from list in each ID - sql

Please help me to the SQL Oracle with the showing only 1 row result in each ID group, Order by DATE DESC below:
From this list
Table: Employees
____________________________
ID | Employees | Date
___+___________+____________
1 | A | 2017-08-01
1 | A | 2017-08-08
2 | B | 2017-07-01
2 | B | 2017-07-10
2 | B | 2017-07-05
Result
____________________________
ID | Employees | Date
___+___________+____________
1 | A | 2017-08-08
2 | B | 2017-07-10

There are several different ways of achieving this. It really depends on what business rules you want to use to choose the one date per employee. You haven't specified anything, but your desired output suggests you need the most recent date. If so you can use the max() aggregate function with a GROUP BY clause.
select id,
employees,
max(date) as date
from employee
group by id,
employees

You can use the analytic function :
https://docs.oracle.com/cloud/latest/db112/SQLRF/functions004.htm#SQLRF06174
select *, RANK() OVER (PARTITION BY Employees ORDER BY date desc) from Employees
then put an outer select to select rank 1

select id, emplyoees, max(date) as date1
from employees
group by id, emplyoees;

Related

Select the highest value of column 2 per column 1

Given the following table P_PROV
+----+-----------+-----------+
| id | date | person_id |
+----+-----------+-----------+
| 1 |19/06/2019 | 1 |
| 2 |18/07/2010 | 2 |
| 3 |19/06/2020 | 1 |
| 4 |17/06/2020 | 2 |
| 5 |28/06/2020 | 3 |
+----+-----------+-----------+
I want this output
+----+-----------+-----------+
| id | date | person_id |
+----+-----------+-----------+
| 3 |19/06/2020 | 1 |
| 4 |17/06/2020 | 2 |
| 5 |28/06/2020 | 3 |
+----+-----------+-----------+
Putting this in words, I want to return per person the maximum date. I tried something like this
SELECT DISTINCT pp.date, pp.id FROM P_PROV pp
WHERE (SELECT MAX(aa.date)
FROM P_PROV aa) = pp.date;
This one is only returning one row (of course, because the MAX will return the maximum date only), but I really don't know how to approach this issue, any kind of help would be appreciated
ROW_NUMBER provides one way to handle this:
SELECT id, date, person_id
FROM
(
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY person_id ORDER BY date DESC) rn
FROM yourTable t
) t
WHERE rn = 1;
Oracle has a fun way to do this using aggregation:
select max(id) keep (dense_rank first order by date desc) as id,
max(date) as date, person_id
from P_PROV
group by person_id;
Given that your ids are increasing, this probably also does what you want:
select max(id) as id, max(date) as date, person_id
from P_PROV
group by person_id;

Obtain the most used type for an id based on its views

I'm trying to get out the most viewed type from my SQL, am pretty new so I'm not sure which logic to use, between either RANK, COUNT and/or SUM.
So I have three columns: ID, seller_type, and view_count
+----+-------------+------------+--------+-------+
| ID | Seller_type | view_count | Week | Day |
+----+-------------+------------+--------+-------+
| 33 | Vendor | 54 | Week1 | Day 1 |
| 33 | Vendor | 45 | Week1 | Day 2 |
| 33 | Marketplace | 68 | Week2 | Day 4 |
| 33 | Marketplace | 12 | Week2 | Day 2 |
| 32 | Vendor | 93 | Week2 | Day 3 |
| 33 | Third-party | 74 | Week1 | Day 6 |
| 32 | Third-party | 10 | Week1 | Day 1 |
+----+-------------+------------+--------+-------+
Basically I have a table in which there is an ID, a seller type and the number of views for an id. A unique ID can have more than one row with the same seller (as they can be different weeks/days). What I want to do is for example, calculate which seller_type has had the most views for ID 33 during week 1. In this case Vendor (54+45), and not Marketplace (12).
So my plan was to start using the SUM:
WITH A AS (
SELECT
ID
,Seller_type
,SUM(view_count) as Total_views
,Week
FROM Table_A
GROUP BY
ID
,Seller_type
,Week
)
SELECT
CASE WHEN Week = 'Week1' THEN MAX(Total_views)
CASE WHEN Week = 'Week2' THEN
FROM A
So I have the totals, for each seller for each ID in my subquery A, and I wanted use a CASE in my main query to indicate to select the max total_views' seller type. But I don't know how to write my CASE WHEN.
Any comments/indications will be appreciated!
Thank you!
You seem to basically want aggregation:
select seller_type, week, sum(view_count)
from t
group by seller_type, week;
Then you want some filtering and to choose the top one:
select seller_type, week, sum(view_count)
from table_a
where week = 'Week 1'
group by seller_type, week
order by sum(view_count)
fetch first 1 row only;
Note that not all databases support the standard fetch first clause -- but all support the functionality in some say (say with select top or limit).
If I get this right you want to get the seller type with the most views per week. You can use rank() or row_number() for that. With rank() you'll get all of the top records should there be more than one record with the same top view count. Replace rank() for row_count() to only (randomly) select one of the records in such a case.
WITH
a
...
SELECT id,
seller_type,
total_views,
week
FROM (SELECT id,
seller_type,
total_views,
week
rank() OVER (PARTITION BY week
ORDER BY total_views DESC) r
FROM a)
WHERE r = 1;

Get count of all instances before a certain date

I have a table like this:
--------------------------------------
RecID|name |date
--------------------------------------
1 |John | 05/09/2016
2 |John | 05/02/2016
3 |Mary | 05/09/2016
4 |Mary | 05/08/2016
5 |Mary | 03/02/2016
and I want to get the count for name for each instance in which that name has appeared on or before that date in the row. So I want the output to look like this:
--------------------------------------
RecID|name |date |count
--------------------------------------
1 |John | 05/09/2016 | 2
2 |John | 05/02/2016 | 1
3 |Mary | 05/09/2016 | 3
4 |Mary | 05/08/2016 | 2
5 |Mary | 03/02/2016 | 1
Any ideas on how I should go about doing this?
You can use the count function with a window specification.
select t.*, count(*) over(partition by name order by date) as cnt
from tablename t
This will produce incorrect results if there are mutliple rows on a given date for a name. One way to avoid this is using a correlated sub-query.
select t.*,
(select count(distinct t2.date)
from tablename t2
where t2.name=t.name and t2.date<=t.date) as cnt
from tablename t
Or use row_number.
select t.*, row_number() over(partition by name order by date) as cnt
from tablename t
Or use dense_rank if there can be multiple rows for the same name on a given date.
select t.*, dense_rank() over(partition by name order by date) as cnt
from tablename t
The easiest solution of all would be to use dense_rank.
use
count(*) count
and
group by date
if your date is already a string (i.e. without hour/minute information)

Selecting a row after multiple groupings in postgres

i have a table in a postgres DB which has the following structure:
id | date | groupme1 | groupme2 | value
----------------------------------------
1 |
2 |
3 |
Now i want to achieve the following:
Grouping the table after groupme1 and groupme2
Get the value for every group
But only the last entry for each group-compination (odered after date)
Example:
id | date | groupme1 | groupme2 | value
---------------------------------------
| | A | 1 | 4
| | A | 2 | 7
| | A | 3 | 3
| | B | 1 | 9
My current approach looks like this:
SELECT a.*
FROM table AS a
JOIN (SELECT max(id) AS id
FROM table
GROUP BY groupme1, groupme2) AS b
ON a.id = b.id
The Problems of this approach:
it asumes that higher dates have a higher id
it takes long
Is there a faster and better way of doing this? Can windowing function help with this?
I think you just want window functions:
select t.*
from (select t.*,
row_number() over (partition by groupme1, groupme2 order by date desc) as seqnum
from t
) t
where seqnum = 1;
Or, a better way to do this in Postgres uses distinct on:
select distinct on (groupme1, groupme2) t.*
from t
order by groupme1, groupme2, date desc;

Using aggregate function to return minimum value

Please, help me to create a query to determine minimum date_time from the table below:
ID | Name | Date_Time | Location
---------------------------------------
001 | John | 01/01/2015 | 901
001 | john | 02/01/2015 | 903
001 | john | 05/01/2015 | 905
001 | john | 06/01/2015 | 904
002 | Jack | 01/01/2015 | 903
002 | Jack | 03/01/2015 | 904
002 | Jack | 04/01/2015 | 905
003 | Sam | 01/01/2015 | 904
003 | Sam | 03/01/2015 | 903
003 | Sam | 04/01/2015 | 901
003 | Sam | 06/01/2015 | 903
I tried this query:
SELECT ID, NAME, MIN(DATE_TIME), LOCATION
FROM TABLE
GROUP BY (ID)
but I got this error message:
ORA-00979: not a GROUP BY expression
If you use aggregation function, you have specify for which fields the agregation should be applied. So you are using group by clause. In this case you probably mean to find the minimum date_time for each id, name combination.
select id, name, min(date_time)
from my_table group by id, name
When you group something, all other rows will be left clustered to that grouped key. For a key, you can only fetch one of the row(entity) in SELECT.
Shortcut is, what ever in GROUP BY can be in SELECT freely. Otherwise, they have to be enclosed in a AGGREGATE function.
When you group by id,
001 key has 4 rows clustered to it.. Just think, what would happen when you specify non grouped column in SELECT. Where-as when you use MIN(date).. out of 4 dates, a minimum of one is taken.
So, your query has to be
SELECT ID,MIN(NAME),MIN(LOCATION),MIN(DATE)
FROM TABLE
GROUP BY ID
OR
SELECT ID,LOCATION,NAME,MIN(DATE)
FROM TABLE
GROUP BY ID,LOCATION,NAME
OR
Analytical approach.
SELECT ID,LOCATION,DATE,MIN(DATE) OVER(PARTITION BY ID ORDER BY NULL) AS MIN_DATE
FROM TABLE.
Still, it is upto the requirements, on how the query has to be re-written.
EDIT: To fetch rows corresponding the Min date, we can create a SELF JOIN like one below.
SELECT T.ID,T.NAME,T.LOCATION,MIN_DATE
FROM
(
SELECT ID,MIN(DATE) AS MIN_DATE
FROM TABLE T1
GROUP BY ID
) AGG, TABLE T
WHERE T.ID = AGG.ID
AND T.DATE = AGG.MIN_DATE
OR
SELECT ID,NAME,LOCATION,MIN_DATE
FROM
(
SELECT ID,
NAME,
LOCATION,
MIN(DATE) OVER(PARTITION BY ID ORDER BY NULL) MIN_DATE,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY NULL) RNK
FROM TABLE
)
WHERE RNK = 1;
Try grouping all the other columns ... and if the table is name 'table' try changing the table name to something else in the schema.
SELECT ID , NAME , MIN(DATE_TIME) , LOCATION FROM TABLE GROUP BY ID, Name, Location
select t1.name,t1.id,t1.location,t1.date from (select id,MIN(Date) as min_date from table group by id ) t2 inner join TABLE t1 on t1.date=t2.min_date and t1.id=t2.id;