Nhibernate query condition within sum - nhibernate

I am trying the following code but nhibernate is throwing the following exception:
Expression type 'NhSumExpression' is not supported by this SelectClauseVisitor.
var data =
(
from a in session.Query<Activity>()
where a.Date.Date >= dateFrom.Date && a.Date.Date <= dateTo.Date
group a by new { Date = a.Date.Date, UserId = a.RegisteredUser.ExternalId } into grp
select new ActivityData()
{
UserID = grp.Key.UserId,
Date = grp.Key.Date,
Bet = grp.Sum(a => a.Amount < 0 ? (a.Amount * -1) : 0),
Won = grp.Sum(a => a.Amount > 0 ? (a.Amount) : 0)
}
).ToArray();
I've been looking around and found this answer
But I am not sure what I should use in place of the Projections.Constant being used in that example, and how I should create a group by clause consisting of multiple fields.

It looks like your grouping over multiple columns is correct.
This issue reported in the NHibernate bug tracker is similar: NH-2865 - "Expression type 'NhSumExpression' is not supported by this SelectClauseVisitor."
Problem is that apart from the less-than-helpful error message, it's not really a bug as such. What happens in NH-2865 is that the Sum expression contains something which NHibernate doesn't know how to convert into SQL, which result in this exception being thrown by a later part of the query processing.
So the question is, what does you sum expression contains that NHibernate cannot convert? The thing that jumps to mind is the use of the ternary operator. I believe the NHibernate LINQ provider has support for the ternary operator, but maybe there is something in this particular combination that is problematic.
However, I think your expressions can be written like this instead:
Bet = grp.Sum(a => Math.Min(a.Amount, 0) * -1), // Or Math.Abs() instead of multiplication.
Won = grp.Sum(a => Math.Max(a.Amount, 0))
If that doesn't work, try to use a real simple expression instead, like the following. If that works, we at least know the grouping itself work as expected.
Won = grp.Sum(a => a.Amount)

Related

SQL case query with DISTINCT in cakephp3 ORM

I am trying to build a case query with distinct count in cakephp 3.
This is the query in SQL:
select COUNT(distinct CASE WHEN type = 'abc' THEN app_num END) as "count_abc",COUNT(distinct CASE WHEN type = 'xyz' THEN app_num END) as "count_xyz" from table;
Currently, I got this far:
$query = $this->find();
$abc_case = $query->newExpr()->addCase($query->newExpr()->add(['type' => 'abc']),' app_num','string');
$xyz_case = $query->newExpr()->addCase($query->newExpr()->add(['type' => 'xyz']),'app_num','string');
$query->select([
"count_abc" => $query->func()->count($abc_case),
"count_xyz" => $query->func()->count($xyz_case),
]);
But I can't apply distinct in this code.
Using keywords in functions has been a problem for quite some time, see for example this issue ticket: https://github.com/cakephp/cakephp/issues/10454.
This has been somewhat improved in https://github.com/cakephp/cakephp/pull/11410, so that it's now possible to (mis)use a function expression for DISTINCT as kind of a workaround, ie generate code like DISTINCT(expression), which works because the parentheses are being ignored, so to speak, as DISTINCT is not a function!
I'm not sure if this works because the SQL specifications explicitly allow parentheses to be used like that (also acting as a whitespace substitute), or because it's a side-effect, so maybe check that out before relying on it!
That being said, you can use the workaround from the linked PR until real aggregate function keyword support is being added, ie do something like this:
"count_abc" => $query->func()->count(
$query->func()->DISTINCT([$abc_case])
)
This would generate SQL similar to:
(COUNT(DISTINCT(CASE WHEN ... END)))

How to compare the month parts of two dates?

