Get results from multiple tables after grouping using Linq-to-SQL - sql-server

I think the answer should be easy, but I'm just struggling:
Would like to have data from 2 tables in a LINQ query similar to:
from f in Faults
join af in AvailabilityFaults on f.FaultID equals af.FaultID
join a in Availabilities on new { af.CalendarDay, af.CircuitNumber}
equals new { a.CalendarDay, a.CircuitNumber}
join e in ExternalImportAvailabilities on new { a.CalendarDay, a.CircuitNumber }
equals new { e.CalendarDay, e.CircuitNumber }
where a.CalendarDay.Value.Day != 1
group f by f.FaultID into groupF
select new {groupF, e.CalendarDay}
The problem here comes in that it can't find e.CalendarDay in the select clause.
I also tried things like: CalendarDay= e.Max(e=>e.CalendarDay), but e is not in the current context.
How can I add data from table 'e' in the select clause?

If I understood you properly, this is what you are looking for:
from f in Faults
join af in AvailabilityFaults on f.FaultID equals af.FaultID
join a in Availabilities on af.CalendarDay equals a.CalendarDay and af.CircuitNumber equals a.CircuitNumber
join e in ExternalImportAvailabilities on a.CalendarDay equals e.CalendarDay and a.CircuitNumber equals e.CircuitNumber
where a.CalendarDay.Value.Day != 1
group new {f, e} by f.FaultID into groupFE
select new {GroupF = groupFE.Select( gfe => gfe.f), CalendarDay = groupFE.Max( gfe => gfe.e.CalendarDay)}

If you want e.CalendarDay in your select, then you normally would include it in the group, as in
group f by new { f.FaultID , e.CalendarDay } into groupF
select new {groupF.Key.FaultID, groupF.Key.CalendarDay}

Related

EF6 query that behaves strangely using contains

