SQL Outer Join views - sql

first of all sorry the question does not describes my problem and I don't know how to tell it as question.
I want to count how many women and men with position supervisor works in every branch.
So this is the SQL:
CREATE VIEW Women (BranchID,AnzahlF,position ) AS
SELECT Branch.BranchID, COUNT(*) As AnzahlF,Staff.position
FROM Staff full outer JOIN
Branch ON Branch.BranchID = Staff.BranchFK
WHERE gender LIKE 'F' AND position LIKE 'Supervisor'
GROUP BY Branch.BranchID,Staff.position
CREATE VIEW Men (BranchID,AnzahlM,position ) AS
SELECT Branch.BranchID,COUNT(*) As AnzahlM ,Staff.position
FROM Staff Full outer JOIN
Branch ON Branch.BranchID = Staff.BranchFK
WHERE gender LIKE 'M' AND position LIKE 'Supervisor'
GROUP BY Branch.BranchID,Staff.position
Select ISNULL(Women.BranchID, Men.BranchID) AS BranchID,
Case When (Women.AnzahlF is Null) THEN 0
ELSE Women.AnzahlF END As ANzahlFSuperv,
Case When (Men.AnzahlM is Null) THEN 0
ELSE Men.AnzahlM END As ANzahlMSuperv
from Women Full outer join Men On Women.BranchID = Men.BranchID
Group by Women.BranchID, Men.BranchID,Women.ANzahlF,Men.AnzahlM Order by BranchID
This is the output:
BranchID,ANzahlFSuperv, ANzahlMSuperv
B001,2,0
B003,1,1
B004,1,1
B005,1,0
B006,1,0
B007,0,2
B008,1,1
B009,0,1
B010,0,1
B011,1,0
B012,0,1
B013,1,0
B014,1,0
=> Missing B002, 0,0
But I do not get the Branch with ID 'B002' it has ANzahlFSuperv = 0 and ANzahlMSuperv = 0. So it has no supersivisor. So how to get this result?
The solution must be in the views so how to get this BranchID.
I tried everything but worthless.
Hope you guys can hep me!
Thanks!

Why would you use views for this? Just a left join and group by:
SELECT b.BranchID, COUNT(*) As AnzahlM,
SUM(CASE WHEN s.gender = 'F' THEN 1 ELSE 0 END) as females,
SUM(CASE WHEN s.gender = 'M' THEN 1 ELSE 0 END) as males
FROM Branch b LEFT JOIN
Staff s
ON b.BranchID = s.BranchFK AND s.position = 'Supervisor'
GROUP BY b.BranchID;

Related

SQL Query and case statement to show multiple entries by joining two tables

Here is an example of two tables and I would like to create a view as end result. There are two tables as input to the view, one that has countries with location id and the other has location names, I want to join them and then do a case to replace multiple counts with single entry.
Any help is appreciated.
Here is the screenshot of how tables look like, Table Entity and Table Location as input tables to the view
I first thought of getting counts of multiple location Id from Entity Table and then create a select Case statement but no avail, here is the draft code.
select
Country, count(locationid),
case LocationName
when Cnt > 1 then 'Mulitple offices'
when Cnt = 0 then 'Unknown Location'
else LocationName
end
from
Entity
group by
Country
UPDATE:
I just made a SQLFiddle (pretty handy) and realized both solutions from #Gordon and #P.Eh but with some issues. See specific in comments below
i think this is what you wanted:
- when location_id in location table is NULL or 0 then 'Unknow location'
select
COU.Country,
case
when COU.pleace is null OR COU.pleace=0 then 'Unknow location'
when COU.number_of_pleaces > 1 then 'Multiple offices'
else coalesce(LOC.Location_name,'cound not math location')
end as [location name]
from
( select country,
sum(1) as number_of_pleaces,
max(Location_id) as pleace
from #entity
group by country
) COU
left join
#location LOC
on LOC.Location_id = COU.pleace
and COU.number_of_pleaces = 1
You seem to want something like this:
select e.Country, count(l.locationId),
(case when count(l.locationId) > 1 Then 'Mulitple offices'
when count(l.locationId) = 0 then 'Unknown Location'
else max(l.LocationName)
end)
from Entity e left join
Location l
on e.locationId = l.locationId
group by e.Country;

