LINQ To SQL SubSelect Like Query - linq-to-sql

imagine there are two tables.
Order
+----------------+
| ID |
| Name |
+----------------+
OrderStatus
+----------------+
| ID |
| OrderId |
| StatusId |
+----------------+
A Order can have more than one OrderStatus, which could be called OrderStatusHistory.
I'll have an StronglyTypeObject Order, which is descripted as follows
namespace my.project
{
public class Order
{
Int64 OrderId { get; set; }
String Name { get; set; }
Int64 StatusId { get; set; }
}
}
This StatusId in the Order Object is meant to be the current (last) StatusId from the OrderStatus Table.
I have tried to build a IQueryable List of Objects with LINQ. Here is my, not working ;), Linq Code
var result = from r in dbContext.ORDER
select new Order
{
OrderId = r.ID,
Name = r.Name,
StatusId = dbContext.OrderStatus
.Where(p => p.OrderId == r.ID).Last().StatusId
}
I have also tried working with Max(p=>p.XXX) but it hasn't worked out.
Does anyone has a hint on this problem?
Any Help would be much appreciated...
Gordon

Based on your comment, I've updated the below to use First and, in that case, you will need to do an OrderByDescending on the key to get it in the right order.
var result = from r in dbContext.ORDER
select new Order
{
OrderId = r.ID,
Name = r.Name,
StatusId = dbContext.OrderStatus
.Where(p => p.OrderId == r.ID)
.OrderByDescending( p => p.ID )
.First()
.StatusId
}
Also, if you have a FK relationship defined, it should be much easier to get the last StatusId without creating an intermediate object. In this case, I think you can use Last (if the objects are preloaded) since you'll be doing LINQtoObjects, not LINQToSQL. YMMV.
var currentStatus = order.OrderStatuses.Last().StatusId;
The latter could be added as a method on a partial class for ORDER so that you can refer to it as.
var currentStatus = order.CurrentStatus;
public partial class ORDER
{
public int64 CurrentStatus
{
get
{
return this.OrderStatuses.Last().StatusId;
}
}
}

alright, how about this: (take two, order descending and take first .. top 1 )
var result = from r in dbContext.ORDER
select new Order
{
OrderId = r.ID,
Name = r.Name,
StatusId = dbContext.OrderStatus
.Where(p => p.OrderId == r.ID)
.OrderByDescending(p => p.OrderID)
.Select(p => p.StatusId)
.FirstOrDefault()
}

Related

Spring Boot: How to create dynamic query involving 'join' and 'group by' using predicate

