SQL lambda output not correct using nested select - sql

I have 2 tables, Terms and Site_Program_Term:
Terms columns: Id, SiteId, Name, IsHoliday, DateStart, DateEnd, Year, IsActive
Site_Program_Term columns: Id, ProgName, SiteId, TermId, Year
Using the above 2 tables, I need to display output with primarily below columns
Name, StartDate, EndDate, NumberOfPrograms
I am getting the desired output but the 'NumberOfPrograms' is not showing the correct values. It should give me a count of all the Programs for a set of particular Term, Year and Site.
Thanks in advance.
Below is the base code.
var terms = db.Terms.Where(t => t.SiteId == id && t.IsActive).ToList().Select(t => new
{
Id = t.Id,
SiteId = t.SiteId,
Name = t.Name,
IsHoliday = t.IsHoliday,
DateStart = t.DateStart,
DateEnd = t.DateEnd,
Year = t.Year,
PubProgram = (
db.Site_Program_Term
.Where (spt => spt.SiteId == id && spt.Year == t.Year)
.Select(s => s).Count()
),
}).OrderBy(n => n.DateStart).ToList();

It should give me a count of all the
Programs for a set of particular Term, Year and Site.
But your Where condition only includes Year and Site, but not the Term. So
.Where(spt => spt.SiteId == id && spt.Year == t.Year)
should actually be:
.Where(spt => spt.SiteId == id && spt.Year == t.Year && spt.TermId == t.Id)
Note that if you are by any chance using Entity Framework (Core) and you have properly configured navigation property on the Term class such as
public class Term
{
// ...
public List<Site_Program_Term> SiteProgramTerms { get; set; }
}
then you can rewrite your query as
var terms = db.Terms
.Where(t => t.SiteId == id && t.IsActive)
.Select(t => new
{
Id = t.Id,
SiteId = t.SiteId,
Name = t.Name,
IsHoliday = t.IsHoliday,
DateStart = t.DateStart,
DateEnd = t.DateEnd,
Year = t.Year,
PubProgram = t.SiteProgramTerms.Where(spt => spt.SiteId == id && spt.Year == t.Year).Count()
})
.OrderBy(n => n.DateStart)
.ToList();
Note that this time you don't need to include spt.TermId == t.Id condition, because SiteProgramTerms property contains only site program terms with TermId that matches the Id of the Term. Also note that the first .ToList() should be omitted, because it causes query to be sent to the SQL server as multiple SELECT queries (as opposed to just one SELECT when .ToList() is not present).

Related

Filter Data based on priority in LINQ

The fields in my Rate Table are Route, VehicleMasterId, VehicleType, Material, Client, UnitRate etc.
The priority order on which I have to fetch a single row is : VehicleNo > Route > Client > VehicleType, Material
Suppose I have 2 rows with same data except 1 has Client and Vehicle Type and the other one has VehicleNo.Then based on my priority, I should pick the rate of the row with VehicleNo.
To excute this In linq I have first picked all the rows with matching data. Here is my code.
public RateMasterDataModel GetRateMasterforCn(Consignment cn){
// I will always pass all the above fields in cn
var rateMaster = (from rate in Context.RateMaster
where rate.FromDate <= cn.Cndate
&& rate.ToDate >= cn.Cndate
&& (rate.VehicleTypeId != null ? rate.VehicleTypeId == cn.VehicleTypeId : true)
&& (rate.VehicleMasterId != null ? rate.VehicleMasterId == cn.VehicleMasterId : true)
&& (rate.ClientId != null ? rate.ClientId == cn.ClientId : true)
&& (rate.RouteId != null ? rate.RouteId == cn.RouteId : true)
&& (rate.MaterialMasterId != null ? rate.MaterialMasterId == cn.MaterialMasterId : true)
select new RateMasterDataModel
{
RateMasterId = rate.RateMasterId,
FromDate = rate.FromDate,
ToDate = rate.ToDate,
ClientId = rate.ClientId ,
VehicleMasterId = rate.VehicleMasterId,
VehicleTypeId = rate.VehicleTypeId,
MaterialMasterId = rate.MaterialMasterId,
UnitRate = rate.UnitRate,
LoadTypeId = rate.LoadTypeId,
LoadingPointId = rate.RouteId,
CalculationMasterId = rate.CalculationMasterId
}).ToList();
}
Please suggest how to filter after this.
You can use below code to get records ordered by VehicleNo > Route
.OrderBy(v=>v.VehicleNo).ThenBy(r=>r.RouteId)
Add multiple .ThenBy() clause as per your column requirement for sorting the data.
You mean to say if the row which doesn't have the vehicalno. filld-up then the row having Route must be selected.is it correct?

