SQL two different Aggregate functions with LEFT Join - sql

How can I return two aggregate function with different condition with a LEFT join?
I already have this:
SELECT VehicleType.vehicleTypeName, COUNT(*) as SALE
FROM Transactions
LEFT JOIN VehicleType
ON Transactions.VehicleTypeID = VehicleType.vehicleTypeID
WHERE Transactions.isRefund = 0
GROUP BY VehicleType.vehicleTypeName
This returns the gross vehicle count
Name | Sale
---------------
vehicle1 | 10
vehicle2 | 15
I want to know how to get the net count per vehicle (Count of Vehicles as Sale less the count of vehicles as refund) if possible
Name | NetCount
---------------
vehicle1 | 8
vehicle2 | 10
If not something like this.
Name | Sale | Refund
-------------------------
vehicle1 | 10 | 2
vehicle2 | 15 | 5

You can calculate it suming a conditional expression :
SELECT VehicleType.vehicleTypeName,
sum(case when Transactions.isRefund = 0 then 1 else 0 end) as Sale,
sum(case when Transactions.isRefund = 1 then 1 else 0 end) as Refund
FROM Transactions
LEFT JOIN VehicleType ON Transactions.VehicleTypeID = VehicleType.vehicleTypeID
GROUP BY VehicleType.vehicleTypeName
And your first result would be :
SELECT VehicleType.vehicleTypeName,
sum(case when Transactions.isRefund = 0 then 1 else -1 end) as NetCount
FROM Transactions
LEFT JOIN VehicleType ON Transactions.VehicleTypeID = VehicleType.vehicleTypeID
GROUP BY VehicleType.vehicleTypeName

Related

How to COUNT different values without adding to GROUP BY

I have a data set that contains a name for every "job" record, and whether the job passed or failed. I want to show the Name, number of jobs, how many passed, and how many failed in one row.
I am grouping the name and using COUNT on the name to count the total number of jobs, which works fine, but I can't show how many passed and how many failed without adding them to the GROUP BY clause causing the data to separate again.
SELECT I.Name, Count(I.Name) As NumberOfJobs,
CASE WHEN WI.resultTypeID = 1 THEN COUNT(WI.resultTypeID) END AS [Passed],
CASE WHEN WI.resultTypeID = 2 THEN COUNT(WI.resultTypeID) END AS [Failed],
FROM DB.DBO.People AS I
INNER JOIN DB2.dbo.Jobs AS WI ON I.JOBID = WI.JOBID
GROUP BY I.Name, wi.resultTypeID
+-----------+-----------+--------+--------+
| Name | NumofJobs | Passed | Failed |
+-----------+-----------+--------+--------+
| Dale Test | 2 | 2 | NULL |
| Dale Test | 2 | NULL | 2 |
+-----------+-----------+--------+--------+
This is what happens when I add ResultTypeID to the GROUP BY, but I want this:
+-----------+-----------+--------+--------+
| Name | NumofJobs | Passed | Failed |
+-----------+-----------+--------+--------+
| Dale Test | 4 | 2 | 2 |
+-----------+-----------+--------+--------+
Is there anyway to do this?
You want conditional aggregation. The case expression is an argument to the aggregation function:
SELECT I.Name, Count(*) As NumberOfJobs,
SUM(CASE WHEN WI.resultTypeID = 1 THEN 1 ELSE 0 END) AS [Passed],
SUM(CASE WHEN WI.resultTypeID = 2 THEN 1 ELSE 0 END) AS [Failed],
FROM DB.DBO.People I INNER JOIN
DB2.dbo.Jobs WI
ON I.JOBID = WI.JOBID
GROUP BY I.Name;
I am guessing that wi.resultTypeID is not NULL, so I replaced the COUNT() with SUM() because I prefer SUM() in this case.
You don't need to group your query by wi.resultTypeID .
simply remove wi.resultTypeID from group by statement and put it inside aggregate function:
SELECT I.Name, Count(I.Name) As NumberOfJobs,
SUM(CASE WHEN WI.resultTypeID = 1 THEN 1 ELSE 0 END) AS [Passed],
SUM(CASE WHEN WI.resultTypeID = 2 THEN 1 ELSE 0 END) AS [Failed],
FROM DB.DBO.People AS I
INNER JOIN DB2.dbo.Jobs AS WI ON I.JOBID = WI.JOBID
GROUP BY I.Name