My post request body will be like
{
"queryCondition":[
{
"filter":"status",
"filterlist":["Closed","New","Resolved"...]
},
{
"filter":"assigned_team",
"filterlist":["A","B","C"...]
},
{
"filter":"assigned_to",
"filterlist":["ram","govind","ajith"...]
},
{
"filter":"duration",
"filterlist":["2020-02-01","2020-05-01"....]
}
....
....
],
"durationField":"created_date"
}
I receive the columns(filter) and values(filterlist) dynamically with which I need to build this query.
SELECT * FROM tickets
WHERE ticket_id IN (SELECT ticket_id FROM Tickets WHERE created_date >= '2020-02-01') AND created_date '2020-05-01'
AND status IN ('Closed','Resolved','New')
AND assigned_team IN ('A' , 'B', 'C')
AND assigned_to IN ('ram','govind','ajith');
I built this query dynamically using Predicate and it is working fine.
#Override
public List<Tickets> conditionedQuery(QueryCondition queryCondition) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tickets> query = cb.createQuery(Tickets.class);
Root<Tickets> ticket = query.from(Tickets.class);
List<Predicate> predicatessub = new ArrayList<>();
for(FilterConditions fc:queryCondition.getQueryCondition()) {
if(fc.getFilter().equals("duration")) {
Predicate ps = cb.greaterThanOrEqualTo(ticket.get(queryCondition.getDurationField()), fc.getFilterlist()[0]);
Predicate pe = cb.lessThan(ticket.get(queryCondition.getDurationField()), fc.getFilterlist()[1]);
predicatessub.add(cb.and(ps,pe));
}else
{
List<Predicate> predicates = new ArrayList<>();
for(int i=0; i<fc.getFilterlist().length; i++) {
Predicate p = cb.equal(ticket.get(fc.getFilter()),fc.getFilterlist()[i]);
predicates.add(p);
}
predicatessub.add(cb.or(predicates.toArray(new Predicate[predicates.size()])));
}
}
query.select(ticket)
.where(cb.and(predicatessub.toArray(new Predicate[predicatessub.size()])));
return entityManager.createQuery(query)
.getResultList();
}
QueryCondition.class
public class QueryCondition {
private List<FilterConditions> filterCondition;
private String durationField;
}
FilterConditions.class
public class FilterConditions {
private String filter;
private String[] filterlist;
}
Now I would like to build a quite more complex query involving joins and group by. Below is the sample query like what I would like to build using predicate.
SELECT
YEAR(pt.created_date),
MONTH(pt.created_date),
pt.assigned_team,
COUNT(tk.ticket_id)
FROM
(SELECT
*
FROM
tickets
WHERE
ticket_id IN (SELECT
ticket_id
FROM
Tickets
WHERE
resolved_date >= '2020-02-01')
AND resolved_date < '2020-05-01'
and assigned_team IN ('A' , 'B', 'C')) pt
LEFT JOIN
(SELECT
*
FROM
tickets
WHERE
status IN ('Closed','Resolved','New')
AND assigned_to IN ('ram','govind','ajith')) tk ON tk.ticket_id = pt.ticket_id
GROUP BY YEAR(pt.created_date) , MONTH(pt.created_date), pt.assigned_team order by
pt.assigned_team,YEAR(pt.created_date),MONTH(pt.created_date) asc;
Kindly advise how this can be achieved with Predicate or is there any other simpler way than Predicate.
Maybe this works:
First create a list with all field your model but removing associations. After pass this list in CriteriaQuery.
List<Expression<?>> groupByList = new ArrayList<>();
// ticket => Root<Tickets>
ticket.getModel().getAttibutes().stream()
.filter(a -> !a.isAssociation())
.forEach(a -> groupByList.add(ticket.get(a.getName())));
query.select(ticket)
.where(cb.and(predicatessub.toArray(new Predicate[predicatessub.size()])))
.groupBy(groupByList); // <== add group by
If you have join just do the same thing creating a cast with object EntityTypeImpl<?>. ex:
// import org.hibernate.metamodel.model.domain.internal.EntityTypeImpl
// join => Join<?,?>
((EntityTypeImpl<MyEntity>) join.getModel())
.getDeclaredAttributes().stream()
.filter(a -> !a.isAssociation())
.forEach(a -> groupByList.add(join.get(a.getName())));

Convert complex nested selects in Entity Framework query

