Take precedence on a specific value from a table - sql

For each person's distinct record that has a toyota,
only take toyota and filter out that person's other cars
else bring all cars.
The actual script will not match my logic above. I was trying to simplify my question by using random names and car brands, but the objective was the same since I wanted to get a specific address code and filter out the rest if it did exist for other vendor names (see below). Thank you, GMB.
GPMEM.dbo.PM00200 a -- Vendor Master
LEFT JOIN GPMEM.dbo.PM30200 b -- Historical/Paid Transactions
ON a.VENDORID = b.VENDORID
LEFT JOIN GPMEM.dbo.PM20000 c -- Open/Posted Transactions
ON a.VENDORID = c.VENDORID
LEFT JOIN (
SELECT d.*,
rank() over(
partition by d.VENDORID
order by case when d.ADRSCODE = 'ACH' THEN 0 ELSE 1 END
)rn
FROM GPMEM.dbo.PM00300 d
) d -- Vendor Address Master
ON a.VENDORID = d.VENDORID
WHERE
d.rn = 1

You can use window functions:
select colA, colB
from (
select
t.*,
rank() over(
partition by colA
order by case when colB = 'Toyota' then 0 else 1 end
) rn
from mytable t
) t
where rn = 1
The trick likes in the order by clause in the over() clause of window function rank(): if a person has a Toyota, it will be ranked first, and their (possible) other cars will be ranked second. If it has no Toyota, all their car will be ranked first.

You can do this with filtering logic:
select t.*
from t
where t.colb = 'toyota' or
not exists (select 1 from t t2 where t2.cola = t.cola and t2.colb = 'toyota');
If I were to use window functions for this, I would simply count the toyotas:
select t.*
from (select t.*,
sum(case when colb = 'toyota' then 1 else 0 end) over (partition by cola) as num_toyotas
from t
) t
where colb = 'toyota' or num_toyotas = 0;

Related

SQL compare one column, then another, by using max over partition by

DB: SAP HANA
I have asked this question before, but now I'm facing more complicated question. When qty is the same, I want to return biggest no.
A
user
no
qty
A
10
20
A
11
20
B
12
40
B
13
10
B
id
user
1
A
2
B
Expected result
id
user
no
1
A
11
2
B
12
I try
SELECT
B.id,
B.user,
C.max_qty_no
FROM
B
LEFT JOIN (
SELECT
A.user,
CASE
WHEN A.qty = (
MAX(A.qty) OVER (PARTITION BY A.user)
) THEN A.no
END as max_qty_no
FROM
A
) C ON C.user = B.user AND
C.max_qty_no IS NOT NULL;
return
id
user
no
1
A
10
1
A
11
2
B
12
You want to rank the A rows per user and only select the best-ranked row. So far this ranking was on one column only, so you could simply compare the value with the maximum value. Now, however, the ranking must be done considering two columns instead of just one. You can use ROW_NUMBER for this ranking:
select id, user, no
from
(
select
b.id, b.user, a.no,
row_number() over (partition by b.user order by a.qty desc, a.no desc) as rn
from a
join b on b.user = a.user
) ranked
where rn = 1;
Since you want the MAX(no) per user having the largest quantity you need to apply additional selection criteria. The partitioning takes care of selecting the rows with MAX(qty) per user but you still need to select the rows with MAX(no) for each distinct user - you can do this by using the MAX aggregate function combined with a GROUP BY. With this small change you can return the expected results:
SELECT
B.id,
B.user,
MAX(C.max_qty_no)
FROM
B
LEFT JOIN (
SELECT
A.user,
CASE
WHEN A.qty = (
MAX(A.qty) OVER (PARTITION BY A.user)
) THEN A.no
END as max_qty_no
FROM
A
) C ON C.user = B.user AND
C.max_qty_no IS NOT NULL
GROUP BY B.id, B.user;

select value based on max of other column