Filtering data only if criteria is met

I am trying to configure this query that I have but I can't think of a way to do it. My query is below:
select per.Forenames, per.Surname, p.Identifier2
from patient p
join Person per on per.PersonID = p.PersonID
where not exists (select 1
from Episode e
where e.PatientID = p.PatientID and
e.EpisodeTypeID in ('FCB9EAA0-C814-413E-A5FC-48547EF973B7',
'E422A8FA-839B-44AD-9A60-6973FEF39361',
'08929D40-863E-4D46-94BD-B4DF9352A855',
'C8BE80C4-AA0A-41ED-A44C-BCBE2CC980C0',
'8C3848C7-8621-43CF-A58D-D4A6ED4DC166',
'C244B01A-E9DD-4BF4-B336-1479A5A7C88D',
'632FAC1E-6B04-4A69-8BF2-0C2E2B0AD8AB'
) and
e.EpisodeDate between '2016-04-01' and '2016-12-15'
)
and p.PatientStatus = 'Current'
group by p.Identifier2, per.Forenames, per.Surname
So what this query is doing is finding certain people only if they haven't attended the specified episode types. Now what I want to do, is filter this even further. I want to make it so that if they have have attended the episode type with the ID '9254B31D-A304-498C-ADE4-F4003997C8FA' then they should still appear on this list, but only if their attended status is set to 'Yes'. Otherwise I don't want them to show up.
Where can I add this filter?
This type of logic is easier to do using group by and having:
select per.Forenames, per.Surname, p.Identifier2
from patient p join
Person per
on per.PersonID = p.PersonID join
Episode e
on e.PatientID = p.PatientID
group by per.Forenames, per.Surname, p.Identifier2
having sum(case when e.EpisodeTypeID in ('FCB9EAA0-C814-413E-A5FC-48547EF973B7',
'E422A8FA-839B-44AD-9A60-6973FEF39361',
'08929D40-863E-4D46-94BD-B4DF9352A855',
'C8BE80C4-AA0A-41ED-A44C-BCBE2CC980C0',
'8C3848C7-8621-43CF-A58D-D4A6ED4DC166',
'C244B01A-E9DD-4BF4-B336-1479A5A7C88D',
'632FAC1E-6B04-4A69-8BF2-0C2E2B0AD8AB'
) and
e.EpisodeDate between '2016-04-01' and '2016-12-15'
then 1 else 0
end) = 0 and
sum(case when e.EpisodeTypeId = '9254B31D-A304-498C-ADE4-F4003997C8FA' and
e.AttendedStatus <> 'Yes' -- assumes AttendedStatus is not NULL
then 1 else 0
end) = 0;
Each sum() expression counts the number of matches for the conditions
I would just add another condition for and exists if you want an additional condition above and beyond the existing conditions like this:
select per.Forenames, per.Surname, p.Identifier2
from patient p
...
and p.PatientStatus = 'Current'
and exists (
select 1
from Episode e1
where e1.PatientID = p.PatientID
and e1.EpisodeTypeID = '9254B31D-A304-498C-ADE4-F4003997C8FA'
and e1.AttendedStatus = 'Yes'
)
group by ...

To compute sum regarding to a constraint

