SQL nested query and use of MAX to extract most recent transaction and/or comment - sql

We have a SQL database table recording customer comments (ARCMM). I want to extract the most recent comment for each customer. Some customers do not have any comments (i.e. no entries in ARCMM).
The most recent comment for a customer will have the most recent date (field DATEENTR) and, for that date, the highest value of field CNTUNIQ. The query below does not work as expected. Best fix?
Query:
SELECT
----- Customer masterfile
[ARCUS].[IDCUST],
[ARCUS].[NAMECUST],
----- Customer comments
[ARCMM].[CNTUNIQ],
[ARCMM].[DATEENTR],
[ARCMM].[TEXT]
FROM
[ARCUS]
----- Table ARCMM roto ID AR0021 Customer Comments -----
LEFT JOIN [ARCMM]
ON
[ARCMM].[IDCUST] = [ARCUS].[IDCUST]
AND
[ARCMM].[CNTUNIQ] =
(
SELECT MAX([CNTUNIQ])
FROM [ARCMM] ARCMMcopy2
WHERE
[ARCMMcopy2].[IDCUST] = [ARCMM].[IDCUST]
AND
[ARCMM].[DATEENTR] =
(
SELECT MAX([DATEENTR])
FROM [ARCMM] ARCMMcopy1
WHERE
[ARCMMcopy1].[IDCUST] = [ARCMM].[IDCUST]
)
)
Sample table ARCMM data:
IDCUST DATEEENTR CNTUNIQ TEXT
Bob 20200311 1 Bob has woken up
Bob 20200311 2 Bob is having breakfast
Bob 20200629 1 Bob is sleeping <most recent for IDCUST Bob
Jill 20200128 1 Order started
Jill 20200218 1 Order sent
Jill 20200218 2 Goods received
Jill 20200218 3 Goods counted
Jill 20200325 1 Invoice received
Jill 20200325 2 Invoice processed <most recent for IDCUST Jill
Alison 20200225 1 Swimming
Alison 20200425 1 Walking
Alison 20200425 2 Running
Alison 20200425 3 Running
Alison 20200425 4 Sprinting
Alison 20200425 5 Jogging
Alison 20200425 6 Stopped <most recent for IDCUST Alison
Results from my SQL query attempt:
IDCUST NAMECUST CNTUNIQ DATEENTR TEXT
Bob Bob Brown Null Null Null
Jill Jill Jenkins Null Null Null
Alison Alison Allpress 6 20200425 Stopped
Desired results:
IDCUST NAMECUST CNTUNIQ DATEENTR TEXT
Bob Bob Brown 1 20200629 Bob is sleeping
Jill Jill Jenkins 2 20200325 Invoice processed
Alison Alison Allpress 6 20200425 Stopped

You could use row_number() within the left join, if your database supports window functions:
SELECT
c.[IDCUST],
c.[NAMECUST],
m.[CNTUNIQ],
m.[DATEENTR],
m.[TEXT]
FROM [ARCUS] c
LEFT JOIN (
SELECT
m.*,
ROW_NUMBER() OVER(
PARTITION BY [IDCUST]
ORDER BY [DATEENTR] DESC, [CNTUNIQ] DESC
) rn
FROM [ARCMM] m
) m ON m.[IDCUST] = c.[IDCUST] and m.rn = 1

Related

How do I conditionally select a unique value in SQL?

I've been tasked with returning only rows with unique IDs but returning a row for every ID in SQL. How would I go about this?
Logic:
For primary row, select where JOB_INDICATOR = ā€˜Pā€™. If there are multiple rows, then use the record where PRIM_ROLE_IND = ā€˜Yā€™. If there are still multiple then select the lowest numbered EMPL_RCD starting at 0.
Example starting point:
id
name
job
job_indicator
prim_role_ind
empl_rcd
1001
John Doe
Director
P
N
0
1001
John Doe
Professor
P
Y
1
1001
John Doe
Coach
N
N
2
1002
Bob Jones
Head Janitor
P
Y
0
1002
Bob Jones
Associate Janitor
P
Y
1
1003
Susan Smith
Groundskeeper
P
N
0
1003
Susan Smith
Professor
P
N
1
Desired return:
id
name
job
job_indicator
prim_role_ind
empl_rcd
1001
John Doe
Professor
P
Y
1
1002
Bob Jones
Head Janitor
P
Y
0
1003
Susan Smith
Groundskeeper
P
N
0
So far, I have the below, but a new requirement was added to do conditional components.
SELECT *
FROM EMPLOYEE
WHERE JOB_INDICATOR = 'P'
You can use window function ROW_NUMBER() to accomplish this:
SELECT *
FROM
(
SELECT EMPLOYEE.*, ROW_NUMBER() OVER (PARTITION BY id ORDER BY
prim_role_ind DESC, empl_rcd ASC) as rn
FROM EMPLOYEE
WHERE JOB_INDICATOR = 'P'
) dt
WHERE rn = 1

Query Last data group by column

I have this data;
date owner p.code product
---- ----- ----- ------
21.08.2020 Micheal 5 apple
22.08.2020 Micheal 5 apple
15.08.2020 George 4 biscuit
14.08.2020 George 4 biscuit
10.08.2020 Micheal 4 biscuit
23.08.2020 Alice 2 pear
15.08.2020 Alice 2 pear
14.08.2020 Micheal 2 pear
11.08.2020 Micheal 2 pear
I want to group them trought to product and show last date and last owner.
like this ;
date owner p.code product
---- ----- ------ ------
22.08.2020 Micheal 5 apple
15.08.2020 George 4 biscuit
23.08.2020 Alice 2 pear
In Oracle, you can phrase this using group by:
select product, code,
max(date) as max_date,
max(owner) keep (dense_rank first order by date desc) as owner_at_max_date
from t
group by product, code;
The keep syntax is Oracle's rather verbose way of implementing a first() aggregation function.
You can use window functions:
select *
from (
select t.*, row_number() over(partition by product order by date desc) rn
from mytable t
) t
where rn = 1

