Conditional apex query - apex

I would like to return an SOQL query based on a conditional statement.
my code looks the following:
string FullQuery = '';
if(selectedRegion == 'All'){
FullQuery = 'select id, name, Region__r.Id, Region__r.Name, CreatedDate from Country__c where Id in (select country_link__c from Market__c where Focus_Year__c = True)';
} else {
FullQuery = 'select id, name, Region__r.Id, Region__r.Name, CreatedDate from Country__c where Id in (select country_link__c from Market__c where Focus_Year__c = True) and Region__r.Id == :selectedRegion';
}
List<Country__c> RelevantCountryList2 = Database.query(FullQuery);
system.debug(RelevantCountryList2);
When I do that I get an error : FATAL_ERROR System.QueryException: unexpected token: '=='
any idea what am I doing wrong ?

This is occurring because of second query in which you are using ==
FullQuery = 'select id, name, Region__r.Id, Region__r.Name, CreatedDate from Country__c where Id in (select country_link__c from Market__c where Focus_Year__c = True) and Region__r.Id == :selectedRegion';
instead of this use single =
FullQuery = 'select id, name, Region__r.Id, Region__r.Name, CreatedDate from Country__c where Id in (select country_link__c from Market__c where Focus_Year__c = True) and Region__r.Id = :selectedRegion';
Thanks

Related

Linq query select property list with join

I'm trying to get a list in a linq with joins and get all events for specific user.
This is the query I have so far:
var runnerObject = from r in _context.Runners
join re in _context.RunnerEvents
on r.RunnerId equals re.RunnerId
join e in _context.Events
on re.EventId equals e.EventId
where r.RunnerId == runnerId
select new RunnerVM
{
RunnerId = r.RunnerId,
FirstName = r.FirstName,
LastName = r.LastName,
UserId = r.UserId,
Events = //get all events in Events table for the runnerId
};
Events should be all entries from Events table for that runner, based on their id which is joined in the RunnerEvents table. How can I get that?
Something like this?
var runnerObject = from r in _context.Runners
join re in _context.RunnerEvents
on r.RunnerId equals re.RunnerId
join e in _context.Events
on re.EventId equals e.EventId
where r.RunnerId == runnerId
select new RunnerVM
{
RunnerId = r.RunnerId,
FirstName = r.FirstName,
LastName = r.LastName,
UserId = r.UserId,
Events = r.Events.Select(e => new Event { }).ToList()
};

LINQ - Conditional Join

I have a condition where joining table have a condition. Let's say I have a table called Mapper Student Teacher where Mapper table have a column named AcNoId which contains a id from both table Student and Teacher. The table structure is
Mapper
Student
Teacher
TestOption is a enum and is defined as a
public enum TestOption
{
Teacher = 1,
Student = 2
}
Now I have a condition where if TestOption is a type of Student it should perform a join with Student table and if is a type of Teacher it should perform a join with Teacher table
This is how I have tried so far
(from m in _context.Mapper
where m.TestOption == TestOption.Student
join s in _context.Student
on m.AcNoId equals s.Id into tempStudent
from st in tempStudent.DefaultIfEmpty()
where m.TestOption == TestOption.Teacher
join t in _context.Teacher
on m.AcNoId equals t.Id into tempTeacher
from ta in tempTeacher.DefaultIfEmpty()
select new
{
Type = m.TestOption.ToString(),
Student = st.StudentName ?? string.Empty,
Teacher = ta.TeacherName ?? string.Empty
}).ToList();
Instead of conditional join this query perform a following query on SQL Profiler
exec sp_executesql N'SELECT [m].[TestOption], COALESCE([s].[StudentName], #__Empty_0) AS [Student], COALESCE([t].[TeacherName], #__Empty_1) AS [Teacher]
FROM [Mapper] AS [m]
LEFT JOIN [Student] AS [s] ON [m].[AcNoId] = [s].[Id]
LEFT JOIN [Teacher] AS [t] ON [m].[AcNoId] = [t].[Id]
WHERE ([m].[TestOption] = 2) AND ([m].[TestOption] = 1)',N'#__Empty_0 nvarchar(4000),#__Empty_1 nvarchar(4000)',#__Empty_0=N'',#__Empty_1=N''
How can I do this????
You can use the below code, with no need to use join or where statements on _context.Mapper:
(from m in _context.Mapper
select new
{
Type = m.TestOption.ToString(),
Student = _context.Student
.FirstOrDefault(s =>
m.TestOption == TestOption.Student &&
s.Id == m.AcNoId) ?? string.Empty,
Teacher = _context.Teacher
.FirstOrDefault(t =>
m.TestOption == TestOption.Teacher &&
t.Id == m.AcNoId) ?? string.Empty,
})
.ToList();