I am trying to query query the current month, here is my query:
$clients = $this->Clients;
$query = $clients->find();
if($this->Auth->user('role') !== 'admin'){
$query->where(['user_id =' => $this->Auth->user('id')]);
$query->where(['MONTH(dob) = ' => 'EXTRACT(month FROM (NOW()))']);
$query->order(['dob' => 'ASC']);
}
It returns 0 records (my field is a date type), however this query in phpmyadmin works:
SELECT * FROM `clients` WHERE MONTH(dob) = EXTRACT(month FROM (NOW()))
What am I doing wrong?
Just look at the actual generated query (check out your DBMS query log, or try DebugKit), it will look different, as the right hand side value in a key => value condition set is subject to parameter-binding/casting/quoting/escaping. In your case it will be treated as a string, so the condition will finally look something like:
WHERE MONTH(dob) = 'EXTRACT(month FROM (NOW()))'
That will of course not match anything.
You could pass the whole SQL snippet as a single array value, or as an expression object, that way it would be inserted into the query as is (do not insert user values that way, that would create an SQL injection vulnerability!), but I'd suggest to use portable function expressions instead.
CakePHP ships with functions expressions for EXTRACT and NOW, so you can simply do something like:
use Cake\Database\Expression\IdentifierExpression;
use Cake\Database\Expression\QueryExpression;
use Cake\ORM\Query;
// ...
$query->where(function (QueryExpression $exp, Query $query) {
return $exp->eq(
$query->func()->extract('MONTH', new IdentifierExpression('dob')),
$query->func()->extract('MONTH', $query->func()->now())
);
});
Looks a bit complicated, but it's worth it, it's cross DBMS portable as well as auto-quoting compatible. The generated SQL will look something like
WHERE EXTRACT(MONTH FROM (dob)) = (EXTRACT(MONTH FROM (NOW())))
See also
Cookbook > Database Access & ORM > Query Builder > Advanced Conditions
Cookbook > Database Access & ORM > Query Builder > Using SQL Functions
API > \Cake\Database\Expression\QueryExpression::eq()
API > \Cake\Database\FunctionsBuilder::extract()
API > \Cake\Database\FunctionsBuilder::now()

How to get the latest/last record with a group by clause with NHibernate Linq provider

I have used too much time (days) on this and I really hope someone can help me out.
I found a good article on describing my problem in a generic way so let's stick to it.
I am trying to build this query but NHibernate fails to build the correct sql and returns a sql query exception.
Column vSagsAendring.Id is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. It could not execute the following query:
select
viewsagsae0_.Id as Id155_,
viewsagsae0_.SagId as SagId155_,
viewsagsae0_.JournalNr as JournalNr155_,
viewsagsae0_.LbfNr as LbfNr155_,
viewsagsae0_.OrgNr as OrgNr155_,
viewsagsae0_.OrgNavn as OrgNavn155_,
viewsagsae0_.AfdNavn as AfdNavn155_,
viewsagsae0_.SagsType as SagsType155_,
viewsagsae0_.Status as Status155_,
viewsagsae0_.SagsbehandlerInit as Sagsbeh10_155_,
viewsagsae0_.Dato as Dato155_,
viewsagsae0_.JournalAktionType as Journal12_155_,
viewsagsae0_.Beskrivelse as Beskriv13_155_,
viewsagsae0_.Ekstern as Ekstern155_
from vSagsAendring viewsagsae0_
group by viewsagsae0_.SagId
var query = from p in _session.Query<ViewSagsAendring>()
group p by p.SagId
into grp
select grp.OrderByDescending(g => g.Dato).First();
This is another version also took from the article:
var query = from p in _session.Query<ViewSagsAendring>()
group p by p.SagId
into grp
let maxDato = grp.Max(g => g.Dato)
from p in grp
where p.Dato == maxDato
select p;
It's have been a long journey, but now it's over. I hope that I can help someone else in the same situation by answering my own question.
var aendring = from sagsAendring in _session.Query<ViewSagsAendring>()
where sagsAendring.Dato ==
(
from innersagsAendring in _session.Query<ViewSagsAendring>()
where innersagsAendring.SagId == sagsAendring.SagId
select innersagsAendring.Dato
).Max()
select sagsAendring;
var result = aendring.ToList();
And because you can chain linq statements you can build a linq filter like this
if(Filters.VisInterneAendringer == false)
query = query.Where(x => x.Ekstern == true);
if (Filters.VisKunNyesteAendringer)
{
query = query.Where(sagsAendring => sagsAendring.Dato ==
(
from innerSagsAendring in Session.Query<ViewSagsAendring>() where innerSagsAendring.SagId == sagsAendring.SagId
select innerSagsAendring.Dato
).Max());
}
return query;
Your queries seem legit for LINQ in EntityFramework.
I'm not sure about hibernate, you might try to use QueryOver API instead of Query
http://nhibernate.info/blog/2009/12/17/queryover-in-nh-3-0.html

Having problems converting conditional where clause in LINQ back over to SQL

