convert this SQL to link Left Outer Join and a Case statement - linq

I am having problem converting this sql statement into Linq. It has a Left outer join and a case statement. I will try the answer given below.
SELECT [job_required_document].[job_required_document_id]
,[document_name]
,[document_description]
,CASE WHEN dlink.job_required_document_id is NULL THEN 0 ELSE 1 END AS DocIsRequired
FROM [dbo].[job_required_document]
LEFT OUTER JOIN [dbo].[job_description_document_link] dlink ON job_required_document.job_required_document_id = dlink.job_required_document_id
AND dlink.job_description_id = #JobDescriptionId
**This is what i had done. But the docIsRequired is coming 1 for all the records. I think the case statement is wrong.
var query = from document in context.job_required_documents
join dlink in context.job_description_document_links.Where(q => q.job_description_id == job_description_id && q.job_description_id == job_description_id)
on document.job_required_document_id equals dlink.job_required_document_id into documentdlink
from q in documentdlink.DefaultIfEmpty()
select new Documents
{
job_description_id = job_description_id,
job_required_document_id = document.job_required_document_id,
document_name = document.document_name,
document_description = document.document_description,
docIsRequired = document.job_required_document_id == null ? 0 : 1
};

Create a custom class with following details:
public class DocumentDetails
{
public int docID {get;set;}
public string docName {get;set;}
public string docDesc {get;set;}
public int docIsRequired {get;set;}
}
Now use this LINQ to fetch data into this class type's collection as mentioned below:
List<DocumentDetails> documentDetialsList = (from document in datacontext.job_required_document
from dlink in datacontext.job_description_document_link
.where(x=> x.job_required_document_id == document.job_required_document_id).DefaultIfEmpty()
where dlink.job_description_id == #JobDescriptionId
select new {
docID = document.[job_required_document_id],
docName = document.[document_name],
docDesc = document.[document_description],
docIsRequired = dlink.job_required_document_id == null ? 0 : 1
}).ToList();
Hopefully it would help...

Related

Querydsl - filter on Left join with subquery

I have one of the complex query dynamically generated through Querydsl predicate and JPQL. I am also using the Q classes.
I am able to generate the following query by passing a predicate to the JPA repository.
select company0_.id as id1_18_,
company0_.name as name2_18_
from company company0_
left outer join companyAddress companyadd1_ on company0_.id=companyadd1_.company_id
where company0_.id in
(select companyadd2_.company_id
from companyAddress companyadd2_
where companyadd2_.address_type='1')
order by companyadd1_.addressline1;
but I want the query mentioned below
select company0_.id as id1_18_,
company0_.name as name2_18_
from company company0_
left outer join companyAddress companyadd1_ on company0_.id=companyadd1_.company_id
and companyadd1_.status = 'Active' -- New added(Failed to implement this)
where company0_.id in
(select companyadd2_.company_id
from companyAddress companyadd2_
where companyadd2_.address_type='1'
and and companyadd2_.status = 'Active') -- New Added(I am able to achieve this)
order by companyadd1_.addressline1;
We are using following kind of code, I can not possibly to share exact code due to security concern but you can help me by providing basic structure or code to achieve this.
final JPQLQuery<QCompanyAlias> subQuery = new JPAQuery<>();
BooleanExpression exp = null;
QueryBase<?> q = (QueryBase<?>) subQuery.from(qCompanyAddress);
if (requestMap.containsKey(CompanyQueryConstants.ADDRESS_TYPE)) {
BooleanExpression addrExp = null;
for (String addressType : addressTypes) {
if (addrExp == null) {
addrExp = qCompanyAddress.addressType.addressTypeCode.eq(addressType);
} else {
addrExp = addrExp.or(qCompanyAddress.addressType.addressTypeCode.eq(addressType));
}
}
exp = addrExp;
}
To add join on two conditions use
new JPAQuery(em)
.from(qCompany)
.leftJoin(qCompany, qCompanyAddress.company)
.on(
qCompany.id.eq(qCompanyAddress.company.id)
.and(qCompanyAddress.status.eq(status))
);
For subquery try to use this
final JPQLQuery<QCompanyAlias> subQuery = new JPAQuery<>();
BooleanExpression exp = null;
QueryBase<?> q = (QueryBase<?>) subQuery.from(qCompanyAddress);
if (requestMap.containsKey(CompanyQueryConstants.ADDRESS_TYPE)) {
BooleanExpression addrExp = null;
for (String addressType : addressTypes) {
if (addrExp == null) {
addrExp = qCompanyAddress.addressType.addressTypeCode.eq(addressType);
} else {
addrExp = addrExp.or(qCompanyAddress.addressType.addressTypeCode.eq(addressType));
}
}
exp = addrExp;
}
BooleanExpression statusExp = qCompanyAddress.status.eq(status);
if(exp == null) {
exp = statusExp;
} else {
exp = statusExp.and(exp);
}
But in your case I can't understand the reason for filtering by status twice. Subquery filtering should be enought. I suspect that you can achieve the same result without subquery. It depends on your entities.

