SQL Server - Apply OrderBy on columns when used CAST() - sql-server

In my gaming application i have Teams and each Team can have any number of players, if a player participates in a match i am giving him 5 points. Each time the player participates in a match he will get 5 points added to his count.
my stored procedure takes TeamId as the input parameter.
Now i want to calculate the Total Participation points each team has got by month, but here the Participation Points each player has scored should be added to the last month in which the player has played the Match.
Lets say Team1 has Player1 and player1 has played total of 4 matches, 1 match in 04/2020 , 2 matches in 06/2020 and 1 match in 08/2020 , here for playing 4 matches Player1 of Team1 got 20 participation points and the last match Player1 played is in 08/2020 so all the 20 points should be added to 08/2020 for Team1
In the player table across each Player i have a [TotalMatchesPlayed] by each player, [TotalMatchesPlayed] * 5 will give me the [TotalParticipationPoints] for each player.
This should repeat for all the players in the Team.
SELECT DISTINCT TP.[TeamId], ISNULL(P.[TotalMatchesPlayed], 0) * 5 AS [ParticipationPoints], CAST(MONTH(PA.[ActivityDate]) AS VARCHAR(2)) AS [Month], CAST(YEAR(PA.[ActivityDate]) AS VARCHAR(4)) AS [Year] FROM [TeamPlayers] TP
INNER JOIN dbo.[Player] P
ON TP.[PlayerId] = P.[PlayerId]
INNER JOIN dbo.[PlayerActivity] PA
ON PA.[PlayerId] = P.[PlayerId] AND PA.[ActivityTypeId] = 14
WHERE TP.[TeamId] = 12
my issue with above query is [PlayerActivity] table has a row each time a player participates in a match, now i want to take only the latest date and add all the participation points to that month and year which i am not able to achieve
I tried adding ORDER BY PA.[ActivityDate] DESC but thts throwing an error
Order by items must appear in the select list if SELECT DISTINCT is
specified.
my sample output should be as below
ParticipationPoints | Month | Year
50 03 2020
0 04 2020
20 05 2020
sample table designs and data in the below link.
http://sqlfiddle.com/#!18/41766/1

Does this work for you:
SELECT
TP.[TeamId]
, SUM(ISNULL(P.[TotalMatchesPlayed], 0)) * 5 AS [ParticipationPoints]
, DATEPART(MONTH,PA.[ActivityDate]) AS [Month]
, DATEPART(YEAR,PA.[ActivityDate]) AS [Year]
FROM [TeamPlayer] TP
INNER JOIN dbo.[Player] P
ON TP.[PlayerId] = P.[PlayerId]
INNER JOIN dbo.[PlayerActivity] PA
ON PA.[PlayerId] = P.[PlayerId] AND PA.[PlayerActivityTyepId] = 14
WHERE TP.[TeamId]=45
GROUP BY TP.[TeamId], DATEPART(MONTH,PA.[ActivityDate]), DATEPART(YEAR,PA.[ActivityDate])
ORDER BY DATEPART(MONTH,PA.[ActivityDate]) DESC, DATEPART(YEAR,PA.[ActivityDate]) DESC

You called some [Activity Date] fields so you should select one of the casts above as order by
SELECT DISTINCT TP.[TeamId], ISNULL(P.[TotalMatchesPlayed], 0) * 5 AS [ParticipationPoints], CAST(MONTH(PA.[ActivityDate]) AS VARCHAR(2)) AS [Month], CAST(YEAR(PA.[ActivityDate]) AS VARCHAR(4)) AS [Year] FROM [TeamPlayers] TP
INNER JOIN dbo.[Player] P
ON TP.[PlayerId] = P.[PlayerId]
INNER JOIN dbo.[PlayerActivity] PA
ON PA.[PlayerId] = P.[PlayerId] AND PA.[ActivityTypeId] = 14
WHERE TP.[TeamId] = 12
ORDER BY CAST(MONTH(PA.[ActivityDate]) AS VARCHAR(2)) desc

Related

Choose row that equal to the max value from a query

I want to know who has the most friends from the app I own(transactions), which means it can be either he got paid, or paid himself to many other users.
I can't make the query to show me only those who have the max friends number (it can be 1 or many, and it can be changed so I can't use limit).
;with relationships as
(
select
paid as 'auser',
Member_No as 'afriend'
from Payments$
union all
select
member_no as 'auser',
paid as 'afriend'
from Payments$
),
DistinctRelationships AS (
SELECT DISTINCT *
FROM relationships
)
select
afriend,
count(*) cnt
from DistinctRelationShips
GROUP BY
afriend
order by
count(*) desc
I just can't figure it out, I've tried count, max(count), where = max, nothing worked.
It's a two columns table - "Member_No" and "Paid" - member pays the money, and the paid is the one who got the money.
Member_No
Paid
14
18
17
1
12
20
12
11
20
8
6
3
2
4
9
20
8
10
5
20
14
16
5
2
12
1
14
10
It's from Excel, but I loaded it into sql-server.
It's just a sample, there are 1000 more rows
It seems like you are massively over-complicating this. There is no need for self-joining.
Just unpivot each row so you have both sides of the relationship, then group it up by one side and count distinct of the other side
SELECT
-- for just the first then SELECT TOP (1)
-- for all that tie for the top place use SELECT TOP (1) WITH TIES
v.Id,
Relationships = COUNT(DISTINCT v.Other),
TotalTransactions = COUNT(*)
FROM Payments$ p
CROSS APPLY (VALUES
(p.Member_No, p.Paid),
(p.Paid, p.Member_No)
) v(Id, Other)
GROUP BY
v.Id
ORDER BY
COUNT(DISTINCT v.Other) DESC;
db<>fiddle

