How to calculate total working time in LINQ? - c#

I have a table which holds clocking in/out records for every user :
RecID User In/Out ClockInOutTime
8 1 IN 25/02/2011 09:36:44
9 1 OUT 25/02/2011 11:36:44
10 1 IN 25/02/2011 12:36:44
11 1 OUT 25/02/2011 17:36:44
12 1 IN 26/02/2011 00:00:00
13 1 OUT 26/02/2011 12:00:00
14 1 IN 26/02/2011 09:00:44
15 1 OUT 26/02/2011 12:36:44
Any ideas how I can work out the total time worked for every month using LINQ?
cheers

SELECT (SELECT SUM(DATEDIFF(SECOND,[ClockInOutTime], GETDATE()))
FROM [swp].[dbo].[Table_1] t1
WHERE [In/Out] = 'IN'
AND t1.[User] = t.[User]) -
Coalesce((SELECT SUM(DATEDIFF(SECOND,[ClockInOutTime], GETDATE()))
FROM [swp].[dbo].[Table_1] t2
WHERE [In/Out] = 'OUT'
AND t2.[User] = t.[User]),0)
FROM [swp].[dbo].[Table_1] t
GROUP BY [User]
SQL way to solve this, not the best, but works even when last event don't have OUT timestamp i.e when last session still hasn't been closed.

This is non-trivial to do in either Linq or SQL. There is no easy way to link each OUT record with the corresponding IN record in SQL.
You have two options:
Querying the data and calculating in code within a for loop.
Changing the table schema like: RecID, User, ClockInTime, ClockOutTime
Option 1 is easy to implement, but I would seriously consider option 2. How do you define in your business rules that each IN record must be followed by a corresponding OUT record (or be last record)?

TimeSpan output = new TimeSpan(0,0,0);
using (var enumerator = input.GetEnumerator())
{
while (enumerator.MoveNext())
{
var begin = enumerator.Current.ClockInOutTime;
if(!enumerator.MoveNext())
break;
var end = enumerator.Current.ClockInOutTime;
output += (end - begin);
}
}
Yes, it isn't LINQ but I wanted to offer a solution - secondly, if the dates aren't alternating (so after an IN is always an OUT) it'll break.

There is no solution that will only use linq. This is due to the fact that you need to introduce error handling as well (if a user forgets to sign out, usually there is a maximum time that will be applied then etc).
I would group the data by user, order it by date time and then run through it in a for each and do the calculation within the for each.

Related

Get row that has some specific conditions

I've got 2 tables
Table 1 has a detail of progress of assignment in table 2
Table 1. Assignment
ID | Class | Content | StudentID | LastReviewDate
..............
3133 GC ABCD 1059 4/6/2018 17:35
Table 2. Detail
MondayOfWeekDate here is begin day of the week
AssignmentID | MondayOfWeekDate | Percent | StudentID | LastReviewDate
3133 3/19/2018 0 1059 3/23/2018 17:20
3133 4/2/2018 100 1689 4/7/2018 10:35
..............
I tried to write SQL to indicate between 3/26/2018 to 4/1/2018 to show assignment 3133 in that week but failed
SELECT Assignment.* FROM Assignment,Detail
WHERE Assignment.Class = 'GC'
AND Assignment.ID=Detail.AssignmentID
AND Detail.MondayOfWeekDate >'2018/03/26'
AND Percent<100
Of course the result was nothing but i can't find the right code to execute
Can you please me how to write SQL to show that information properly ? Thank you a lot.
SELECT Assignment.* FROM Assignment,Detail
WHERE Assignment.Class = 'GC'
AND Assignment.ID=Detail.AssignmentID
AND Detail.MondayOfWeekDate BETWEEN '2018-03-26' and '2018-04-01'
AND Percent<100
You will get no results as there is nothing between 3/26/2018 to 4/1/2018??
The only mistake you made is your date which is to use between and its a better idea to use iso formatted date yyyy-MM-dd
I cannot directly speak to why you don't see anything. But I can suggest how to write a more readable and maintainable query. This might fix your problems but I don't know.
Some simple guidelines:
Never use commas in the FROM clause.
Always use proper, explicit, standard JOIN syntax.
Use table aliases.
Use standard date formats.
So, start with a query like this:
SELECT a.*
FROM Assignment a JOIN
Detail d
ON a.ID = d.AssignmentID
WHERE a.Class = 'GC' AND
d.MondayOfWeekDate > '2018-03-26' AND
d.Percent < 100;
Also check for syntax errors when you run the query. It can be easy to make small errors that cause the query not to run.
Your query fails because the row
3133 4/2/2018 100 1689 4/7/2018 10:35
Is the one bigger than >'2018/03/26' but its Percent is not <100, but 100.
What are you exactly looking for?