SQL calculate the sum of a column based on the date in two differents variables

I've a simple table in this forme :
BillItem (id,amount, volume, bill_date,....other fields)
I want to obtain in my query 4 differents sum of fields amount and volume based on the date
for example, in my table i've this data :
Id | amount | volume | bill_date | libelle
1 | 10 | 50 | 02/04/2016| bill1
2 | 20 | 55 | 02/04/2016| bill1
2 | 88 | 66 | 02/05/2016| bill1
3 | 30 | 60 | 03/05/2016| bill2
4 | 40 | 10 | 02/04/2016| bill3
5 | 50 | 20 | 02/05/2016| bill3
and the result must be like this :
bill1, sum_date_1=30, sum_date_2=88, sum_volume_date_1=105, sum_volume_date_2=66
bill2, sum_date_1=0, sum_date_2=30, sum_volume_date_1=0, sum_volume_date_2=60
bill3, sum_date_1=40, sum_date_2=50, sum_volume_date_1=10, sum_volume_date_2=20
i've this query with only two sum variable :
select ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE,
sum(bi.ACTUAL_AMOUNTVAT),sum(bi.ACTUAL_VOLUME), bi.BILL_DATE
from bill_item bi left outer join ANALYTIC_SECTION ans on ans.TREE_PATH=bi.REPORT_SECTION
where bi.account_id=7
and bi.BILL_DATE<='31/05/2016' and bi.BILL_DATE>='01/04/2016'
and ans.REPORT_TYPE='ARPE_REPORT' and ans.ACCOUNT_ID=7
group by ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE, bi.BILL_DATE;
Is it possible to obtain two differents sum for each field (amount and volume) ?
I've resolved the query like this :
select distinct ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE,
sum(Case when bi.BILL_DATE<'01/05/2016' then bi.ACTUAL_AMOUNTVAT ELSE 0 END) as amount_m1,
sum(Case when bi.BILL_DATE>='01/05/2016' then bi.ACTUAL_AMOUNTVAT ELSE 0 END) as amount_m,
sum(Case when bi.BILL_DATE<'01/05/2016' then bi.ACTUAL_VOLUME ELSE 0 END) as volume_m1,
sum(Case when bi.BILL_DATE>='01/05/2016' then bi.ACTUAL_VOLUME ELSE 0 END) as volume_m
--,bi.BILL_DATE
from bill_item bi left outer join ANALYTIC_SECTION ans on ans.TREE_PATH=bi.REPORT_SECTION
where bi.account_id=7
and bi.BILL_DATE<='06/05/2016' and bi.BILL_DATE>='06/04/2016'
and ans.REPORT_TYPE='ARPE_REPORT' and ans.ACCOUNT_ID=7
group by ans.SERVICE_TYPE, ans.SERVICE_SUB_TYPE;
Thank's All for your help
it looks to me like you want to summarize by each date and then return the value for the months in ascending fields?
It might work with a subquery and a ranking option, although this may not be the most efficient route.
Select t.Id
, Max(Case When t.BillRank=1 Then t.SumVolume Else Null End) As Volume1
, Max(Case When t.BillRank=2 Then t.SumVolume Else Null End) As Volume2
, Max(Case When t.BillRank=1 Then t.SumVAT Else Null End) As Vat1
, Max(Case When t.BillRank=2 Then t.SumVAT Else Null End) As Vat2
From ( Select ans.SERVICE_TYPE
, ans.SERVICE_SUB_TYPE
, Sum(bi.ACTUAL_AMOUNTVAT) As SumVAT
, Sum(bi.ACTUAL_VOLUME) As SumVolume
, bi.BILL_DATE
, bi.Id
, Rank() Over ( Partition By bi.Id Order By bi.BILL_DATE Asc ) As BillRank
From bill_item As bi
Left Outer Join ANALYTIC_SECTION as ans
On ans.TREE_PATH = bi.REPORT_SECTION
Group By ans.SERVICE_TYPE
, ans.SERVICE_SUB_TYPE
, bi.BILL_DATE
, bi.Id
) t
Group By t.Id;