Translate complex query to linq

I am trying to use the following query with Entity framework, that is why I am trying to use linq.
SELECT DISTINCT
elt.Type,
( SELECT TOP 1 Value
from ELD
where ELDTID = 2 AND ELID = el.ELID
) ID,
( SELECT TOP 1 Value
from ELD
Where ELDTID = 1 AND ELID = el.ELID
) Company,
( SELECT TOP 1 Value
from ELD
Where ELDTID = 5 AND ELID = el.EventLogID
) Message,
( SELECT Max(ET)
FROM EL el
INNER JOIN ELD eld ON eld.ELID = el.ELTID
WHERE el.ELID = el.ELID
) ET
FROM EL el
INNER JOIN ITS.ELT elt ON elt.ELTID = el.ELTID
WHERE ELSID = 3
It's not visible from the example whether ELSID is a EL or ELT attribute, assuming ELT.
I think to have spotted a mistake in your ET subquery: where el.ELID = el.ELID. You probably want to reference the el from the outer query on one side, but can't because you're using the same name. Used el1 in this answer for the subquery EL.
Tried to create a matching data model (next time please provide us with one, as you'll probably have it already).
public class Elt
{
public int ELTID;
public int ELSID;
public int Type;
}
public class El
{
public int ELID;
public int ELTID;
public int EventLogID;
}
public class Eld
{
public int ELDTID;
public int ELID;
public int ET;
public int Value;
}
public class Db
{
public IQueryable<El> Els;
public IQueryable<Elt> Elts;
public IQueryable<Eld> Elds;
}
Your SQL then should correspond to this query:
from el in db.Els
join elt in db.Elts on el.ELTID equals elt.ELTID
where elt.ELSID == 3
select new
{
elt.Type,
ID = (from eld in db.Elds where eld.ELDTID == 2 && eld.ELID == el.ELID select eld.Value).FirstOrDefault(),
Company = (from eld in db.Elds where eld.ELDTID == 1 && eld.ELID == el.ELID select eld.Value).FirstOrDefault(),
Message = (from eld in db.Elds where eld.ELDTID == 5 && eld.ELID == el.EventLogID select eld.Value).FirstOrDefault(),
ET = (
from el1 in db.Els
join eld in db.Elds on el1.ELTID equals eld.ELDTID
where el1.ELID == el.ELID
select eld)
.Max(eld => eld.ET),
};
If you don't have navigation properties (child collection of ELs on ELT, child collection of ELDs on EL, parent properties on EL and ELD) I'd suggest you add them, which would simplify the query further (join conditions injected by EF):
from elt in db.Elts
from el in elt.Els
...
Try this, may be you need
(from el in _context.EL.Where(p=>p.ELSID == 3)
join elt from _context.ELT on elt.ELTID equals el.ELTID
let ID = el.ELD.Where(p=>p.ELDTID == 2 ).Select(p=>p.Value).FirstOrDefault()
let Company = el.ELD.Where(p=>p.ELDTID == 1 ).Select(p=>p.Value).FirstOrDefault()
let Message = el.ELD.Where(p=>p.ELDTID == 5 && p.ELID = el.EventLogID).Select(p=>p.Value).Max()
let ET = el.Select(p=>p.ET).MAX()
select new
{
elt.Type,
ID,
Company,
Message,
ET
}).ToList();

Strongly typed linq group by

Relevant Model;
public class category_trans
{
[Key]
[Column(Order = 1)]
public int category_id { get; set; }
[Key]
[Column(Order = 2)]
public int language_id { get; set; }
public string name { get; set; }
}
present Linq query, working:
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
select ct;
I would like to group query result in order to get distinct category_trans.name (now I am getting multiple ones).
Trying
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
group ct by ct.name into g
select new
{
category_id = g.category_id,
name = g.name
};
gives me errors on both g.category_id and g.name
IGrouping <string,category_trans> does not contain a definition for 'category_id'...
Why grouping seems to lose reference to model members and how may it be fixed?
Because a group can contain multiple, you can use the Key property for the name:
IQueryable<category_trans> APC =
from ct in db.category_trans
from c in db.Categories
from l in db.ISO_Languages
where (
ct.category_id == c.ID
&& ct.language_id == l.ID
&& l.code.Substring(0,2) == culture
&& c.IsDeleted == false)
group ct by ct.name into g
select new
{
category_id = g.First().category_id,
name = g.Key
};
I have used First to get the first category_id, you might want to use a different logic.

LINQ join issue, returns all null value

I am new in EF and LINQ and I am facing a strange issue. When I check null value in my select new block, all values from child table is coming null. Below is the LINQ query.
My Linq Code
var linqResult = from pd in entities.tblpackagedetails
join ps in entities.tblpackageselecteds
on pd.PackageDetailsID equals ps.PackageDetailsID
into tabJoin
from tj in tabJoin.Where(ps => ps.UserID == userID
&& ps.IsActive == true).DefaultIfEmpty()
select new
{
IsComplete = (tj == null) ? false : tj.IsComplete,
IsActive = (tj == null) ? false : tj.IsActive,
UserID = (tj == null) ? 0 : tj.UserID,
IsMandatory = pd.IsMandatory,
PackageSelectedID = (tj == null) ? 0 : tj.PackageSelectedID,
IsSelected = (tj == null ? false : tj.IsSelected),
pd.Amount,
pd.Code,
pd.Description,
pd.Points,
pd.PackageDetailsID
};
foreach (var result in linqResult)
{
packagesSelected.Add(new PackageDetailDataModel()
{
Amount = result.Amount,
Code = result.Code,
Description = result.Description,
IsComplete = result.IsComplete,
IsMandatory = result.IsMandatory,
PackageDetailsID = result.PackageDetailsID,
PackageSelectedID = result.PackageSelectedID,
Points = result.Points,
IsActive = result.IsActive,
UserID = result.UserID,
IsSelected = result.IsSelected
});
}
SQL generated by Visualizer
SELECT
`Extent1`.`PackageDetailsID`,
`Extent2`.`IsComplete`,
`Extent2`.`IsActive`,
`Extent2`.`UserID`,
`Extent1`.`IsMandatory`,
`Extent2`.`PackageSelectedID`,
`Extent2`.`IsSelected`,
`Extent1`.`Amount`,
`Extent1`.`Code`,
`Extent1`.`Description`,
`Extent1`.`Points`
FROM `tblpackagedetails` AS `Extent1`
LEFT OUTER JOIN `tblpackageselected` AS `Extent2`
ON (`Extent1`.`PackageDetailsID` = `Extent2`.`PackageDetailsID`)
AND ((`Extent2`.`UserID` = #linq_0) AND (1 = `Extent2`.`IsActive`))
When I ran above sql in MySQL workbench I got below output (repalcing #linq_0 with userID).
My Parent Table Structure
Child Table Structure
Output I want
But the values for IsComplete, IsActive, UserID, PackageSelectedID and IsSelected null as a result condition checking in select new block assign false or 0.
If I remove null checking, I get value for first 3 rows and in fourth iteration I get below exception.
The cast to value type 'Boolean' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type
Please help... :(
Working code block
packagesSelected = new List<PackageDetailDataModel>();
var linqResult = from pd in entities.tblpackagedetails
join ps in entities.tblpackageselecteds
on pd.PackageDetailsID equals ps.PackageDetailsID
into tabJoin
from tj in tabJoin.Where(ps => ps.UserID == userID
&& ps.IsActive == true).DefaultIfEmpty()
select new
{
IsComplete = (bool?)tj.IsComplete,
IsActive = (bool?)tj.IsActive,
UserID = (int?)tj.UserID,
IsMandatory = pd.IsMandatory,
PackageSelectedID = (int?)tj.PackageSelectedID,
IsSelected = (bool?)tj.IsSelected,
pd.Amount,
pd.Code,
pd.Description,
pd.Points,
pd.PackageDetailsID
};
foreach (var result in linqResult)
{
packagesSelected.Add(new PackageDetailDataModel()
{
Amount = result.Amount,
Code = result.Code,
Description = result.Description,
IsComplete = (result.IsComplete ?? false),
IsMandatory = result.IsMandatory,
PackageDetailsID = result.PackageDetailsID,
PackageSelectedID = (result.PackageSelectedID ?? 0),
Points = result.Points,
IsActive = (result.IsActive ?? false),
UserID = (result.UserID ?? 0),
IsSelected = (result.IsSelected ?? false)
});
}
Thanks to 2Kay :)
When tj is null, EF consieders all properties of tj as null. It's ok, but when EF trying to materialize them into value-types it fails. So the solution is to use nullable types..
Try this query:
var linqResult = from pd in entities.tblpackagedetails
join ps in entities.tblpackageselecteds
on pd.PackageDetailsID equals ps.PackageDetailsID
into tabJoin
from tj in tabJoin.Where(ps => ps.UserID == userID
&& ps.IsActive == true).DefaultIfEmpty()
select new
{
IsComplete = (bool?) tj.IsComplete,
IsActive = (bool?) tj.IsActive,
UserID = (int?) tj.UserID,
IsMandatory = pd.IsMandatory,
PackageSelectedID = (int?) tj.PackageSelectedID,
IsSelected = (bool?) tj.IsSelected,
pd.Amount,
pd.Code,
pd.Description,
pd.Points,
pd.PackageDetailsID
};

The type of one of the expressions in the join clause is incorrect. Types inference failed in the call to 'Group Join'

var val1 =
(from main in
(from irac in objIRAC
join iip in objIIP on irac.InvoiceItemID equals iip.InvoiceItemID
join oritc in
(from ritc in objRITC
join iips in objIIP on ritc.InvoiceItemPremiumID equals iips.InvoiceItemPremiumID
where ritc.ReconID == ReconId
&& ritc.BookOfBusinessInvoiceItemPremiumID == BookOfBusinessInvoiceItemPremiumID
&& ritc.InvoiceID == InvoiceId
group new { ritc, iips } by new { ritc.ReconID, iips.InvoiceItemID }
into ritcs
select new
{
ritcs.Key.ReconID,
ritcs.Key.InvoiceItemID
}) on ReconId equals oritc.ReconID into oritcs // ReconId
from oritc in oritcs.DefaultIfEmpty()
where iip.InvoiceItemPremiumID == BookOfBusinessInvoiceItemPremiumID
select new
{
oritc.InvoiceItemID,
irac.CommissionOnTypeID,
irac.BOBFamilyID,
irac.RateTypeID,
irac.Rate,
irac.CommissionAdjustmentFlag
})
join sub in objIRAC // Here getting the error
on new { main.InvoiceItemID, main.CommissionOnTypeID, main.BOBFamilyID, main.RateTypeID, main.Rate, main.CommissionAdjustmentFlag }
equals new { sub.InvoiceItemID, sub.CommissionOnTypeID, sub.BOBFamilyID, sub.RateTypeID, sub.Rate, sub.CommissionAdjustmentFlag }
into subs
from osub in subs.DefaultIfEmpty()
select new { osub.InvoiceItemID });
Check the types of
main.InvoiceItemID, main.CommissionOnTypeID, main.BOBFamilyID, main.RateTypeID, main.Rate, main.CommissionAdjustmentFlag
and
sub.InvoiceItemID, sub.CommissionOnTypeID, sub.BOBFamilyID, sub.RateTypeID, sub.Rate, sub.CommissionAdjustmentFlag
They should match exactly. That is: if one property is a Nullable<int> and the corresponding property is int it does not match. Since all properties but InvoiceItemID are from the same entity InvoiceItemID is probably the culprit.
Odds are one of your foreign key references are nullable, you'll need to do an explicit cast in order for the join (presuming inner join) to work.
var result =
from a in context.T1
join b in context.T2
on new { s.Field1, s.Field2 }
equals new {
Field1 = (int)b.T3.Field1 /* Join via sub-table, explicit conversion from int? to int */
,Field2 = (int)b.Field2 /* explicit conversion from int? to int */
}
where
b.T3.Field1 != null /* Specify no nulls allowed for inner-join fields */
&& b.Field2 != null
&& b.Field3 == InputVar1 /* Input Search Variables */
&& b.Field4 == InputVar2
select a;
The above example was renamed from an actual query I am using.

Resources