Convert mySQL query to KNEX - mysql

I am converting series of queries to Knex syntax.
I am having problem with this query:
SELECT id,reviewed,log_reference,CONVERT(notification USING utf8),create_time,update_time,store,user_id
FROM store_failure_log
WHERE reviewed = 0
AND create_time BETWEEN NOW() - INTERVAL 18 HOUR AND NOW();
More precisely this line:
SELECT id,reviewed,log_reference,CONVERT(notification USING utf8),create_time,update_time,store,user_id
I have this Knex in place:
knex('store_failure_log')
.select('id', 'reviewed', 'log_reference', 'CONVERT(notification USING utf8)', 'create_time', 'update_time', 'store', 'user_id').convert('notification USING utf8')
.where('reviewed', 0)
.where(knex.raw('create_time BETWEEN NOW() - INTERVAL 18 HOUR AND NOW()'))
that produces this sql query:
select `id`, `reviewed`, `log_reference`, `CONVERT(notification USING utf8)`, `create_time`, `update_time`, `store`, `user_id` from `store_failure_log` where `reviewed` = 0 and create_time BETWEEN NOW() - INTERVAL 18 HOUR AND NOW()
Problem is in the: Convert(notification USING utf8).
The query is not valid, since the Convert is in parentheses. How can I write it with the knex?
In general how do I include SQL function calls in the KNEX syntax?

You can use raw to include SQL function calls in your Knex query, like you've already done in your where:
knex('store_failure_log')
.select(knex.raw('id, reviewed, log_reference, CONVERT(notification USING utf8), create_time, update_time, store, user_id'))
.where('reviewed', 0)
.where(knex.raw('create_time BETWEEN NOW() - INTERVAL 18 HOUR AND NOW()'))

Here is fixed version of #Veve's answer with correct quoting of identifiers and knex.raw syntax:
knex('store_failure_log')
.select('id', 'reviewed', 'log_reference', knex.raw('CONVERT(?? USING utf8)', ['notification']), 'create_time', 'update_time', 'store', 'user_id')
.where('reviewed', 0)
.where(knex.raw('?? BETWEEN NOW() - INTERVAL 18 HOUR AND NOW()', ['create_time']))
https://runkit.com/embed/lh2i1qif7obx

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.

Datetime changing on JSON response

I am selecting start and end date of a project from project_stage named table.
Here is the table elements
stage_start_date stage_end_date planned_start_date planned_end_date
2019-01-28 10:12:01 2020-12-08 09:05:28 2019-01-12 01:01:00 2020-12-01 00:00:00
Here datatype is DATETIME
Here is the code
SELECT ps.stage_start_date AS actual_start_date,
ps.stage_end_date AS actual_end_date,
ps.stage_planned_start_date AS planned_start_date,
ps.stage_planned_end_date AS planned_end_date
FROM project_stage AS ps
JOIN map_project_user AS mpu ON mpu.user_id = 22 AND mpu.project_id = 1 AND mpu.tenant_id = ps.tenant_id AND ps.project_id = mpu.project_id;
Result on JSON response
{
"actual_start_date": "2019-01-28T04:42:01.000Z",
"actual_end_date": "2020-12-08T03:35:28.000Z",
"planned_start_date": "2019-01-11T19:31:00.000Z",
"planned_end_date": "2020-11-30T18:30:00.000Z"
}
Here date time is changing its not the actual datetime which is in the table,why the date is changing on result.Here is the expected output
Expected Result
{
"actual_start_date": "2019-01-28 10:12:01",
"actual_end_date": "2020-12-08 09:05:28",
"planned_start_date": "2019-01-12 01:01:00",
"planned_end_date": "2020-12-01 00:00:00"
}
MYSQL DATATYPE is DATETIME. Data base timezone is in UTC and System timezone is also showing UTC, How can I covert this datetime corresponding to timezone of users system
According to the data examples, the Timezone issue appears to be in the code that converts the SQL result to JSON. Since the time difference between the database and the JSON is -05:30, it seems that the "JSON transformer" assumes that the result of the SQL query is IST (UTC +05: 30) and converts the time to UTC (subtracts 5:30).
The correct fix should be done in the "JSON transformer". However, if the requirement is to achieve the "corrected date" by modifying the SQL query, you can use the CONVERT_TZ (dt, from_tz, to_tz) function. This adds +5:30 and "JSON transformer" subtracts 5:30 later resulting the time being unchanged.
Something like that:
SELECT DATE_FORMAT(CONVERT_TZ(ps.stage_start_date, '+00:00', '+05:30'), "%Y-%m-%d %H:%i:%s") AS actual_start_date,
DATE_FORMAT(CONVERT_TZ(ps.stage_end_date, '+00:00', '+05:30'), "%Y-%m-%d %H:%i:%s") AS actual_end_date,
DATE_FORMAT(CONVERT_TZ(ps.stage_planned_start_date, '+00:00', '+05:30'), "%Y-%m-%d %H:%i:%s") AS planned_start_date,
DATE_FORMAT(CONVERT_TZ(ps.stage_planned_end_date, '+00:00', '+05:30'), "%Y-%m-%d %H:%i:%s") AS planned_end_date
FROM project_stage AS ps
JOIN map_project_user AS mpu ON mpu.user_id = 22
AND mpu.project_id = 1
AND mpu.tenant_id = ps.tenant_id
AND ps.project_id = mpu.project_id;
Edit: Another option: simply add +5:30 to the dates:
SELECT ps.stage_start_date + interval 5 hour + 30 minute AS actual_start_date,
ps.stage_end_date + interval 5 hour + 30 minute AS actual_end_date,
ps.stage_planned_start_date + interval 5 hour + 30 minute AS planned_start_date,
ps.stage_planned_end_date + interval 5 hour + 30 minute AS planned_end_date
FROM project_stage AS ps
JOIN map_project_user AS mpu ON mpu.user_id = 22
AND mpu.project_id = 1
AND mpu.tenant_id = ps.tenant_id
AND ps.project_id = mpu.project_id;
Your backend changes the time format. Check you API which connects to the database.

