I am trying to calculate a weighted average in SQL Server. I'm aware that there are tons of questions out there addressing the problem, but I have the additional problem that I query a lot of other columns with a GROUP BY and aggregate functions like sum() and avg().
Here is my query:
SELECT
AVG(tauftr.kalkek) AS 'PurchPrice',
SUM(tauftr.amount) AS 'Amount',
AVG(tauftr.price) AS 'SellingPrice',
tauftr.product AS 'Product',
auftrkopf.ins_usr AS 'Seller',
DATEPART(wk, auftrkopf.date) AS 'Week',
AVG([margin]) AS 'Margin' /* <--- THIS IS WRONG */
/* CALCULATE HERE: WEIGHTED AVERAGE BETWEEN 'amount' and 'margin' */
FROM
[tauftr] AS tauftr
JOIN
tauftrkopf AS auftrkopf ON tauftr.linktauftrkopf = auftrkopf.kopfnr
WHERE
auftrkopf.[status] = 'L'
AND auftrkopf.typ = 'B'
AND auftrkopf.date >= '01.03.2017'
AND auftrkopf.ins_usr ='HS'
GROUP BY
tauftr.product, auftrkopf.ins_usr, DATEPART(wk,auftrkopf.date)
I suppose it could be possible to use a INNER JOIN with exactly the same WHERE clause, but I don't want to execute the query two times. And I don't know ON what field to JOIN...
Is it possible without creating a table? (I do not have write permissions)
Assuming you want Weighed Avg Margin ...
Select
...
sum(amount*margin)/sum(amount) as 'Weighted Avg'
...
From ...
Group By ...
Edit - To avoid the dreaded Divide-By-Zero
case when sum(amount)=0 then null else sum(amount*margin)/sum(amount) end as 'Weighted Avg'
Edit 2 - NullIf()
...
sum(amount*margin)/NullIf(sum(amount),0) as 'Weighted Avg'
...
Related
I have the following query:
SELECT DISTINCT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT
FROM TGFCAB CAB, TGFPAR PAR, TSIBAI BAI
WHERE CAB.CODPARC = PAR.CODPARC
AND PAR.CODBAI = BAI.CODBAI
AND CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI
Which the result is this. Company names and neighborhood hid for obvious reasons
The query at the moment, for those who don't understand Latin languages, is giving me clients, company name, company neighborhood, and the total value of movements.
in the WHERE clause it is only filtering sales movements of companies from an established city.
But if you notice in the Select statement, the column that is retuning the value that aggregates the total amount of value of sales is a SUM().
My goal is to return only the company that have the maximum value of this column, if its a tie, display both of em.
This is where i'm struggling, cause i can't seem to find a simple solution. I tried to use
WHERE AMOUNT = MAX(AMOUNT)
But as expected it didn't work
You tagged the question with the whole bunch of different databases; do you really use all of them?
Because, "PL/SQL" reads as "Oracle". If that's so, here's one option.
with temp as
-- this is your current query
(select columns,
sum(vrlnota) as amount
from ...
where ...
)
-- query that returns what you asked for
select *
from temp t
where t.amount = (select max(a.amount)
from temp a
);
You should be able to achieve the same without the need for a subquery using window over() function,
WITH T AS (
SELECT
CAB.CODPARC,
PAR.RAZAOSOCIAL,
BAI.NOMEBAI,
SUM(VLRNOTA) AS AMOUNT,
MAX(VLRNOTA) over() AS MAMOUNT
FROM TGFCAB CAB
JOIN TGFPAR PAR ON PAR.CODPARC = CAB.CODPARC
JOIN TSIBAI BAI ON BAI.CODBAI = PAR.CODBAI
WHERE CAB.TIPMOV = 'V'
AND STATUSNOTA = 'L'
AND PAR.CODCID = 5358
GROUP BY CAB.CODPARC, PAR.RAZAOSOCIAL, BAI.NOMEBAI
)
SELECT CODPARC, RAZAOSOCIAL, NOMEBAI, AMOUNT
FROM T
WHERE AMOUNT=MAMOUNT
Note it's usually (always) beneficial to join tables using clear explicit join syntax. This should be fine cross-platform between Oracle & SQL Server.
im looking for solutions to my problem.
i have a query
select em_name, sum(abs_day_left)
from pp_employees,
pp_types_abs,
pp_abs
where em_id = abs_em_id and abs_abs_id = abs_id and
abs_kod in ('12','13','14','15')
group by em_name
i want to make more columns with another abs_kod number (image attachment)
for example
second column
... abs_kod in
('656','44','323','33')
third column
... abs_kod in
('63','55','565','556')
and more..
example table
thanks for help and nice weekend
One more thing...
the formula counts days from the whole month
how to make it count correctly the days when it sets the parameters for the half month, for example from 1980-01-01 to 1980-03-15
thanks in advance
bob
I think that you are looking for conditional aggregation:
select
em_name,
sum(case when abs_kod in (12,13,14,15) then abs_day_left end) abs_day_left_1,
sum(case when abs_kod in (656,44,323,33) then abs_day_left end) abs_day_left_2,
sum(case when abs_kod in (63,55,565,556) then abs_day_left end) abs_day_left_3
from pp_employees
inner join pp_abs on em_id = abs_em_id
inner join pp_types_abs on abs_abs_id = abs_id
where and abs_kod in (12,13,14,15,656,44,323,33,63,55,565,556)
group by em_name
Notes:
always use explicit joins instead of old-shool, implicit joins - I tried to fix this but I am unsure I did it correctly, for a reason that lies in the following point...
always qualify the columns in the query with the table they belong to
I have the following tables below and their schema:
INV
id, product code, name, ucost, tcost, desc, type, qoh
1,123,CPASS 700,1.00,5.00,CPASS 700 Lorem, COM,5
2,456,Shelf 5,2.00,6.00,Shelf 5 KJ, BR,3
GRP
id,type,desc
1,COM,COMPASS
2,BR,SHELF
Currently I have a query like this:
SELECT INV.*,GRP.DESCR AS CATEGORY
FROM INV LEFT JOIN GRP ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0
There is no problems with that query.
Right now,I want to know the SUM of the TCOST of every INV record where their QOH is 0.
In this situation, does that I mean all I have to do is to write a separate query like the one below:
SELECT SUM(TCOST)
FROM INV
WHERE QOH = 0
Does it make any sense for me to try and combine those two queries as one ?
First understand that SUM is the aggregate function hence either you can run the Query like
(SELECT SUM(TCOST) FROM INV WHERE QOH=0) as total
This will return Sum of TCOST in INV Table for mentioned condition.
Another approach is finding the Sum based on the some column (e.g. Type)
you could write query like
SELECT Type , SUM(TCOST) FROM INV WHERE QOH=0 GROUP BY type ;
Its not clear on what criteria you want to sum . But I think above two approaches would provide you fare idea .
Mmm, you could maybe use a correlated query, though i'm not sure it's the best approach since I'm not sure I understand what your attempting to do:
SELECT INV.*,
GRP.DESCR AS CATEGORY ,
(SELECT SUM(TCOST) FROM INV WHERE QOH=0) as your_sum
FROM INV LEFT JOIN GRP ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0
If you want only one value for the sum(), then your query is fine. If you want a new column with the sum, then use window functions:
SELECT INV.*, GRP.DESCR AS CATEGORY,
SUM(INV.TCOST) OVER () as sum_at_zero
FROM INV LEFT JOIN
GRP
ON INV.TYPE = GRP.TYPE
WHERE INV.QOH = 0;
It does not make sense to combine the queries by adding a row to the first one, because the columns are very different. A SQL result set requires that all rows have the same columns.
So this is the requirement I need to meet:
Aggregated data of all the schools in the ESD, grouped by
SchoolDistrict.SchoolDistrictID
(get the same data as the school district scenario above, then add grouping by district, filtered to
EducationServiceDistrict. EducationServiceDistrictID
)
Also calculate percentage of pass, fail, and untested
How do I calculate the percentage pass, fail, and untested?
This is the query I have written so far.
CREATE VIEW district_map AS
SELECT * and SchoolDistrictID,
EducationServiceDistrict
FROM SchoolDistrict_View
and SchoolDistrict,
EducationServiceDistrict
GROUP BY EducationServiceDistrict.EducationServiceDistrictID
ORDER BYLeadWaterTestLocation.PassFail
This is the general idea of how these problems are solved - if you understand this simplified version you will be able to solve your problem.
select d.districtName,
s.studentCount,
case when s.studentCount=0 then 0 else s.passed / s.studentCount * 100 end as PassedPct
from district d
join (select districtId,
sum(studentCount) as studentCount,
sum(passed) as passed
from schools
group by districtId) as s
on d.districtId = s.districtId
order by d.districtName
My question: the owners would like to know the revenue generated so far (i.e. where CheckOutDate < DATE()) for each room type in each hotel.
The calculation must be done in the SQL statement.
Determine the length of stay for each reservation (i.e. number of days) using the DateDiff function datediff('d', checkindate, checkoutdate) and multiply this value by the room rate.
Your output should be formatted as shown on the next page. Your Revenue totals may be different. Keep in mind, the Revenue amount may change on a daily basis, as we want to include only those reservations that are completed, not current or future reservations.
select
room.hotelID, room.roomtype,
datediff('d', Reservation.CheckOutDate, Reservation.CheckInDate) * ROOM_TYPE.RoomRate as Revenue
from
Reservation
inner join
Room on Room.hotelID = Reservation.HotelID
inner join
ROOM_TYPE on ROOM_TYPE.RoomType = Room.roomtype
group by
Room.HotelID, Room.roomtype;
I am getting syntax error statement missing in this.
How to resolve this error in MS Access?
When using a Group By clause, any columns that are not part of the grouping must be aggregated. In your case, Room.HotelID and Room.RoomType are the grouping columns. So they are fine in your SELECT clause, as-is. But Revenue needs to be aggregated. I expect that you will want to use the SUM aggregation to sum up all of the Revenue values for each room type. Try this...
select room.hotelID,
room.roomtype,
SUM( datediff(day,Reservation.CheckOutDate,Reservation.CheckInDate )*ROOM_TYPE.RoomRate) as Revenue
from Reservation
inner join Room on Room.hotelID=Reservation.HotelID
inner join ROOM_TYPE on ROOM_TYPE.RoomType=Room.roomtype
group by Room.HotelID, Room.roomtype;
Running the query below against your data in Access 2010 produced this result set:
hotelID roomtype Revenue
------- -------- ----------
1000 D $23,000.00
1000 F $23,100.00
1000 S $20,700.00
1111 D $36,500.00
1111 F $16,450.00
1111 S $15,300.00
SELECT
rm.hotelID,
rm.roomtype,
Sum(DateDiff('d', rs.CheckInDate, rs.CheckOutDate) * rt.RoomRate) AS Revenue
FROM
(
ROOM AS rm INNER JOIN RESERVATION AS rs
ON (rm.roomno = rs.RoomNo) AND (rm.hotelID = rs.HotelID)
)
INNER JOIN ROOM_TYPE AS rt
ON rm.roomtype = rt.RoomType
WHERE rs.CheckOutDate < Date()
GROUP BY rm.hotelID, rm.roomtype;
You should still learn how to use the Query Builder but I think the parens should look something like this:
select
Room.HotelID, Room.roomtype,
sum(
datediff('d',Reservation.CheckOutDate,Reservation.CheckInDate) *
ROOM_TYPE.RoomRate
) as Revenue
from
((Reservation inner join Room on Room.hotelID = Reservation.HotelID)
inner join ROOM_TYPE on ROOM_TYPE.RoomType = Room.roomtype)
group by
Room.HotelID, Room.roomtype;
So in summary:
Be careful with grouping columns and aggregates
Access uses quotes around it's datediff argument unlike some other systems
Nesting of joins needs parentheses
In MS Access we have to mention in brackets () on the clause in from statement. Apart from that datediff function has to be a part of aggregate function.