selecting a column on different order from sub query - sql

I have following table structure. I want to select distinct user_id according to office_id with lastest login_datetime.
tbl_id user_id office_id login_datetime
----------------------------------------
1 2 28 12/28/2012 5:35:50 AM
2 2 15 12/28/2012 5:35:50 AM
3 3 20 12/28/2012 5:35:50 AM
4 4 28 12/28/2012 5:35:50 AM
5 2 28 12/28/2012 5:35:50 AM
6 4 15 12/28/2012 5:35:50 AM
7 3 20 12/28/2012 5:35:50 AM
I tried like :
SELECT user_id as u_id,office_id,
(select login_datetime from tbl t2 where t2.user_id=u_id AND ROWNUM=1 ORDER BY t2.tbl_id DESC ) as LAST_LOGIN
FROM tbl
GROUP BY user_id,office_id
But, its not working for me, any help ?

use Window Function
SELECT tbl_id, user_id, office_id,login_datetime
FROM
(
SELECT tbl_id, user_id, office_id,login_datetime,
ROW_NUMBER() OVER (PARTITION BY user_id, "office_id"
ORDER BY login_datetime DESC) rn
FROM tableName
) a
WHERE a.rn = 1
SQLFiddle Demo

Another sollution is a direct group by with a keep dense rank:
select user_id, office_id,
max(login_datetime) keep (dense_rank first order by login_datetime desc) as latest_login_datetime
from tbl
group by user_id, office_id
or if you want unique user_id:
select user_id,
max(office_id) keep (dense_rank first order by login_datetime desc) as lastest_office_id,
max(login_datetime) keep (dense_rank first order by login_datetime desc) as latest_login_datetime
from tbl
group by user_id

OK, i have changed the example to ORACLE
I will explain the query
First i am selecting a distinct user_id and office id (so if a user belongs to two offices he will returns twice)
and then the MAX login_datetime to get the latest datetime,
Then in the WHERE i am filtering the query by the office_id where it equles to all distinct offices. (basically i am returning a distinct table of office_id)
In the end i am grouping by user_id and office_id because of the MAX function
SELECT
DISTINCT "user_id" ,
"office_id",
MAX("login_datetime")
FROM TableName
WHERE "office_id" IN (SELECT DISTINCT "office_id" FROM TableName)
GROUP BY
"user_id",
"office_id"
SQL Fiddle example

Related

List the most up-to-date product of each category,postqresql queries

user_id product_id category_id date_added date_update
1 2 1 2.3.2021 null
1 3 1 2.3.2020 2.4.2023
1 4 2 2.3.2020 null
1 5 2 2.3.2020 2.4.2023
2 5 2 2.3.2020 2.4.2023
2 4 1 2.3.2020 null
List the most up-to-date product of each category
You can use row_number()
select * from
(
select *,row_number() over(parition by userid,category_id order by date_update) as rn
from tablename
)A where rn=1
OR you can also use distinct on
select distinct on (user_id,category_id) *
FROM tablename
ORDER BY user_id,category_id, date_update
List the most up-to-date product of each category
You can use distinct on. Let me assume that if the update date is null, then you want the creation date:
select distinct on (category_id) t.*
from t
order by category_id, coalesce(date_update, date_added) desc;
If you wanted this per user/category combination, the logic would be:
select distinct on (user_id, category_id) t.*
from t
order by user_id, category_id, coalesce(date_update, date_added) desc;
Using Window function
select u_id,c_id, p_id, coalesce (date_update, date_added) as date ,
rank () over (partition by u_id, c_id order by coalesce (date_update, date_added) desc) as r
from inventory
) t where r = 1

How to get rid of multiple branch_id?

I have a SQL Query of this:
SELECT
COUNT(PERMISSION_ID) AS USER_TOTAL_PERMISSION_PER_BRANCH,
USER_ID,
BRANCH_ID
FROM BRANCH_PERMISSION_USER
GROUP BY USER_ID, BRANCH_ID
ORDER BY USER_ID, USER_TOTAL_PERMISSION_PER_BRANCH DESC
But I have a problem because I only want the first row per user_id. The main goal is to get the list of user together it's branch and top 1 or the distinct on the USER_TOTAL_PERMISSION_PER_BRANCH
Here is the sample output:
Expected output should be:
[USER_TOTAL_PERMISSION_PER_BRANCH][USER_ID][BRANCH_ID]
135 1 1
135 2 1
134 3 1
1 4 1
1 5 1
1 6 1
You can use window functions:
SELECT USER_TOTAL_PERMISSION_PER_BRANCH, USER_ID, BRANCH_ID
FROM (SELECT COUNT(*) AS USER_TOTAL_PERMISSION_PER_BRANCH,
USER_ID, BRANCH_ID,
ROW_NUMBER() OVER (PARTITION BY USER_ID ORDER BY COUNT(*) DESC) as seqnum
FROM BRANCH_PERMISSION_USER
GROUP BY USER_ID, BRANCH_ID
) ub
WHERE seqnum = 1
You can turn your query to a CTE a do filtering using correlation:
with cte as (
select
count(permission_id) as user_total_permission_per_branch,
user_id,
branch_id
from branch_permission_user
group by user_id, branch_id
)
select c.*
from cte c
where c.user_total_permission_per_branch = (
select max(c1.user_total_permission_per_branch)
from cte c1
where c1.user_id = c.user_id and c1.branch_id = c.branch_id
)
Thanks to Sir #Gordon
I use his logic. Here is it:
SELECT USER_TOTAL_PERMISSION_PER_BRANCH, USER_ID, BRANCH_ID
FROM (SELECT COUNT(*) AS USER_TOTAL_PERMISSION_PER_BRANCH,
USER_ID, BRANCH_ID,
ROW_NUMBER() OVER (PARTITION BY USER_ID ORDER BY COUNT(*) DESC) as seqnum
FROM BRANCH_PERMISSION_USER
GROUP BY USER_ID, BRANCH_ID
) ub
WHERE seqnum = 1