Converting SQL query to Hibernate query

I need help converting some sql to hibernate sql.
SQL:
String sql = "select time, hour(time) as hour, minute(time) as minute "
+ "from db where time >= DATE_ADD(now(), INTERVAL -24 HOUR) "
+ "group by 2 order by time LIMIT 500";
I use SQLQuery to add scalars. I tried this for HQL:
String hql = "select time, hour(time), minute(time) from db as O "
+ "where O.time >= :time group by 2 order by O.time";
Query query = session.createQuery(hql);
query.setDate("time", calend.getTime()); //calend is a Calendar object
However, this doesn't work. Error says it's an hql error.
Instead of using setDate try using setParameter.
From this link about HQL dates:
"setDate" will truncate the HQL date value and ignore the hours, minutes, seconds.
Using setParameter will interpret the values as the parameter that you indicated by :variable.

Illuminate database query with date_sub

I'm struggling with a query using the Illuminate database query builder.
When I use the query the result is not as I expected.
When using the query from the querylog directly with mysql cli, I get the expected result.
With query builder:
->table('CompanyTools')
->select(
'CompanyTools.toolId',
$db->raw('COUNT(CompanyTools.toolId) as count')
)
->whereYear('CompanyTools.date', '>', 'YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))')
->groupBy('CompanyTools.toolId')
->orderBy('count', 'DESC')
->take(1)
->get();
Result:
Array ( [toolId] => 88 [count] => 55 )
With mysql cli:
select `CompanyTools`.`toolId`, COUNT(CompanyTools.toolId) as count from `CompanyTools`
where year(`CompanyTools`.`date`) > YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))
group by `CompanyTools`.`toolId`
order by `count` desc
limit 1
Result:
ToolId: 88
count: 17
If I (in the query builder) replace 'YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))'with 2013 I get:
Array ( [toolId] => 88 [count] => 17 )
Somehow the date_sub get ignored so the result includes all years
I tried with ->whereYear('CompanyTools.date', '>', $db->raw('YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))')) without any luck.
I guess I could use php to calculate the desired year, but I would rather get the query right.
Thx in advance
/ j
UPDATE
Replacing
->whereYear('CompanyTools.date', '>', 'YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))')
with
->where($db->raw('YEAR(CompanyTools.date)'), '>', $db->raw('YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))'))
solves it. Not clever enough to figure out why, but perhaps the whereYear function is supposed to be used diffently
As you already found out using
->where($db->raw('YEAR(CompanyTools.date)'), '>', $db->raw('YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))'))
Or alternatively
->whereRaw('YEAR(CompanyTools.date) > YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))')
solves the problem.
But why is that?
For every "normal" query, Laravel uses bindings. Obviously SQL functions like YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR)) don't work with bindings.
Normally, you can use DB::raw('YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))') and the Laravel won't use bindings. For example in where() (Expression is the class DB::raw() returns)
if ( ! $value instanceof Expression)
{
$this->addBinding($value, 'where');
}
But the whereYear() function doesn't do such a thing. It uses addDateBasedWhere() and just adds a binding without checking if the value is an instance of Expression
protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
{
$this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');
$this->addBinding($value, 'where');
return $this;
}
This means the query will use bindings and therefore NOT execute the date calculation at all.

date_format parsing not working

Why does my date comparison not work,
mostly returning either all dates or none
Select DATE_FORMAT(time,'%Y - %m - %d'),'2013 - 01 - 10'
FROM signature,files
WHERE files.id = signature.id and instruction like '%BM%'
AND DATE_FORMAT(time,'%Y-%m-%d') < date('2013-01-10') ;
doesn't return result. when I change to DATE_FORMAT(time,'%Y-%m-%d') < '2013-01-10' same and even this DATE_FORMAT(time,'%Y %m %d') didn't work
DATE_FORMAT function returns a string with character set and collation.I am not sure if you will get correct comparison using DATE_FORMAT function.
Select DATE(time),'2013 - 01 - 10'
FROM signature,files
WHERE files.id = signature.id and instruction like '%BM%'
AND Date(time) < Date('2013-01-10') ;
or use STR_TO_DATE function.
SELECT STR_TO_DATE('2013,05,01','%Y,%m,%d');