I have an EF6 SQL Server query that behaves strangely when it is supplied with a List<int> of IDs to use. If bookGenieCategory = a value it works. If selectedAges is empty (count = 0) all is well. If the selectedAges contains values that exist in the ProductCategory.CategoryId column, the contains fails and NO rows are returned.
Note: AllocationCandidates is a view, which works properly on its own.
CREATE VIEW dbo.AllocationCandidate
AS
SELECT
p.ProductID, p.SKU as ISBN, p.Name as Title,
pv.MSRP, pv.Price, pv.VariantID, pv.Inventory,
ISNULL(plt.DateLastTouched, GETDATE()) AS DateLastTouched,
JSON_VALUE(p.MiscText, '$.AgeId') AS AgeId,
JSON_VALUE(p.MiscText, '$.AgeName') AS AgeName
FROM
dbo.Product AS p WITH (NOLOCK)
INNER JOIN
dbo.ProductVariant AS pv WITH (NOLOCK) ON pv.ProductID = p.ProductID
LEFT OUTER JOIN
dbo.KBOProductLastTouched AS plt WITH (NOLOCK) ON plt.ProductID = p.ProductID
WHERE
(ISJSON(p.MiscText) = 1)
AND (p.Deleted = 0)
AND (p.Published = 1)
AND (pv.IsDefault = 1)
GO
Do I have a typo here or a misplaced parenthesis in the following query?
var returnList = (from ac in _db.AllocationCandidates
join pc in _db.ProductCategories on ac.ProductID equals pc.ProductID
where (bookGenieCategory == 0
|| bookGenieCategory == pc.CategoryID)
&&
(selectedAges.Count == 0 ||
selectedAges.Contains(pc.CategoryID))
orderby ac.AgeId, ac.DateLastTouched descending
select ac).ToList();
Firstly, I would recommend extracting the conditionals outside of the Linq expression. If you only want to filter data if a value is provided, move the condition check outside of the Linq rather than embedding it inside the condition. This is generally easier to do with the Fluent Linq than Linq QL. You should also aim to leverage navigation properties for relationships between entities. This way an AllocationCandidate should have a collection of ProductCategories:
var query = _db.AllocationCandidates.AsQueryable();
if (bookGenieCategory != 0)
query = query.Where(x => x.ProductCategories.Any(c => c.CategoryID == bookGenieCategory);
The next question is what does the selectedAges contains? There is an Age ID on the AllocationCandidate, but your original query is checking against the ProductCategory.CategoryId??
If the check should be against the AllocationCandidate.AgeId:
if (selectedAges.Any())
query = query.Where(x => selectedAges.Contains(x.AgeID));
If the check is as you wrote it against the ProductCategory.CategoryId:
if (selectedAges.Any())
query = query.Where(x => x.ProductCategories.Any(c => selectedAges.Contains(c.AgeID)));
Then add your order by and get your results:
var results = query.OrderBy(x => x.AgeId)
.ThenByDescending(x => x.DateLastTouched);
.ToList();

Get data from a table after joining based on null value of joined table using LINQ

I have some tables like "Job" "AppliedJob" "JobOffer" "Contract" "Employeer"
Description: When employeer post job it is stored in "Job" table with his ID. If a freelancer apply to the job it is stored in "AppliedJob" table with his ID. Then Employeer sees the application and send offer to the freelancer and it is stored in "JobOffer" table. If freelancer accept the offer it is then stored in "Contract" table. At first in "Contract" ContractID, OfferID and StratDate are stored and CompletedDate is stored as null. When the contract is completed the CompletedDate field is modified with date.
Want: I want to return all jobs with no completed contract
I tried:
[HttpGet]
[Route("api/PrivateApi/GetEmployeerPostedJob/")]
public object GetEmployeerPostedJob(int id)
{
var data = (from j in db.Jobs
where j.EmployeerID == id
join apl in db.AppliedJobs
on j.JobID equals apl.JobID
join o in db.JobOffers
on apl.AppliedJobID equals o.AppliedJobID
join con in db.Contracts
on o.OfferID equals con.OfferID
where con.CompletedDate == null
select new
{
j.JobTitle,
j.JobID,
j.Budget,
j.Deadline,
j.Employeer,
j.JobDetails,
j.PublishDate,
j.ReqSkill,
j.NoOFFreelancer,
j.Preference,
Category1=j.Category,
totalAppliedFreelancer=(from aple in db.AppliedJobs where j.JobID ==aple.JobID select aple).Count(),
Category = (from gg in db.Categories where gg.CategoryID == j.Category select gg.CategoryName).FirstOrDefault()
}).ToList();
return data.AsEnumerable();
}
But it returns no jobs.
How can i get all jobs which are not completed yet(CompletedDate == null in Contract table)?
I want to show the employeer's posted jobs on his page but only those jobs that are not completed
The above (die to one to many relationships involved) can be paraphrased as
return all jobs with no completed contract
while the way you wrote the query, besides the possible data duplication, it answers the question
return all jobs with existing, but not completed contract
i.e. is missing the jobs w/o applied job, applied job w/o offer and offer w/o contract.
The correct query would be something like this:
from job in db.Jobs
where job.EmployeerID == id
join jobContract in (
from appliedJob in db.AppliedJobs
join offer in db.JobOffers on appliedJob.AppliedJobID equals offer.AppliedJobID
join contract in db.Contracts on offer.OfferID equals contract.OfferID
select new { appliedJob, offer, contract }
) on job.JobID equals jobContract.appliedJob.JobID into jobContracts
where !jobContracts.Any(jobContract => jobContract.contract.CompletedDate != null)
select ...
The query could further be simplified by using navigation properties, but since I don't see navigation properties between AppliedJob and JobOffer, I'm leaving that for you.
Update: Here is the same query with navigation properties (by simplified I meant no need for join operators):
from job in db.Jobs
let completedContracts =
from appliedJob in job.AppliedJobs
from offer in appliedJob.JobOffers
from contract in offer.Contracts
where contract.CompletedDate != null
select contract
where !completedContracts.Any()
select ...
This LINQ query doesn't work on live server (sql-2008) But works on my local VS 2013. Is there any mistake if my live database is empty at first time?
#Ivan Stoev
public object BrowseJobs()
{
var skills = db.Skills.ToDictionary(d => d.SkillID, n => n.SkillName);
var jobData = (from j in db.Jobs where j.Preference==2
//from cj in j.ClosedJobs.DefaultIfEmpty()
join cj in db.ClosedJobs.DefaultIfEmpty()
on j.JobID equals cj.JobID into closedJob
where !closedJob.Any()
join c in db.Categories on j.Category equals c.CategoryID
join jobContract in
(
from appliedJob in db.AppliedJobs.DefaultIfEmpty()
from offer in appliedJob.JobOffers.DefaultIfEmpty()
from contract in db.Contracts.DefaultIfEmpty()
select new { appliedJob, offer, contract }
).DefaultIfEmpty()
on j.JobID equals jobContract.appliedJob.JobID into jobContracts
where !jobContracts.Any(jobContract => jobContract.contract.CompletedDate != null)
select new
{
JobTitle = j.JobTitle,
JobID = j.JobID,
ReqSkillCommaSeperated = j.ReqSkill,
Category = c.CategoryName,
Budget=j.Budget,
Deadline=j.Deadline,
JobDetails=j.JobDetails,
PublishDate=j.PublishDate,
TotalApplied=(from ap in db.AppliedJobs where j.JobID == ap.JobID select ap.AppliedJobID).DefaultIfEmpty().Count()
}).AsEnumerable()
.Select(x => new
{
JobID = x.JobID,
JobTitle = x.JobTitle,
Category = x.Category,
Budget = x.Budget,
Deadline = x.Deadline,
JobDetails = x.JobDetails,
PublishDate = x.PublishDate,
SkillNames = GetSkillName(x.ReqSkillCommaSeperated, skills),
TotalApplied = (from ap in db.AppliedJobs where x.JobID == ap.JobID select ap.AppliedJobID).DefaultIfEmpty().Count()
}).ToList();
return jobData.AsEnumerable();
}

T-SQL Left Join using "or" operator

I have the following query where I would like to pull in either or both on a match. There can be more than one ID or HIC for a member. So if the member ID = member ID then pull the max (loaddate) for the most current HIC Likewise if HIC = HIC pull in the member ID with the max (loaddate). I want to include the left join in case a match isn't found for either scenario.
Code:
SELECT
CH1.*
,AVM.MBR_ID
,AVM.HIC
,AVM.MBR_LST_NM
,AVM.MBR_FST_NM
,CAST(AVM.MBR_BIRTH_DT AS DATE) AS 'MBR_BIRTH_DT'
,AVM.LOADDATE
FROM
#CHECK1 CH1
LEFT JOIN
AVRIL.DBO.VW_MBR AVM ON (CH1.HICN = AVM.HIC OR CH1.MEMBER_ID = AVM.MBR_ID)
WHERE
CH1.HEALTH_PLAN = 'AVRIL'
AND AVM.LOADDATE=(SELECT MAX(LOADDATE) FROM AVRIL.DBO.VW_MBR)
Thanks,
Michael
When you use a field from the VW_MBR table in the where clause, it effectively turns the left join into an inner join. Put the condition in the join:
...
FROM #CHECK1 CH1
LEFT JOIN AVRIL.DBO.VW_MBR AVM ON (CH1.HICN = AVM.HIC OR CH1.MEMBER_ID = AVM.MBR_ID)
AND AVM.LOADDATE=(SELECT MAX(LOADDATE) FROM AVRIL.DBO.VW_MBR)
WHERE CH1.HEALTH_PLAN = 'AVRIL'
Here may be an alternative to the first proposed answer, I'm using a custom query as jointure in order to optimize it (I use only the max load dates instead of trying the jointure on every rows of the table).
SELECT CH1.*
,MAVM.MBR_ID
,MAVM.HIC
,MAVM.MAX_LOADDATE
FROM #CHECK1 CH1
LEFT JOIN (SELECT AVM.HIC
,AVM.MBR_ID
MAX(AVM.LOADDATE) AS [MAX_LOADDATE]
FROM AVRIL.DBO.VW_MBR AVM
GROUP BY AVM.HIC, AVM.MBR_ID) MAVM (MAVM.HIC = CH1.HICN
OR MAVM.MBR_ID = CH1.MEMBER_ID)
WHERE CH1.HEALTH_PLAN = 'AVRIL'
I simplified the query to focus on the changes in comparison with the other proposed query. You can still add an INNER JOIN clause to AVRIL.DBO.VM_MBR in order to get additional columns:
...
LEFT JOIN (...) MAVM ...
INNER JOIN AVRIL.DBO.VW_MBR AVM2 ON AVM2.HIC = MAVM.HIC
AND AVM2.MBR_ID = MAVM.MBR_ID
AND AVM2.LOADDATE = MAVM.MAX_LOADDATE
...
So you can use AVM2.xxx for the columns you want to use.
Hope this will help

ibm 1 sql creating - need to add 2 tables

I have following view which is working but not sure how to add 2 tables to join.
This table is adres1 and it will join on the IDENT# and IDSFX# to table
prodta.adres1 called adent# and adsfx#, there I need a col. ads15.
then i also need to get the ship to, row in this adres1. this we get first from the order table, prodta. oeord1 in col. odgrc#. This grc# is 11 pos and is combined 8 and 3 of the ent and suf. these 2 represent the ship to record and looking in same table adres1 (we do have many logical views on them if it's easier, like adres15) we can get col. ADSTTC for the ship to state.
Not sure if can included these 2 new parts to the current view created code below. Please ask if something not clear, it's an old system and somewhat developed convoluted.
CREATE VIEW Prolib.SHPWEIGHTP AS SELECT
T01.IDORD#,
T01.IDDOCD,
T01.IDPRT#,
t01.idsfx#,
T01.IDSHP#,
T01.IDNTU$,
T01.IDENT#,
(T01.IDNTU$ * T01.IDSHP#) AS LINTOT,
T02.IAPTWT,
T02.IARCC3,
T02.IAPRLC,
T03.PHVIAC,
T03.PHORD#,
PHSFX#,
T01.IDORDT,
T01.IDHCD3
FROM PRODTA.OEINDLID T01
INNER JOIN PRODTA.ICPRTMIA T02 ON T01.IDPRT# = T02.IAPRT#
INNER JOIN
(SELECT DISTINCT
PHORD#,
PHSFX#,
PHVIAC,
PHWGHT
FROM proccdta.pshippf) AS T03 ON t01.idord# = T03.phord#
WHERE T01.IDHCD3 IN ('MDL','TRP')
I'm not exactly clear on what you're asking, and it looks like some of the column-names are missing from your description, but this should get you pretty close:
CREATE VIEW Prolib.SHPWEIGHTP AS
SELECT T01.IDORD#,
T01.IDDOCD,
T01.IDPRT#,
t01.idsfx#,
T01.IDSHP#,
T01.IDNTU$,
T01.IDENT#,
( T01.IDNTU$ * T01.IDSHP# ) AS LINTOT ,
T02.IAPTWT,
T02.IARCC3,
T02.IAPRLC,
T03.PHVIAC,
T03.PHORD#,PHSFX#,
T01.IDORDT,
T01.IDHCD3,
t04.ads15
FROM PRODTA.OEINDLID T01
INNER JOIN PRODTA.ICPRTMIA T02
ON T01.IDPRT# = T02.IAPRT#
INNER JOIN (SELECT DISTINCT
PHORD#,
PHSFX#,
PHVIAC,
PHWGHT
FROM proccdta.pshippf) AS T03
ON t01.idord# = T03.phord#
JOIN prodta.adres1 as t04
on t04.adent# = t01.adent#
and t04.adsfx# = t01.adsfx#
JOIN prodta.oeord1 t05
on t05.odgrc# = T01.IDENT# || T01.SUFFIX
WHERE T01.IDHCD3 IN ('MDL','TRP')
Let me know if you need more details.
HTH !

Translating a QueryExpression into SQL: what's a Natural join?

I am trying to translate a QueryExpression that is in some existing code into a T-SQL select statement.
I've run across the following statement and I'm having trouble understanding what they mean by a Natural Join:
linkEntity1.JoinOperator = JoinOperator.Natural;
Would this be equivalent to an Inner Join in T-SQL? Googling has not been much help.
Here's the rest of the QueryExpression Code:
QueryExpression query = new QueryExpression();
query.EntityName = "showinfo";
ColumnSet columns = new ColumnSet();
columns.Attributes = new String[] { "company" };
query.ColumnSet = columns;
query.Criteria = new FilterExpression();
query.Criteria.FilterOperator = LogicalOperator.And;
ConditionExpression condition1 = new ConditionExpression();
condition1.AttributeName = "company";
condition1.Operator = ConditionOperator.NotNull;
query.Criteria.Conditions = new ConditionExpression[] { condition1 };
LinkEntity linkEntity1 = new LinkEntity();
linkEntity1.JoinOperator = JoinOperator.Natural;
linkEntity1.LinkFromEntityName = "show";
linkEntity1.LinkFromAttributeName = "showid";
linkEntity1.LinkToEntityName = "showintegration";
linkEntity1.LinkToAttributeName = "showcode";
linkEntity1.LinkCriteria = new FilterExpression();
linkEntity1.LinkCriteria.FilterOperator = LogicalOperator.And;
ConditionExpression condition2 = new ConditionExpression();
condition2.AttributeName = "showend";
condition2.Operator = ConditionOperator.Null;
linkEntity1.LinkCriteria.Conditions = new ConditionExpression[] { condition2 };
query.LinkEntities = new LinkEntity[] { linkEntity1 };
There is no equivalent in SQL Server of a natural join where table intersect is based on column names by the RDBMS.
I'm glad of that because it is at best ambiguous and at worst dangerous. JOINs should be explicit. Examples why:
having a InsertedBy column in both tables (quite common): should we have to prefix with the table name to remove ambiguity?
future DDL that add columns that change JOIN semantics
See
Natural join in SQL Server
SQL Server - lack of NATURAL JOIN / x JOIN y USING(field)
Edit:
It looks like natural join means "don't repeat the column in the output" (like USING in MySQL would do) according to the JoinOperator Enumeration.
If I understand this (debatable!) it's misleading. Especially when I read the "LeftOuter" narrative..
A natural join compares all columns in the two tables that have the same column names. It's equivalent to an inner join with the matching columns explicitly listed.
Yes - the natural join is inner join - so you can write:
select * from tab1, tab2 where tab1.col1 = tab2.col1
as
select * from tab1 inner join tab2 on tab1.col1 = tab2.col1

Resources