I'm using PostgreSQL 8.4.
I have the following sql-query:
SELECT p.partner_id,
CASE WHEN pa.currency_id = 1 THEN SUM(amount) ELSE 0 END AS curUsdAmount,
CASE WHEN pa.currency_id = 2 THEN SUM(amount) ELSE 0 END AS curRubAmount,
CASE WHEN pa.currency_id = 3 THEN SUM(amount) ELSE 0 END AS curUahAmount
FROM public.player_account AS pa
JOIN player AS p ON p.id = pa.player_id
WHERE p.partner_id IN (819)
GROUP BY p.partner_id, pa.currency_id
The thing is that query does not what I expected. I realize that, but now I want to understand what exactly that query does. I mean, what SUM will be counted after the query executed. Could you clarify?
I think you have the conditions backwards in the query:
SELECT p.partner_id,
SUM(CASE WHEN pa.currency_id = 1 THEN amount ELSE 0 END) AS curUsdAmount,
SUM(CASE WHEN pa.currency_id = 2 THEN amount ELSE 0 END) AS curRubAmount,
SUM(CASE WHEN pa.currency_id = 3 THEN amount ELSE 0 END) AS curUahAmount
FROM public.player_account pa JOIN
player p
ON p.id = pa.player_id
WHERE p.partner_id IN (819)
GROUP BY p.partner_id;
Note that I also removed currency_id from the group by clause.
Maybe one row per (partner_id, currency_id) does the job. Faster and cleaner that way:
SELECT p.partner_id, pa.currency_id, sum(amount) AS sum_amount
FROM player_account pa
JOIN player p ON p.id = pa.player_id
WHERE p.partner_id = 819
AND pa.currency_id IN (1,2,3) -- may be redundant if there are not other
GROUP BY 1, 2;
If you need 1 row per partner_id, you are actually looking for "cross-tabulation" or a "pivot table". In Postgres use crosstab() from the additional module tablefunc , which is very fast. (Also available for the outdated version 8.4):
SELECT * FROM crosstab(
'SELECT p.partner_id, pa.currency_id, sum(amount)
FROM player_account pa
JOIN player p ON p.id = pa.player_id
WHERE p.partner_id = 819
AND pa.currency_id IN (1,2,3)
GROUP BY 1, 2
ORDER BY 1, 2'
,VALUES (1), (2), (3)'
) AS t (partner_id int, "curUsdAmount" numeric
, "curRubAmount" numeric
, "curUahAmount" numeric); -- guessing data types
Adapt to your actual data types.
Detailed explanation:
PostgreSQL Crosstab Query

Join one column to two tables

I have a database that holds info for accounts, posts, and which posts a user likes.
AccountData
id || username
PostData
id || text || accountid
LikesDislikesData
liked(bool) || accountid || postid
I have a view set up because I need specific data from the DB to bind inside of my app. Here is the code I am using:
SELECT trippin.PostData.id, trippin.AccountData.username, trippin.PostData.posttext,
trippin.CategoryData.categoryname, trippin.PostData.__createdat as CreatedAt,
SUM(CASE WHEN likes.liked = 1 THEN 1 ELSE 0 END) as Likes,
SUM(CASE WHEN likes.liked = 0 THEN 1 ELSE 0 END) as DisLikes
FROM trippin.PostData
INNER JOIN trippin.AccountData ON trippin.PostData.accountid = trippin.AccountData.id
INNER JOIN trippin.CategoryData ON trippin.CategoryData.id = trippin.PostData.categoryid
LEFT OUTER JOIN trippin.LikesDislikesData likes ON likes.postid = trippin.PostData.id
GROUP BY (trippin.AccountData.username), (trippin.PostData.posttext), (trippin.PostData.id), (trippin.categorydata.categoryname), (trippin.PostData.__createdat)
The problem is that, any time I add a join to (likes.accountid = trippin.AccountData.id), rows are either duplicated or the output is just incorrect.
I think it might be a design issue, but I am not sure and cannot find anything that helps my exact problem.
So basically, each post is made by a user. Then, each post is liked or disliked (or nothing) by other users. I need all of this data inside of a view so I can pass it to my app.
select postID
, sum(case when liked = true then 1 else 0 end) as liked
, sum(case when liked = true then 1 else 0 end) as disliked
from trippin.LikesDislikesData
group by postID
The above statement should give you liked and disliked by post ID. Call it a subquery and join to your main:
SELECT trippin.PostData.id, trippin.AccountData.username, trippin.PostData.posttext,
trippin.CategoryData.categoryname, trippin.PostData.__createdat as CreatedAt,
Likes,
DisLikes
FROM trippin.PostData
INNER JOIN trippin.AccountData ON trippin.PostData.accountid = trippin.AccountData.id
INNER JOIN trippin.CategoryData ON trippin.CategoryData.id = trippin.PostData.categoryid
LEFT OUTER JOIN (
select postID
, sum(case when liked = true then 1 else 0 end) as liked
, sum(case when liked = true then 1 else 0 end) as disliked
from trippin.LikesDislikesData
group by postID)
likes ON likes.postid = trippin.PostData.id
Should work for what you are after. I removed your group by clause as you'll no longer need the sums syntax. Quite often you'll find creating a subquery to do counts and sums by ID and joining that to the main query by that ID will be the easiest solution to obtain counts...atleast without having to group by the entire select statement