EF Core 2.1, linq query not producing group by sql query and I can see just Order By at last

I have a linq query which has Group By clause, but the Group By is not happening on sql server.
I tried a simple query and the Group By is happening on sql server.
Please guide me why this different behavior??
I want that group-by on server for performance improvement.
Simple query where I get group-by if I log the sql query:
var testt = (from doc in _patientRepository.Documents
group doc by doc.DocumentType into G
select new
{
Key = G.Key
}).ToList();
Generated sql:
Executed DbCommand (247ms) [Parameters=[], CommandType='Text',
CommandTimeout='30']
SELECT [doc].[DocumentType] AS [Key]
FROM [Document] AS [doc]
GROUP BY [doc].[DocumentType]
Issue query:
var patX = (from doc in _patientRepository.Documents
join pat in _patientRepository.Patients
on doc.PatientId.ToString().ToLower() equals pat.PatientId.ToString().ToLower()
where doc.Source.ToLower() != "testclient.server.postman" &&
pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
select new Document()
{
DocumentId = doc.DocumentId,
CreationDateTime = doc.CreationDateTime,
DocumentType = doc.DocumentType,
PatientId = doc.PatientId,
DocumentTypeVersion = doc.DocumentTypeVersion,
Source = doc.Source,
PayloadLeft = DocumentMapper.DeserializePayload(doc.PayloadLeft),
PayloadRight = DocumentMapper.DeserializePayload(doc.PayloadRight),
PayloadBoth = DocumentMapper.DeserializePayload(doc.PayloadBoth),
IsSalesforceSynced = doc.IsSalesforceSynced,
HcpId = pat.HcpId
}).GroupBy(p => new { p.PatientId, p.DocumentType })
.Select(g => g.OrderByDescending(p => p.CreationDateTime).FirstOrDefault())
.Where(x => x.IsSalesforceSynced == false)
.ToList();
Why don't it have group-by sql generated:
Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (200ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [doc].[DocumentId], [doc].[CreationDateTime], [doc].[DocumentType], [doc].[PatientId], [doc].[DocumentTypeVersion], [doc].[Source], [doc].[PayloadLeft], [doc].[PayloadRight], [doc].[PayloadBoth], [doc].[IsSalesforceSynced], [pat].[HcpId]
FROM [Document] AS [doc]
INNER JOIN [Patient] AS [pat] ON LOWER(CONVERT(VARCHAR(36), [doc].[PatientId])) = LOWER(CONVERT(VARCHAR(36), [pat].[PatientId]))
WHERE ((LOWER([doc].[Source]) <> N'testclient.server.postman') AND ([pat].[Deleted] = 0)) AND LOWER([pat].[HcpId]) IN (N'4e7103a9-7dff-4fa5-b540-a32a31be2997', N'abc1', N'def2', N'ghi3')
ORDER BY [doc].[PatientId], [doc].[DocumentType]
I tried below approach but same sql generated:
var patX = ((from doc in _patientRepository.Documents
join pat in _patientRepository.Patients
on doc.PatientId.ToString().ToLower()
equals pat.PatientId.ToString().ToLower()
where doc.Source.ToLower() != "testclient.server.postman" &&
pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
select new Document()
{
DocumentId = doc.DocumentId,
CreationDateTime = doc.CreationDateTime,
DocumentType = doc.DocumentType,
PatientId = doc.PatientId,
DocumentTypeVersion = doc.DocumentTypeVersion,
Source = doc.Source,
PayloadLeft = DocumentMapper.DeserializePayload(doc.PayloadLeft),
PayloadRight = DocumentMapper.DeserializePayload(doc.PayloadRight),
PayloadBoth = DocumentMapper.DeserializePayload(doc.PayloadBoth),
IsSalesforceSynced = doc.IsSalesforceSynced,
HcpId = pat.HcpId
}).GroupBy(p => new { p.PatientId, p.DocumentType })
.Select(g => g.OrderByDescending(p => p.CreationDateTime).FirstOrDefault())
.Where(x => x.IsSalesforceSynced == false))
.ToList();
Before version 2.1, in EF Core the GroupBy LINQ operator would always be evaluated in memory.Now support translating it to the SQL GROUP BY clause in most common cases.
change code : try to place .GroupBy before .select method(first select)
Consider re-ordering the query so the select to the new class is last:
var p1 = from doc in _patientRepository.Documents
join pat in _patientRepository.Patients on doc.PatientId.ToString().ToLower() equals pat.PatientId.ToString().ToLower()
where doc.Source.ToLower() != "testclient.server.postman" && pat.Deleted == false && sfHCPs.Contains(pat.HcpId.ToLower())
group new { doc, pat.HcpId } by new { doc.PatientId, doc.DocumentType } into dpg
select dpg.OrderByDescending(dp => dp.doc.CreationDateTime).FirstOrDefault();
var patX = (from dp in p1
where !dp.doc.IsSalesforceSynced
select new Document() {
DocumentId = dp.doc.DocumentId,
CreationDateTime = dp.doc.CreationDateTime,
DocumentType = dp.doc.DocumentType,
PatientId = dp.doc.PatientId,
DocumentTypeVersion = dp.doc.DocumentTypeVersion,
Source = dp.doc.Source,
PayloadLeft = DocumentMapper.DeserializePayload(dp.doc.PayloadLeft),
PayloadRight = DocumentMapper.DeserializePayload(dp.doc.PayloadRight),
PayloadBoth = DocumentMapper.DeserializePayload(dp.doc.PayloadBoth),
IsSalesforceSynced = dp.doc.IsSalesforceSynced,
HcpId = dp.HcpId
})
.ToList();

Entity Framework - Select * from Entities where Id = (select max(Id) from Entities)

I have an entity set called Entities which has a field Name and a field Version. I wish to return the object having the highest version for the selected Name.
SQL wise I'd go
Select *
from table
where name = 'name' and version = (select max(version)
from table
where name = 'name')
Or something similar. Not sure how to achieve that with EF. I'm trying to use CreateQuery<> with a textual representation of the query if that helps.
Thanks
EDIT:
Here's a working version using two queries. Not what I want, seems very inefficient.
var container = new TheModelContainer();
var query = container.CreateQuery<SimpleEntity>(
"SELECT VALUE i FROM SimpleEntities AS i WHERE i.Name = 'Test' ORDER BY i.Version desc");
var entity = query.Execute(MergeOption.OverwriteChanges).FirstOrDefault();
query =
container.CreateQuery<SimpleEntity>(
"SELECT VALUE i FROM SimpleEntities AS i WHERE i.Name = 'Test' AND i.Version =" + entity.Version);
var entity2 = query.Execute(MergeOption.OverwriteChanges);
Console.WriteLine(entity2.GetType().ToString());
Can you try something like this?
using(var container = new TheModelContainer())
{
string maxEntityName = container.Entities.Max(e => e.Name);
Entity maxEntity = container.Entities
.Where(e => e.Name == maxEntityName)
.FirstOrDefault();
}
That would select the maximum value for Name from the Entities set first, and then grab the entity from the entity set that matches that name.
I think from a simplicity point of view, this should be same result but faster as does not require two round trips through EF to sql server, you always want to execute query as few times as possible for latency, as the Id field is primary key and indexed, should be performant
using(var db = new DataContext())
{
var maxEntity = db.Entities.OrderByDecending(x=>x.Id).FirstOrDefault()
}
Should be equivalent of sql query
SELECT TOP 1 * FROM Entities Order By id desc
so to include search term
string predicate = "name";
using(var db = new DataContext())
{
var maxEntity = db.Entities
.Where(x=>x.Name == predicate)
.OrderByDecending(x=>x.Id)
.FirstOrDefault()
}
I think something like this..?
var maxVersion = (from t in table
where t.name == "name"
orderby t.version descending
select t.version).FirstOrDefault();
var star = from t in table
where t.name == "name" &&
t.version == maxVersion
select t;
Or, as one statement:
var star = from t in table
let maxVersion = (
from v in table
where v.name == "name"
orderby v.version descending
select v.version).FirstOrDefault()
where t.name == "name" && t.version == maxVersion
select t;
this is the easiest way to get max
using (MyDBEntities db = new MyDBEntities())
{
var maxReservationID = _db .LD_Customer.Select(r => r.CustomerID).Max();
}

How to use NHibernate's ICriteria for grouping, fetching associations and T-SQL functions

I want to create the following T-SQL statement:
SELECT SUM (sa.Amount) as 'SumAmount',
SUM(sa.Cost) as 'SumCost',
gg.[Description] as 'Goodsgroup', Month(sa.[Date]) as 'Month'
FROM SalesmanArticle sa
INNER JOIN Article a
ON a.ArticleId = sa.ArticleId
INNER JOIN GoodsGroup gg
ON gg.GoodsGroupId = a.GoodsGroupId
GROUP BY gg.[Description], Month(sa.[Date])
ORDER BY 'Month', 'Goodsgroup'
Is this possible with NHibernates ICriteria?
How can I use the Month-T-SQL-Function?
Do I have to join manually or does the ICriteria API knows that when I use the propetyName 'SalesmanArticle.Article.Goodsgroup.Description' it has to join the Article and the Goodsgroup?
EDIT:
For now I have written this code here:
// typesafe properties
string article = typeof(Article).Name;
string goodsGroup = typeof(GoodsGroup).Name;
string salesmanArticle = typeof(SalesmanArticle).Name;
string amount = Reflector.GetPropertyName<SalesmanArticle>(x => x.Amount);
string cost = Reflector.GetPropertyName<SalesmanArticle>(x => x.Cost);
string description = string.Format("{0}.{1}",
goodsGroup, Reflector.GetPropertyName<SalesmanArticle>(x => x.Article.GoodsGroup.Description));
string date = Reflector.GetPropertyName<SalesmanArticle>(x => x.Date);
string formatedDate = string.Format("MONTH([{0}])", date);
return GetSession()
// FROM
.CreateCriteria(typeof(SalesmanArticle), salesmanArticle)
// JOIN
.CreateCriteria(article, article, JoinType.InnerJoin)
.CreateCriteria(goodsGroup, goodsGroup, JoinType.InnerJoin)
// SELECT
.SetProjection(Projections.ProjectionList()
.Add(Projections.Sum(amount))
.Add(Projections.Sum(cost))
// GROUP BY
.Add(Projections.GroupProperty(description))
.Add(Projections.SqlGroupProjection(formatedDate, formatedDate, new[]{"MyDate"} , new[] { NHibernateUtil.Int32 })))
.List();
But an AdoException is thrown:
could not execute query [ SELECT
sum(this_.Amount) as y0_,
sum(this_.Cost) as y1_,
goodsgroup2_.Description as y2_,
MONTH([Date]) FROM [SalesmanArticle]
this_ inner join [Article] article1_
on this_.ArticleId=article1_.ArticleId
inner join [GoodsGroup] goodsgroup2_
on
article1_.GoodsGroupId=goodsgroup2_.GoodsGroupId
GROUP BY goodsgroup2_.Description,
MONTH([Date]) ]
[SQL: SELECT
sum(this_.Amount) as y0_,
sum(this_.Cost) as y1_,
goodsgroup2_.Description as y2_,
MONTH([Date]) FROM [SalesmanArticle]
this_ inner join [Article] article1_
on this_.ArticleId=article1_.ArticleId
inner join [GoodsGroup] goodsgroup2_
on
article1_.GoodsGroupId=goodsgroup2_.GoodsGroupId
GROUP BY goodsgroup2_.Description,
MONTH([Date])]
The strange thing is that NHibernate tries to create 2 queries?!
AND BOTH of them are correct!
Instead of the codeline
.Add(Projections.SqlGroupProjection(formatedDate, formatedDate, new[]{"MyDate"} , new[] { NHibernateUtil.Int32 })))
I used
.Add(Projections.SqlFunction("MONTH", NHibernateUtil.Int32, Projections.GroupProperty(date))))
The problem with the SqlFunction is that it creates a GROUP BY sa.Date instead of MONTH(sa.Date). But this method worked syntactically correct.
So I switched to the SqlGroupProjection method.
But anyway it does not work.
Can anybody help me?
I solved it. Here is the correct code:
public class SalesmanArticleRepository : Repository<SalesmanArticle>, ISalesmanArticleRepository
{
public IList GetAllAll()
{
// typesafe properties
string article = typeof(Article).Name;
string goodsGroup = typeof(GoodsGroup).Name;
string salesmanArticle = typeof(SalesmanArticle).Name;
string amount = Reflector.GetPropertyName<SalesmanArticle>(x => x.Amount);
string cost = Reflector.GetPropertyName<SalesmanArticle>(x => x.Cost);
string description = string.Format("{0}.{1}",
goodsGroup, Reflector.GetPropertyName<SalesmanArticle>(x => x.Article.GoodsGroup.Description));
string date = Reflector.GetPropertyName<SalesmanArticle>(x => x.Date);
string formatedDateSql = string.Format("month({{alias}}.[{0}]) as mydate", date);
string formatedDateGroupBy = string.Format("month({{alias}}.[{0}])", date);
return GetSession()
// FROM
.CreateCriteria(typeof(SalesmanArticle), salesmanArticle)
// JOIN
.CreateCriteria(article, article, JoinType.InnerJoin)
.CreateCriteria(goodsGroup, goodsGroup, JoinType.InnerJoin)
// SELECT
.SetProjection(Projections.ProjectionList()
.Add(Projections.Sum(amount))
.Add(Projections.Sum(cost))
// GROUP BY
.Add(Projections.GroupProperty(description))
.Add(Projections.SqlGroupProjection(formatedDateSql, formatedDateGroupBy, new[] { "mydate" }, new[] { NHibernateUtil.Int32 })))
.List();
}
}