How to select if a row exists in HQL - sql

EDIT: Specifically talking about querying against no table. Yes I can use exists, but I'd have to do
select case when exists (blah) then 1 else 0 end as conditionTrue
from ARealTableReturningMultipleRows
In T-SQL I can do:
select case when exists(blah) then 1 else 0 end as conditionTrue
In Oracle I can do:
select case when exists(blah) then 1 else 0 end as conditionTrue from DUAL
How can I achieve the same thing in HQL?
select count() seems like the second-best alternative, but I don't want to have to process every row in the table if I don't need to.

Short answer: I believe it's NOT possible.
My reasoning:
According to Where can I find a list of all HQL keywords? Hibernate project doesn't publish HQL grammar on their website, it's available in the Hibernate full distribution as a .g ANTLR file though.
I don't have much experience with .g files from ANTLR, but you can find this in the file (hibernate-distribution-3.6.1.Final/project/core/src/main/antlr/hql.g):
selectFrom!
: (s:selectClause)? (f:fromClause)? {
// If there was no FROM clause and this is a filter query, create a from clause. Otherwise, throw
// an exception because non-filter queries must have a FROM clause.
if (#f == null) {
if (filter) {
#f = #([FROM,"{filter-implied FROM}"]);
}
else
throw new SemanticException("FROM expected (non-filter queries must contain a FROM clause)");
}
which clearly states there are some HQL queries having no FROM clause, but that's acceptable if that's a filter query. Now again, I am not an expert in HQL/Hibernate, but I believe a filter query is not a full query but something you define using session.createFilter (see How do I turn item ordering HQL into a filter query?), so that makes me think there's no way to omit the FROM clause.

I'm use fake table with one row for example MyDual.
select case when exists(blah) then 1 else 0 end as conditionTrue from MyDual

According to http://docs.jboss.org/hibernate/core/3.3/reference/en/html/queryhql.html#queryhql-expressions it looks like they support both case and exists statements.

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()

Handle null values within SQL IN clause

I have following sql query in my hbm file. The SCHEMA, A and B are schema and two tables.
select
*
from SCHEMA.A os
inner join SCHEMA.B o
on o.ORGANIZATION_ID = os.ORGANIZATION_ID
where
case
when (:pass = 'N' and os.ORG_ID in (:orgIdList)) then 1
when (:pass = 'Y') then 1
end = 1
and (os.ORG_SYNONYM like :orgSynonym or :orgSynonym is null)
This is a pretty simple query. I had to use the case - when to handle the null value of "orgIdList" parameter(when null is passed to sql IN it gives error). Below is the relevant java code which sets the parameter.
if (_orgSynonym.getOrgIdList().isEmpty()) {
query.setString("orgIdList", "pass");
query.setString("pass", "Y");
} else {
query.setString("pass", "N");
query.setParameterList("orgIdList", _orgSynonym.getOrgIdList());
}
This works and give me the expected output. But I would like to know if there is a better way to handle this situation(orgIdList sometimes become null).
There must be at least one element in the comma separated list that defines the set of values for the IN expression.
In other words, regardless of Hibernate's ability to parse the query and to pass an IN(), regardless of the support of this syntax by particular databases (PosgreSQL doesn't according to the Jira issue), Best practice is use a dynamic query here if you want your code to be portable (and I usually prefer to use the Criteria API for dynamic queries).
If not need some other work around like what you have done.
or wrap the list from custom list et.

Django select only rows with duplicate field values

suppose we have a model in django defined as follows:
class Literal:
name = models.CharField(...)
...
Name field is not unique, and thus can have duplicate values. I need to accomplish the following task:
Select all rows from the model that have at least one duplicate value of the name field.
I know how to do it using plain SQL (may be not the best solution):
select * from literal where name IN (
select name from literal group by name having count((name)) > 1
);
So, is it possible to select this using django ORM? Or better SQL solution?
Try:
from django.db.models import Count
Literal.objects.values('name')
.annotate(Count('id'))
.order_by()
.filter(id__count__gt=1)
This is as close as you can get with Django. The problem is that this will return a ValuesQuerySet with only name and count. However, you can then use this to construct a regular QuerySet by feeding it back into another query:
dupes = Literal.objects.values('name')
.annotate(Count('id'))
.order_by()
.filter(id__count__gt=1)
Literal.objects.filter(name__in=[item['name'] for item in dupes])
This was rejected as an edit. So here it is as a better answer
dups = (
Literal.objects.values('name')
.annotate(count=Count('id'))
.values('name')
.order_by()
.filter(count__gt=1)
)
This will return a ValuesQuerySet with all of the duplicate names. However, you can then use this to construct a regular QuerySet by feeding it back into another query. The django ORM is smart enough to combine these into a single query:
Literal.objects.filter(name__in=dups)
The extra call to .values('name') after the annotate call looks a little strange. Without this, the subquery fails. The extra values tricks the ORM into only selecting the name column for the subquery.
try using aggregation
Literal.objects.values('name').annotate(name_count=Count('name')).exclude(name_count=1)
In case you use PostgreSQL, you can do something like this:
from django.contrib.postgres.aggregates import ArrayAgg
from django.db.models import Func, Value
duplicate_ids = (Literal.objects.values('name')
.annotate(ids=ArrayAgg('id'))
.annotate(c=Func('ids', Value(1), function='array_length'))
.filter(c__gt=1)
.annotate(ids=Func('ids', function='unnest'))
.values_list('ids', flat=True))
It results in this rather simple SQL query:
SELECT unnest(ARRAY_AGG("app_literal"."id")) AS "ids"
FROM "app_literal"
GROUP BY "app_literal"."name"
HAVING array_length(ARRAY_AGG("app_literal"."id"), 1) > 1
Ok, so for some reason none of the above worked for, it always returned <MultilingualQuerySet []>. I use the following, much easier to understand but not so elegant solution:
dupes = []
uniques = []
dupes_query = MyModel.objects.values_list('field', flat=True)
for dupe in set(dupes_query):
if not dupe in uniques:
uniques.append(dupe)
else:
dupes.append(dupe)
print(set(dupes))
If you want to result only names list but not objects, you can use the following query
repeated_names = Literal.objects.values('name').annotate(Count('id')).order_by().filter(id__count__gt=1).values_list('name', flat='true')

LINQ To NHibernate ignores the 'Group by' clause

I use NHibernate 3.2.0 and I cannot make the LINQ provider generate a proper SQL query for this statement:
var result = (from translation in session.Query<TmTranslation>()
where translation.Id > 0
group translation by translation.Language into grp
select new { Lang = grp.Key.Code }).ToList();
The generated SQL is
select tmtranslat0_.id as id32_,
tmtranslat0_.status as status32_,
tmtranslat0_.text as text32_,
tmtranslat0_.last_revision as last4_32_,
tmtranslat0_.fk_id_translation_unit as fk5_32_,
tmtranslat0_.fk_id_translator as fk6_32_,
tmtranslat0_.fk_id_last_modifier as fk7_32_,
tmtranslat0_.fk_id_last_match_category as fk8_32_,
tmtranslat0_.fk_id_language as fk9_32_
from "TRANSLATION" tmtranslat0_
where tmtranslat0_.id > 0
which, of course leads to loading all the entities from the database and grouping the result set in memory (the result itself is correct).
I would like something like this
select tmtranslat0_.fk_id_language
from "TRANSLATION" tmtranslat0_
where tmtranslat0_.id > 0
group by tmtranslat0_.fk_id_language
to be generated instead.
Am I missing something?
Thank you very much.
The only thing I can suggest, is to use QueryOver API.