Order by date diff in LINQ

I must find the most near event, in time, in a table.
If an event occurred 3 days ago, and the next one will be tomorrow, I want the second one. Viceversa if an event occurred yesterday, and the next one will be next month, I want the first one.
So I compose my query basing on the absolute difference in seconds between the dates, limiting to 1 result.
In pseudo SQL something like:
select * from events order by ABS(UNIXTIME(kickoff) - UNIXTIME(now)) limit 1
in LINQ:
Context.Events.OrderBy(m => Math.Abs( m.KickOff - DateTime.UtcNow ).TotalSeconds ).FirstAsync();
It work, but the order by is composed in memory and not in SQL "The LINQ expression 'orderby Abs(([m].KickOff - DateTime.UtcNow).TotalSeconds) asc' could not be translated and will be evaluated locally."
How I can have the order by query executed in sql?
EDIT
Removing Abs
Context.Events.OrderBy(m => m.KickOff - DateTime.UtcNow ).FirstAsync();
works as I want
[...] ORDER BY `m`.`kickoff` - UTC_TIMESTAMP()
but it don't give me the result I expect
Assuming you are using Entity Framework and SQL Server, you can use SqlFunctions.DateDiff, in System.Data.Entity.SqlServer:
Context.Events.OrderBy(m => Math.Abs(SqlFunctions.DateDiff("ss", DateTime.UtcNow, m.KickOff)))
.FirstAsync();
This assumes now as start date and kick off as end date. You can swap them if you need.

Filter datatable between two dates

