Conditional Aggregation with multiple case and group by - sql

The query below gives me average of case when QuoteStatusID = 6 but it I am having issues with associating the average by Street column.
QuoteTable
QuoteID
QuoteStateID
ProjectManager_userID
Shipping_AddressID
1
6
12
56
2
6
12
56
3
26
12
56
4
6
12
18
5
26
12
18
Shipping_AddressID
56: 338 Elizabeth St
18: 83 East St
select [User].UserID, [User].fname, [User].lname,[User].JobTitle, address.Street,
(select avg(case when QuoteStatusID = 6 then 1.0 else 0 end) as QuoteAccept
from Quote q
where ProjectManager_UserID = userId
) as AcceptanceRate
from [User]
join quote on [user].UserID=Quote.ProjectManager_UserID
join Address on quote.Shipping_AddressID=Address.AddressID
where userID in (select distinct ProjectManager_UserID from quote)
order by AcceptanceRate desc;
Current output 3/5 =0.60
userid
fname
Lname
Street
AcceptanceRate
12
Jon
Smith
338 Elizabeth St
0.6
12
Jon
Smith
83 East St
0.6
Desired output 2/3=0.66 AND 1/2=0.50
userid
fname
Lname
Street
AcceptanceRate
12
Jon
Smith
338 Elizabeth St
0.66
12
Jon
Smith
83 East St.
0.50

I think you don't need a sub-query. Just avg as part of the query you have and use group by to give you distinct users and addresses.
select U.UserID, U.fname, U.lname, U.JobTitle, A.Street
, avg(case when Q1.QuoteStatusID = 6 then 1.0 else 0 end) as QuoteAccept
from [User] U
inner join Quote Q on Q.ProjectManager_UserID = U.UserID
inner join [Address] A on A.AddressID = Q.Shipping_AddressID
group by U.UserID, U.fname, U.lname, U.JobTitle, A.Street
order by AcceptanceRate desc;
Note: Short aliases make a query more readable. And you don't need your where clause, since the join on Quote already ensures the same condition.

Can you simply amend your avg to be
select avg(case when QuoteStateID = 6 then 1.0 else 0 end) over(partition by Shipping_AddressId) as QuoteAccept
Edit
To still use as a subquery it will need correlating in the where clause on Shipping_AddressId also

Related

Filter records in outer join based on criteria

I have 2 simple tables as follows:-
Student
---------------------------------------------
student_id student_name student_class
107 paul A Level-I
108 susan Diploma
109 jack O Level-II
---------------------------------------------
Student_Positions
--------------------------------------------------
position_id student_id position date
1 107 1 1-1-2020
2 107 1 1-1-2021
3 109 2 1-1-2021
4 109 1 1-6-2019
I want a left outer join on these tables for the latest position of every student as fol:-
student_id student_name position date
107 paul 1 1-1-2021
108 susan
109 jack 2 1-1-2021
I have made multiple tries with different positions of max(date) and group by but in vain.
Please help with correct query
The canonical SQL solution uses a window function such as row_number():
select s.*, sp.position, sp.date
from students s left join
(select sp.*,
row_number() over (partition by student_id order by date desc) as seqnum
from student_positions sp
) sp
on sp.student_id = s.student_id and sp.seqnum = 1;

Oracle left join with conditions from the target table