I've got myself in a bit of a pickle!
I've done a snazzy LINQ statement that does the job in my web app, but now I'd like to use this in a stored procedure:
var r = (from p in getautocompleteweightsproducts.tblWeights
where p.MemberId == memberid &&
p.LocationId == locationid
select p);
if (level != "0")
r = r.Where(p => p.MaterialLevel == level);
if (column == "UnitUserField1")
r = r.Where(p => p.UnitUserField1 == acitem);
if (column == "UnitUserField2")
r = r.Where(p => p.UnitUserField2 == acitem);
return r.OrderBy(p => p.LevelNo).ToList();
However, I can't for the life of me get the conditional where clause to work!!
If someone can point me in the right direction, I'd be most grateful.
Kind regards
Maybe something like this?
SELECT *
FROM dbo.weights
WHERE member_id = #memberid
AND location_id = #locationid
AND material_level = CASE WHEN #level = '0' THEN material_level
ELSE #level END
AND #acitem = CASE #column WHEN 'UnitUserField1' THEN unit_user_field_1
WHEN 'UnitUserField2' THEN unit_user_field_2
ELSE #acitem END
ORDER BY level_no
Have you tried LinqPAD, I'm pretty sure last time I played with that you could enter "LINQ to SQL" code and see the resulting SQL that produced. Failing that, place a SQL trace/profiler on your code running the LinqTOSQL and find the query being executed in the trace.
LukeH's answer will give you the correct rows, but there is something lost when you try to replace a query-generating-machine with a single query. There are parts of that query that are opaque to the optimizer.
If you need the original queries as-would-have-been-generated-by-linq, there are two options.
Generate every possible query and control which one runs by IF ELSE.
Use Dynamic sql to construct each query (although this trades away many of the benefits of using a stored procedure).
If you do decide to use dynamic sql, you should be aware of the curse and blessings of it.

Linq.Where-to-SQL on a text field comparing to a list of values

Customer.text is a field in an T-SQL DB (that I do not control and thus may not alter) of type "text".
I'd like to do something like this:
List<string> compare = new List<string>();
compare.Add("one");
compare.Add("two");
var q = from t in customer
where t.text.Contains( compare.First())
select t;
this will work.
But now I'd like to do something like: (!NOT WORKING!)
var q = from t in customer
where compare.Contains( t.text )
select t;
How can I achieve this? Is it even possible?
EDIT: The problem is obviously not exactly clear: A text column in SQL cannot be queried using "=" but only with LIKE. Thus the compare.Contains( t.text ) will result in an error, as it is converted into a query using "=".
What I did not tell - I thought it is irrelevant - is, that I use LINQ-to-ORM (LLBLGen in this case).
What I tried instead:
var q = from t in customer
where compare.Any( x => t.text.Contains(x) )
select t;
Now this did not work also. Currently I'm not at work, but the exception was something with a ConstantExpression not being convertable into a SetExpression.
I hope this gave some clarification.
EDIT2:
Joseph pointed this out to me: PredicateBuilder. It creates an Expression on a given ObjectType.
Now my problem is, that my type is an anonymous type out of multiple joins.
Is there an easy or elegant way to handle this?
Now I might be missing something, but your code looks like it should work. Did you include the namespaces at the top of the file?
using System.Linq;
using System.Linq.Expressions;
You could also rewrite it without the Linq2Sql syntax, like:
var q = customer.Where(c => compare.Contains(c.text));
You could build your query using LinqKit's free predicate builder class. Here is a blog post which describes its use and has a link to the download site.
http://thecodeslinger.wordpress.com/2008/10/28/linqkit-predicatebuildert-goodness/
Below is a code sample from the post
//First get a list of keywords that match the description entered.
string[] parts = txtInclude.Text.Split(new[] {‘ ‘});
string[] noparts = null;
if(txtButNot.Text.Trim().Length > 0)
noparts = txtExclude.Text.Trim().Split(new[] {‘ ‘});
var pred = PredicateBuilder.True<Pet>();
//here is where you would loop through your compare object
parts.ForEach(p => pred = pred.And(pl => pl.description.Contains(p)));
if(noparts != null)
noparts.ForEach(p => pred = pred.And(pl => !pl.description.Contains(p)));
var pets = from s in db.Pets.Where(pred)
select s;
You have to convert the text field to string
var query = from t in dataContext.table
where compare.Contains(t.textField.ToString())
select t;