How to use MySql date_add in Nhibernate? - mysql

This really puzzled for hours, I searched all over the internet, but got no working solution. Can someone point where the problem is ... thanks !
I created my own dialect class
public class MySQLDialectExtended : MySQLDialect
{
public MySQLDialectExtended()
{
RegisterFunction("date_add_interval", new SQLFunctionTemplate(NHibernateUtil.Date, "date_add(?1, INTERVAL ?2 ?3)"));
}
}
Then I try to use it as follows:
query.Append(
" ( date_add_interval(D.ApprovalDate, 1, YEAR) < current_timestamp() < date_add_interval(D.RenewalDate, -1, YEAR) )");
It fails with following exception:
NHibernate.Hql.Ast.ANTLR.QuerySyntaxException : Exception of type 'Antlr.Runtime.NoViableAltException' was thrown. near line 1, column 677
where the column number is at the end of the first 'YEAR' word.
Edit:
here is my configuration
<property name="dialect">MyCompanyName.MySQLDialectExtended, MyCompanyName</property>
<property name="hbm2ddl.keywords">none</property>

Can you post the whole NHibernate query?
UPDATE: Well, the query is obviously malformed and erroneous:
Select distinct D from MBIgnition.Core.Domain.Model.Deal D where 1=1 and
( (D.MortgageStatus = 30 ) or
(D.MortgageStatus = 35 ) or
(D.MortgageStatus = 40 ) or
(D.MortgageStatus = 45 ) or
(D.MortgageStatus = 55 ) or
(D.MortgageStatus = 50 ) ) and
// next line is erroneous as the first AND operator does not have a lefthand side operand
(( and ( date_add_interval(D.ApprovalDate, 1, YEAR) < current_timestamp() < date_add_interval(D.RenewalDate, -1, YEAR) ) ) )
As you can see, there's an AND operator in your code without any lefthand-side arguments. There should be something wrong with your HQL. Double check it again and if you couldn't pinpoint the error it will be useful to post the HQL or the criteria building code here.

Related

LINQ MY SQL QUERY "BIGINT UNSIGNED value is out of range in"

I've got a strange situation.
I've got a column (smallint) that has a single value of 6
I run using a linq to retrieve records that are Validity_months old.
var query = db.table_1
.AsNoTracking()
.Include(b => b.table_2)
.ThenInclude(t => t.table_3)
.Where(e => e.Date <= DateTime.UtcNow.AddMonths(e.table_2.ValidityMonths))
.Where(e => e.Date >= DateTime.UtcNow.AddMonths(e.table_2.ValidityMonths*-1)));
This throws an MySQL Error
Error Code: 1690. BIGINT UNSIGNED value is out of range in '(database.s0.validity_months * (-(1)))'
The query above translates into SQL Query
SELECT *
FROM `table_1` AS `s`
LEFT JOIN `table_2` AS `s0` ON `s`.`table_2_id` = `s0`.`id`
LEFT JOIN `table_3` AS `s1` ON `s0`.`table_3_id` = `s1`.`id`
WHERE (`s`.`date` <= DATE_ADD(UTC_TIMESTAMP(), INTERVAL CAST(`s0`.`validity_months` AS signed) month)))
AND (`s`.`date` >= DATE_ADD(UTC_TIMESTAMP(), INTERVAL CAST(`s0`.`validity_months` * -1 AS signed) month));
This ran into MySQL throws the same error. The problem is at
CAST(`s0`.`validity_months` * -1 AS signed)
How do I negate a field in LINQ. This feels like it's a EFCore bug, but I am not sure yet.

Django annotate with frequency