Delete duplicated record

I have a table which contains a lot of duplicated rows like this:
id_emp id date ch_in ch_out
1 34103 2019-09-01
1 34193 2019-09-01 17:00
1 34194 2019-09-02 07:03:21 16:59:26
1 34104 2019-09-02 07:03:21 16:59:26
1 33361 2019-09-02 NULL NULL
I want just one row for each date and others must delete with condition like I want the output must be:
id_emp id date ch_in ch_out
1 34193 2019-09-01 17:00
1 34104 2019-09-02 07:03:21 16:59:26
I tried to use distinct but nothing working:
select distinct id_emp, id, date_1, ch_in,ch_out
from ch_inout
where id_emp=1 order by date_1 asc
And I tried too using this query to delete:
select *
from (
select *, rn=row_number() over (partition by date_1 order by id)
from ch_inout
) x
where rn > 1;
But nothing is working the result is empty.
You can use aggregation:
select id_emp, max(id) as id, date, min(ch_in), max(ch_out)
from ch_inout
group by id_emp, date;
This returns the maximum id for each group of rows. That is not exactly what is returned in the question, but you don't specify the logic.
EDIT:
If you want to delete all but the largest id for each id_emp/date combination, you can use:
delete c from ch_inout c
where id < (select max(c2.id)
from ch_inout c2
where c2.id_emp = c.id_emp and c2.date = c.date
);
You can use ROW_NUMBER() to identify the records you want to delete. Assuming that you want to keep the record with the lowest id on each date:
SELECT *
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY date ORDER BY id) rn
FROM ch_inout t
) x
WHERE rn > 1
You can easily turn this into a DELETE statement:
WITH cte AS (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY date ORDER BY id) rn
FROM ch_inout t
)
DELETE FROM cte WHERE rn > 1

How to group by one column, aggregate by another column and get another column as result in postgresql?

This seems something simple, but couldn't find an answer for this question last few hours.
I have a table request_state, where "id" is primary key, it can have multiple entries with same state_id. I want to get the id after grouping by state_id using max datetime.
So I tried this, but it gives error "state_id" must appear in the GROUP BY clause or be used in an aggregate function
select id, state_id, max(datetime)
from request_state
group by id
but when I use following query, I get multiple entries with same state_id.
select id, state_id, max(datetime)
from request_state
group by id, state_id
My table:
id state_id date_time
cef 1 Jan 1
ter 1 Jan 2
ijk 1 Jan 3
uuu 2 Feb 1
rrr 2 Feb 2
This is what I want as my result,
id state_id date_time
__ ________ _________
ijk 1 Jan 3
rrr 2 Feb 2
You seem to want:
select max(id) as id, state_id, max(datetime)
from request_state
group by state_id;
If you want the row where datetime is maximum for each state, then use distinct on:
select distinct on (state) rs.*
from request_state rs
order by state, datetime desc;
Try this query:
select id, state_id, date_time from (
select id, state_id, date_time,
row_number() over (partition by state_id order by date_time desc) rn
from tbl
) a where rn = 1
You can use correlated suqbuery :
select t.*
from table t
where date_time = (select max(date_time) from table t1 where t1.state_id = t.state_id);

Oracle Nested Grouping

The question is: For each day, list the User ID who has read the most number of messages.
user_id msgID read_date
1 1 10
1 2 10
2 2 10
2 2 23
3 2 23
I believe the date is an outer group and user_id is an inner group, but how to do group nesting in sql? Or somehow avoid this?
This is a task for a Window Function:
select *
from
(
select user_id, read_date, count(*) as cnt,
rank()
over (partition by read_date -- each day
order by count(*) desc) as rnk -- maximum number
from tab
group by user_id, read_date
) dt
where rnk = 1
This might return multiple users for one with the same maximum count, if you want just one (randomly) switch to ROW_NUMBER
select user_id
from
(
select user_id,count(msgID)
from table
group by read_date
)
where rownum <= 1;