Average of a player - sql-server

I have a record that contains stat for a certain cricket player.
It has columns having dates, oppositions, Runs, Balls, Dismissals, Match_Number.
I want to do a query (SQL SERVER) to find out the batting average where every runs (Sum) is to be added; innings having a count of all innings except DNB but dismissal should not have a count of "Not Out", "Retired Hurt", "DNB" grouped by the opposition.
Note : DNB means Did not Bat.
The query doesn't have the required number of innings to calculate the average
So the problem is can't gather information for a single entity (count of no. of innings) having two set of parameters.
Without DNB
Without DNB, Not Out, Retired Hurt.
Please suggest.

You can put a case expression within an aggregate to exclude certain rows from a count/sum/average etc. So you could use something like this:
SELECT a.Opposition,
Matches = COUNT(*),
Innings = COUNT(CASE WHEN a.Dismissal <> 'DNB' THEN 1 END),
Runs = SUM(a.Runs),
Average = SUM(a.Runs) / NULLIF(COUNT(CASE WHEN a.Dismissal NOT IN ('DNB', 'Not Out', 'Retired not out') THEN 1 END), 0)
FROM dbo.SRTundlkarODI AS a
GROUP BY a.Opposition;
N.B. I have wrapped the COUNT for the average in NULLIF(<exp>, 0) so that should the batsmen have never got out you avoid a divide by zero error.

Related

SQLite Find amount of stocks using data from different tables

I have the following data structure shown in the picture and the task is to find the number of shares for each client.
In the table deals, I have a column called euro_amount which has positive numbers if the client has sold some stocks or negative in case of buying.
I have to calculate the total number of shares the client holds.
My idea was to group deals by clients, then for every deal separately check if the euro_amount is positive, if yes then divide by sell_price(get a positive number), else divide buy buy_price(get a negative number) (from table prices). Then sum up the total amount of stocks.
But I am struggling with implementing it.
What I have tried is:
select *,
case when d.euro_amount<0 then (d.euro_amount/p.buy_price) as stock_in
else (d.euro_amount/p.sell_price) as stock_out
(stock_in+stock_out) as sum
inner join prices p on p.id_price=d.id_price
end
from table deals d
group by d.id_client;
I am getting an error: Error: near "as": syntax error
Join the 3 tables, group by client and aggregate with a CASE expression that meets your requirement:
select c.id_client, c.first_name, c.last_name,
sum(d.euro_amount / case when d.euro_amount > 0 then p.sell_price else p.buy_price end) totalsum
from clients c
left join deals d on d.id_client = c.id_client
left join prices p on p.id_price = d.id_price
group by c.id_client, c.first_name, c.last_name
To avoid integer division, if the columns euro_amount, sellprice and buy_price are integers, use this:
sum(1.0 * d.euro_amount / case when d.euro_amount > 0 then p.sell_price else p.buy_price end) totalsum

lag over columns/ variables SPSS