I have two tables Customers, Sales like below:
Customers :
id
first_name
last_name
gender
age
customer_since
1
Daniel
Black
M
34
2014-10-13
2
Erik
Brown
M
25
2015-06-10
3
Diana
Trump
F
39
2015-10-25
4
Anna
Yao
F
19
2017-02-20
5
Christian
Sanders
M
42
2018-01-31
Sales:
id
date
book_id
customer_id
quantity
amount
1
2019-09-02
2
3
1
14.99
2
2019-10-01
1
2
1
12.99
3
2019-10-01
3
4
1
15.75
I need to get all customers and their data in sales table even if they don't have records in sales table so I did left join like below :
SELECT c.id as customer_id, c.first_name, c.last_name, c.gender, c.age, c.customer_since,
s.sdate AS sales_date, s.id as sale_id
FROM customers c
LEFT JOIN sales s
ON c.id = s.customer_id
and I got this result:
CUSTOMER_ID
FIRST_NAME
LAST_NAME
GENDER
AGE
CUSTOMER_SINCE
SALES_DATE
SALE_ID
3
Diana
Trump
F
39
25-OCT-15
02-SEP-19
1
2
Erik
Brown
M
25
10-JUN-15
01-OCT-19
2
1
Daniel
Black
M
34
13-OCT-14
-
-
4
Anna
Yao
F
19
20-FEB-17
-
-
5
Christian
Sanders
M
42
31-JAN-18
-
-
which is expected my question is what if I need to get all customers that only in the source table and customers from the target table which has specific condition I run the below query
SELECT c.id as customer_id, c.first_name, c.last_name, c.gender, c.age, c.customer_since,
s.sdate AS sales_date, s.id as sale_id
FROM customers c
LEFT JOIN sales s
ON c.id = s.customer_id
where s.id=1
and I got only one record like this
CUSTOMER_ID
FIRST_NAME
LAST_NAME
GENDER
AGE
CUSTOMER_SINCE
SALES_DATE
SALE_ID
3
Diana
Trump
F
39
25-OCT-15
02-SEP-19
1
but I need this result
CUSTOMER_ID
FIRST_NAME
LAST_NAME
GENDER
AGE
CUSTOMER_SINCE
SALES_DATE
SALE_ID
3
Diana
Trump
F
39
25-OCT-15
02-SEP-19
1
1
Daniel
Black
M
34
13-OCT-14
-
-
4
Anna
Yao
F
19
20-FEB-17
-
-
5
Christian
Sanders
M
42
31-JAN-18
-
-
What should I do?
You need to move the criteria from the WHERE clause of the second query to the ON clause of the join:
SELECT c.id as customer_id, c.first_name, c.last_name, c.gender, c.age,
c.customer_since, s.sdate AS sales_date, s.id AS sale_id
FROM customers c
LEFT JOIN sales s
ON c.id = s.customer_id AND s.id = 1;
By restricting the sales ID in the WHERE clause, you filter off any non matching records. By moving this restriction to the join, all records would be retained, but NULL values would appear for all non matching sales fields.

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.

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

Aggregate Functions To Pull More Record Field Data

I would like to know what would be the best way to get the data from a specific row when I use a Group By query. The real query is more complex than the example I'm providing here so I'm looking for something other than a sub-select on the Sales table.
I'm using MSSQL 2008 and I would like something that allow me to get the date field from the Sales record that has the max(amount).
Query
select uid, firstName, lastName, AmountFromTagetedRow, DateFromTargetedRow,
from users u inner join
sales s on u.uid = s.custID
group by uid, firstName, lastName
order by uid
USERS
uid firstName lastName
1 Sam Smith
2 Joe Brown
3 Kim Young
SALES
sid Custid date amount ...
1 1 2016-01-02 100
2 3 2016-01-12 485
3 1 2016-01-22 152
4 2 2016-02-01 156
5 1 2016-02-02 12
6 1 2016-03-05 84
7 2 2016-03-10 68
RESULTS
uid firstName LastName amount date
1 Sam Smith 152 2016-01-22
2 Joe Brown 156 2016-02-01
3 Kim Young 485 2016-01-12
Your posted query doesn't match your amount but something like this should get you pointed in the right direction.
with SortedResults as
(
select uid
, firstName
, lastName
, AmountFromTagetedRow
, DateFromTargetedRow
, ROW_NUMBER() over (partition by u.uid order by AmountFromTagetedRow desc) as RowNum
from users u inner join
sales s on u.uid = s.custID
group by uid
, firstName
, lastName
)
select *
from SortedResults
where RowNum = 1
order by uid