Find distinct value of one field in a period - c#

I have a table (detecc) with these fields:
uname string
door string
dt double (seconds since 1/1/1970)
I have this query that works well:
double dt1= SeconsdSince1970(DateTime.Now);
double dt0= dt1 - 3600;
var doorSearch = new string[] { "D1", "D2" };
System.Int32 cNow = (from d in detecc
where doorSearch.Contains(d.door) &&
(d.dt >= dt0 && d.dt <= dt1)
select d.uname).Distinct().Count();
But if I want to retrieve the users (uname), I get all records (duplicates):
double dt1= SeconsdSince1970(DateTime.Now);
double dt0= dt1 - 3600;
var doorSearch = new string[] { "D1", "D2" };
var lisUname = (from d in detecc
where doorSearch.Contains(d.door) &&
(d.dt >= dt0 && d.dt <= dt1)
select d.uname).Distinct();
How can I get distinct usernames?

If you are working with mongodb collections try this
// .ToList() converts to poco list
var lisUname = (from d in detecc
where doorSearch.Contains(d.door) &&
(d.dt >= dt0 && d.dt <= dt1)
select d.uname).ToList();
// distinct is now executed in c# context rather mongodb context
var distinctList = lisUname.Distinct();
A cleaner syntax
var list = detecc
.Where(d => doorSearch.Contains(d.door) && (d.dt >= dt0 && d.dt <= dt1)
.Select(x => x.uname)
.ToList();
Performance
Note: .Select will always end your query and pass all data (complete documents) to native code. So you get back the complete data from server and your code then is selecting your desired fields. If you want to pull only requested fields another mongodb query approach is required.
Refer to Documentation on Linq driver
For better performance regarding distinct use:
var list = detecc
.Where(d => doorSearch.Contains(d.door) && (d.dt >= dt0 && d.dt <= dt1)
.Distinct() // that way distinct is executed on server side
.ToList();
Why chaining .Count() with mongodb does not work
.Count() is meant to be used seperately with own parameters.
Refer to this article Why the MongoDB Count Property Returns All Records
// Example
int userCount = db.GetCollection("detecc")
.Count(Query.EQ("uname", searchedUName));
A more performant approach with aggregation
For a more performant approach use mongodb aggretation like
db.collection.aggregate([
{ "$match": { "$and": [ { "prop1": "" }, { "prop2": "" } ] } },
{ "$group": { "_id": "$messageId" } }
])
Please refer to this answer: MongoDb Distinct with query C# driver

Related

Linq searches for recent birthdays (within 15 days)

How to use Linq
A table
Field Birthday
Linq searches for recent birthdays (within 15 days)
from a in Employee where a.PositionStatus == true select new{ a.Name,a.Birthday}
Try below query
var fromdate = DateTime.Now;
var todate = DateTime.Now.AddDays(15);
var result =
(
from a in Employee
where a.PositionStuats==true && a.DateOfBirth.Value.Month >= fromdate.Month &&
a.DateOfBirth.Value.Month <= todate.Month &&
a.DateOfBirth.Value.Day >= fromdate.Day && a.DateOfBirth.Value.Day <= todate.Day
select new{ a.Name,a.Birthday}
).ToList();
Depending on your entity frame work version you can also replace a.DateOfBirth.Value.Month with a.DateOfBirth.Month.

Linq to SQL C#: items between dates

I have 2 tables in my SQL server 2012:
Errors (id, cityID, centerID, date)
InspectionVisits (id, cityID, centerID, datePerformed)
I am trying to get the number of errors between inspection visits to see if there is an improvement in the center with the specific centerID and build a chart.
This is my code so far but I can't find out how I can write the where clause to get the number of errors between these inspection visits:
var errorsPerIV = from e in dc.Errors
where e.cityID == ctid && e.centerID == centerid
group e by e.date.Date into g
join iv in dc.InspectionVisits on g.FirstOrDefault().cityID equals iv.cityID
where iv.centerID == g.FirstOrDefault().centerID
select new
{
Day = g.Key.Day + "/" +
g.Key.Month + "/" +
g.Key.Year,
Errors = g.Count()
};
Sample Case
Something like: 5 errors between Inspection_Visit_1 and Inspection_Visit_2, 2 errors between Inspection_Visit_2 and Inspection_Visit_3 and 1 error between Inspection_Visit_3 and today.
EDIT
Maybe it could work if I show queries per day and mark only the inspection visits in the chart's x axis.
I'm not sure if there is a better way, but you could do something like the following
Suppose you have a class like
public class Summary
{
public DateTime? PreviousInspection;
public DateTime? NextInspection;
public int Errors;
}
then you can get most of the information by having a query like
var errorsPerIV = (from e in dc.Errors
where e.cityID == ctid && e.centreID == centreid
// Find the date of the previous inspection (if any)
let previousInspection = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed <= e.date orderby i.datePerformed descending select i.datePerformed).FirstOrDefault()
// Find the date of the next inspection (if any)
let nextInspection = (from i in dc.InspectionVisits where i.cityID == e.cityID && i.centreID == e.centreID && i.datePerformed > e.date orderby i.datePerformed ascending select i.datePerformed).FirstOrDefault()
group e by new { previousInspection , nextInspection } into results
orderby results.Key.previousInspection
select new Summary
{
PreviousInspection = results.Key.previousInspection,
NextInspection = results.Key.nextInspection ,
Errors = results.Count()
})
.ToList();
However if there are no errors between two visits, then these visits will not appear in your list, so you need to find all the visits and see if there missing, ie something like
var inspectionsDates = (from i in InspectionVisits where i.cityID == ctid && i.centreID == centreid orderby i.datePerformed select i.datePerformed).ToList();
for(int i=0; i< inspectionsDates.Count-1; i++)
{
if (!errorsPerIV.Any(a=>a.PreviousInspection == inspectionsDates[i]))
{
errorsPerIV.Add(new Summary() { PreviousInspection = inspectionsDates[i], NextInspection = inspectionsDates[i + 1], Errors = 0});
}
}
I think this would be easiest processed on the client side.
First you want to get the interesting InspectionVisits and order them by date, then convert to an Enumerable to pull them to the client:
var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp).AsEnumerable();
Now using an extension method that processes along the Enumerable to compute a running value (it is called Scan because it is modeled after the APL Scan operator, which is like an Aggregate that returns all the intermediate values):
// TKey combineFn(T prev, T cur)
// TKey lastKeyFn(T cur)
public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, Func<T, T, TResult> combineFn, Func<T, TResult> lastKeyFn) {
using (var srce = src.GetEnumerator()) {
if (srce.MoveNext()) {
var prev = srce.Current;
while (srce.MoveNext()) {
yield return combineFn(prev, srce.Current);
prev = srce.Current;
}
yield return lastKeyFn(prev);
}
}
}
You can compute the periods for the inspection visits:
var IVPeriods = orderedIVs.Scan((prev, cur) => new { Begin = prev, End = cur }, cur => new { Begin = cur, End = DateTime.Now });
Finally, with the periods you can count the Errors that occurred between each period:
var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });
If you want to process this on the server side, you must join the InspectionVisits table to itself so you can create the periods. I have not tested this with SQL server:
var orderedIVs = InspectionVisits.Where(iv => iv.cityID == ctid && iv.centerID == centerid).Select(iv => iv.dateperformed).OrderBy(ivdp => ivdp);
var IVPeriods = (from ivb in orderedIVs
from ive in orderedIVs
where ivb < ive
group ive by ivb into iveg
select new { Begin = iveg.Key, End = iveg.OrderBy(iv => iv).First() })
.Concat((from ivb in orderedIVs orderby ivb descending select new { Begin = ivb, End = DateTime.Now }).Take(1));
var errorsPerIV = IVPeriods.Select(ivp => new { Day = ivp.Begin.Date, Count = Errors.Where(e => ivp.Begin <= e.date && e.date <= ivp.End).Count() });

