nhibernate group by and join query - nhibernate

I need nhiberante query (not HQL) equivalent following SQL:
SELECT ur.*
FROM (SELECT MAX(requestTime) rt, macAddress ma
FROM UpdateRequests
GROUP BY macAddress) mur
JOIN dbo.UpdateRequests ur
ON mur.ma = ur.macAddress AND mur.rt = ur.requestTime
I had no luck with other similar examples on stackoverflow.
Having UpdateRequest mapping, it seems that is not possible with Query API, how about QueryOver?

Finally one Guru suggested me to change SQL query without changing execution plan:
SELECT ur.*
FROM [dbo].[UpdateRequests] AS ur
WHERE ur.[RequestTime] = (SELECT MAX(mur.[RequestTime])
FROM [dbo].[UpdateRequests] mur
WHERE mur.[MacAddress] = ur.[MacAddress])
So in code it transforms into:
session
.Query<UpdateRequest>()
.Where(ur => ur.RequestTime == session.Query<UpdateRequest>()
.Where(mur => mur.MacAddress == ur.MacAddress)
.Max(mur => mur.RequestTime))
.ToList();
And this is exactly what i need.

Related

Linq .Except behavior not as expected

I have two tables: tableA and tableB, both have an attribute "CommissionNumber" which contains strings in the form of D123456789 (one letter followed by a fixed number of digits).
I need to find the commissionnumbers in table A, that are not in table B.
In SQL, this could look like:
SELECT *
FROM tableA
WHERE CommissionNumber NOT IN
(
select CommissionNumber from tableB
)
Which gives me no results. However, if i try this:
var tableA= dbContext.tableA.Select(x => x.CommissionNumber).ToList();
var tableB= dbContext.tableB.Select(x => x.CommissionNumber).ToList();
IEnumerable<string> missingFiles = tableA.Except(tableB);
I get 92 hits. I don't understand what's wrong, my SQL query of the use of the .Except function.
Any ideas?
You have created two LINQ queries which retrieves all data from two tables into the memory and applied Except. It is wrong if you care about performance.
If you want to create SQL in the same way, LINQ query should be written accordingly. LINQ equivalent for IN operator is Contains.
var query = dbContext.tableA
.Where(x => !dbContext.tableB.Select(b => b.CommissionNumber).Contains(x.CommissionNumber));
Or by EXISTS which have analogue Any
var query = dbContext.tableA
.Where(x => !dbContext.tableB.Any(b => b.CommissionNumber == x.CommissionNumber));
Also the same result you can achieve by LEFT JOIN
var query =
from a in dbContext.tableA
join b in dbContext.tableB on a.CommissionNumber equals b.CommissionNumber into gj
from b in gj.DefaultIfEmpty()
where b.CommissionNumber == null
select a;
gsharp was on the right track. I had some case sensitivity issues in my data which was ignored in the native SQL query but taken seriously by EF core.

Query on other query linq

Just assume a simple example:I have two table and use this query:
select *
from People
where PersonGuid = (select PersonGuid from Sellers where Guid = '')
How can I write this query with linq?
I tried this:
var person = from p in loginContext.Person where p.PersonGuid =
(loginContext.Seller.Where(s => s.Guid == sellerGuid).FirstOrDefaultAsync());
but that's wrong.
What is the right way to write it?
If you were to rewrite your SQL query as
select *
from People AS PE
JOIN
Sellers AS Sellers
ON PE.PersonGuid = SE.PersonGuid
WHERE
SE.Guid = ''
then the LINQ becomes a little more obvious:
var person = (from p in loginContext.Person
join s in loginContext.Seller on p.PersonGuid equals s.PersonGuid
where s.guid == ""
select p)
.FirstOrDefault();
Although your SQL would suggest FirstOrDefault is not required.
Warning - not tested.
You could probably try using LINQ JOIN clause.
It should be something like:
from p in loginContext.Person
join s in loginContext.Seller on new { p.PersonGuid = s.Guid }
where s.Guid = sellerGuid
You can know more about LINQ JOIN here -> Microsoft C# Reference
Hope it helps!
I will assume that select PersonGuid from Sellers where Guid = '' will provide a single value no matter what, otherwise your SQL statement would fail.
Accepted answer works, but if you want a solution that keeps the spirit of the SQL query, you can rely on Any function. Something along the following:
var person = await loginContext.Person
.Where(p => loginContext.Sellers
.Any(s => s.PersonGuid == p.PersonGuid && s.Guid = ""))
.FirstOrDefaultAsync();
I expect this to be transformed into an SELECT ... WHERE EXISTS which is similar to your initial query.

Giving different record set after changing simple SQL query to LINQ query

