How to get the % of a SUM colum in the same query - sql

I have a query that SUM all the amounts GROUP BY different categories. I would like to get as well the % of that SUM amount by the total.
My query is the next:
SELECT category.name, SUM(account.amount_default_currency) FROM account
INNER JOIN accounts ON account.accounts_id = accounts.id
INNER JOIN category ON account.category_id = category.id
INNER JOIN category_type ON category.category_type_id = category_type.id
GROUP BY category.name;
And I get:
name
SUM
salary
230
restaurants
2254
How could I do it?

Divide each sum by the total, which you can get in the same SELECT with a window function over the aggregate function:
SELECT c.name
, sum(a.amount_default_currency) AS sum
, round(sum(a.amount_default_currency) * 100.0
/ sum(sum(a.amount_default_currency)) OVER (), 2) AS pct
FROM category c
JOIN account a ON a.category_id = c.id
GROUP BY c.name; -- c.id ??
This works because window functions are applied after aggregate functions. See:
Postgres window function and group by exception
I removed joins to accounts and category_type, which are probably just ballast for the query, assuming the dropped joins wouldn't eliminate rows.
If category.id is the primary key of that table, rather use GROUP BY c.id, and include c.id in the SELECT list. ("name" is not necessarily unique?) See:
PostgreSQL - GROUP BY clause

Related

SQL dividing a count from one table by a number from a different table