I have a column within my database called "Month". What i want to do is filter the table between two months. Example of this is March - June. I have code which works BUT only works alphabetically
string strquery = "select * from tbl_DR_data ";
string strq2 = "where";
if (DropDownList6.SelectedItem.Text != "All" && DropDownList8.SelectedItem.Text != "All") {
string month1 = DropDownList6.SelectedItem.Text.ToString();
string month2 = DropDownList8.SelectedItem.Text.ToString();
strq2 = strq2 + "[Month] BETWEEN'" + month1 + "'AND'" + month2 + "'";
}
When DropDownList6 = March and DropDownList8 = June. Nothing appears in the gridview which im binding BUT if the swap them around so DDL6= June and DDL8 = March it works :S
Is there a work around so that i can have the months ordered in how the months are meant to be instead of being alphabetical
why are you really storing months as strings in the database?, if you follow that path, trust me you are even going to encounter much more serious problems down the road & they will be a hell to debug. You can always get the month part from any date or dateTime column value.
try this code, Hope this what you expected
SET #FromMonth = 'March'
SET #ToMonth = 'June'
SELECT
*
FROM
tbl_DR_data
WHERE
DATEPART(mm,CAST([Month]+ ' 1900' AS DATETIME)) >= DATEPART(mm,CAST(#FromMonth+ ' 1900' AS DATETIME))
AND DATEPART(mm,CAST([Month]+ ' 1900' AS DATETIME)) <= DATEPART(mm,CAST(#ToMonth+ ' 1900' AS DATETIME))
This is the MSDN Syntax for BETWEEN
test_expression [ NOT ] BETWEEN begin_expression AND end_expression
Why no results when When DropDownList6 = March and DropDownList8 = June?
With above values, your query becomes like
[Month] BETWEEN 'March' AND 'June'
Unfortunately, when you are filtering using BETWEEN, begin_expression should be less than or equal to end_expression. In your case, since you have stored months (names) as string in the database, March comes after June in alphabetical order and so above condition returns false and no results.
This is a fiddle example same as yours with no results.
You can get the month from a date/datetime field using various methods. In sql server, you can use;
//Month NUMBER
month(datefield) as month_Number
//Month NAME
datename(month,datefield) as month_Name
Just to give you worst side of your design. Just check the results of filtering by where month between 'april' and 'may'.
Your results include: August, December, February, January, July, June, March
Solution:
For the time since you don't have full date, I guess it would be better to update your database filed as below and change the datatype of [month] to int type;
update yourTable
set [month] = case [month] when 'January' then 1
when 'February' then 2
...
when 'December' then 12 end
Now change the data type of [month] to int type and then pass the month number from the code as
[month] BETWEEN 3 and 6
Also, try to avoid key words such as Month for field names.
I am not sure what your DB design is or even what the Month Fields data-type is, I agree with other first & foremost that this is not a good design as i cant think of a scenario where i can search only based on month. This should be a complete date fields.
It can give us better idea if you give us very brief about this functionality like where you want to use it & purpose.
Please don't take me as a critic... more details about your project can help us better understand & recommend solution. May be i am wrong as i may have not understand your question.
You can use nice solution given but think for the road ahead..

calculate total of rows all in the same column in sql database

I have a sql table called expenses with an int column called cost. In my application this data is displayed on a grid which is refreshing every time I insert a new row with a linq2sql insert. What I would like to do is have an integer variable in my application that is the sum of all the fields in the cost column every time I insert a row.
Is there a simple way to sum these fields with linq2sql every time I do an insert. Please try to avoid lambda as I haven't gotten to learning that yet.
Thanks!
Assuming that you use query syntax instead of lambdas, here you are:
var totalCost = (from expensesRow in dataContext.Expenses
select expensesRow.cost)
.Sum();
Which in fact is the same as:
var totalCost = dataContext.Expenses
.Sum(x => x.cost);
Here dataContext is an instance of your Linq2Sql DataContext class.
You get to learn lambdas today. http://www.theabsentmindedcoder.com/2010/06/linq-sum.html has exactly what you want, and uses a very simple lambda to get there. You could get away without the lambda by making your select gather up only the one column of integers you're trying to sum, but why do extra work to not learn things?
Say I had a table called users with the following structure:
[Table users] - id | username | points_awarded
Then, I can find the total number of points I awarded every user by running the query:
SELECT SUM(points_awarded) as total_points FROM users
You can also count how many users have points greater than our equal to N. For example, says N = 500, then I can run:
SELECT COUNT(id) as num_users_with_points FROM users WHERE points_award >= 500
Check out:
http://dev.mysql.com/doc/refman/5.1/en/counting-rows.html
http://dev.mysql.com/doc/refman/5.0/en/func-op-summary-ref.html
for more information.

NHibernate - How do I use a sum project on paged results

I'm trying to use paging in conjunction with a sum projection to get a sum of the values in a column for just the page of results I'm interested in. I'm using .NET, C# and NHibernate 3.1
I have an ICriteria to start with which is related to all rows from the associated db table.
I'm then doing the following to get a version with the first page (say, 10 items out of 40):
ICriteria recordsCriteria = CriteriaTransformer.Clone(criteria);
recordsCriteria.SetFirstResult(0);
recordsCriteria.SetMaxResults(10);
I'm using this ICriteria for something else so I then create two further clones:
ICriteria totalAggCriteria = CriteriaTransformer.Clone(criteria);
ICriteria pageAggCriteria = CriteriaTransformer.Clone(recordsCriteria);
If I take a look inside these two new ones the first has 40 items in and the second has 10 - exactly what I want.
Let's say the objects coming back from the DB have a column called "ColA" and it's of type Int32.
From this, I want the sum of all 40 ColA values and the sum of the first 10 ColA values.
To get the sum of all 40 ColA values, I do the following:
totalAggCriteria.SetProjection(NHibernate.Criterion.Projections.Sum("ColA"));
var totalSum = totalAggCriteria.UniqueResult();
The value in totalSum is correct.
To get the sum of the first 10 ColA values, I'm trying the following:
pageAggCriteria.SetProjection(NHibernate.Criterion.Projections.Sum("ColA"));
vat pageSum = pageAddCriteria.UniqueResult();
However, this gives me the same value as the previous one - for all 40 ColA values.
I've also tried the following but it gives the same result:
pageAggCriteria.SetProjection(NHibernate.Criterion.Projections.Sum(column));
pageAggCriteria.SetFirstResult(firstResult.Value);
pageAggCriteria.SetMaxResults(pageSize.Value);
pageSum = pageAggCriteria.UniqueResult();
And also:
pageAggCriteria.SetFirstResult(firstResult.Value);
pageAggCriteria.SetMaxResults(pageSize.Value);
pageAggCriteria.SetProjection(NHibernate.Criterion.Projections.Sum(column));
pageSum = pageAggCriteria.UniqueResult();
Can anyone give an idea on where I'm going wrong and how I can actually get the sum of the ColA values in the first 10 results?
Thanks
Probably easiest to do that sum client side. The aggregate function is operating on the whole table. What you are trying to do is run the aggregate function against the paged result which I don't think is possible with NH.
In other words, you want select sum(colA) from (select top 10 ...) but that criteria will give you select top 10 sum(colA) from ...)

Categories