update stats from nested query based on isnull and aggregate values

I have a parent system table called lt_program_data it contains customer data, percents tracked for that customer and a year data field, as those percents are tracked on a yearly basis.
The percents are populated from a localized table based on some criteria, then the parent table lt_program_data is updated based on the year and customer values.
However, in some cases we only have past data and what the user is requesting is in the cases where we have customer data, but no percents corresponding to this season we use the max season value.
our logic is like this for now:
update lt_program_data
set percent = ( select percent
from #percent b
where b.year = a.fyear and b.customer = a.customer)
from lt_program_data
This works great, but now we have to say something like
if b.year is null select Max year for that customer for the data we have.
select *
From #lt_program_data a
join #percent b on b.fyear= isnull(a.fyear,max(a.fyear)) and b.customer = a.customer
I tried to write a select and then an update but get the following message:
Msg 1015, Level 15, State 1, Line 3
An aggregate cannot appear in an ON clause unless it is in a subquery contained in a HAVING clause or select list, and the column being aggregated is an outer reference.
Please help sort this out.
Here is a sample of our output
lt_program_data
customer year .. percent .. Other columns
1 2016 ..
2 2016
1 2017
2 2017
3 2017
etc.
percent table looks like this
customer year percent
1 2016 40
2 2016 64
3 2016 11
The expected result will take lt_program_data for
customer year percent
1 2016 40
1 2017 40
2 2016 64
2 2017 64
3 2017 NULL
It matches customer number and percent for the given year that exists in the percent table (so the value for customer 1 becomes 40 and customer 2 becomes 64) since no data for those customers exist for 2017 season, it uses the same data (max existing) data for the respective customers from 2016 season. in the case of customer 3 since there is nothing its left NULL.
The percent table goes back to 2016, so what we want to say is since the max data we have for our customers goes back to 2016, we will populate the 2017 value in lt_program_data for customer 1 with the 2016 value of 40.
I hope this query will work for you.
update lt_program_data
set percent_poverty = case
when b.year is null -- year in #percent is null (no join found)
then (select top 1 poverty_percent -- then get first percent by ordering year descending
from #percent
where customer = a.customer
order by year desc)
else b.poverty_percent -- else get the percent
end
from lt_program_data a -- lets left join both tables on year and customer
left join #percent b on b.year = a.fyear and b.customer = a.customer

TSQL - DateTime difference between more than two rows