select distinct out of three column connectiontable

I have a ef connection table with 3 columns in it. I want to select distinct value out of two of them.
I want to select distinct rows orderby ResourceId and MetaDataId.
So i want it to return row (1, 3 and 4) in this case.
Ive tried this:
ctx.ResourceMetas.Where(a => a.ResourceId == resourceid).Distinct()});
But obviously this gets the distinct values out of all three. Can i somehow choose to get distinct out of just the two?
You may group by your distinct values and then get the max or min of the leftovers.
In your case that you only have one more field you could do it like:
ctx.ResourceMetas
.GroupBy(x=>new{x.ResourceId, x.MetaDataId})
.Select
(
x=>new
{
MetaListId = x.Min(m=>m.MetaListId ),
ResourceId = x.Key.ResourceId,
MetaDataId = x.Key.MetaDataId
}
)
.Where(a => a.ResourceId == resourceid)
But in a scenario that you would like the distinct values of 2 fields out of more than three then you would have to do it like:
ctx.ResourceMetas
.GroupBy(x=>new{x.ResourceId, x.MetaDataId})
.Select
(
x=>new
{
MetaListId = x.Where(i=>i.MetaListId == x.Min(m=>m.MetaListId)).FirstOrDefault().MetaListId ,
OtherField = x.Where(i=>i.MetaListId == x.Min(m=>m.MetaListId)).FirstOrDefault().OtherField ,
ResourceId = x.Key.ResourceId,
MetaDataId = x.Key.MetaDataId
}
)
.Where(a => a.ResourceId == resourceid)

Linq union all equivalent of sql code

