Getting undesired output in SQL Server while using pivot - sql

I am looking for an output of:
but getting this instead:
The two tables which I have used are Table 1 and Table 2.
I am providing the links for the table as well Kaggle Dataset
The code I have
SELECT *
FROM
(SELECT
nr.region, Medal,
COUNT(Medal) AS 'Total_Medal'
FROM
athlete_events AS ae
JOIN
noc_regions AS nr ON ae.NOC = nr.NOC
WHERE
Medal <> 'NA'
GROUP BY
Medal, nr.region) AS t1
PIVOT
(COUNT(Medal)
FOR Medal IN ([Gold], [Silver], [Bronze])
) pivot_table
ORDER BY
Total_Medal DESC
Please help me to solve this, consider me a novice

Try This, you will get exact output
Select OH.Region,
Count(Case When O.Medal='Gold' Then 1 End) AS Gold,
Count(Case When O.Medal='Silver' Then 1 End) AS Silver,
Count(Case When O.Medal='Bronze' Then 1 End) AS Bronze
from athlete_events O
Join noc_regions OH On OH.NOC = O.NOC
Group by OH.Region
Order By Gold Desc,Silver Desc,Bronze Desc

I think you probably just want something like:
SELECT *
FROM (
SELECT nr.region, medal
FROM athlete_events ae INNER JOIN noc_regions nr
ON ae.noc = nr.noc
WHERE medal <> 'NA'
) t1
PIVOT(COUNT(medal) FOR medal in ([Gold], [Silver], [Bronze])) pt
ORDER BY gold+silver+bronze DESC
There doesn't appear to be any reason for your inner grouping and counting. The pivot can handle the count and, since you don't want total medal in your output (except for the order), you can just sum the medal fields in the ORDER BY.

So I kinda gave up on pivot myself. Caused me to many problems in the past (always my own mistakes ofcourse) and when I can I try a different approache.
So I'll show you a query that tells you how you can get the results you want, but it won't answer your pivot question. This method isn't the most efficient, but it's fine for a size of these records and I think it makes the query even more readable.
SELECT
region
, SUM(Gold) AS 'Gold'
, SUM(Silver) AS 'Silver'
, SUM(Bronze) AS 'Bronze'
FROM
noc_regions
LEFT JOIN (SELECT NOC, COUNT(*) AS 'Gold' FROM athlete_events WHERE Medal = 'Gold' GROUP BY NOC) gold_events ON noc_regions.NOC = gold_events.NOC
LEFT JOIN (SELECT NOC, COUNT(*) AS 'Silver' FROM athlete_events WHERE Medal = 'Silver' GROUP BY NOC) silver_events ON noc_regions.NOC = silver_events.NOC
LEFT JOIN (SELECT NOC, COUNT(*) AS 'Bronze' FROM athlete_events WHERE Medal = 'Bronze' GROUP BY NOC) bronze_events ON noc_regions.NOC = bronze_events.NOC
GROUP BY
region
ORDER BY
Gold DESC
, Silver DESC
, Bronze DESC
, region ASC;

Related

How to do this query using self join or anything but without using window function

Below is the solution but I want to know other ways to accomplish the same results (preferably in PostgreSQL).
This is the DB
Question - How many customers have churned straight after their initial free trial? what percentage is this rounded to the
nearest whole number?
WITH ranking AS (
SELECT
s.customer_id,
s.plan_id,
p.plan_name,
ROW_NUMBER() OVER (
PARTITION BY s.customer_id
ORDER BY s.plan_id) AS plan_rank
FROM dbo.subscriptions s
JOIN dbo.plans p
ON s.plan_id = p.plan_id)
SELECT
COUNT(*) AS churn_count,
ROUND(100 * COUNT(*) / (
SELECT COUNT(DISTINCT customer_id)
FROM dbo.subscriptions),0) AS churn_percentage
FROM ranking
WHERE plan_id = 4 -- Filter to churn plan
AND plan_rank = 2
You can achieve the same results with a single aggregation on customer_id with a few CASE WHEN statements:
SELECT count(*) as total_customers
,count(case when total_subscriptions = 2
and includes_free = 1
and includes_churn = 1 then 1 end) as churn_count
,100 * count(case when total_subscriptions = 2
and includes_free = 1
and includes_churn = 1 then 1 end) / count(*) as target_percent
FROM (
SELECT customer_id
,count(*) as total_subscriptions
,max(case when plan_id = 0 then 1 else 0 end) as includes_free
,max(case when plan_id = 4 then 1 else 0 end) as includes_churn
FROM dbo.subscriptions
GROUP BY customer_id
) AS tbl
-- Remove any records for people who didnt use the free trial
-- or people who are still on the free trial
WHERE includes_free = 1 AND total_subscriptions > 1
The difference between our solutions are:
Yours doesn't specify that the customer actually had a free trial
Mine doesn't include customers who went from Free -> Churn -> (something else)
Depending on your requirements you might want to make further alterations/use a different approach.