How do I display rows from a max count?

I want to return all the data, from max count query with hospital that has most number of patients. What I seem to be getting when I try to nest queries is display of all rows of hospital data. I've tried to look at similar questions in stack overflow and other sites it seems to be a simple query to do but i am not getting it.
select max(highest_hospital) as max_hospital
from (select count(hospital) as highest_hospital
from doctor
group by hospital)
highest_hospital
-------------
3
Doc ID Doctor Patient Hospital Medicine Cost
------ ------- ------ --------- ------ --------
1 Jim Bob Patient1 Town 1 Medicine 1 4000
2 Janice Smith Patient2 Town 2 Medicine 3 3000
3 Harold Brown Patient3 Town 2 Medicine 5 2000
4 Larry Owens Patient4 Town 2 Medicine 6 3000
5 Sally Brown Patient5 Town 3 Medicine 7 4000
6 Bob Jim Patient6 Town 4 Medicine 8 6000
Outcome should be return of 3 rows
Doc ID Doctor Patient Hospital Medicine Cost
------ ------- ------ --------- ------ --------
2 Janice Smith Patient2 Town 2 Medicine 3 3000
3 Harold Brown Patient3 Town 2 Medicine 5 2000
4 Larry Owens Patient4 Town 2 Medicine 6 3000
You can use window functions:
select d.*
from (select d.*, max(hospital_count) over () as max_hospital_count
from (select d.*, count(*) over (partition by hospital) as hospital_count
from doctor d
) d
) d
where hospital_count = max_hospital_count;
Edit:
Using GROUP BY is a pain. If you are only looking for a single hospital (even when there are ties), then in Oracle 12C you can do:
select d.*
from doctor d
where d.hospital = (select d2.hospital
from doctor d2
group by d2.hospital
order by count(*) desc
fetch first 1 row only
);
You can do this in earlier versions of Oracle using an additional subquery.

SQL Server group by? [duplicate]

This question already has answers here:
Retrieving last record in each group from database - SQL Server 2005/2008
(2 answers)
Closed 4 years ago.
I'm not sure how to word my question so perhaps an example would be best. I'm looking for a function or statement that would produce the following result from a single table. For each name, return the row with largest id.
ID NAME ADDRESS
1 JOHN DOE 123 FAKE ST.
2 JOHN DOE 321 MAIN ST.
3 JOHN DOE 333 2ND AVE.
4 MARY JANE 222 1ST. AVE
5 MARY JANE 444 POPLAR ST.
6 SUZY JO 999 8TH AVE.
DESIRED RESULT
3 JOHN DOE 333 2ND AVE.
5 MARY JANE 444 POPLAR ST.
6 SUZY JO 999 8TH AVE.
One option is to use the row_number window function. This allows you to establish a row number to the result set. Then you can define the grouping and ordering within the over clause, in this case you want to partition by (group) the name field and order by the id field descending. Finally you filter those results where rn = 1 which returns the max result for each grouping.
select *
from (
select *, row_number() over (partition by name order by id desc) rn
from yourtable
) t
where rn = 1

get extra rows for each group where date doesn't exist

I've been playing with this for days, and can't seem to come up with something. I have this query:
select
v.emp_name as Name
,MONTH(v.YearMonth) as m
,v.SalesTotal as Amount
from SalesTotals
Which gives me these results:
Name m Amount
Smith 1 123.50
Smith 2 40.21
Smith 3 444.21
Smith 4 23.21
Jones 1 121.00
Jones 2 499.00
Jones 3 23.23
Jones 4 41.82
etc....
What I need to do is use a JOIN or something, so that I get a NULL value for each month (1-12), for each name:
Name m Amount
Smith 1 123.50
Smith 2 40.21
Smith 3 444.21
Smith 4 23.21
Smith 5 NULL
Smith 6 NULL
Smith ... NULL
Smith 12 NULL
Jones 1 121.00
Jones 2 499.00
Jones 3 23.23
Jones 4 41.82
Jones 5 NULL
Jones ... NULL
Jones 12 NULL
etc....
I have a "Numbers" table, and have tried doing:
select
v.emp_name as Name
,MONTH(v.YearMonth) as m
,v.SalesTotal as Amount
from SalesTotals
FULL JOIN Number n on n.Number = MONTH(v.YearMonth) and n in(1,2,3,4,5,6,7,8,9,10,11,12)
But that only gives me 6 additional NULL rows, where what I want is actually 6 NULL rows for each group of names. I've tried using Group By, but not sure how to use it in a JOIN statement like that, and not even sure if that's the correct route to take.
Any advice or direction is much appreciated!
Here's one way to do it:
select
s.emp_name as Name
,s.Number as m
,st.salestotal as Amount
from (
select distinct emp_name, number
from salestotals, numbers
where number between 1 and 12) s left join salestotals st on
s.emp_name = st.emp_name and s.number = month(st.yearmonth)
Condensed SQL Fiddle
You could do:
SELECT EN.emp_name Name,
N.Number M,
ST.SalesTotal Amount
FROM ( SELECT Number
FROM NumberTable
WHERE Number BETWEEN 1 AND 12) N
CROSS JOIN (SELECT DISTINCT emp_name
FROM SalesTotals) EN
LEFT JOIN SalesTotals ST
ON N.Number = MONTH(ST.YearMonth)
AND EN.emp_name = ST.emp_name