I have the following queries:
var majorClients = maj in dbContext.MajorClients
where (maj.startdate > startDate)
where (maj.status == "Active")
Select new Client{EntityPK = maj.mjPrimaryKey,Name = maj.name, Type = "Maj"};
var minorClients = min in dbContext.MinorClients
where (min.startdate > startDate)
where (min.status == "Active" || min.status== "Inactive")
Select new Client{EntityPK = min.mnPrimaryKey,Name = min.name, Type = "Min"};
There are clients that could appear in both major and minor tables. I would like to return a list of all occurrences of clients in both tables, however if there are matching clients by name, then I would only want to return the matching record from the majorClients table.
I have written a sql query to return the results:
SELECT mjPrimaryKey AS EntityPK,name,'Maj' AS TYPE
FROM majorClients
WHERE status = 'Active' AND startDate > #startDate
UNION ALL
SELECT mnPrimaryKey,name,'Min' FROM minorClients
WHERE status IN ('Active','Inactive') AND startDate > #startDate
WHERE name NOT IN (SELECT name FROM majorClients WHERE status = 'Active' AND startDate > #startDate)
How would I represent this query in linq?
Try this linq. To exclude duplicates from minorClients, I've used Contains method. To union all objects - Union method:
var majorClients = from maj in dbContext.MajorClients
where maj.startdate > startDate
&& maj.status == "Active"
select new Client
{
EntityPK = maj.mjPrimaryKey,
Name = maj.name,
Type = "Maj"
};
var minorClients = from min in dbContext.MinorClients
where min.startdate > startDate
&& min.status == "Active" || min.status== "Inactive"
&& !(from maj in dbContext.MajorClients
where maj.startdate > startDate
&& maj.status == "Active"
select maj.name).Contains(min.Name)
select new Client
{
EntityPK = min.mnPrimaryKey,
Name = min.name,
Type = "Min"
};
var allClients = majorClients.Union(minorClients);

Losing Aliases when trying to group-by rows in linq sql query, how to group-by rows in this case

I am a newbie intern way over my head, I promise I have researched this thoroughly and tried many different things. The following linq query works, but I want to group rows by last and first name and then only show the rows that appear more than once. When I un-comment out the group by statement, all my aliases below become unrecognized and changing them to the actual db.table names doesn't help.
var query = from emps in db.Employees
join c in db.Cards on emps.SbiID equals c.SbiID
where c.StateID == 0 && c.CardNumberNumeric > 100000
//group emps by new {emps.Surname, emps.Name};
//orderby grp.Count() // something like 'where grp.Count > 1
select new
{
Surname = emps.Surname,
Name = emps.Name,
CorpID = emps.Identifier,
CardNum = c.CardNumber,
CostCenter = emps.EmployeeUserField.UF13,
Supervisor = (from e in db.Employees
where
e.Identifier.Equals(emps.EmployeeUserField.UF5)
select e.Surname).FirstOrDefault()
+ ", "
+ (from e in db.Employees
where e.Identifier.Equals(emps.EmployeeUserField.UF5)
select e.Name).FirstOrDefault(),
SupervisorID = emps.EmployeeUserField.UF5,
EmpCommence = emps.CommencementDateTime,
CardCommence = c.CommencementDateTime,
WorkPhone = emps.Telephone,
State = (from cf in db.ComboFields
from sp in db.StringProperties
where cf.ComboIndex.Equals(c.StateID)
&& cf.StringID.Equals(sp.StringID)
&& cf.TableName.Equals("Card")
&& cf.FieldName.Equals("StateID")
select sp.DefaultValue).FirstOrDefault()
};
this.tagsGridView.DataSource = query;
this.tagsGridView.DataBind();
I think the problem you're running into is that you're not flattening out your groups appropriately. For example:
var duplicateEmployees = db.Employees
.GroupBy(emp => emp, new EmployeeComparer())
.Where(grp => grp.Count() > 1)
.SelectMany(grp => grp.AsEnumerable());
var duplicateEmployeeInfo =
from emps in duplicateEmployees
join c in db.Cards on emps.SbiID equals c.SbiID
where c.StateID == 0 && c.CardNumberNumeric > 100000
select new
{
... what to select
};
With:
public class EmployeeComparer : IEqualityComparer<Employee>
{
public bool Equals(Employee x, Employee y)
{
return x.Surname == y.Surname && x.Name == y.Name;
}
public int GetHashCode(Employee obj)
{
unchecked { return (17 * obj.Surname.GetHashCode()) ^ (23 * obj.Name.GetHashCode()); }
}
}
This groups the employees by name, finds the groups that have a count > 1, then returns the elements of those groups. No guarantees on performance, but this should solve your issue.

Convert difficult ordered SQL statement to LINQ (edmx)

Need help with converting next statement to LINQ:
SELECT * FROM comments
WHERE good_id = '19' AND allow = '1'
ORDER BY IF(parent_id = 0, id, parent_id) DESC, id ASC
It's statement show the comment in next order list:
--Comment
--Subcomment (if parent_id != 0)
--Subcomment (if parent_id != 0)
--Comment
--Subcomment (if parent_id != 0)
etc.
But I don't know how to implement it on LINQ. Any ideas?
have not compiled this but I think I am on the right path.
var comments = from c in db.Comments
where c.good_id == 19 && c.allow = "1"
orderby myFunction(c.parent_id, c.id) descending, id ascending
select comments;
public int myFunction(int parentID, in ID)
{
return parent_id == 0 ? id : parent_id;
}
If models have relationships set up correctly then you don't need to do anything fancy
var comments = dataCoontext.Comments
.Where(c => c.GoodId = 19 &&
c.Allow = 1 &&
c.ParentId = 0) //Get top level comments
.OrderBy(c => c.Id);
foreach(var comment in comments){
Console.WriteLine("Comment:" + comment.Text);
GetSubComments(comment);
}
Then use the following
public void GetSubComments(Comment comment){
foreach(var subComment in comment.Children){ //You can apply any sub ordering to Children
Console.WriteLine("Sub comment:" + subComment.Text);
GetSubComments(subComment);
}
}