I have write the below query in simple SQL,
I want to change it to use LINQ, I have tried, but my LINQ query and the original SQL statement are giving different record sets.
Simple SQL Query:
select *
from Paymentadvices
where status = 3
and Id in (select PaymentAdviceId from Approvals where ApprovedById = 13)
LINQ:
var myPaymentAdviceList = from pa in db.PaymentAdvices
where pa.Status == 3
join Ap in db.Approvals on pa.Id equals
Ap.PaymentAdviceId
where Ap.EmployeeId == 13
orderby pa.PaidDate descending
select pa;
I am not supposed to use join I guess, What should I use ?
var a = db.Approvals.Where( x => x.ApprovalById = 13).PaymentAdviceId;
var b = db.Paymentadvices.Where(x => x.status ==3 && a.Contains(x.Id);
.Contains() makes the WHERE IN () , you don't need a join there
var a = from a in db.Approvals
where a.ApprovedById == 3
select a.PaymentAdviceId;
var b = (from p in db.PaymentAdvices
where p.Status == 3 &&
a.Contains(p.Id)
select p).ToList();
those are both linq , the top is just lambda expressions which are commonly used in Linq queries. I would reccommend that you get used to reading/ writing both styles . The majority of code you'll come across in lambda style
I believe something like the below would work:
var query = from p in db.PaymentAdvices
where p.Status == 3 && db.Approvals
.Select(a => a.Id)
.Where(a => a.ApprovedById == 13)
.Contains(p.Id)
select p;
Though it's worth noting that #Scott Selby and #axrwkr solutions above are essentially the exact same thing in another form.

How to use SQL Count() in QueryOver

I'm trying to perform some simple SQL (using sql server 2005) using QueryOver in NHibernate. The query is to count how many times a serial number is repeatedly used in a list of items and then to select the serial numbers with only 1 use. Note that I don't want distinct serial numbers since I don't want to use serial numbers that have >1 use.
The SQL query to do this is:
SELECT SERNUM, expr1
FROM (SELECT SERNUM, COUNT(SERNUM) AS expr1
FROM ITEM
GROUP BY SERNUM) AS derivedtbl_1
WHERE (expr1 = 1)
What I have done so far using QueryOver is:
var query = session.QueryOver<Item>()
.Select(Projections.ProjectionList()
.Add(Projections.Count<Item>(x => x.Sernum))
.Add(Projections.GroupProperty("Sernum"))).List();
This code generates SQL (though it does not like putting the Count() column to a List). I'm not sure how to access the column of data generated by Count() to say only return where it is 1. The SQL that it does generates is:
SELECT count(this_.SERNUM) as y0_,
this_.SERNUM as y1_
FROM ITEM this_
GROUP BY this_.SERNUM
I'm not sure if I'm approaching this the right way but hopefully someone can point me to it.
var serials = session.QueryOver<Item>()
.Where(Restrictions.Eq(Projections.Count<Item>(i => i.Id), 1));
.Select(Projections.GroupProperty<Item>(i => i.Sernum))
.List();
should generate something like
SELECT SERNUM FROM ITEM GROUP BY SERNUM HAVING COUNT(*) = 1
to get the items, do something like
var subquery = QueryOver.Of<Item>()
.Where(Restrictions.Eq(Projections.Count<Item>(i => i.Id), 1));
.Select(Projections.GroupProperty<Item>(i => i.Sernum));
var items = QueryOver.Of<Item>()
.WithSubquery.Where(i => i.Sernum).In(subquery);
.List();

NHibernate Left Outer Join Unrelated Entities

I have 2 entities Role & Translation.
Role -> Role_ID, Code
Translation -> Code, Language, Name
The idea is to say for a certain role, that it has English name, French name and so on.
For example:
A Role(1, 'Rol_001') can have the relations: Translation('Rol_001', 'English', '') & Translation('Rol_001', 'French', '').
I would like to express the following SQL query in HQL:
select r.Role_ID, t.Name
from Role r left outer join Translation t
on r.Code = t.Code and t.Language = #lang;
In my mapping files I don't have any relation between the 2 entities but the following HQL query works as if it is inner join
IQuery query = session.CreateQuery("select new Lookup(r.Role, t.Name) from Role r, Translation t where r.Code = r.Code and t.Language = :language");
If I change the HQL to left outer join, I get the Path expected for join exception.
Can you help me with the following:
1- Do I need to change my mapping files?
2- If I can keep the mapping files as is, how write such a query in HQL?
3- How does HQL really works? Why such a simple outer join query is not working? I must be missing something here!
Edit:
Now I am using the following code based on the suggetion to use CreateSQL:
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.SetString("language", language);
IList rawLookup = query.List();
IList medicineTypesLookup = new List(rawLookup.Count);
foreach (object[] lookup in rawLookup)
{
medicineTypesLookup.Add(new Lookup((int)lookup[0], (string)lookup[1], (bool)lookup[2]));
}
return medicineTypesLookup;
This is working however I want to use query.List() to get the result directly instead of converting it myself.
I tried to use query.AddEntity(typeof(Lookup)); but I get the exception NHibernate.MappingException: No persister for: DAL.Domain.Lookup.
The Lookup is just a POCO and doesn't map to any database table. Its mapping file is simply <import class="Lookup" />
Finally I found the answer:ISession session = NHibernateHelper.Session;
ISQLQuery query = session.CreateSQLQuery("select m.MedicineTypeID as ID, t.Name, m.IsDeleted from MedicineType m left outer join Translation t on m.Code = t.Code and t.Language = :language");
query.setString("language", language);
IList lookup = query.SetResultTransformer(Transformers.AliasToBean()).List();
return lookup;
And the lookup is a POCO class with a parameterless constructor and 3 properties ID, Name and IsDeleted.
I would like to thank Kelly and Diego Mijelshon for their hints. Although they don't provide the full answer the but using Session.CreateSqlQuery() was a very useful hint.
So the complete solution is Session.CreateSQLQuery and query.SetResultTransformer
Note: Transformers.AliasToBean() is so java.
Edit: http://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/impl/SQLQueryImpl.html for correct method of setString()
You must to define the relationship in the mappings or do a subquery