I want to do something I thought was really simple.
My (mock) data looks like this:
data list free/totalscore.1 to totalscore.5.
begin data.
1 2 6 7 10 1 4 9 11 12 0 2 4 6 9
end data.
These are total scores accumulating over a number of trials (in this mock data, from 1 to 5). Now I want to know the number of scores earned in each trial. In other words, I want to subtract the value in the n trial from the n+1 trial.
The most simple syntax would look like this:
COMPUTE trialscore.1 = totalscore.2 - totalscore.1.
EXECUTE.
COMPUTE trialscore.2 = totalscore.3 - totalscore.2.
EXECUTE.
COMPUTE trialscore.3 = totalscore.4 - totalscore.3.
EXECUTE.
And so on...
So that the result would look like this:
But of course it is not possible and not fun to do this for 200+ variables.
I attempted to write a syntax using VECTOR and DO REPEAT as follows:
COMPUTE #y = 1.
VECTOR totalscore = totalscore.1 to totalscore.5.
DO REPEAT trialscore = trialscore.1 to trialscore.5.
COMPUTE #y = #x + 1.
END REPEAT.
COMPUTE trialscore(#i) = totalscore(#y) - totalscore(#i).
EXECUTE.
But it doesn't work.
Any help is appreciated.
Ps. I've looked into using LAG but that loops over rows while I need it to go over 1 column at a time.
I am assuming respid is your original (unique) record identifier.
EDIT:
If you do not have a record indentifier, you can very easily create a dummy one:
compute respid=$casenum.
exe.
end of EDIT
You could try re-structuring the data, so that each score is a distinct record:
varstocases
/make totalscore from totalscore.1 to totalscore.5
/index=scorenumber
/NULL=keep.
exe.
then sort your cases so that scores are in descending order (in order to be bale to use lag function):
sort cases by respid (a) scorenumber (d).
Then actually do the lag-based computations
do if respid=lag(respid).
compute trialscore=totalscore-lag(totalscore).
end if.
exe.
In the end, un-do the restructuring:
casestovars
/id=respid
/index=scorenumber.
exe.
You should end up with a set of totalscore variables (the last one will be empty), which will hold what you need.
you can use do repeat this way:
do repeat
before=totalscore.1 to totalscore.4
/after=totalscore.2 to totalscore.5
/diff=trialscore.1 to trialscore.4 .
compute diff=after-before.
end repeat.

T-SQL : Cannot perform an aggregate function on an expression containing an aggregate or a subquery

I am trying to add the result of the total of some amount and substract it to the total but i see the following error:
Imagine something like this
First Subquery : 1 3 5 7
Second Subquery : 2 4 6
Total : (1+3+5+7) - (2+4+6) = 4
This is my query but as I said i see the following error:
Select SUM ((
(select SUM (amount) FROM transfer tr1
where transfer_type = 'Positive' group by transfer_id)
EXCEPT
(SELECT SUM (amount) from transfer tr2
where transfer_type = 'Negative' group by transfer_id)))
How could I convert the query not to see the error :
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Many thanks in advance
You can construct a query in a way to turn additions into subtractions for 'Negative' values, like this:
SELECT
transfer_id
, SUM (
CASE 'transfer_type'
WHEN 'Positive' THEN amount
WHEN 'Negative' THEN -amount
ELSE NULL
END
) AS total
FROM transfer
GROUP BY transfer_id
Now a single SUM is used, with the sign of the addition controlled by the CASE expression.

Counting Columns with conditions, assigning values based on count

I have a table with call logs. I need to assign time slots for next call based on which time slot the phone number was reachable in.
The relevant columns of the table are:
Phone Number | CallTimeStamp
CallTimeStamp is a datetime object.
I need to calculate the following:
Time Slot: From the TimeStamp, I need to calculate the count for each time slot (eg. 0800-1000, 1001-1200, etc.) for each phone number. Now, if the count is greater than 'n' for a particular time slot, then I need to assign that time slot to that number. Otherwise, I select a default time slot.
Weekday Slot: Same as above, but with weekdays.
Priority: Basically a count of how many times a number was reached
Here's I have gone about solving these issues:
Priority
To calculate the number of times a phone number is called is straight forward. If a number exists in the call log, I know that it was called. In that case, the following query will give me the call count for each number.
SELECT DISTINCT(PhoneNumber), COUNT(PhoneNumber) FROM tblCallLog
GROUP BY PhoneNumber
However, my problem is that I need to change the values in the field Count(PhoneNumber) based on the value in that column itself. How do I go about achieving this? (eg. If Count(PhoneNumber) gives me a value > 20, I need to change it to 5).
Time Slot / Weekday
This is where I'm completely stumped and am looking for the "database" way of doing things.
Unfortunately, I can't get out of my iterative process of thinking. For example, if I was aggregating for a certain phone number (say '123456') and in a certain time slot (say between 0800-1000 hrs), I can write a query like this:
DECLARE #T1Start time = '08:00:00.0000'
DECLARE #T2End time = '10:00:00.0000'
SELECT COUNT(CallTimeStamp) FROM tblCallLog
WHERE PhoneNumber = '123456' AND FORMAT(CallTimeStamp, 'hh:mm:ss') >= #T1Start AND FORMAT(CallTimeStamp, 'hh:mm:ss') < #T2End
Now, I could go through each and every Distinct Phone Number in the table, count the values for each time slot and then assign a slot value for the phone number. However, there has to be a way that does not involve me iterating through a database.
So, I am looking for suggestions on how to solve this.
Thanks
You can use DATEPART Function to get week day slot.
To calculate time slot you can try dividing number of minutes from beginning of day and dividing it by size of the time slot. It would return you slot number. You can use either CASE statement to translate it to proper string or look table where you can store slot descriptions.
SELECT
PhoneNumber
, DATEPART(WEEKDAY, l.CallTimeStamp) AS DayOfWeekSlot
, DATEDIFF(MINUTE, CONVERT(DATE, l.CallTimeStamp), l.CallTimeStamp) / 120 AS TwoHourSlot /*You can change number of minutes to get different slot size*/
, COUNT(*) AS Count
FROM tblCallLog l
GROUP BY PhoneNumber
, DATEPART(WEEKDAY, l.CallTimeStamp)
, DATEDIFF(MINUTE, CONVERT(DATE, l.CallTimeStamp), l.CallTimeStamp) / 120
You could try this to return the phone number, the day of the week and a 2 hour slot. If the volume of calls is greater than 20 the value is set to 5 (not sure why to 5?). The code for the 2 hour section is adapted from this question How to Round a Time in T-SQL where the value 2 in (24/2) is the number of hours in your time period.
SELECT
PhoneNumber
, DATENAME(weekday,CallTimeStamp) as [day]
, CONVERT(smalldatetime,ROUND(CAST(CallTimeStamp as float) * (24/2),0)/(24/2)) AS RoundedTime
, CASE WHEN COUNT(*) > 20 THEN 5 ELSE COUNT(*) END
FROM
tblCallLog
GROUP BY
PhoneNumber
, DATENAME(weekday,dateadd(s,start_ts,'01/01/1970'))

Report Builder 3.0 - grouping rows by time of day

I am trying to create a table within a report that appears as follows:
The data set is based on this query:
SELECT
DATENAME(dw, CurrentReadTime) AS 'DAY',
DATEPART(dw, CurrentReadTime) AS 'DOW',
CAST(datename(HH, CurrentReadTime) as int) AS 'HOD',
AVG([Difference]) AS 'AVG'
FROM
Consumption
INNER JOIN Readings ON Readings.[RadioID-Hex] = Consumption.[RadioID-Hex]
WHERE
CONCAT([Building], ' ', [Apt]) = #ServiceLocation
GROUP BY
CurrentReadTime
ORDER BY
DATEPART(DW, CurrentReadTime),
CAST(DATENAME(HH, CurrentReadTime) AS INT)
The data from this table returns as follows:
In report builder, I have added this code to the report properties:
Function GetRangeValueByHour(ByVal Hour As Integer) As String
Select Case Hour
Case 6 To 12
GetRangeValueByHour = "Morning"
Case 12 to 17
GetRangeValueByHour = "Afternoon"
Case 17 to 22
GetRangeValueByHour = "Evening"
Case Else
GetRangeValueByHour = "Overnight"
End Select
Return GetRangeValueByHour
End Function
And this code to the "row group":
=Code.GetRangeValueByHour(Fields!HOD.Value)
When I execute the report, selecting the parameter for the target service location, I get this result:
As you will notice, the "Time of Day" is displaying the first result that meets the CASE expression in the Report Properties code; however, I confirmed that ALL "HOD" (stored as an integer) are being grouped together by doing a SUM on this result.
Furthermore, the actual table values (.05, .08, etc) are only returning the results for the HOD that first meets the requirements of the CASE statement in the VB code.
These are the things I need resolved, but can't figure out:
Why isn't the Report Properties VB code displaying "Morning", "Afternoon", "Evening", and "Overnight" in the Time of Day column?
How do I group together the values in the table? So that the AVG would actually be the sum of each AVG for all hours within the designated range and day of week (6-12, 12-18, etc on Monday, Tuesday etc).
To those still reading, thanks for your assistance! Please let me know if you need additional information.
I'm still not sure if I have a clear picture of your table design, but I'm imagining this as a single row group that's grouped on this expression: =Code.GetRangeValueByHour(Fields!HOD.Value). Based on this design and the dataset above, here's how I would solve your two questions:
Use the grouping expression for the value of the Time of Day cell, like:
Add a SUM with a conditional for the values on each day of the week. Example: the expression for Sunday would be =SUM(IIF(Fields!DOW.Value = 1, Fields!AVG.Value, CDec(0))). This uses CDec(0)instead of 0 because the AVG values are decimals and SSRS will otherwise throw an aggregate of mixed data types error by interpreting 0 as an int.

Resources