(django 3.2.12, python 3.9.3, MySQL 8.0.28)
Imagine models like the following:
class User(models.Model):
email = models.EmailField(...)
created_datetime = models.DatetimeField(...)
class UserLog(models.Model):
created_datetime = models.DatetimeField(...)
user = models.ForeignKey('user.User' ...)
login = models.BooleanField('Log in' ..)
And the following query, destined to annotate each user in the queryset with the frequency of their logs(when log.login=True):
users = User.objects.filter(
Q(...)
).annotate(
login_count=Count('userlog', filter=Q(userlog__login=True)),
login_duration_over=Now() - F('created_datetime'),
login_frequency=ExpressionWrapper(
F('login_duration_over') / F('login_count'),
output_field=models.DurationField()
),
)
This results in a SQL error:
(1064, "You have an error in your SQL syntax;)
The generated SQL (fragment for login_frequency) looks like this:
(
INTERVAL TIMESTAMPDIFF(
MICROSECOND,
`user_user`.`created_datetime`,
CURRENT_TIMESTAMP
) MICROSECOND / (
COUNT(
CASE WHEN `user_userlog`.`login` THEN `user_userlog`.`id` ELSE NULL END
)
)
) AS `login_frequency`,
and MySQL does not seem to like it. A similar code works on SQLlite and, I am told on PG.
What is wrong with the ExpressionWrapper on MySQL, any idea?
Found a workaround:
users = User.objects.filter(
Q(...)
).annotate(
login_count=Count('userlog', filter=Q(userlog__login=True)),
login_duration_over=Now() - F('created_datetime'),
login_frequency=Cast(
ExpressionWrapper(
Cast(F('login_duration_over'), output_field=models.BigIntegerField()) / F('login_count'),
output_field=models.BigIntegerField()
),
output_field=models.DurationField()
)
)
this forces the DIVIDE operation to be performed db-side on bigints and once that is done, cast it back to a timedelta.
MySQL stopped screaming and the results are correct.
Even though that work, this feels ugly. Could there not be a better way?

Laravel with POSTGRESS . You might need to add an explicit type cast error, operator does not exist:

I switched my database from Mysql to Postgress and now I'm getting the above error after switching.
My function looks like this.
public function viewRunDetails(Run_List $id) {
$this->viewRun = $id->id;
$profile = Profile::where('id', $id->profile_id)->first();
$runnerlist = Runners_List::where('run_list_id', $id->id)->get();
$pace = $runnerlist->sum('distance');
$TotalPace = Runners_List::where('run_list_id', $id->id)->selectRaw('SEC_TO_TIME( SUM(
TIME_TO_SEC( `pace` ) ) / ?) as total', [$pace])->first();
$variable = substr($TotalPace->total, 0, strpos($TotalPace->total, "."));
}
distance is in the decimal format while the pace is in time format.
Specifically, I'm getting the error here
$TotalPace = Runners_List::where('run_list_id', $id->id)->selectRaw('SEC_TO_TIME( SUM(
TIME_TO_SEC( `pace` ) ) / ?) as total', [$pace])->first();
Any ideas why it's showing that error? Thanks.

INTERVAL near line 1 : Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException

Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException:
unexpected token: INTERVAL near
Here is my query:
private static final String USERS_NOT_ACTION_IN_LAST_MONTH = select u from myTable u where u.lastDate + INTERVAL 30 DAY <= CURDATE();
#Override
public List<MyEntity> getItemOlderThanMonth() {
Query q = _entityManager.createQuery(USERS_NOT_ACTION_IN_LAST_MONTH, MyEntity.class);
return q.getResultList();
}
I want to get records older than 30 days.
You can calculate current date by query parameter(current_date- 30)
private static final String USERS_NOT_ACTION_IN_LAST_MONTH = select u from myTable u where u.lastDate <= :calculated_date;
where calculated_date=current_date -30 days.
By using _entityManager.createQuery hibernate sql (hql) script is created and hibernate doesn't have INTERVAL key word.
But createNativeQuery is native sql and behaves as normal sql script. So for mysql db INTERVAL key word should work. So smth like that
_entityManager.createNativeQuery("select * from myTable u where u.lastDate + INTERVAL 30 DAY <= CURDATE()");

How to select DB Expression as value using knex/Bookshelf

I'm trying to execute the following query using knex.js and MySql
SELECT
m.id,
TIME(date_created) AS `timestamp`,
u.username,
m.`message`
FROM
`messages` AS m
INNER JOIN users AS u ON u.id = m.user_id
WHERE
m.game_id IS NULL
AND m.`date_created` > DATE_SUB(
CURRENT_TIMESTAMP (),
INTERVAL 12 HOUR
)
ORDER BY
m.`date_created` ASC
LIMIT 50
For proper handling expressions in where closure such as DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL 12 HOUR) according to documentation there is whereRow() method in knex.
I tried to use select() method as
select('messages.id', 'TIME(date_created) AS timestamp', 'users.username', 'messages.message')
But knex masks TIME(date_created) expression in a way it should to be a column name. Does anybody know a way to use a custom expressions in selects?
I did not found exact answer on my question but I've found a solution which allows me to move forward. I created separate model which uses standard Bookshelf(knex) export:
var Bookshelf = require('bookshelf')(knex);
module.exports.DB = Bookshelf;
And created separate method in that model where I could use DB.knex.raw() for masking DB expressions in SELECT. So I became able to write the query above in the following way:
var DB = require('./../db').DB;
var Messages = DB.Model.extend({
tableName: 'messages',
hasTimestamps: false,
idAttribute: 'id'
},
{
getMessagesHistory: function (gameId) {
return this.query().select('messages.id', DB.knex.raw('TIME(date_created) AS timestamp'), 'users.username', 'messages.message')
.innerJoin('users', 'messages.user_id', 'users.id')
.whereRaw("messages.game_id " + (gameId == 0 ? "IS NULL" : "= " + gameId))
.andWhereRaw("messages.date_created > DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL 12 HOUR)")
.orderBy('messages.date_created', 'ASC')
.limit(50);
}
}
);
module.exports = Messages;
You can wrap any argument with knex.raw() to tell the framework that it's a raw piece of SQL:
select(
'messages.id',
knex.raw('TIME(date_created) AS timestamp'),
'users.username',
'messages.message',
)
See http://knexjs.org/#Raw-Bindings