SQL-Using JOINS to get the values from three tables

I have used four tables, namely VehicleMaster, OwnerMaster, CustomerMaster and CustomerVehicle in my project
As name explains, first three are master tables and last table (CustomerVehicle) contains CustomerID & VehicleID. The table hierarchy are as follows
OwnerMaster (OwnerID, OwnerName)
VehicleMaster (VehicleID, OwnerID, VehicleDetails blah blah...)
CustomerMaster (CustomerID, CustomerName..)
CustomerVehicle (CustomerID, VehicleID)
Now i would like to get how many vehicles are running under each owner. output should be something like this.
OwnerName, TotalVehicles, No of Running Vehicles, NonRunning Vehicles.
xxxx, 40, 34, 6
Any help will be much appreciated.
Thanks,
Something like this should work; left join the tables and (distinct) count the respective fields;
SELECT om.OwnerName,
COUNT(DISTINCT vm.VehicleID) TotalVehicles,
COUNT(DISTINCT cv.VehicleID) Running,
COUNT(DISTINCT vm.VehicleID)-COUNT(DISTINCT cv.VehicleID) NotRunning
FROM OwnerMaster om
LEFT JOIN VehicleMaster vm ON om.OwnerID = vm.OwnerID
LEFT JOIN CustomerVehicle cv ON vm.VehicleID = cv.VehicleID
GROUP BY om.OwnerID, om.OwnerName
An SQLfiddle to test with.
There can be owners without vehicles, we don't know... something like this should work as well:
SELECT
om.OwnerName,
count(vm.VehicleID) Total,
sum(case when vm.VehicleID is not null
and cv.VehicleID is not null then 1 else 0 end) Runnng,
sum(case when vm.VehicleID is not null
and cv.VehicleID is null then 1 else 0 end) NotRunnng
FROM OwnerMaster om
LEFT JOIN VehicleMaster vm ON om.OwnerID = vm.OwnerID
LEFT JOIN CustomerVehicle cv ON vm.VehicleID = cv.VehicleID
GROUP BY om.OwnerName
Thank you very much Elhana. I have updated my query to get the exact result what i need. Modified query as:
SELECT
om.OwnerName,
count(vm.VehicleID) Total,
sum(case when vm.VehicleID is not null
and cv.VehicleID is not null then 1 else 0 end) Runnng,
sum(case when vm.VehicleID is not null
and cv.VehicleID is null then 1 else 0 end) NotRunnng
FROM OwnerMaster om
LEFT JOIN VehicleMaster vm ON om.OwnerID = vm.OwnerID and vm.isactive = 1
LEFT JOIN (select distinct VehicleID from CustomerVehicle where isactive = 1) cv
ON vm.VehicleID = cv.VehicleID
where om.isactive = 1
GROUP BY om.OwnerName
SQL Fiddle