Aggregate sum of rows identified by max value - sql

I am trying to aggregate a the max value of each type, then sum all of this to one value:
resource_id
price
a
100
a
84
b
33
b
100
A 100 and B 100 would be selected (max value of each type of A and B).
Expected return:
200
What I have so far:
SELECT resource_id, MAX(price)
FROM costs
GROUP BY resource_id
It is currently returning A = 100 and B = 100... just need a little help on how to sum all this into a return of just 200
Thanks!

Wrap your query...
select sum(m_price)
from (
SELECT resource_id, MAX(price) as m_price
FROM costs
GROUP BY resource_id
)z

Related

SQL Distinct / GroupBy

Ok, I’m stuck on an SQL query and tried long enough that it’s time to ask for help :) I'm using Objection.js – but that's not super relevant as I really just can't figure out how to structure the SQL.
I have the following example data set:
Items
id
name
1
Test 1
2
Test 2
3
Test 3
Listings
id
item_id
price
created_at
1
1
100
1654640000
2
1
60
1654640001
3
1
80
1654640002
4
2
90
1654640003
5
2
90
1654640004
6
3
50
1654640005
What I’m trying to do:
Return the lowest priced listing for each item
If all listings for an item have the same price, I want to return the newest of the two items
Overall, I want to return the resulting items by price
I’m trying to write a query that returns the data:
id
item_id
name
price
created_at
6
3
Test 3
50
1654640005
2
1
Test 1
60
1654640001
5
2
Test 2
90
1654640004
Any help would be greatly appreciated! I'm also starting fresh, so I can add new columns to the data if that would help at all :)
An example of where my query is right now:
select * from "listings" inner join (select "item_id", MIN(price) as "min_price" from "listings" group by "item_id") as "grouped_listings" on "listings"."item_id" = "grouped_listings"."item_id" and "listings"."price" = "grouped_listings"."min_price" where "listings"."sold_at" is null and "listings"."expires_at" > ? order by CAST(price AS DECIMAL) ASC limit ?;
This gets me listings – but if two listings have the same price, it returns multiple listings with the same item_id – not ideal.
Given the postgresql tag, this should work:
with listings_numbered as (
select *, row_number() over (
partition by item_id
order by price asc, created_at desc
) as rownum
from listings
)
select l.id, l.item_id, i.name, l.price, l.created_at
from listings_numbered l
join items i on l.item_id=i.id
where l.rownum=1
order by price asc;
This is a bit of an advanced query, using window functions and a common table expression, but we can break it down.
with listings_numbered as (...) select simply means to run the query inside of the ..., and then we can refer to the results of that query as listings_numbered inside of the select, as though it was a table.
We're selecting all of the columns in listings, plus one more:
row_number() over (partition by item_id order by price asc, created_at desc). partition by item_id means that we would like the row number to reset for each new item_id, and the order by specifies the ordering that the rows should get within each partition before we number them: first increasing by price, then decreasing by creation time to break ties.
The result of the CTE listings_numbered looks like:
id
item_id
price
created_at
rownum
2
1
60
1654640001
1
3
1
80
1654640002
2
1
1
100
1654640000
3
5
2
90
1654640004
1
4
2
90
1654640003
2
6
3
50
1654640005
1
If you look at only the rows where rownum (the last column) is 1, then you can see that it's exactly the set of listings that you're interested in.
The outer query then selects from this this dataset, joins on items to get the name, filters to only the listings where rownum is 1, and sorts by price, to get the final result:
id
item_id
name
price
created_at
6
3
Test 3
50
1654640005
2
1
Test 1
60
1654640001
5
2
Test 2
90
1654640004
Aggregation functions, as the MIN function you employed in your query, is a viable option, yet if you want to have an efficient query for your problem, window functions can be your best friends. This class of functions allow to compute values over "windows" (partitions) of your table given some specified columns.
For the solution to this problem I'm going to compute two values using the window functions:
the minimum value for "listings.price", by partitioning on "listings.item_id",
the maximum value for "created_at", by partitioning on "listings.item_id" and listings.price
SELECT *,
MIN(price) OVER(PARTITION BY item_id) AS min_price,
MAX(created_at) OVER(PARTITION BY item_id, price) AS max_created_at
FROM listings
Once you have all records of listings associated to the corresponding minimum price and latest date, it's necessary for you to select the records whose
price equals the minimum price
created_at equals the most recent created_at
WITH cte AS (
SELECT *,
MIN(price) OVER(PARTITION BY item_id) AS min_price,
MAX(created_at) OVER(PARTITION BY item_id, price) AS max_created_at
FROM listings
)
SELECT id,
item_id,
price,
created_at
FROM cte
WHERE price = min_price
AND created_at = max_created_at
If you need to order by price, it's sufficient to add a ORDER BY price clause.
Check the demo here.

