Using Count Function and Percentage of Count Total in One Select Statement - oracle-sqldeveloper

I have three data tables Employees, Departments, and Locations.
I want to show the total number of employees in each state and what percentage of the employees are located in that each state. The Employees table and the Departments table have one identical column called Department_ID, and the Departments table and the Locations table have one identical column called Location_ID. Here's what I wrote for my code:
select l.state_province e.count(*) as "Employees in State",
e.count(*)*100/sum(e.count(*)) over ()
from employees e
full outer join departments d on e.department_id = d.department_id
full outer join locations l on l.location_id = d.location_id
order by l.state_province;
However, the error "from keyword not found where expected" shows up when I run the code. How do I fix it?

You need group by. And regular joins should be fine:
select l.state_province, count(*) as "Employees in State",
count(*) * 100/sum(count(*)) over ()
from employees e join
departments d
on e.department_id = d.department_id join
locations l
on l.location_id = d.location_id
group by l.state_province
order by l.state_province;

Related

Counting and showing only rows whom only have 1 result in specific column

There are 3 tables, and i want to show only those who have result and record in one sport but have never competed ad any other sport. How am I supposed to do the counting?
SELECT p.ID, p.name
FROM people p, results r, sports s
WHERE r.result=s.record
AND p.ID=r.peopleID
AND r.sportID=s.ID
GROUP BY p.ID
HAVING COUNT(r.sportID) = 1;

Join 2 tables where two sets of numbers overlap within the joining columns

I need to join 2 tables with postgresql where two sets of numbers overlap within the joining columns.
The image below explains it - I am needing to take a table of congresspeople and their party affiliation and join it with a table of districts (based on when the districts were drawn or redrawn). The result will be the rows that show the dates that the district, state and congressperson were the same. Wherever there are dates of a district that are known and the congressperson dates are unknown, the dates that are known for the district are filled for that portion, and the dates for the congressperson are left blank - and vice versa.
For example, for the first rows in the tables:
Congressperson Table:
Arkansas, District 5, Republican: 1940-1945
District Table:
Arkansas, District 5: 1942-1963
Results in the following combinations (Start_Comb and End_Comb):
1940-1942
1942-1945
And for the combination where the district is unknown (1940-1942), the district dates are left blank.
The final set of date columns (gray) is simply the combinations that are only for the district (this is super easy).
In case you're wondering what this is for, I am creating an animated map, kind of like this, but for congressional districts over time:
https://www.youtube.com/watch?v=vQDyn04vtf8
I'll end up with something where there is a map where for every known district, there is a known or unknown party.
Haven't got very far, this is what I did:
SELECT *
FROM congressperson
JOIN districts
ON Start_Dist BETWEEN Start_Cong AND End_Cong
WHERE district.A = district.B
OR End_Dist BETWEEN Start_Cong AND Start_Dist
OR Start_Cong = Start_Dist OR End_Cong= End_Dist;
The idea is to make list of unique dates from both tables first. Then for each such date find next date (in this particular case dates are grouped by state, district, and next date is looked for particular state, district).
So now we have list of ranges we are looking for. Now we can join (for this paticular task left join) other tables by required conditions:
select
r.state,
c.start_cong,
c.end_cong,
c.party,
coalesce(c.district, d.district) district,
d.start_dist,
d.end_dist,
start_comb,
end_comb,
case when d.district is not null then start_comb end final_start,
case when d.district is not null then end_comb end final_end
from (
with dates as (
select
*
from (
SELECT
c.state,
c.district,
start_cong date
FROM congressperson c
union
SELECT
c.state,
c.district,
end_cong
FROM congressperson c
union
SELECT
d.state,
d.district,
start_dist
FROM district d
union
SELECT
d.state,
d.district,
end_dist
FROM district d
) DATES
group by
state,
district,
date
order by
state,
district,
date
)
select
dates.state,
dates.district,
dates.date start_comb,
(select
d.date
from
dates d
where
d.state = dates.state and
d.district = dates.district and
d.date > dates.date
order by
d.date
limit 1
) end_comb
from
dates) r
left join congressperson c on
c.state = r.state and
c.district = r.district and
start_comb between c.start_cong and c.end_cong and
end_comb between c.start_cong and c.end_cong
left join district d on
d.state = r.state and
d.district = r.district and
start_comb between d.start_dist and d.end_dist and
end_comb between d.start_dist and d.end_dist
where
end_comb is not null
order by
r.state, coalesce(c.district, d.district), start_comb, end_comb, start_cong, end_cong

T-SQL filter on external join

Using the schema below, write a query which lists the total value of all sales for customers who bought an item from a sales person with the same middle initial as them. Your query should return a single result.
The quantity and price columns are both ints.
The Sales - Employee link is on EmployeeID - SalesPersonID.
Something like this should work:
select sum(s.Quantity * p.Price) as TotalSales
from Sales s
join Products p on s.ProductID = p.ProductID
join Customers c on s.CustomerID = c.CustomerID
join Empleyees e on s.SalesPersonID = e.EmployeeID
where c.MiddleInitial = e.MiddleInitial

Postgresql-Select one row from table where value in many table matches?

I have two tables, call them a and b, where a is related to b in a one-to-many relationship. I would like to select any rows from table a where any of the many related records in table b match a criteria. A basic join doesn't work, because that will return one result for each row in table b that matches - I just want one result for each row in table a with one or more related records matching.
For simplified example, say I have a table Departments and related table Employees, where each employee has one department, but each department obviously can have multiple employees. I want a query that will give me one row per department that has one or more employees matching a given criteria - say the departments that have one or more employees that have earned "employee of the month". How would I do this? Thanks.
SELECT * FROM department d
WHERE EXISTS (
SELECT * FROM employee e
JOIN badges b ON b.person_id = e.person_id AND b.badge = 'EotM'
WHERE e.dep_id = d.dep_id
AND e.gender = 'F'
);
select distinct on (d.id)
d.name
from
department d
inner join
employee e on d.id = e.department_id
where e.age between 60 and 65
How to order it by any column:
select *
from (
select distinct on (d.id)
d.*
from
department d
inner join
employee e on d.id = e.department_id
where e.age between 60 and 65
) s
order by name
Sounds like a job for a subquery. Something like: Select * from dept where id in (select deptID from Emp where wasEOTM = true); ought to do the job.

Subtracting a value in a column by average of the same column without using a variable in ISQL

I have 2 tables:
Marks (studentnum,marks)
Student (SNum, SName)
if I do
Select SName, marks-avg(marks) from Marks join Student on SNum = studentnum
then I only get 1 row returned.
Is there a way to return all the list of students' names and the difference of the student's mark and the average (student's mark - average) without assigning a variable for average?
You're wanting to mix aggregate and non-aggregate values. This uses a subquery, but there might be other options depending on your server.
SELECT
SName,
marks - (SELECT avg(marks) FROM Marks as m2 WHERE m2.studentnum = m.studentnum)
FROM
Marks as m INNER JOIN
Student as s
ON s.SNum = m.studentnum
Try this:
Select s.sname, (m.mark - temp.avg)
From marks m
Inner join (select studentnum, avg(mark) as avg from marks group by studentnum) temp
On temp.studentnum = m.studentnum
Inner Join students s
On s.snum = m.studentnum