Oracle: apply condition over aggregated functions and not for all rows of the SQL statement - sql

I want to calculate for an Oracle table the max value from a select statement based not on all rows returned from the query, but on a condition over a sub-set of those rows.
I'll try to explain better with an example:
ID | NAME | AGE | COMPANY
1 | Alex | 28 | A
2 | Alan | 22 | A
3 | Bob | 21 | B
4 | Carl | 20 | C
5 | Dave | 24 | C
6 | Eric | 26 | C
7 | Matt | 33 | D
I want to obtain the max age for every company under-25 years, but I also want to count the total numbers of persons for every company.
So, I want this result:
COMPANY | DEPENDENTS | MAX_UNDER_25
A | 2 | 22
B | 1 | 21
C | 3 | 24
D | 1 | (null)
How can I obtain this result with a single SQL query, without joining the elaboration for the sum of records and the other elaboration for the max with condition ?
I want to avoid this select:
SELECT R1.COMPANY, R1.DEPENDENTS, R2.MAX_UNDER_25 FROM
(SELECT COMPANY, COUNT(*) DEPENDENTS FROM TABLE
GROUP BY COMPANY) R1
LEFT JOIN
(SELECT COMPANY, MAX(AGE) MAX_UNDER_25 FROM TABLE
WHERE AGE < 25
GROUP BY COMPANY) R2
ON R1.COMPANY = R2.COMPANY;
It is possible to obtain that with a more simple query?

Simply use a case expression when aggregating MAX:
select company, count(*), max(case when age < 25 then age end)
from table
group by company

Related

SQL: How do I get a filtered count for each distinct row in a table?

Note: this is different from questions that ask "I want a count for each distinct row in a table" which has been answered numerous times. This is a filtered count, so the counting part of the query needs a more complex WHERE clause. Consider this dataset:
customer_id | user_id | age
-----------------------------
1 | 932 | 20
1 | 21 | 3
1 | 2334 | 32
2 | 232 | 10
2 | 238 | 28
3 | 838 | 39
3 | 928 | 83
4 | 842 | 12
I want to query this table and know the number of users over the age of 13 for each distinct customer_id. So the result would be:
customer_id | over_13_count
-----------------------------
1 | 2
2 | 1
3 | 2
4 | 0
I've tried something like this but it just runs forever, so I think I'm doing it wrong:
SELECT DISTINCT customer_id,
(SELECT COUNT(*) FROM mytable AS m2 WHERE m2.customer_id = m1.customer_id AND age > 13) AS over_13_count
FROM mytable AS m1
ORDER BY customer_id
Just use conditional aggregation:
SELECT customer_id,
SUM(CASE WHEN age > 13 THEN 1 ELSE 0 END) asover_13_count
FROM mytable m1
GROUP BY customer_id

SQL select values sum by same ID

here is my table called "Employee"
eID | name |
==============
1 | Mike |
2 | Josh |
3 | Mike |
And table called "Sells"
sID | eID | | price |
=========================
1 | 1 | | 8 |
2 | 3 | | 9 |
3 | 3 | | 5 |
4 | 1 | | 4 |
5 | 1 | | 3 |
This should be my expected result: returns the total income per employee
name | Income |
==================
Mike | 15 |
Josh | 0 |
Mike | 14 |
Actually, I know use the query "SUM...GROUP BY..." to get the incomes of 15 and 14, but I don't know how to get the income of 0 which is not shown on the "Sells" table.
Could someone give me some help? Thanks a lot.
You just need to use a left outer join, so you can get the sum for missing values too. You could use case expression to deal with null values
SELECT e.name,
COALESCE(SUM(price), 0) as Income
FROM employees e
LEFT OUTER JOIN sells s
ON e.eid = s.eid
GROUP BY e.eid, e.name
Edited: case expression is not needed. I put coalesce on the return of sum fuction, in order to deal with missing values (SUM over an empty set returns NULL)

Subtract the value of a row from grouped result