how to select in select postgresql

I have a column named jenis_kelamin I want to display the gender of male and female in a select query. I have a query like this but the data that comes out is still wrong, what is the correct way?
SELECT distinct mj.id, mj.nama_pd,
(select distinct count(jenis_layanan) from public.isian_kuis
where jenis_kelamin = 'Laki' ) as jumlah_laki,
(select distinct count(jenis_layanan)
from public.isian_kuis
where jenis_kelamin = 'Perempuan' ) as jumlah_perempuan
FROM public.master_jabatan mj
join isian_kuis ik on ik.jabatan_id = mj.id
group by mj.id,mj.nama_pd
order by mj.id;
I have an example of the image of my query, this is still wrong
The correct data is that at ID 30 it has two men and one woman, in ID 29 there is only one woman
No need to use nested select just use Group By like this:
SELECT distinct mj.id,
mj.nama_pd,
SUM(CASE WHEN jenis_kelamin = 'Laki' THEN 1 ELSE 0 end) AS jumlah_laki,
SUM(CASE WHEN jenis_kelamin = 'Perempuan' THEN 1 ELSE 0 end) AS jumlah_perempuan
FROM public.master_jabatan mj
join isian_kuis ik on ik.jabatan_id = mj.id
group by mj.id,mj.nama_pd
order by mj.id;
In Postgres, you can use conditional aggregation which looks like:
SELECT mj.id, mj.nama_pd,
COUNT(*) FILTER (WHERE jenis_kelamin = 'Laki')AS jumlah_laki,
COUNT(*) FILTER (WHERE jenis_kelamin = 'Perempuan') AS jumlah_perempuan
FROM public.master_jabatan mj JOIN
isian_kuis ik
ON ik.jabatan_id = mj.id
GROUP BY mj.id, mj.nama_pd
ORDER BY mj.id;
Note that it is very, very rare to use SELECT DISTINCT with GROUP BY. This might slow down the query. And FILTER is standard SQL so is a good way to implement this.

How to get count of items present in each category but not present in other categories?

I have a table with different visit_types to hospital. They are Inpatient, Outpatient, Emergency
I would like to know the count of subjects solely present under each visit_type but not in other visit_types. In the above example the
Inpatient count - 4
Outpatient count -2
Emergency count - 3
I tried the below but not sure whether it is accurate?
SELECT count(DISTINCT PERSON_ID) FROM Visit WHERE PERSON_ID NOT IN
(select distinct person_id from Visit where visit_type = 'Inpatient')
AND VISIT_type = 'Outpatient';
SELECT count(DISTINCT PERSON_ID) FROM Visit WHERE PERSON_ID NOT IN
(select distinct person_id from Visit where visit_type = 'Inpatient')
AND VISIT_type = 'Emergency';
When I do this, it includes common subjects between Emergency and Outpatient?
How can I get the count correctly?
With a CTE which returns for each person_id all the types:
with cte as (
select person_id,
sum(case visit_type when 'Inpatient' then 1 else 0 end) Inpatient,
sum(case visit_type when 'Outpatient' then 1 else 0 end) Outpatient,
sum(case visit_type when 'Emergency' then 1 else 0 end) Emergency
from Visit
group by person_id
)
select
case
when Inpatient > 0 then 'Inpatient'
when Outpatient > 0 then 'Outpatient'
when Emergency > 0 then 'Emergency'
end visit_type,
count(*) counter
from cte
group by visit_type
See the demo.
Results:
visit_type | counter
:--------- | ------:
Outpatient | 2
Emergency | 3
Inpatient | 4
I would like to know the count of subjects solely present under each category but not in other categories.
You can aggregate by patient, keeping track of the categories. Then aggregate again:
select visit_type, count(*)
from (select patientId, min(visit_type) as visit_type
from t
group by patientId
having min(visit_type) = max(visit_type)
) p
group by visit_type;
An alternative method uses group by but filters before aggregation:
select visit_type, count(*)
from t
where not exists (select 1
from t t2
where t2.patientid = t.patientid and
t2.visit_type <> t.visit_type
)
group by visit_type;
Note: In this case, the count(*) is counting rows. If your data has duplicates, use count(distinct visit_type).
I have no idea what "I consider Inpatient category as base category" is supposed to mean, but the question itself is quite clear.
EDIT:
I am unclear on the relationships between the different categories that you want. You may find it most flexible to use:
select visit_type, count(*)
from (select patientId,
bool_or(visit_type = 'Inpatient') as has_inpatient,
bool_or(visit_type = 'Outpatient') as has_oupatient,
bool_or(visit_type = 'Emergency') as has_emergency,
count(distinct visit_type) as num_visit_types
from t
group by patientId
) p
where num_visit_types = 1
group by visit_type;
This version is the same as the earlier two queries. But you can use the has_ flags for additional filtering -- for instance where num_visit_types = 1 or (num_visit_types = 2 and has_inpatient) if you want people with one type or one type plus "inpatient".
You can use this query!
SELECT
C.visit_type,
COUNT(*) AS count_per_visit_type
FROM (
SELECT
person_id
FROM (
SELECT
person_id,
ARRAY_AGG(DISTINCT visit_type) AS visit_type_array
FROM visit
GROUP BY person_id
) A
WHERE LENGTH(visit_type_array) = 1
) B
JOIN visit C
ON B.person_id = C.person_id
GROUP BY C.visit_type

