I'm working on creating a dynamic linq expression with an optional where clause containing "exists".
Almost everything about this can be dynamic. The user can choose to filter only root level nodes or filter both the root and all descendant nodes. I've mocked up a working example of one of the paths this query takes that is giving me issues.
Example of T-SQL:
SELECT 'Child exists', ge.id AS [run_id], aseq.id, aseq.name
FROM group_execution ge
JOIN automation_sequences aseq ON (aseq.id=ge.automation_sequence_id)
WHERE EXISTS
(SELECT ge2.patriarch_id FROM group_execution ge2
JOIN automation_sequences aseq2 ON (aseq2.id=ge2.automation_sequence_id)
WHERE aseq2.name LIKE '%test%'
AND ge2.patriarch_id = ge.patriarch_id
UNION ALL
SELECT ase.patriarch_id FROM automation_sequence_executions ase
JOIN automation_sequences aseq2 ON (aseq2.id=ase.automation_sequence_id)
WHERE aseq2.name LIKE '%test%'
AND ase.patriarch_id = ge.patriarch_id)
The problem I'm having is LINQ is giving me an execution plan that looks like this:
SELECT 'Child exists', ge.id AS [run_id], aseq.id, aseq.name
FROM group_execution ge
JOIN automation_sequences aseq ON (aseq.id=ge.automation_sequence_id)
WHERE EXISTS
(SELECT 1
FROM
(SELECT ase.patriarch_id FROM automation_sequence_executions ase
JOIN automation_sequences aseq2 ON (aseq2.id=ase.automation_sequence_id)
WHERE aseq2.name LIKE '%test%'
UNION ALL
SELECT ge2.patriarch_id FROM group_execution ge2
JOIN automation_sequences aseq2 ON (aseq2.id=ge2.automation_sequence_id)
WHERE aseq2.name LIKE '%test%'
) AS sub
WHERE sub.patriarch_id = ge.patriarch_id)
Notice how it's putting the "where" clause outside the union. This plan is killing the performance of my query. The first query runs in less than a second but the second takes up to 30 seconds.
I'm having a ton of trouble figuring out how to write the LINQ expression to mimic the first query I have above.
Below is my code so far:
//creates first query from group_execution table and does joins
IQueryable<Contract_SeqExecution> result = this.queryGroups(context);
...
//query for children:
IQueryable<ATSM_DAL.App_Data.automation_sequence_executions> tcChildrenRuns = null;
IQueryable<ATSM_DAL.App_Data.group_execution> groupChildrenRuns = null;
if (queryTestCase)
tcChildrenRuns = (from ase in context.automation_sequence_executions
where ase.parent_group_exec_id != null
select ase);
if (queryGroup)
groupChildrenRuns = (from ge in context.group_execution
where ge.parent_group_exec_id != null
select ge);
//Perform Union All on child results
decendantResults = this.UnionAllTCGroups(tcChildrenRuns, groupChildrenRuns);
//handles dynamic filter, example: aseq2.name LIKE '%test%'
decendantResults = decendantResults.Where(filterExpression);
...
//Perform the EXISTS statement in where clause. This is the problem code
Expression<Func<Contract_SeqExecution, bool>> childFilter = r => decendantResults.AsExpandable().Any(c => c.patriarchId == r.patriarchId);
childFilter = childFilter.Or(filterExpression);
result = result.AsExpandable().Where(childFilter);
Related
Looks like I found a bug:
I have the tables
Users 1 <---> * Customers
and now I want to get the specific type of the Users, whose Customers contain some text in the Name or Surname column:
var result = (from x in Query()
where x.UserTypeID == 123
&&
(
x.Customer.Name.Contains(myString)
||
x.Customer.Surname.Contains(myString)
)
select x)
.ToList();
I ran by myself the SQL query generated by that code and I saw some rows exist. But, the compiler returns an empty list. Why ?
This is the generated SQL query
SELECT
`Filter1`.`ID`,
`Filter1`.`UserTypeID`,
`Filter1`.`CustomerID`
FROM (
SELECT
`UL`.`ID`,
`UL`.`UserTypeID`,
`UL`.`CustomerID`
FROM `Users` AS `UL`
LEFT OUTER JOIN `Customer` AS `CUST` ON `UL`.`CustomerID` = `CUST`.`ID`
WHERE `UL`.`UserTypeID` = 123
) AS `Filter1`
LEFT OUTER JOIN `Customer` AS `Extent3` ON `Filter1`.`CustomerID` = `Extent3`.`ID`
WHERE (`Filter1`.`Name` LIKE '%p__linq__0%') OR (`Extent3`.`Surname` LIKE '%p__linq__1%')
I changed the p__linq__0 and p__linq__1 to e.g. a and I saw some results
I have a SQL query that I am trying to convert to LINQ:
SELECT * FROM TABLE1
WHERE LICENSE_RTK NOT IN(
SELECT KEY_VALUE FROM TABLE2
WHERE REFERENCE_RTK = 'FOO')
So I wrote one query for inner query and then one query for the outer one and used Except:
var insideQuery = (from pkcr in this.Repository.Context.TABLE2 where pkcr.Reference_RTK == "FOO" select pkcr.Key_Value);
var outerQuery = (from pl in this.Repository.Context.TABLE1 select pl).Except(insideQuery);
But this is wrong. Cannot even compile it. What is the correct way of writing this?
You cannot compile second query, because Except should be used on Queryables of same type. But you are trying to apply it on Queryable<TABLE1> and Queryable<TypeOfTABLE2Key_Value>. Also I think you should use Contains here:
var keys = from pkcr in this.Repository.Context.TABLE2
where pkcr.Reference_RTK == "FOO"
select pkcr.Key_Value;
var query = from pl in this.Repository.Context.TABLE1
where !keys.Contains(pl.License_RTK)
select pl;
NOTE: Generated query will be NOT EXISTS instead of NOT IN, but that's what you want
SELECT * FROM FROM [dbo].[TABLE1] AS [Extent1]
WHERE NOT EXISTS
(SELECT 1 AS [C1]
FROM [dbo].[TABLE2] AS [Extent2]
WHERE ([Extent2].[Reference_RTK] == #p0) AND
([Extent2].[Key_Value] = [Extent1].[License_RTK]))
This might be one of those situations where plain SQL commands are better than LINQ. Here's a simplified version of the SQL statement I'm trying to translate:
SELECT * FROM IDTable AS idt
INNER JOIN NameTable AS nt ON nt.IDTableID=idt.Id
AND nt.Id= (SELECT TOP(1) Id
FROM NameTable AS nt2
WHERE nt2.IDTableID=11 ORDER BY nt2.DateInserted DESC)
I have the LINQ query to pull records when just joining on IDs and I've seen how to join on multiple columns, but I have no idea how to plug the subquery into the mix.
If this isnt entirely clear, please let me know and I'll edit to elaborate.
Maybe something like this?
var results = from id in db.IDTable
join n in db.NameTable on id.Id equals n.IDTableID
where n.Id = (
from n2 in db.NameTable
where n2.IDTableID = 11
orderby n2.DateInserted desc
).First()
select new { id, n };
I'd like to reproduce the following SQL into C# LinqToSql
SELECT TOP(10) Keywords.*
FROM Keywords
LEFT OUTER JOIN IgnoreWords
ON Keywords.WordID = IgnoreWords.ID
WHERE (DomainID = 16673)
AND (IgnoreWords.Name IS NULL)
ORDER BY [Score] DESC
The following C# Linq gives the right answer.
But I can't help think I'm missing something (a better way of doing it?)
var query = (from keyword in context.Keywords
join ignore in context.IgnoreWords
on keyword.WordID equals ignore.ID into ignored
from i in ignored.DefaultIfEmpty()
where i == null
where keyword.DomainID == ID
orderby keyword.Score descending
select keyword).Take(10);
the SQL produced looks something like this:
SELECT TOP (10)
[t0].[DomainID]
, [t0].[WordID]
, [t0].[Score]
, [t0].[Count]
FROM [dbo].[Keywords] AS [t0]
LEFT OUTER JOIN
( SELECT 1 AS [test]
, [t1].[ID]
FROM [dbo].[IgnoreWords] AS [t1]
) AS [t2]
ON [t0].[WordID] = [t2].[ID]
WHERE ([t0].[DomainID] = 16673)
AND ([t2].[test] IS NULL)
ORDER BY [t0].[Score] DESC
How can I get rid of this redundant inner selection?
It's only slightly more expensive but every bit helps!
I think you can do something like this to eliminate the left join and maybe get more efficiency:
var query = (from keyword in context.Keywords
where keyword.DomainID == ID
&& !(from i in context.IgnoreWords select i.ID).Contains(keyword.WordID)
orderby keyword.Score descending
select keyword)
.Take(10);
I have this hql query, which works perfect:
select m
from Media m
join m.Productlines p
join m.Categories c
join m.Spaces sp
join m.Solutions so
where m.Uid != 0
and p.Uid in (:productlines)
and c.Uid in (13)
and sp.Uid in (52)
and so.Uid in (15,18)
group by m.Uid
But now it needs to be parameterized/made dynamic, not only the parameters, but also the joins (it is possible to select only from Media, without any joins, and so no *.Uid in will be required in this case).
I dont want to mess around with a StringBuilder instance and build the hql query that way, I would rather like to use the Criteria API, but I cant get a
SELECT m.*
....
GROUP BY m.Uid
query to work with Criteria.
If I add a
Projections.GroupProperty("Uid")
to my query, nhibernate selects
SELECT m.Uid
....
GROUP BY m.Uid
which is of course wrong.
After that, I also need to count the unique rows the query returned, as the result is paged.
So, my other query is quite similiar, but I cant find a Criteria equivalent for
SELECT COUNT(DISTINCT m.Uid)
Here is the HQL:
select count(distinct m.Uid)
from Media m
join m.Productlines p
join m.Categories c
join m.Spaces sp
join m.Solutions so
where m.Uid != 0
and p.Uid in (:productlines)
and c.Uid in (13)
and sp.Uid in (52)
and so.Uid in (15,18)
How can this be done with Criteria API?
Please, (N)Hibernate experts - help me with this, I cant find a working solution. Any help is greatly appreciated!
Group columns are implicitly returned as result, but you can add more columns. AFAIK, you can return full entities:
var query = session.CreateCriteria(typeof(Media), "m")
.Add(Projections.GroupProperty("m"))
.Add(Restrictions.NotEq("m.Uid", 0));
// dynamically add filters
if (filterProductLines)
{
query
.CreateCriteria("m.Productlines", "p")
.Add(Restrictions.Eq("p.Uid", productLines));
}
// more dynamic filters of this kind follow here...
IList<Media> results = query.List<Media>();
To count the full number of results you can just build up the same query with different projection:
var query = session.CreateCriteria(typeof(Media), "m")
.SetProjection(Projections.CountDistinct("m.Uid"));
// rest of the query the same way as above
long totalNumberOfResults = query.UniqueResult<long>();
I'm getting unsure about the Projections.GroupProperty("m"), you need to try this. If it doesn't work, you could make it an DetachedQuery that only returns ids:
var subquery = DetachedCriteria.For(typeof(Media), "m")
.Add(Projections.GroupProperty("m.Uid"))
.Add(Restrictions.NotEq("m.Uid", 0));
// add filtering
var query = session.CreateCriteria(typeof(Media), "outer")
.Add(Subqueries.PropertyIn("outer.Uid", subquery));
IList<Media> results = query.List<Media>();
This creates a sql query like this:
select outer.* // all properties of Media to create an instance
from Media outer
where outer.Uid in (
select Uid
from media m
where // filter
)
var count = session.CreateCriteria(typeof(Media))
// Add other criterias...
.SetProjection(Projections.CountDistinct("Id")) // or whatever the id property of Media class is called
.UniqueResult<long>();
As to your GROUP BY question, the query:
SELECT m.*
....
GROUP BY m.Uid
makes no sense because you need to select only columns that appear in the group by clause or aggregate functions. Could you elaborate a little more as to what exactly are you trying to achieve?