Sql Count Where Groupby SubQuery

Hey I have this query,
SELECT item_type.id, item_type.item_type,
(SELECT COUNT(*) FROM item WHERE item.sale_transaction_id IS NULL) as stock_qty,
(SELECT COUNT(*) FROM item WHERE item.sale_transaction_id IS NOT NULL) as sold_qty
FROM item
JOIN item_type ON item.item_type_id = item_type.id
GROUP BY item.item_type_id
This gives me a result:
| id | item_type | stock_qty | sold_qty|
----------------------------------------
| 1 | Book | 12 | 12 |
| 2 | Pencil | 12 | 12 |
| ........... # etc
But this does not work as intended, I need to do it like this to make it work:
SELECT item_type.id, item_type.item_type,
COUNT(item.purchase_transaction_id) - COUNT(item.sale_transaction_id) as stock_qty,
COUNT(item.sale_transaction_id) as sold_qty
FROM item
JOIN item_type ON item.item_type_id = item_type.id
GROUP BY item.item_type_id
and the result is what I want and this is the correct/expected output:
| id | item_type | stock_qty | sold_qty|
----------------------------------------
| 1 | Book | 1 | 0 |
| 2 | Pencil | 0 | 5 |
| ........... # etc
In my Table structure, each item that has sale_transaction_id is marked as sold.
My question is why the first one is not working as intended? and how do I make it to work as 2nd one? Is it actually possible using subquery for this type of query?
SELECT item_type.id, item_type.item_type,
SUM(case when item.sale_transaction_id IS NULL then 1 else 0 end) as stock_qty,
SUM(case when item.sale_transaction_id IS NOT NULL then 1 else 0 end) as sold_qty
FROM item
JOIN item_type ON item.item_type_id = item_type.id
GROUP BY item_type.id, item_type.item_type
Is this what you need?
You need to add correlation to the subqueries:
SELECT item_type.id, item_type.item_type,
(SELECT COUNT(item.purchase_transaction_id) - COUNT(item.sale_transaction_id)
FROM item
WHERE item.item_type_id = i.item_type_id) as stock_qty,
(SELECT COUNT(item.sale_transaction_id)
FROM item
WHERE item.item_type_id = i.item_type_id ) as sold_qty
FROM item AS i
JOIN item_type ON i.item_type_id = item_type.id
GROUP BY i.item_type_id
The subqueries are now correlated: they are executed for each item_type_id of the outer query and return results for this exact value each time.
But this seems like an overkill, since you can get the same result applying aggregation in the outer query, just like you do in the second query of your question.
Start from "item_type" table, instead of "item" table and use left join, otherwise you will never get a row in the query result if you not have items from a type.
SELECT
item_type.id,
item_type.item_type,
SUM(CASE WHEN item.id IS NOT NULL AND item.sale_transaction_id IS NULL THEN 1 ELSE 0 END) AS stock_qty,
SUM(CASE WHEN item.id IS NOT NULL AND item.sale_transaction_id IS NOT NULL THEN 1 ELSE 0 END) AS sold_qty
FROM
item_type
LEFT JOIN
item
ON
item.item_type_id = item_type.id
GROUP BY
item_type.id, item_type.item_type
Avoid using subselects. Each subselect you use will be executed for each row and that will slow down performance a lot. You can run explain on both queries (subselect and join version) and you will see what I mean
It will be helpful if you post an example data of initial tables.

SUM in multi-currency