I am struggling with taking a Count() from one table and dividing it by a correlating number from a different table in Microsoft SQL Server.
Here is a fictional example of what I'm trying to do
Lets say I have a table of orders. One column in there is states.
I have a second table that has a column for states, and second column for each states population.
I'd like to find the order per population for each sate, but I have struggled to get my query right.
Here is what I have so far:
SELECT Orders.State, Count(*)/
(SELECT StatePopulations.Population FROM Orders INNER JOIN StatePopulations
on Orders.State = StatePopulations.State
WHERE Orders.state = StatePopulations.State )
FROM Orders INNER JOIN StatePopulations
ON Orders.state = StatePopulations.State
GROUP BY Orders.state
So far I'm contending with an error that says my sub query is returning multiple results for each state, but I'm newer to SQL and don't know how to overcome it.
If you really want a correlated sub-query, then this should do it...
(You don't need to join both table in either the inner or outer query, the correlation in the inner query's where clause does the 'join'.)
SELECT
Orders.state,
COUNT(*) / (SELECT population FROM StatePopulation WHERE state = Orders.state)
FROM
Orders
GROUP BY
Orders.state
Personally, I'd just join them and use MAX()...
SELECT
Orders.state,
COUNT(*) / MAX(StatePopulation.population)
FROM
Orders
INNER JOIN
StatePopulation
StatePopulation.state = Orders.state
GROUP BY
Orders.state
Or aggregate your orders before you join...
SELECT
Orders.state,
Orders.order_count / StatePopulation.population
FROM
(
SELECT
Orders.state,
COUNT(*) AS order_count
FROM
Orders
GROUP BY
Orders.state
)
Orders
INNER JOIN
StatePopulation
StatePopulation.state = Orders.state
(Please forgive typos and smelling pistakes, I'm doing this on a phone.)

ACCESS SQL - Sum column grouped by a criteria and show also empty results

For this question, let's say I have two tables: Transactions and Categories.
Transactions:
Transaction_ID
Category_ID_FK
TransactionDate
TransactionValue
Categories:
Category_ID
CategoryDescritpion
I am trying to write a query that will sum all the TransactionValues that took place in a month (December, for instance) grouped by Category_ID and display the results for all category even when there are no Transactions on the table for a specific Category.
SELECT Categories.Category_ID, Sum(TransactionValue)
FROM Categories LEFT JOIN Transactions ON Categories.Category_ID = Transactions.Category_ID_FK
WHERE Month(TransactionDate) = '12'
With this, I only get results from categories that have at least one transaction on the Transactions table.
Thank you all!
You can use a subquery in MS Access:
SELECT c.Category_ID, Sum(t.TransactionValue)
FROM Categories as c LEFT JOIN
(SELECT t.*
FROM Transactions as t
WHERE Month(t.TransactionDate) = 12
) as t
ON c.Category_ID = t.Category_ID_FK
GROUP BY c.Category_ID;
The typical SQL syntax would include the condition in the ON clause:
SELECT c.Category_ID, Sum(t.TransactionValue)
FROM Categories as c LEFT JOIN
Transactions as t
ON c.Category_ID = t.Category_ID_FK AND Month(t.TransactionDate) = 12
GROUP BY c.Category_ID;
I don't think that MS Access supports this syntax.

SQL Sum returning wrong number

I am adding up the amount of tickets sold for a sporting event, the answer should be under 100 but my answer is in the thousands.
SELECT Stubhub.Active.Opponent,
SUM(Stubhub.Active.Qty) AS AQty, SUM(Stubhub.Sold.Qty) AS SQty
FROM Stubhub.Active INNER JOIN
Stubhub.Sold ON Stubhub.Active.Opponent = Stubhub.Sold.Opponent
GROUP BY Stubhub.Active.Opponent
This is type of problem occurs because you are getting a cartesian product between each table for each opponent. The solution is to pre-aggregate by opponent:
SELECT a.Opponent, a.AQty, s.SQty
FROM (SELECT a.Opponent, SUM(a.Qty) as AQty
FROM Stubhub.Active a
GROUP BY a.Opponent
) a INNER JOIN
(SELECT s.Opponent, SUM(s.QTY) as SQty
FROM Stubhub.Sold s
GROUP BY s.Opponent
) s
ON a.Opponent = s.Opponent;
Notice that in this case, you do not need the aggregation in the outer query.

Taking SUM of values that are obtained from SUM of another column

I have a MSSQL query in which I am taking grandtotal, which is a SUM of values that are obtained by taking SUM.
select
s.name
,SUM(b.dAmount) as total
,SUM(SUM(b.dAmount)) as grandtotal
from t_sales a
left outer join t_cust b on (b.dId=a.id)
where a.custId=#customerId
GROUP BY b.name;
I can get the name and total but grandtotal doesn't return any value. Please let me know what is the correct way to take SUM of values that are SUM of another values.Thanks
Try this:
select
b.name
,SUM(b.dAmount) as total
, SUM(SUM(b.dAmount)) OVER () as grandtotal
from t_sales a
left outer join t_cust b on (b.dId=a.id)
where a.custId=#customerId
GROUP BY b.name;
In MySQL, you can't do this without either using a nested subquery or by using the GROUP BY WITH ROLLUP
For your case, I'd recommend just using the WITH ROLLUP keyword, this will add another row to the result set with the grand total. It will also avoid returning the same total on every row of the set.
SELECT IFNULL(s.name, 'Total'), SUM(b.dAmount) as total
FROM t_sales a
LEFT OUTER JOIN t_cust b ON (b.dId = a.id)
WHERE a.custId = #customerId
GROUP BY b.name WITH ROLLUP;
If you're not directly displaying the results you can remove the IFNULL(s.name, 'Total') bit - the super-aggregate will have nulls on all columns not in the SUM()
You can also try this
create table #t_sales (name varchar(100),amount numeric(18,2))
INSERT INTO #t_sales (name,amount)
values ('A',100.00)
,('B',200.00)
,('C',300.00)
,('C',400.00)
INSERT INTO #t_sales (name,amount)
values ('A',100.00)
,('B',200.00)
,('C',300.00)
,('C',400.00)
SELECT CASE WHEN (GROUPING(name) = 1) THEN 'GRAND TOTAL'
ELSE ISNULL(name, ' ')
END AS [type]
,SUM(amount) AS TOTAL
FROM #t_sales
GROUP BY name WITH ROLLUP

Sub-Query Problem

I have two tables like this
Member and their Purchases
I need the output like this
Member_ID | CountofProducts
(and the Product Value not should be 0 and Purchase Status = 1)
SELECT
MemberName,
(SELECT COUNT(*) AS Count
FROM dbo.Purchases
WHERE MemberName = dbo.Members.MemberName
AND Res_Status = 1) AS Count
FROM
dbo.Members
I can get their total CountofPurchased Products from the above query but I need to avoid their count=0 how to do it ?
You can try something like
SELECT m.MemberName,
COUNT(p.*) Cnt
FROM Members m INNER JOIN
Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
There is no need for the HAVING clause, as the INNER JOIN will exclude all entries in Members that do not have Purchases.
SELECT m.MemberName, COUNT(p.*) AS CountOfProducts
FROM Members m
INNER JOIN Purchases p ON m.MemberName = p.MemberName
WHERE p.Res_Status = 1
GROUP BY m.MemberName
HAVING COUNT(p.*) > 0
I think the above will somewhat do what you want. The key is that you probably do not even need your subquery, but rather you can handle the query (possibly with greater efficiency) just using a join.