I'm trying to find out how to calculate difference between multiple rows from one simple query. Here it is:
SELECT [DateTime],EmployeeId,ControlPointID,EventTypeID
FROM [Events]
WHERE Day([DateTime]) = 4
AND Month([DateTime]) = 7
AND Year([DateTime]) = 2017
AND EmployeeId = 451
AND ControlPointID IN ( 3, 6 )
AND EventTypeID IN ( 1, 2 )
ORDER BY [DateTime]
Result:
DateTime EmployeeId ControlPointID EventTypeID
2017-07-04 11:32:10.000 451 6 1
2017-07-04 16:07:00.000 451 3 2
2017-07-04 16:42:50.000 451 6 1
2017-07-04 20:04:10.000 451 3 2
I need to calculate difference between [DateTime] in minutes.
EventTypeId = 1 means that Employee enters to the building and EventTypeId=2 means that Employee leaves. I can calculate difference between first Enter Event and last Leave Event. In this case it's 512 minutes. But, i have problem to calculate work time, when someone enters twice and leaves twice. It should be 477 minutes. Calculation should looks like this:
DateDiff = (2017-07-04 16:07:00.000 - 2017-07-04 11:32:10.000) +
(2017-07-04 20:04:10.000 - 2017-07-04 16:42:50.000)
Can you help me figure it out, please ?
Given a building entry, finding the first leave after that entry can be done with cross apply:
select entry.EmployeeId, entry.DateTime, exit.DateTime
from Events entry
cross apply (select top 1 e.DateTime
from Events e
where e.EmployeeId = entry.EmployeeId
and e.DateTime > entry.DateTime
and e.EventTypeId = 2
order by e.DateTime asc
) as exit
where entry.EventTypeId = 1
at which point you just need to use the applicable T/SQL function to get the difference in whatever unit you want (eg. in minutes with datediff(minute, entry.DateTime, exit.DateTime).
To get the total of all the differences simply sum the differences:
select EmployeeId, sum(mins)
from (
select entry.EmployeeId, entry.DateTime as EntryDateTime, exit.DateTime as ExitDateTime, datediff(minute, EntryDateTime, ExitDateTime) as mins
from Events entry
cross apply (select top 1 e.DateTime
from Events e
where e.EmployeeId = entry.EmployeeId
and e.DateTime > entry.DateTime
and e.EventTypeId = 2
order by e.DateTime asc
) as exit
where entry.EventTypeId = 1
) as input
group by EmployeeId
Edit: added overall summation (with diff on the inside for clarity)
This can be done using LAG window function, since 2008 does not supports it we need to left join with Row_Number to find the previous entry
;WITH cte
AS (SELECT Row_number()OVER(Partition by EmployeeID ORDER BY [DateTime]) rn,*
FROM Yourresult)
SELECT a.EmployeeID,
Sum(Datediff(minute, b.[DateTime], a.[DateTime]))
FROM cte a
LEFT JOIN cte b
ON a.EmployeeID = b.EmployeeID
AND a.rn = b.rn + 1
WHERE a.[EventTypeId] = 2
GROUP BY a.EmployeeID
Note : This considers there isn't any wrong punches. Just like your sample data

Displaying all columns in SQL and also sum of columns with same ID in the last Repeating row

I have 2 tables
OrderDetails:
Id Name type Quantity
------------------------------------------
2009 john a 10
2009 john a 20
2010 sam b 25
2011 sam c 50
2012 sam d 30
ValueDetails:
Id Value
-------------------
2009 300
2010 500
2011 200
2012 100
I need to get an output which displays the data as such :
Id Name type Quantity Price
-------------------------------------------------
2009 john a 10
2009 john a 20 9000
2010 sam b 25
2011 sam c 50
2012 sam d 30 25500
The price is calculated by Value x Quantity and the sum of the values is displayed in the last repeating row of the given Name.
I tired to use sum and group by but I get only two rows. I need to display all 5 rows. How can I write this query?
You can use Row_Number with max of Row_Number to get this formatted sum
;with cte as (
select od.*, sm= sum( od.Quantity*vd.value ) over (partition by Name),
RowN = row_number() over(partition by Name order by od.id)
from #yourOrderDetails od
inner join #yourValueDetails vd
on od.Id = vd.Id
)
select Id, Name, Type, Quantity,
case when max(RowN) over(partition by Name) = row_number() over(partition by Name order by Id)
then sm else null end as ActualSum
from cte
Your input tables:
create table #yourOrderDetails (Id int, Name varchar(20), type varchar(2), Quantity int)
insert into #yourOrderDetails (Id, Name, type, Quantity) values
(2009 ,'john','a', 10 )
,(2009 ,'john','a', 20 ) ,(2010 ,'sam ','b', 25 )
,(2011 ,'sam ','c', 50 ) ,(2012 ,'sam ','d', 30 )
create table #yourValueDetails(Id int, Value Int)
insert into #yourValueDetails(Id, value) values
( 2009 , 300 ) ,( 2010 , 500 )
,( 2011 , 200 ) ,( 2012 , 100 )
SELECT a.ID,
a.Name,
a.Type,
a.quantity,
price = (a.quantity * b.price)
FROM OrderDetails a LEFT JOIN
ValueDetails b on a.id = b.id
This will put the price on every row. If you want to do a SUM by Id,Name and Type it's not going to show the individual records like you show them above. If you want to put a SUM on one of the lines that share the same Id, Name and Type then you'd need a rule to figure out which one and then you could probably use a CASE statement to decide on which line you want to show the SUM total.

Join on max value if join condition not met

I am trying to calculate how much vacation time an employee would have based on the amount of time he worked.
employeeVacation -> id, yearsWorked, vacationHours
employee -> empid, StartDate
I can get the number of years the employee worked by using this
datepart(year, getdate()) - datepart(year,StartDate) as yearsWorked
After you work for more that 8 years the vacation time is the same. Here is my vacation table.
id years vacationHours
1 0 40
2 1 40
3 2 40
4 3 80
5 4 80
6 5 80
7 6 80
8 7 120
select e.empid, ev.vacationhours from employee e join employeevacation ev on
ev.yearsWorked = datepart(year, getdate()) - datepart(year,e.StartDate)
So say you work for 30 years I can't do a join to get the number of vacation hours. Should I be looking to do a join, or should I just cut my losses and insert years into the vacation table up to like 100 so that way I can join and not worry about it.
SELECT e.empid, ISNULL(ev.vacationhour, 0)
FROM employee e
LEFT JOIN employeevacation ev ON ev.yearsWorked = (
SELECT MAX(ev2.yearsWorked)
FROM employeevacation ev2
WHERE yearsWorked<=datepart(year, getdate()) - datepart(year,e.StartDate)
)
Basically, you query the highest threshold he is eligible to.
You can replace 0 with whatever default value you may want. Although you should never hit the default value if you have an employeevacantion row with 0 years.
You can do something like this
select e.empid, coalesce(ev.vacationhours, evMax.vacationhours)
from employee e
left join employeevacation ev on ev.yearsWorked = datepart(year, getdate()) - datepart(year,e.StartDate)
join employeevacation evMax on evMax.yearsWorked = 7

Resources