I am trying to do SUM() in a multi-currency setup. The following will demonstrate the problem that I am facing:-
Customer
-------------------------
Id | Name
1 | Mr. A
2 | Mr. B
3 | Mr. C
4 | Mr. D
-------------------------
Item
-------------------------
Id | Name | Cost | Currency
1 | Item 1 | 5 | USD
2 | Item 2 | 2 | EUR
3 | Item 3 | 10 | GBP
4 | Item 4 | 5 | GBP
5 | Item 5 | 50 | AUD
6 | Item 6 | 20 | USD
7 | Item 3 | 10 | EUR
-------------------------
Order
-------------------------
User_Id | Product_Id
1 | 1
2 | 1
1 | 2
3 | 3
1 | 5
1 | 7
1 | 5
2 | 6
3 | 4
4 | 2
-------------------------
Now, I want the output of a SELECT query that lists the Customer Name and the total amount worth of products purchased as:-
Customer Name | Amount
Mr. A | Multiple-currencies
Mr. B | 25 USD
Mr. C | 15 GBP
Mr. D | 2 EUR
So basically, I am looking for a way to add the cost of multiple products under the same customer, if all of them have the same currency, else simply show 'multiple-currencies'. Running the following query will not help:-
SELECT Customer.Name, SUM(Item.Amount) FROM Customer
INNER JOIN Order ON Order.User_Id = Customer.Id
INNER JOIN Item ON Item.Id = Order.Product_Id
GROUP BY Customer.Name
What should my query be? I am using Sqlite
I would suggest two output columns, one for the currency and one for the amount:
SELECT c.Name,
(case when max(currency) = min(currency) then sum(amount)
end) as amount,
(case when max(currency) = min(currency) then max(currency)
else 'Multiple Currencies'
end) as currency
FROM Customer c INNER JOIN
Order o
ON o.User_Id = c.Id INNER JOIN
Item
ON i.Id = o.Product_Id
GROUP BY c.Name
If you want, you can concatenate these into a single string column. I just prefer to have the information in two different columns for something like this.
The above is standard SQL.
I think your query should looks like this
SELECT
Data.Name AS [Customer Name],
CASE WHEN Data.Count > 1 THEN "Multiple-currencies" ELSE CAST(Data.Amount AS NVARCHAR) END AS Amount
FROM
(SELECT
Customer.Name,
COUNT(Item.Currency) AS Count,
SUM(Item.Amount) AS Amount
FROM
Customer
INNER JOIN Order ON Order.User_Id = Customer.Id
INNER JOIN Item ON Item.Id = Order.Product_Id
GROUP BY
Customer.Name) AS Data
A subquery to get the count of currencies and then ask for them in the main query to show the total or the text "Multiple-currencies".
Sorry if there is any mistake or mistype but I don't have a database server to test it
Hope this helps.
IMO I would start by standardizing variable names. Why call ID in customer table USER_ID in order table? Just a pet peeve. Anyway, you should learn how to build queries.
start with joining the customer table to the order table on then join the result to the item table. The first join is on CUSTOMER_ID and the second join is on PRODUCT_ID. Once you have that working use SUM and GROUP BY
Ok, I managed to solve the problem this way:-
SELECT innerQuery.Name AS Name, (CASE WHEN innerQuery.Currencies=1 THEN (innerQuery.Amount || innerQuery.Currency) ELSE 'Mutliple-Currencies' END) AS Amount, FROM
(SELECT Customer.Name, SUM(Item.Amount), COUNT(DISTINCT Item.Currency) AS Currencies, Item.Currency AS Currency FROM Customer
INNER JOIN Order ON Order.User_Id = Customer.Id
INNER JOIN Item ON Item.Id = Order.Product_Id
GROUP BY Customer.Name)innerQuery

SQL join from the same table

How do I get grand total for orders made by credit card only from such a table :
order_id | meta_name | meta_value
___________________________________
1 | type | credit
1 | total | 1030.00
...
2 | type | check
2 | total | 930.00
..
3 | type | credit
3 | total | 330.00
what is the best way to describe such select operation if you are to search the Internet for a solution to this problem.
suppose I am MySQL.
You can do this with either join or group by. Here is the join method:
select ttype.order_id, cast(ttotal.meta_value as money)
from table ttype join
table ttotal
on ttype.order_id = ttotal.order_id and
ttype.meta_name = 'type' and
ttype.meta_value = 'credit' and
ttotal.meta_name = 'total';
If yo could have more than one total for an order, then you would still want to aggregate:
select ttype.order_id, sum(cast(ttotal.meta_value as money))
from table ttype join
table ttotal
on ttype.order_id = ttotal.order_id and
ttype.meta_name = 'type' and
ttype.meta_value = 'credit' and
ttotal.meta_name = 'total'
group by ttype.order_id