SQL to return one row from many - sql

I have the following data
PersonId
City
Type
UpdateDate
123
Boston
P
01/01/2021
123
Boston
M
02/01/2021
130
Detroit
P
01/01/2021
130
Detroit
M
03/01/2021
140
Dallas
M
02/01/2021
140
Dallas
M
03/01/2021
I want a query that returns one row per PersonId. If the Type is "P" return that row otherwise return the row with the minimum UpdateDate. So the query would return:
PersonId
City
Type
UpdateDate
123
Boston
P
01/01/2021
130
Detroit
P
01/01/2021
140
Dallas
M
02/01/2021
In the past I would write a query like
select * from person, address
where person.PersonId = address.PersonId
group by PersonId
having (Type = 'P') or (UpdateDate = min(UpdateDate))
but this is not allowed anymore.
What should my SQL query be in SQL Server?

Presumably you want the most recent address per person. If so, outer apply is very well suited to this problem:
select p.* a.*
from person p outer apply
(select top (1) a.*
from address a
where a.PersonId = p.PersonId
order by (case when a.type = 'P' then 1 else 2 end),
a.updatedate desc
) a;
No aggregation is called for.

Related

How to assign filters to row number () function in sql

I am trying to extract only single row after name = system in each case where the town is not Austin.
In case 1001 there are 8 rows, row # 4 is system, output should be only the row with Name=Terry and Date Moved=7/4/2019 (Next entry with town /= Austin)
Case Name Town Date Moved Row #(Not in table)
1001 Ted Madisson 9/7/2018 1
1001 Joyal Boston 10/4/2018 2
1001 Beatrice Chicago 1/1/2019 3
1001 System Chicago 1/5/2019 4
1001 John Austin 4/11/2019 5
1001 Simon Austin 6/11/2019 6
1001 Terry Cleveland 7/4/2019 7
1001 Hawkins Newyork 8/4/2019 8
1002 Devon Boston 12/4/2018 1
1002 Joy Austin 12/7/2018 2
1002 Rachael Newyork 12/19/2018 3
1002 Bill Chicago 1/4/2019 4
1002 System Dallas 2/12/2019 5
1002 Phil Austin 3/16/2019 6
1002 Dan Seattle 5/18/2019 7
1002 Claire Birmingham 7/7/2019 8
Tried sub query with row number function and not in ('Austin') filter
ROW_NUMBER() OVER(PARTITION BY Case ORDER BY Moved_date ASC) AS ROWNUM
Please note there are > 10k cases.
You can try this below script-
WITH CTE AS
(
SELECT [Case],[Name],Town,[Date Moved],
ROW_NUMBER() OVER (PARTITION BY [Case] ORDER BY [Date Moved]) [Row #]
FROM your_table
)
SELECT A.*
FROM CTE A
INNER JOIN
(
SELECT C.[Case],C.Town,MAX(C.[Row #]) MRN
FROM CTE C
INNER JOIN
(
SELECT *
FROM CTE A
WHERE A.Name = 'System'
)D ON C.[Case] = D.[Case] AND C.[Row #] > D.[Row #]
AND C.Town = 'Austin'
GROUP BY C.[Case],C.Town
)B ON A.[Case] = B.[Case] AND A.[Row #] = B.MRN+1
Output is -
Case Name Town Date Moved Row #
1001 Terry Cleveland 7/4/2019 6
1002 Dan Seattle 5/18/2019 7
Here are three possibilities. I'm still concerned about ties though. The first one will return multiple rows while the others only one per case:
with matches as (
select t1."case", min(t2."Date Moved") as "Date Moved"
from Movements r1 inner join Movements t2 on t1."case" = t2."case"
where t1.name = 'System' and t2.Town <> 'Austin'
and t2."Date Moved" > t1."Date Moved"
group by t1."case"
)
select t.*
from Movements t inner join matches m
on m."case" = t."case" and m."Date Moved" = t."Date Moved";
select m2.*
from Movements m1 cross apply (
select top 1 * from Movements m2
where m2.Town <> 'Austin' and m2."Date Moved" > m1."Date Moved"
order by m2."Date Moved"
) as match
where m1.name = 'System';
with m1 as (
select *,
count(case when name = 'System') over (partition by "case" order by "Date Moved") as flag
from Movements
), m2 as (
select *,
row_number() over (partition by "case" order by "Date Moved") as rn
from m1
where flag = 1 and name <> 'System' and Town <> 'Austin'
)
select * from m2 where rn = 1;
I'm basically assuming this is SQL Server. You might need a few minor tweaks if not.
It also does not require a town named Austin to fall between the "System" row and the desired row as I do not believe that was a stated requirement.

How do I find each female names' highest occurence and corresponding state in 1910 from public dataset in BigQuery

I don't know how to write the query to find each female names' highest occurence and corresponding state in 1910 from public dataset
For example, I tried for only one name in 1910.
select name,state,number high_occurence from `bigquery-public-data.usa_names.usa_1910_2013` where lower(name)="annie" and year=1910 order by number desc limit 1
Below is for BigQuery Standard SQL
#standardSQL
SELECT
a.name,
a.state,
a.year,
a.number AS high_occurence,
b.number AS number_in_1910
FROM (
SELECT
name,
ARRAY_AGG(STRUCT(year, state, number) ORDER BY number DESC LIMIT 1)[OFFSET(0)].*
FROM `bigquery-public-data.usa_names.usa_1910_2013`
WHERE gender = 'F'
GROUP BY name
) a
JOIN `bigquery-public-data.usa_names.usa_1910_2013` b
ON a.name = b.name
AND a.state = b.state
AND b.year = 1910
AND b.gender = 'F'
ORDER BY high_occurence DESC
with result as
Row name state year high_occurence number_in_1910
1 Mary PA 1918 8184 2913
2 Linda NY 1947 7542 9
3 Patricia NY 1951 5678 33
4 Helen PA 1918 5411 1604
.....
739 Cele NY 1914 5 5
740 Hilja MI 1910 5 5

Fetching same rows that has multiple columns along with other rows

I have a view which results the following rows.
comp Sub-comp Lognum id Firname LAstname
AK AK-G 0 3897 ABC DEF
AK AK-G 0 5432 mark ray
MC MC-A 0 1234 john steve
MC MC-A 0 5678 dan pitcher
MC MC-A 0 9843 james robin
MC MC-A 84 1234 john steve
MC MC-A 84 5678 dan pitcher
MC MC-A 84 9843 james robin
I want to fetch the only the rows that has a lognum (if the same row has 0 also as lognum) along with the other rows that has just 0 as lognum.
The result table should be like this
comp Sub-comp Lognum id Firname LAstname
AK AK-G 0 3897 ABC DEF
AK AK-G 0 5432 mark ray
MC MC-A 84 1234 john steve
MC MC-A 84 5678 dan pitcher
MC MC-A 84 9843 james robin
And the outline of the query is as follows
create view view1 as
select
comp, Sub-comp, "00" as Lognum, id ,Firname ,LAstname
from
table A
inner joins---
UNION
select
select
comp, Sub-comp, Lognum, id ,Firname ,LAstname from
table B
inner joins----
;
Can anyone help?
Thanks!
Try this:
select * from(
select comp,
Sub-comp,
Lognum,
id,
Firname,
LAstname,
row_number() over(partition by id order by lognum desc) rn
from table_name)
where rn = 1;
This will show the line with the biggest lognum grouped by the ID.
This query should work, even in cases where, for a given id value, you have multiple "non-zero" lognum rows.
If you look at the where clause, rows with non-zero lognum values are always returned (t.Lognum != 0). But rows with zero lognum values will also return, but only if the t.rn = 1 condition is true, which will only happen if there aren't any other non-zero lognums for that same id (see the order by clause of the row_number() window function).
select t.comp,
t.Sub-comp,
t.Lognum,
t.id,
t.Firname,
t.LAstname
from (select t.*,
row_number() over (
partition by t.id
order by case when t.lognum = 0 then 1 else 0 end) as rn
from your_view t) t
where t.Lognum != 0 or t.rn = 1

How do I write sql query from this result?

I wasn't sure what could be the title for my question so sorry about that.
I'm trying to write a SQL query to achieve the no. of members who should get reimbursed from a pharmacy.
For example : I went to pharmacy, I took a vaccine but by mistake I paid from my pocket. so now Pharmacy needs to reimburse me that amount. Lets say I have the data like:
MemberId Name ServiceDate PresNumber PersonId ClaimId AdminFee(in $)
1 John 1/1/2011 123 345 456 0
1 John 1/21/2011 123 345 987 20
2 Mike 2/3/2011 234 567 342 0
2 Mike 2/25/2011 234 567 564 30
5 Linda 1/4/2011 432 543 575 0
5 Linda 4/6/2011 987 543 890 0
6 Sonia 2/6/2011 656 095 439 0
This data shows all members from that pharmacy who got reimbursed and who haven't.
I need to find out the member having AdminFee 0 but i also need to check another record for the same member having same PresNumber, same PersonId where the ServiceDate falls within 30 Days of the Original Record.
If another record meets this criteria and the AdminFee field contains a value (is NOT 0) then it means that person has already been reimbursed. So from the data you can see John and Mike have already been reimbursed and Linda and Sonia need to be reimbursed.
Can anybody help me how to write an SQL query on this?
You don't mention what SQL engine you're using, so here is some generic SQL. You'll need to adapt the date math and the return of True/False ( in the second option) to whatever engine you're using:
-- Already reimbursed
SELECT * FROM YourTable YT1 WHERE AdminFee = 0 AND EXISTS
(SELECT * FROM YourTable YT2
WHERE YT2.MemberID = YT1.MemberID AND
YT2.PresNumber = YT1.PresNumber AND
YT2.ServiceDate >= YT1.ServiceDate - 30 AND
AdminFee > 0)
-- Need reimbursement
SELECT * FROM YourTable YT1 WHERE AdminFee = 0 AND NOT EXISTS
(SELECT * FROM YourTable YT2
WHERE YT2.MemberID = YT1.MemberID AND
YT2.PresNumber = YT1.PresNumber AND
YT2.ServiceDate >= YT1.ServiceDate - 30 AND
AdminFee > 0)
or
-- Both in one.
SELECT YT1.*,
CASE WHEN YT2.MemberID IS NULL THEN False ELSE True END AS AlreadyReimbursed
FROM YourTable YT1 JOIN YourTable YT2 ON
YT1.MemberID = YT2.MemberID AND
YT1.PresNumber = YT2.PresNumber AND
YT1.ServiceDate <= YT2.ServiceDate + 30
WHERE YT1.AdminFee = 0 AND YT2.AdminFee > 0)
You need to use datediff function in SQL Server and as parameter to pass day and to join the table above by other alias. I do not have SQL Server but I think it should be like this
Select memberid
from PaymentLog p
inner join PaymentLog d on p.serviceid = d.serviceid
and p.memberid = d.memberid
and p.personid = d.personid
Where adminfee = 0
and datediff(day, p.servicedate, d.servicedate) < 30
I called a table paymentlog

Joining SQL lookup table with data table

I have a lookup table say cities with fields CityId, CityName
CityId CityName
1 New York
2 San Francisco
3 Chicago
I have an orders table which has fields: CityId, CustId, CompletedOrders, PendingOrders
CityId CustId CompletedOrders PendingOrders
1 123 100 50
2 123 75 20
I want a table/report that lists orders details of a given customer in all cities, i.e. the result I need is:
CityId CityName CustId CompletedOrders PendingOrders
1 New York 123 100 50
2 San Francisco 123 75 20
3 Chicago 123 0 0
How to do that ?
SELECT
c.CityId
c.CityName
o.CustId,
o.CompletedOrders
o.PendingOrders
FROM cities c
LEFT JOIN orders o ON ( c.CityId = o.CityId )
This will return all the rows that you want, but for the rows that don't exist in details it will return NULL values, so you would get:
CityId CityName CustId CompletedOrders PendingOrders
1 New York 123 100 50
2 San Francisco 123 75 20
3 Chicago 123 NULL NULL
The solution to get 0 instead depends on your database. With MySQL use IFNULL, with Oracle use NVL.
try this
select c.CityId,c.CityName,o.CustId,o.CompletedOrders,o.PendingOrders
from orders Left join cities
on o.CityId = c.CityId