I have a table supplier_account which has five coloumns supplier_account_id(pk),supplier_id(fk),voucher_no,debit and credit. I want to get the sum of debit grouped by supplier_id and then subtract the value of credit of the rows in which voucher_no is not null. So for each subsequent rows the value of sum of debit gets reduced. I have tried using 'with' clause.
with debitdetails as(
select supplier_id,sum(debit) as amt
from supplier_account group by supplier_id
)
select acs.supplier_id,s.supplier_name,acs.purchase_voucher_no,acs.purchase_voucher_date,dd.amt-acs.credit as amount
from supplier_account acs
left join supplier s on acs.supplier_id=s.supplier_id
left join debitdetails dd on acs.supplier_id=dd.supplier_id
where voucher_no is not null
But here the debit value will be same for all rows. After subtraction in the first row I want to get the result in second row and subtract the next credit value from that.
I know it is possible by using temporary tables. The problem is I cannot use temporary tables because the procedure is used to generate reports using Jasper Reports.
What you need is an implementation of the running total. The easiest way to do it with a help of a window function:
with debitdetails as(
select id,sum(debit) as amt
from suppliers group by id
)
select s.id, purchase_voucher_no, dd.amt, s.credit,
dd.amt - sum(s.credit) over (partition by s.id order by purchase_voucher_no asc)
from suppliers s
left join debitdetails dd on s.id=dd.id
order by s.id, purchase_voucher_no
SQL Fiddle
Results:
| id | purchase_voucher_no | amt | credit | ?column? |
|----|---------------------|-----|--------|----------|
| 1 | 1 | 43 | 5 | 38 |
| 1 | 2 | 43 | 18 | 20 |
| 1 | 3 | 43 | 8 | 12 |
| 2 | 4 | 60 | 5 | 55 |
| 2 | 5 | 60 | 15 | 40 |
| 2 | 6 | 60 | 30 | 10 |

SQL - SELECT all households by last value

I'm facing a problem that I cant wrap my head around so maybe you can help me to solve it!?
I have one table:
id | datetime | property | house_id | household_id | plug_id | value
---+--------------------+----------+----------+--------------+---------+--------
1 |2013-08-31 22:00:01 | 0 | 1 | 1 | 1 | 15
2 |2013-08-31 22:00:01 | 0 | 1 | 1 | 3 | 3
3 |2013-08-31 22:00:01 | 0 | 1 | 2 | 1 | 21
4 |2013-08-31 22:00:01 | 0 | 1 | 2 | 2 | 1
5 |2013-08-31 22:00:01 | 0 | 2 | 1 | 3 | 53
6 |2013-08-31 22:00:02 | 0 | 2 | 2 | 4 | 34
7 |2013-08-31 22:00:02 | 0 | 1 | 1 | 1 | 16
...
The table holds electricity consumption measurements per second for multiple houses that have multiple households (apartments) in them. Each household has multiple electricity plugs. None of the houses or households have a unique id but are identified by a combination of house_id and household_id.
1) I need a SQL query that can give me a list of all the unique households.
2) I want to use the list from 1) to create a SQL query that gives me a list of the highest value for each household (the value is cumulative, so the latest datetime holds the highest value). I need a total value (SUM) for each household (sum of all the plugs in that household), i.e. a list of of households with their total electricity consumption.
Is this even possible? I'm using SQL Server 2012 and the table has 100.000.000 rows.
If I understand correctly, you want the sum of the highest values of value, for house/household/plug combinations. This may do what you want:
select house_id, household_id, sum(maxvalue)
from (select house_id, household_id, plug_id, max(value) as maxvalue
from consumption
group by house_id, household_id, plug_id
) c
group by house_id, household_id;
according to your description I think you can use this query;
select house_id,household_id, max(value), sum(value) from your_table_name group by house_id,household_id

MS Access: Finding the top of each group in an SQL query

In my table, I have four columns.
I have a player name, an ID, an age, and a score.
ID | Player Name | Age | Score
------------------------------
0 | James | 24 | 20
1 | Carly | 24 | 25
2 | Matt | 24 | 19
3 | Jess | 26 | 35
4 | Jimmy | 26 | 32
5 | Tom | 27 | 19
6 | Brian | 27 | 25
I need to write a query to find the top player of each age group, but I am stumped. I've tried sorting both and using the Max() function, and I have tried manually looping through the values to find the top, but with no avail.
This is the sort of result I'd expect:
ID | Player Name | Age | Score
------------------------------
1 | Carly | 24 | 25
3 | Jess | 26 | 35
6 | Brian | 27 | 25
I am quite confused, and I'm sure there's a simple way to achieve this. Thanks.
One way to solve this is to create an inline view of the max scores per age and then join to it
SELECT p.*
FROM players p
INNER JOIN (SELECT age,
Max(score) as mScore
FROM players
GROUP BY age) AS mp
ON p.age = mp.age
AND p.score = mp.mscore
You should note that if there is tie for max more than one record can appear per age