I have a few questions about a table I'm trying to make in Postgres.
The following table is my input:
id
area
count
function
1
100
20
living
1
200
30
industry
2
400
10
living
2
400
10
industry
2
400
20
education
3
150
1
industry
3
150
1
education
I want to group by id and get the dominant function based on max area. With summing up the rows for area and count. When area is equal it should be based on max count, when area and count is equal it should be based on prior function (i still have to decide if education is prior to industry or vice versa). So the result should be:
id
area
count
function
1
300
50
industry
2
1200
40
education
3
300
2
industry
I tried a lot of things and maybe it's easy, but i don't get it. Can someone help to get the right SQL?
One method uses row_number() and conditional aggregation:
select id, sum(area), sum(count),
max(function) over (filter where seqnum = 1) as function
from (select t.*,
row_number() over (partition by id order by area desc) as seqnum
from t
) t
group by id;
Another method uses ``distinct on`:
select id, sum(area) over (partition by id) as area,
sum(count) over (partition by id) as count,
function
from t
order by id, area desc;
Use a scalar sub-query for "function".
select t.id, sum(t.area), sum(t.count),
(
select "function"
from the_table
where id = t.id
order by area desc, count desc, "function" desc
limit 1
) as "function"
from the_table as t
group by t.id order by t.id;
SQL Fiddle
you can use sum as window function:
select distinct on (t.id)
id,
sum(area) over (partition by id) as area,
sum(count) over (partition by id) as count,
( select function from tbl_test where tbl_test.id = t.id order by count desc limit 1 ) as function
from tbl_test t
This is how you get the function for each group based on id:
select id, function
from yourtable yt1
left join yourtable yt2
on yt1.id = yt2.id and yt1.area < yt2.area
where yt2.area.id is null;
(we ensure that no yt2 exists that would be of the same id but of higher areay)
This would work nicely, but you might have several max areas with different values. To cope with this isue, let's ensure that exactly one is chosen:
select id, max(function) as function
from yourtable yt1
left join yourtable yt2
on yt1.id = yt2.id and yt1.area < yt2.area
where yt2.area.id is null
group by id;
Now, let's join this to our main table;
select yourtable.id, sum(yourtable.area), sum(yourtable.count), t.function
from yourtable
join (
select id, max(function) as function
from yourtable yt1
left join yourtable yt2
on yt1.id = yt2.id and yt1.area < yt2.area
where yt2.area.id is null
group by id
) t
on yourtable.id = t.id
group by yourtable.id;

Put together two selects into one

Could you help me put the second select into first one? I need calculate rate of type in first select. Second select works good.
First select:
WITH "global" AS (
SELECT
m.id
,json_build_array(
ce.payload->>'Name',
ce.payload->>'Date',
ce.payload->>’Type,
ce.payload->>’Rate’,
row_number() over (partition by m.id order by ce.payload->>’Date’ desc)) as "value"
FROM public."events" ce
LEFT OUTER JOIN "external"."mapping" m
ON ce.id=m.id
WHERE ce.type IN ('cs_calls','pc_calls')
AND coalesce(ce.payload ->> 'Name', '')!=''
AND m.id IS NOT NULL
)
SELECT
id,
value
FROM “global”
Second select:
select
id,
cast(issue as float)/cast(total_count as float) as Rate
from (select
id,
sum(case when type='Issue' then 1 else 0 end) as issue,
count(*) total_count
from events
GROUP BY id)
If Id is the way to join this tables then you can try the following
select
g.id,
g.value,
((issue * 1.0) / total_count) as Rate
from
(
select
id,
sum(case when type='Issue' then 1 else 0 end) as issue,
count(*) total_count
from events
group by
id
) e
join global g
on e.id = g.id

Rank displaying for all user fine. But how do I display rank for one user?

I have this SQL query:
SELECT
DENSE_RANK() OVER (ORDER BY s.rows DESC) AS Ranks,
Territory, TM_Name,
COALESCE(g.rows, 0) AS RXCount,
COALESCE(s.rows, 0) AS DelCounts,
COALESCE(nd.rows, 0) AS NotDeliveredCount,
COALESCE(trgt.rows, 0) AS Target,
e.TM_Emp_Id
FROM
tblEmployee e
LEFT JOIN
(SELECT EmpCode, SUM(RxGenerate) AS rows
FROM tbl_RX
GROUP BY EmpCode) g ON g.EmpCode = e.TM_Emp_Id
LEFT JOIN
(SELECT EmpCode, SUM(MedToPCount) AS rows
FROM tbl_MedicinToPatient
WHERE Status = 'Delivered'
GROUP BY EmpCode) s ON s.EmpCode = e.TM_Emp_Id
LEFT JOIN
(SELECT EmpCode, COUNT(*) AS rows
FROM tbl_MedicinToPatient
WHERE Status != 'Delivered'
GROUP BY EmpCode) nd ON nd.EmpCode = e.TM_Emp_Id
LEFT JOIN
(SELECT
EmpCode, CurrentTarget AS rows,
CreatedDate, RankOrder, T.PreviousTarget
FROM
(SELECT
EmpCode, CurrentTarget, CreatedDate,PreviousTarget,
RANK() OVER (PARTITION BY EmpCode ORDER BY CreatedDate DESC) RankOrder
FROM
tbl_Target) T
WHERE
RankOrder = 1) trgt ON trgt.EmpCode = e.TM_Emp_Id
It returns the desired output. That is for every user it calculates rank perfectly. But when I add where condition in the end of query such as:
e.TM_Emp_Id = 101
It display a rank of 1. And for every user I get the same result (i.e. Rank 1).
But I want to display original rank. How do I achieve this? Thanks in advance!
You have at least 3 easy options for this.
Encapsulate the whole query into a subquery so that it looks like
select *
from (current code)
where TM_Emp_ID = 101
Same as above except make it a CTE:
WITH MY_QUERY AS
(current code)
SELECT *
FROM MY_QUERY
WHERE TM_EMP_ID = 101
same as both the 2 above except use a temp table
You must rank all users in order to know the rank of a particaular user. So select from your query:
select *
from ( your query here ) q
where tm_emp_id = 101;

SQL query challenge - find top frequent items in columns and summarize result to a pivot table

I am looking for a query to do following transformation.
Basically I want to find top 3 frequent SELL_COUNTRY and top 3 frequent category, on per website, per day bases. (for example, website 1, date 6-5-2017, there are 2*US, 1*JP and 1*UK for SELL_COUNTRY, therefore TOP1_SELL_COUNTRY is US, and JP and UK going to TOP2_SELL_COUNTRY and TOP3_SELL_COUNTRY. Same idea for CATEGORY column)
My current solution involves many subqueries, which works, but I feel it is too complicated. I am interested in how sql master would do it in an elegant way.
Currently I know how to do it uses
From
To
I would do that in 3 steps:
group by country and rank by count
group by category and rank by count
blend results using conditional aggregate (which will just place the values in the necessary cells because the result of the CASE would be just your value and many NULL values, so min() outputs the value)
Like this:
WITH
countries as (
SELECT *, row_number() over (partition by website,date order by count desc)
FROM (
SELECT
website
,date::date
,sell_country
,count(1)
FROM your_table
GROUP BY 1,2,3
)
)
,categories as (
SELECT *, row_number() over (partition by website,date order by count desc)
FROM (
SELECT
website
,date::date
,category
,count(1)
FROM your_table
GROUP BY 1,2,3
)
)
SELECT
website
,date
,coalesce(min(case when t1.row_number=1 then t1.sell_country end),'NA') as top1_sell_country
,coalesce(min(case when t1.row_number=2 then t1.sell_country end),'NA') as top2_sell_country
,coalesce(min(case when t1.row_number=3 then t1.sell_country end),'NA') as top3_sell_country
,coalesce(min(case when t2.row_number=1 then t2.category end),'NA') as top1_sell_category
,coalesce(min(case when t2.row_number=2 then t2.category end),'NA') as top2_sell_category
,coalesce(min(case when t2.row_number=3 then t2.category end),'NA') as top3_sell_category
FROM countries t1
FULL JOIN categories t2
USING (website,date)
GROUP BY 1,2
ORDER BY 1,2
WITH a1 AS
(
SELECT *,
COUNT(*) OVER( PARTITION BY website,SUBSTRING(visit_date,1,8),sell_country ) AS sell_cntry,
COUNT(*) OVER( PARTITION BY website,SUBSTRING(visit_date,1,8),pur_country ) AS pur_cntry
FROM Yourtable
),
a2 AS
(
SELECT website,
visit_date,
sell_country,
RANK() OVER ( PARTITION BY website,SUBSTRING(visit_date,1,8) ORDER BY sell_cntry DESC ) AS sell_cntry_rnk
FROM a1
),
a3 AS
(
SELECT website,
visit_date,
pur_country,
RANK() OVER ( PARTITION BY website,SUBSTRING(visit_date,1,8) ORDER BY pur_cntry DESC ) AS pur_cntry_rnk
FROM a1
),
a4 AS
(
SELECT a2.website AS company,
a2.v_date,
CASE WHEN a2.sell_cntry_rn = 1 THEN a2.sell_country END AS TOP1_SELL_COUNTRY,
CASE WHEN a2.sell_cntry_rn = 2 THEN a2.sell_country END AS TOP2_SELL_COUNTRY,
CASE WHEN a2.sell_cntry_rn = 3 THEN a2.sell_country END AS TOP3_SELL_COUNTRY,
CASE WHEN a3.pur_cntry_rn = 1 THEN a3.pur_country END AS TOP1_PUR_COUNTRY,
CASE WHEN a3.pur_cntry_rn = 2 THEN a3.pur_country END AS TOP2_PUR_COUNTRY,
CASE WHEN a3.pur_cntry_rn = 3 THEN a3.pur_country END AS TOP3_PUR_COUNTRY
FROM (
SELECT Z.*,
ROW_NUMBER() OVER( PARTITION BY website,v_date ORDER BY sell_cntry_rnk,sell_country ) AS sell_cntry_rn
FROM
(
SELECT DISTINCT website,
SUBSTRING(visit_date,1,8) AS v_date,
sell_cntry_rnk,
sell_country
FROM a2
) Z
WHERE Z.sell_cntry_rnk <= 3
) a2
INNER JOIN
(
SELECT *,
ROW_NUMBER() OVER( PARTITION BY website,v_date ORDER BY pur_cntry_rnk,pur_country ) AS pur_cntry_rn
FROM
( SELECT DISTINCT website,
SUBSTRING(visit_date,1,8) AS v_date,
pur_cntry_rnk,
pur_country
FROM a3
) Z
WHERE Z.pur_cntry_rnk <= 3
) a3
ON a2.website = a3.website
AND a2.v_date = a3.v_date
),
a5 AS
(
SELECT company,
v_date,
MAX(TOP1_SELL_COUNTRY) AS TOP1_SELL_COUNTRY,
MAX(TOP2_SELL_COUNTRY) AS TOP2_SELL_COUNTRY,
MAX(TOP3_SELL_COUNTRY) AS TOP3_SELL_COUNTRY,
MAX(TOP1_PUR_COUNTRY) AS TOP1_PUR_COUNTRY,
MAX(TOP2_PUR_COUNTRY) AS TOP2_PUR_COUNTRY,
MAX(TOP3_PUR_COUNTRY) AS TOP3_PUR_COUNTRY
FROM a4
GROUP BY company,
v_date
)
SELECT company,
v_date,
CASE WHEN TOP1_SELL_COUNTRY IS NULL THEN 'NA' ELSE TOP1_SELL_COUNTRY END AS TOP1_SELL_COUNTRY,
CASE WHEN TOP2_SELL_COUNTRY IS NULL THEN 'NA' ELSE TOP2_SELL_COUNTRY END AS TOP2_SELL_COUNTRY,
CASE WHEN TOP3_SELL_COUNTRY IS NULL THEN 'NA' ELSE TOP3_SELL_COUNTRY END AS TOP3_SELL_COUNTRY,
CASE WHEN TOP1_PUR_COUNTRY IS NULL THEN 'NA' ELSE TOP1_PUR_COUNTRY END AS TOP1_PUR_COUNTRY,
CASE WHEN TOP2_PUR_COUNTRY IS NULL THEN 'NA' ELSE TOP2_PUR_COUNTRY END AS TOP2_PUR_COUNTRY,
CASE WHEN TOP3_PUR_COUNTRY IS NULL THEN 'NA' ELSE TOP3_PUR_COUNTRY END AS TOP3_PUR_COUNTRY
FROM a5
ORDER BY company,v_date;