how can i wirte linq to compair the year and month f

I am trying to fetch record from my database year and month vise
I have write following linq
Connection cn = new Connection();
IMongoDatabase db = cn.ConnectionString();
var collection = db.GetCollection<HolidayListModel>("Holiday");
var filter = Builders<HolidayListModel>.Filter.Eq("business_id", businessid);
try
{
var obj = collection.Find(filter).ToListAsync();
if (obj != null && obj.Result != null)
{
if (month == "All")
{
holidaylist = obj.Result.Where(x => DateTime.Parse(x.date).Year == selectedyear).OrderBy(x => DateTime.Parse(x.date)).Select(x => new HolidayListModel()
{
_id = x._id,
business_id = x.business_id,
festival = x.festival,
date = x.date,
day = x.day
}).ToList();
}
else
{
holidaylist = (from x in obj.Result where (( x.date == month )) select x).ToList();
}
Here with lambda expression I got the data year vise using query expression I can't find a way to fetch data using year and month
Can someone guide me how can I write linq for this problem
This is usually done simply by using a start/end value; so foo.Year == selectedYear or foo.date == month becomes simply foo >= #start && foo < #end (the end is usually exclusive, so for a "year" check, that would be 1 Jan of the next year; for a "month" check it would be the 1st of the next month; this means that times in the last day are still always treated correctly).
If you are storing your dates in mongodb as strings that are ISO 8601 or similar, you could also do the same thing simply with string comparisons, without any "parse" logic.

Linq Min in Select new & Multiple GroupBy columns

I want to write following query in Linq
INSERT INTO INOUTNEW (CODE,INDATE,TIME_DATE1,INOUTFLAG,TIME_FLD1,TIME_FLD2,TIME_FLD3)
SELECT CODE,MIN(INDATE),TIME_DATE1,'I',TIME_FLD1,TIME_FLD2,'31/05/2015' FROM INOUT
WHERE TIME_FLD1='T0003' AND INDATE >= '31/05/2015' AND INDATE <= '31/05/2015'
AND TIME_DATE1='31/05/2015'
GROUP BY CODE,TIME_DATE1,TIME_FLD1,TIME_FLD2
SO I am trying this :-
var data = ctx.tblInOut.Where(m => m.CompanyId == companyId && m.Time_Field1 == item.ShiftCode && m.InDate == StrInStart && m.InDate <= StrInEnd && m.Time_Date1 == InputDate).Select(m =>
new
{
EmployeeId = m.EmployeeId,
InDate = Min(m.InDate),
Time_Date1 = m.Time_Date1,
InOutFlag = m.InOutFlag
}).ToList();
I am stuck in Min Part. How to get Min in Select? And How to add multiple GroupBy in Linq?
Try something like this:
var data = ctx.tblInOut
.Where(m =>
m.CompanyId == companyId &&
m.Time_Field1 == item.ShiftCode &&
m.InDate == StrInStart &&
m.InDate <= StrInEnd &&
m.Time_Date1 == InputDate
)
.GroupBy(m =>
new {
m.Code,
m.Time_Date1,
m.Time_FLD1,
m.Time_FLD2
})
.Select(g =>
new
{
m.Key.Code,
InDate = m.Min(gg => gg.InDate),
m.Key.Time_Date1,
Something = "I",
m.Key.Time_FLD1,
m.Key.Time_FLD2,
SomeDate = "31/05/2015"
}).ToList();
To get Min, you must group first - otherwise it's trying to call Min on a single element.
.Key simply references the key of the group (in this case, a tuple of Code, Date1, Time_FLD1, Time_FLD2)

Help creating a LINQ to SQL query

I'm populating a FormView by setting the datasource to an IQueryable object that I get by doing a LINQ query. Basically it returns the number of employees that hold a certain "Position" within a certain "Shift".
int shiftID = 1;
var shiftCount = from x in context.Employees.Take(1)
select new
{
ManagerCount = ((from p in context.Persons
where p.PositionID == 1 && p.ShiftID == shiftID && p.IsEmployee == true
select p.PersonId).Count(),
PartTimeCount = ((from p in context.Persons
where (p.PositionID == 2 || p.PositionID == 3) && p.ShiftID == shiftID && p.IsEmployee == true
select p.PersonId).Count(),
etc, etc...
};
That part works fine. However, when I want to get the number of employees for all shifts, I can't quite figure out how to do it:
//Get all shifts 1, 2, and 3
var shiftCount = from x in context.Employees.Take(1)
select new
{
ManagerCount = ((from p in context.Persons
where p.PositionID == 1 && (p.ShiftID == 1 || p.ShiftID == 2 || p.ShiftID == 3) && p.IsEmployee == true
select p.PersonId).Count()
};
That doesn't work though because it of course returns 3 values and gives the Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression. error.
So I need to get the sum of the three values returned (it's not always three though, it depends on the position).
I've looked at various ways using Sum and LINQ grouping, but can't quite seem to work it out. Can anybody point me in the right direction?
I've got a few answers for you, but first it seems like you have something wrong with your first part of your query. You're doing .Take(1) on Employees which will give you only one x value. Since you don't use x in the remainder of your query then using Employees is redundant.
Now, since all of your queries are quite similar I've tried to remove repetition. The first thing to do is to get a common filter for the shifts you are filtering on.
If you have a single shift, use this:
int shiftID = 1;
var shifts = new [] { shiftID, };
If you have multiple shifts, use this:
var shifts = new [] { 1, 2, 3, };
Either way you end up with an array of integers representing the shifts you want to filter against. All of the below answers require this shifts array.
Then define a query for the employees in those shift, regardless of position for now.
var employeesInShifts =
from p in context.Persons
where p.IsEmployee
where shifts.Contains(p.ShiftID)
select p;
So you can then get the shift counts like so:
var shiftCount =
new
{
ManagerCount = employeesInShifts
.Where(p => p.PositionID == 1)
.Count(),
PartTimeCount = employeesInShifts
.Where(p => p.PositionID == 2 || p.PositionID == 3)
.Count(),
// etc
};
Perhaps a better alternative for you though, would be to turn the employeesInShifts query into a dictionary and then just pluck the values from the dictionary.
var employeesInShifts =
(from p in context.Persons
where p.IsEmployee
where shifts.Contains(p.ShiftID)
group p by p.PositionID into gps
select new
{
PositionID = gps.Key,
Count = gps.Count(),
})
.ToDictionary(pc => pc.PositionID, pc.Count);
var shiftCount =
new
{
ManagerCount = employeesInShifts[1],
PartTimeCount = employeesInShifts[2] + employeesInShifts[3],
// etc
};
The downside to this approach is that you really should check that the dictionary has values for each PositionID before getting the values.
That can be fixed by introducing an array of position ids that you want the dictionary to have and joining your results on that.
var positionIDs = new [] { 1, 2, 3, };
var employeesInShifts =
(from p in context.Persons
where p.IsEmployee
where shifts.Contains(p.ShiftID)
where positionIDs.Contains(p.PositionID)
select p).ToArray();
var allPositionEmployeesInShifts =
from pid in positionIDs
join p in employeesInShifts on pid equals p.PersonId into gps
select new
{
PositionID = pid,
Count = gps.Count(),
};
var countOfPositionID =
allPositionEmployeesInShifts
.ToDictionary(x => x.PositionID, x => x.Count);
var shiftCount =
new
{
ManagerCount = countOfPositionID[1],
PartTimeCount = countOfPositionID[2] + countOfPositionID[3],
// etc
};
Now that guarantees that your final dictionary will contain the counts of all of the position ids that you are wanting to query.
Let me know if this works or if you really needed to join on the Employees table, etc.
I would recommend looking at the Group By examples here:

Categories