Why is SQL subquery in SELECT clause computed multiple times? - sql

SELECT
P.Name,
(SELECT AVG(P1.Salary)
FROM Payroll AS P1
WHERE P.Job = P1.Job)
FROM
Payroll AS P
The query is to compute the average salary for each person's job. Why does the subquery actually return multiple tuples instead of one number (the average salary)?

IT is normal,
Using it in this subquery what you are saying if for every P.Job, compute the average salary.
You might try it this way:
SELECT P.Name, p1.AvgSalaries
FROM Payroll AS P
inner join
(SELECT P1.Job, AVG(P1.Salary) as AvgSalaries
FROM Payroll AS P1 group by P1.Job
) p1
on P.Job = P1.Job

If you have multiple "Job" available in Payroll for each "Name", your query will return multiple rows per "Name". If I guess correct, you need some GROUP BY to apply as below to achieve your required output.
SELECT P.Name, (SELECT AVG(P1.Salary)
FROM Payroll AS P1
WHERE P.Job = P1.Job)
FROM Payroll AS P
GROUP BY P.Name,P.Job

I think you want to do:
SELECT P.Name, P.Job, AVG(P.Salary)
FROM Payroll AS P
GROUP BY P.Name,P.Job

Because,SELECT or WHERE clause works on row by row basis.
In a query you if you see in subquery part of select you have referenced outer main query column. For each row, it may have different values producing different results at each row.
Basically query is looking at each employee's job and finding out avg salary offered for that job and giving it for each of the employee.

Related

Find the average from one table and compare it to another

All i want to do is to join two tables, list ALL the rows from the first table, find the average from the second table from all the rows, then list only the ones that are greater than the average.
This is wahat i have done so far, and i am only getting one greater than the average but there are others.
SELECT winner_age, AVG(actor_age) FROM oscar_winners
INNER JOIN actors ON actors.id = oscar_winners.id
WHERE winner_age > (
SELECT AVG(actor_age)
)
You don't really need a join here:
SELECT o.WINNER_AGE
FROM OSCAR_WINNERS o
WHERE o.WINNER_AGE > (SELECT AVG(a.ACTOR_AGE)
FROM ACTORS a)
Something like this?
SELECT actors.*, (SELECT AVG(actor_age) from actors) as average
FROM oscar_winners
INNER JOIN actors ON actors.id = oscar_winners.id and actors.winner_age > (SELECT AVG(actor_age) from actors)
The problem with your query is because you are using a where clause, while you should probably be using having:
SELECT w.winner_age, AVG(a.actor_age)
FROM oscar_winners w
INNER JOIN actors a
ON actors.id = oscar_winners.id
group by w.winner_age
having w.winner_age > AVG(a.actor_age)

NTILE Function and Using Inner Join in Oracle

I am supposed to use the given Database(Its pretty huge so I used codeshare) to list last names and customer numbers of top 5% of customers for each branch. To find the top 5% of customers, I decided to use the NTILE Function, (100/5 = 20, hence NTILE 20). The columns are pulled from two separate tables so I used Inner joins. For the life of me, I honesly cannot figure out where I am going wrong. I keep getting "missing expression" errors but Do not know what exactly I am missing. Here is the Database
Database: https://codeshare.io/5XKKBj
ERD: https://drive.google.com/file/d/0Bzum6VJXi9lUX1d2ZkhudTE3QXc/view?usp=sharing
Here is my SQL Query so far.
SELECT
Ntile(20) over
(partition by Employee.Branch_no
order by sum(ORDERS.SUBTOTAL) desc
) As Top_5,
CUSTOMER.CUSTOMER_NO,
CUSTOMER.LNAME
FROM
CUSTOMER
INNER JOIN ORDERS
ON
CUSTOMER.CUSTOMER_NO = ORDERS.CUSTOMER_NO
GROUP BY
ORDERS.SUBTOTAL,
CUSTOMER.CUSTOMER_NO,
CUSTOMER.LNAME;
You need to join Employee and the GROUP BY must include all non-aggregated expressions. You can use a subquery to generate the subtotals and get the NTILE in the outer query, e.g.:
SELECT
Ntile(20) over
(partition by BRANCH_NO
order by sum_subtotal desc
) As Top_5,
CUSTOMER_NO,
LNAME
FROM (
SELECT
EMPLOYEE.BRANCH_NO,
CUSTOMER.CUSTOMER_NO,
CUSTOMER.LNAME,
sum(ORDERS.SUBTOTAL) as sum_subtotal
FROM CUSTOMER
JOIN ORDERS
ON CUSTOMER.CUSTOMER_NO = ORDERS.CUSTOMER_NO
JOIN EMPLOYEE
ON ORDERS.EMPLOYEE_NO = EMPLOYEE.EMPLOYEE_NO
GROUP BY
EMPLOYEE.BRANCH_NO,
CUSTOMER.CUSTOMER_NO,
CUSTOMER.LNAME
);
Note: you might want to include BRANCH_NO in the select list as well, otherwise the output will look confusing with duplicate customers (if a customer has ordered from employees in multiple branches).
Now, if you want to filter the above query to just get the top 5%, you can put the whole thing in another subquery and add a predicate on the Top_5 column, e.g.:
SELECT CUSTOMER_NO, LNAME
FROM (... the query above...)
WHERE Top_5 = 1;