Unexpected answer from query with sub-selects

When I execute this query then all departs shown in depart_decode column and there figures also =Nil
select D.depart_decode
(select count (Staff_NO) from AA.table where Depart_code=D.Depart_code and Depart_code=151 and Staff_Sub_NO=5) Manager,
(select count (Staff_NO) from AA.table where Depart_code=D.Depart_code and Depart_code=151 and Staff_Sub_NO=4) HOD,
(select count (Staff_NO) from AA.table where Depart_code=D.Depart_code and Depart_code=151 and Staff_Sub_NO=3) Head,
(select count (Staff_NO) from AA.table where Depart_code=D.Depart_code and Depart_code=151 and Staff_Sub_NO in(1,2)) staff
from AA.Departments D
You query contains this where clause in each subselect:
where Depart_code=D.Depart_code and Depart_code=151
This evaluates to true only, if a Depart_code 151 exists in table Departments and only for that row.
Remove and Depart_code=151and you should get your results.
Using correlated subqueries in a select clause is often a source of performance problems. I would recommend using a subquery to prepare the counts you want and just join that back to the department table.
SELECT
D.depart_decode, c.Manager, c.Hod, c.Head, c.staff
FROM AA.Departments D
INNER JOIN (
SELECT
Depart_code
, COUNT(CASE WHEN Staff_Sub_NO = 5 THEN Staff_NO END) Manager
, COUNT(CASE WHEN Staff_Sub_NO = 4 THEN Staff_NO END) HOD
, COUNT(CASE WHEN Staff_Sub_NO = 3 THEN Staff_NO END) Head
, COUNT(CASE WHEN Staff_Sub_NO < 3 THEN Staff_NO END) staff
FROM AA.table
GROUP BY
Depart_code
) C ON D.Depart_code = C.Depart_code
;

SQL Aggreate Functions

I have table which list a number of cases and assigned primary and secondary technicians. What I am trying to accomplish is to aggregate the number of cases a technician has worked as a primary and secondary tech. Should look something like this...
Technician Primary Secondary
John 4 3
Stacy 3 1
Michael 5 3
The table that I am pulling that data from looks like this:
CaseID, PrimaryTech, SecondaryTech, DOS
In the past I have used something like this, but now my superiors are asking for the number of secondary cases as well...
SELECT PrimaryTech, COUNT(CaseID) as Total
GROUP BY PrimaryTech
I've done a bit of searching, but cant seem to find the answer to my problem.
Select Tech,
sum(case when IsPrimary = 1 then 1 else 0 end) as PrimaryCount,
sum(case when IsPrimary = 0 then 1 else 0 end) as SecondaryCount
from
(
SELECT SecondaryTech as Tech, 0 as IsPrimary
FROM your_table
union all
SELECT PrimaryTech as Tech, 1 as IsPrimary
FROM your_table
) x
GROUP BY Tech
You can group two subqueries together with a FULL JOIN as demonstrated in this SQLFiddle.
SELECT Technician = COALESCE(pri.Technician, sec.Technician)
, PrimaryTech
, SecondaryTech
FROM
(SELECT Technician = PrimaryTech
, PrimaryTech = COUNT(*)
FROM Cases
WHERE PrimaryTech IS NOT NULL
GROUP BY PrimaryTech) pri
FULL JOIN
(SELECT Technician = SecondaryTech
, SecondaryTech = COUNT(*)
FROM Cases
WHERE SecondaryTech IS NOT NULL
GROUP BY SecondaryTech) sec
ON pri.Technician = sec.Technician
ORDER By Technician;
SELECT COALESCE(A.NAME, B.NAME) AS NAME, CASE WHEN A.CASES IS NOT NULL THEN A.CASES ELSE 0 END AS PRIMARY_CASES,
CASE WHEN B.CASES IS NOT NULL THEN B.CASES ELSE 0 END AS SECONDARY_CASES
FROM
(
SELECT COUNT(*) AS CASES, PRIMARYTECH AS NAME FROM YOUR_TABLE
GROUP BY PRIMARYTECH
) AS A
FULL OUTER JOIN
(
SELECT COUNT(*) AS CASES, SECONDARYTECH AS NAME FROM YOUR_TABLE
GROUP BY SECONDARYTECH
) AS B
ON A.NAME = B.NAME