I need to create a table in View by this View Model:
public class ApplicationContentViewModel
{
public BPMSPARS.Models.MySql.application application {get; set;}
public BPMSPARS.Models.MySql.content content { get; set; }
public BPMSPARS.Models.MySql.app_delegation app_delegation { get; set; }
}
But the query for creating new Table is very complex.
I use this query in MySQL, and I can get correct results by using it.
SELECT APP_UID, (SELECT CON_VALUE FROM content WHERE CON_CATEGORY = 'PRO_TITLE' AND CON_ID =
(SELECT PRO_UID from app_delegation WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219')) AS TASK_NAME,
(SELECT CON_VALUE FROM content WHERE CON_CATEGORY = 'TAS_TITLE' AND CON_ID =
(SELECT TAS_UID from app_delegation WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219')) AS PROCESS_NAME FROM app_delegation
WHERE del_thread_status='open' and USR_UID = '00000000000000000000000000000001' AND APP_UID = '9134216305aaaea1b67c4e2096663219'
But, I have to convert this query in linq or EF in MVC.
How Can I write This Query in Entity Framework query?
And How Can I display results in View?
Your SQL query seems (very) peculiar to me, as it is quite redundant. I am going to assume the sub-queries return a single value and enforce it with LINQ.
First I pulled out the common sub-query over app_delegation:
var USR_APP_Delegation = from a in app_delegation
where a.del_thread_status == "open" &&
a.USR_UID == "00000000000000000000000000000001" &&
a.APP_UID == "9134216305aaaea1b67c4e2096663219"
select a;
In LINQ it is easy to combine the two UID queries into one query:
var UIDs = (from a in USR_APP_Delegation
select new { a.PRO_UID, a.TAS_UID })
.Single();
Now you can do the name subqueries:
var TASK_NAME = (from c in content
where c.CON_CATEGORY == "PRO_TITLE" &&
c.CON_ID == UIDs.PRO_UID
select c.CON_VALUE)
.Single();
var PROCESS_NAME = (from c in content
where c.CON_CATEGORY == "TAS_TITLE" &&
c.CON_ID == UIDs.TAS_UID
select c.CON_VALUE)
.Single();
Then you can put all the queries together for the final result:
var ans = (from a in USR_APP_Delegation
select new {
a.APP_UID,
TASK_NAME,
PROCESS_NAME
})
.Single();
Again, this makes it obvious that your e.g. returning APP_UID when you know exactly what it is, and you are combining TASK_NAME and PROCESS_NAME into a query for no real advantage.
I would suggest using join against content makes a much more understandable query (even in SQL) and makes it clearer what is being returned:
var names = from a in app_delegation
join cpro in content on new { CON_ID = a.PRO_UID, CON_CATEGORY = "PRO_TITLE" } equals new { cpro.CON_ID, cpro.CON_CATEGORY }
join ctas in content on new { CON_ID = a.PRO_UID, CON_CATEGORY = "TAS_TITLE" } equals new { ctas.CON_ID, ctas.CON_CATEGORY }
where a.del_thread_status == "open" &&
a.USR_UID == "00000000000000000000000000000001" &&
a.APP_UID == "9134216305aaaea1b67c4e2096663219"
select new {
a.APP_UID,
Task_Name = ctas.CON_VALUE,
Process_Name = cpro.CON_VALUE
};

PetaPoco is mapping 0 for every Id in my object?

I have the following object:
public class VehicleMake
{
[Column(Name = "MakeID")]
public int Id { get; set; }
public string Name { get; set; }
}
To get a list of vehicle makes, I am doing the following:
var vehicleMakes = _db.Fetch<VehicleMake>(#"SELECT DISTINCT(m.MakeID) AS Id, m.Name
FROM vehicles v
INNER JOIN makes m on m.MakeID = v.Make
WHERE [Year] = #year
ORDER BY m.Name", new { year });
When I run the SQL in SSMS, it returns the correct data, but in VS, it is mapping 0 for every Id property.
Ah, I got it, in case anyone else runs into the problem. Since I am doing:
SELECT DISTINCT(m.MakeID) as Id..., there is no reason to have:
[Column(Name = "MakeID")] since I am aliasing the column as Id already.

mapping a linq to sql view result to entities - how to change the group by clause?

Assume that I have a very simple db diagram:
and a view for it:
create view vTraining
as
select t.Id as TrainingId,t.[Status] ,
t.[User], t.Title,t.Organisation,t.[Month],t.[Year],
s.Id as SubjectId, s.Name as SubjectName,
c.Text as Comment
from Training t
join Subject s on s.Training = t.Id
join Comment c on c.Training = t.Id
with sample data:
As you can see, this is a single training with three subjects.
I want to map this result to this structure by linq to sql:
public class ViewModel
{
public string Comment { set; get; }
public List<Item> Trainings { set; get; }
}
public class Item
{
public int TrainingId { set; get; }
public int User { set; get; }
public int Status { set; get; }
public string Title { set; get; }
public string Organisation { set; get; }
public int? Month { set; get; }
public int Year { set; get; }
public List<KeyValuePair<int, string>> Subjects { set; get; }
}
This is my query, that I created:
var data = (from training in dc.vTrainings
group training by new
{
training.TrainingId,
training.Status,
training.Month,
training.Organisation,
training.Title
}
into g
select new ViewModel()
{
Comment = g.Select(x =>
x.Comment).First(),
Trainings = g.Select(
x => new Item()
{
Month = x.Month,
Organisation = x.Organisation,
Title = x.Title,
Year = x.Year,
Subjects = g.Select(
z => new KeyValuePair<int, string>(z.SubjectId, z.SubjectName)).ToList()
}).ToList()
})//.GroupBy(x => x.Trainings).Select(x => x.Key)
.ToList();
Unfortunatelly the result I get is not the one I want:
The ViewModel object is created only ones what is ok, but for each single subject, the new Item is created (should be one). The Subjets list is created correctly. I tried to create a second group by, and some other things, but this is the best result I can get for now.
How to write this query to get one ViewModel object which has one Item object with three subjects?
I finally got my the proper result:
var data = (from g in
(from training in dc.vTrainings
where training.Status ==1
group training
by new
{
training.TrainingId,
training.Status,
training.Month,
training.Organisation,
training.Title
}
into g
select g).AsEnumerable()
select new ViewModel()
{
Comment = g.Select(x =>
x.Comment).FirstOrDefault(),
Trainings = g.GroupBy(x => x.Status).Select(
x => new Item()
{
Month = g.Key.Month,
Organisation = g.Key.Organisation,
Title = g.Key.Title,
Subjects = (from i in g select new KeyValuePair<int, string>(i.SubjectId, i.SubjectName)).ToList()
}).ToList()
}).ToList();
This query works only for one training, but for me this is not a problem, because I filtering it by the newest status. Still I'm curious how to write it for more than one training.
Try this:
var trainings =
dc.vTrainings
.GroupBy(
t => new
{
t.TrainingId,
t.Status,
t.Month,
t.Organisation,
t.Title,
t.User,
t.Year
},
t =>
new
{
t.SubjectId,
t.SubjectName
})
.ToList()
.Select(
t =>
new Item
{
TrainingId = t.Key.TrainingId,
Status = t.Key.Status,
Month = t.Key.Month,
Organisation = t.Key.Organisation,
Title = t.Key.Title,
User = t.Key.User,
Year = t.Key.Year,
Subjects =
t.Select(s => new KeyValuePair<int,string>(s.SubjectId,s.SubjectName)).ToList()
});

LINQ-to-SQL load keyword from variable

public IQueryable<ArticleDisplay> SearchNumberOfArticles(int articleNr, string order)
var result = (
from category in db.ArticleCategories
join article in db.Articles on category.CategoryID equals article.CategoryID
orderby article.Date order
select new ArticleDisplay
{
CategoryID = category.CategoryID,
CategoryTitle = category.Title,
ArticleID = article.ArticleID,
ArticleTitle = article.Title,
ArticleDate = article.Date,
ArticleContent = article.Content
}
).Take(articleNr);
In PHP this would work, but in C# it doesn't. So, how to load keyword from variable and "print" it inside query? Here is order suppose to be replaced with descending or ascending.
Thanks
You can use an if statement
IQueryable<ArticleDisplay> SearchNumberOfArticles(int articleNr, string order)
{
................
var articles = from category in db.ArticleCategories
join article in db.Articles
on category.CategoryID equals article.CategoryID
select article;
if (string.IsNullOrEmpty(order) || order == "ascending" || order = "asc")
{
articles = articles.OrderBy(a => a.Date).Take(articleNr);
}
else
{
articles = articles.OrderByDescending(a => a.Date).Take(articleNr);
}
.............
}