Microsoft Access SQL query count distinct

I am trying to create a queries in MS Access SQL that performs two separate counts function and have drafted the below code:
SELECT DISTINCT A.Name, Count(A.Name) AS X, Count(b.Address) AS Y
FROM PEOPLE AS A INNER JOIN PEOPLE Sub AS b ON A.PID = b.PID
GROUP BY A.Nam
The problem with this query is that both count functions provide a total count of the number of address for each name and I want the first count function to provide a count of names, therefore I would be grateful if someone could advise how I amend this code to change the first count function to a count distinct
Thanks
Nick
Your Query is wrong - GROUP BY must have A.Name - i think it´s an error by copying.
Otherwise change this. What happens if you do it without DISTINCT? Try it with SUM not with COUNT.
Distinct in your query is obsolete because of the GROUP BY clause.
Furthermore it is not clear if your 'People sub' refers to another table or is a self-join. The following code should work:
SELECT P.Name
, COUNT(P.*) AS X
, COUNT(DISTINCT A.Address) AS Y
FROM PEOPLE AS P
INNER JOIN ADDRESS AS A ON A.PID = P.PID
GROUP BY P.Name

Oracle - select statement to rollup multiple tables within a time frame

I have 3 Oracle tables for a project that link a demo Transaction table to Transaction_Customer and Transaction_Employee as shown below. Each transaction can have multiple customers involved and many employees involved.
I am trying to write a SQL query which will list each Customer_ID that has had transactions with multiple employees within a one period. I would like the output to include a single row for each Customer_ID with a comma separated list of which Employee_IDs had a transaction with that customer.
The output should look like this:
Customer_ID|Employees
601|007,008,009
The basic query to join the tables together looks like this:
select * from transactions t
left join transactions_customer tc
on t.t_id = tc.t_id
left join transactions_employee te
on t.t_id = te.t_id
How do I get this do I finish this assignment and get the query working the way intended?
Thank you!
Transactions
T_ID|Date|Amount
1|1/10/2017|100
2|1/10/2017|200
3|1/31/2017|150
4|2/16/2017|175
5|2/17/2017|175
6|2/18/2017|185
Transactions_Customer
T_ID|Customer_ID
1|600
1|601
1|602
2|605
3|606
4|601
5|607
6|607
Transactions_Employee
T_ID|Employee_ID
1|007
1|008
2|009
3|008
4|009
5|007
6|007
Is this what you want?
select tc.Customer_id,
listagg(te.employee_id, ',') within group (order by te.employee_id) as employees
from Transactions_Customer tc join
Transactions_Employee te
on tc.t_id = te.t_id
group by tc.Customer_id;
You only need the Transactions table for filtering on the date. Your question alludes to such filtering but does not exactly describe it, so I left it out.
Edit:
The customer data (and perhaps the employees data too) has duplicates. To avoid these in the output:
select tc.Customer_id,
listagg(te.employee_id, ',') within group (order by te.employee_id) as employees
from (select distinct tc.t_id, tc.customer_id
from Transactions_Customer tc
) tc join
(select distinct te.t_id, te.employee_id
from Transactions_Employee te
) te
on tc.t_id = te.t_id
group by tc.Customer_id;

SQLite COUNT and LEFT JOIN - how to combine?

There are two tables: one (P) that contains list of products, the other one (H) contains history of products consumed. Every product can be consumed 0 or more times. I need to build a query that will return all products from P along with number of times every product has been consumed, sorted by the times it's been consumed. Here is what I did:
SELECT P.ID, P.Name, H.Date, COUNT(H.P_ID) as Count
FROM P
LEFT JOIN H
ON P.ID=H.P_ID
ORDER BY Count DESC
This seems to work only if history table contains data, but if it does not - the result is incorrect. What am I doing wrong?
You need a group by to get the counts that you need. You also need to apply an aggregate function to H.Date, otherwise it is not clear which date to pick:
SELECT P.ID, P.Name, COUNT(H.P_ID) as Count, MAX(H.Date) as LastDate
FROM P
LEFT JOIN H ON P.ID=H.P_ID
GROUP BY P.ID, P.Name
ORDER BY Count DESC
I picked MAX(H.Date) to produce the date of last consumption; if you need a different date from H, change the aggregating function.
I am not sure if sqlite lets you sort by alias; if it does not, replace
ORDER BY Count DESC
with
ORDER BY COUNT(H.P_ID) DESC