SQL how to find the product group with highest porportion of expensive items

I have a table with products, product_group and price level. How can I find the product group with the highest porportion of expensive items?
product | product_group | price_level
1 a expensive
2 a low
3 b low
4 b expensive
5 b expensive
6 c expensive
I have tried this query, but it keeps all price_levels, not just the expensive ones.
select product, product_group, price_level,
count(price_level) over (partition by product_group, price_level) as pl,
count(product) over (partition by product_group) as p
from tbl
Essentially, I want to divide the number of expensive items in one product group by the total number of items in the same product group.
Desired output:
Product group | Percentage
c 1
You can use conditional aggregation:
select product_group,
avg( (price_level = 'expensive')::int ) as expensive_ratio
from tbl
group by product_group
order by expensive_ratio desc
limit 1;
The use of avg() is a convenient way to get the ratio you want. A more verbose method would be:
count(*) filter (where price_level = 'expensive') * 1.0 / count(*)

How to summing up the row values by passing condition

I want to sum up the column (price) by passing sum limitation.
For example I have the below table and I want to limit the records by 10k or 15k.
ID PRICE
x1 10,000
x2 20,000
x3 5,000
x4 7,500.00
I want the result should be
For <=10000
ID PRICE
x1 10,000
<=15000
ID PRICE
x1 10,000
x3 5,000
<=14000
ID PRICE
x3 5,000
x4 7,500.00
I made some search on it.I find some window functions in postgresql i.e OVER function. so I written the below query
WITH cte AS (
SELECT *, sum(price) OVER (order BY id) AS total
FROM test1
)
SELECT *
FROM cte
WHERE total <= amount
But condition <=15,000 and <=14000 not bringing the right result.
I want to sum up the price column and fetch the records which matches the sum result with our given amount or condition and more specifically it should return the records by verifying and returning any other record can accommodate within the amount which we pass
Please help me for it.
Thanks
your Q is not clear but if you want you can sort the price column so you can get the minimum sum that is <= the limit.
try order by on price col:
WITH cte AS (
SELECT *, sum(price) OVER (order BY price) AS total
FROM test1
)
SELECT *
FROM cte
WHERE total <= amount
Play here https://www.db-fiddle.com/f/mckDPhtrY4vRrkF2NjhQcC/0
WITH b AS (
SELECT *, sum(price) OVER (order BY price) AS total
FROM a
)
SELECT *
FROM b
WHERE (total <= your_max OR price <= your_max) AND total >= your_min

SQL select top rows based on limit

Please help me t make below select query
Source table
name Amount
-----------
A 2
B 3
C 2
D 7
if limit is 5 then result table should be
name Amount
-----------
A 2
B 3
if limit is 8 then result table
name Amount
-----------
A 2
B 3
C 2
You can use window function to achieve this:
select name,
amount
from (
select t.*,
sum(amount) over (
order by name
) s
from your_table t
) t
where s <= 8;
The analytic function sum will be aggregated row-by-row based on the given order order by name.
Once you found sum till given row using this, you can filter the result using a simple where clause to find rows till which sum of amount is under or equal to the given limit.
More on this topic:
The SQL OVER() clause - when and why is it useful?
https://explainextended.com/2009/03/08/analytic-functions-sum-avg-row_number/

Simple SQL select sum and values of same column

I have a co-worker who is working on a table with an 'amount' column.
They would like to get the top 5 amounts and the sum of the amounts in the same query.
I know you could do this:
SELECT TOP 5 amount FROM table
UNION SELECT SUM(amount) FROM table
ORDER BY amount DESC
But this produces results like this:
1000 (sum)
100
70
50
30
20
When what they really need is this:
100 | 1000
70 | 1000
50 | 1000
30 | 1000
20 | 1000
My intuitive attempts to achieve this tend to run into grouping problems, which isn't such an issue when you are selecting a different column, but is when you want to use an aggregate function based on the column you are selecting.
You can use a CROSS JOIN for this:
SELECT TOP 5 a.amount, b.sum
FROM table a
CROSS JOIN (SELECT SUM(amount) sum FROM table) b
ORDER BY amount DESC
This might work
SELECT TOP 5 amount, (SELECT SUM(amount) FROM table)
FROM table
ORDER BY amount DESC
Not really pretty, but this shouls do it:
SELECT TOP 5 amount, SAmount
FROM table Join
(SELECT SUM(amount) As SAmount FROM table)
ORDER BY amount DESC
As said by others, I'd probably use two queries.
Another approach using analytic functions (SQL Server 2005+):
SELECT TOP 5 amount, SUM(amount) OVER()
FROM table
ORDER BY
amount DESC