Doctrine 2 DQL - nest expressions and functions in the QueryBuilder - php

I have found DoctrineExtensions module that has implement some useful MySQL functions. But I think my problem is a general one. How would you pass another mysql expression as a parameter into a function?
If I had 2 columns updated_at which is a DATETIME and lifespan which is an INT and I wanted to query whether the lifetime of an entity had expired, I could do this;
WHERE 0 > TIMESTAMPDIFF(SECONDS, NOW(), e.updated_at + INTERVAL e.lifespan SECOND)
IN the QueryBuilder it's something like this.
$qb->andWhere('0 > TIMESTAMPDIFF(SECONDS, CURRENT_TIMESTAMP(), e.updated_at + INTERVAL e.lifespan SECOND)');
But the parser doesn't like + INTERVAL e.lifespan SECOND because it expects the Close Parenthesis instead of a plus sign.

I realized I can use the DateAdd class from the DoctrineExtensions module.
TIMESTAMPDIFF(SECOND, CURRENT_TIMESTAMP(), DATEADD(e.updated_at, INTERVAL e.lifespan SECOND))

Related

mySQL Workbench Error: Syntax Error, unexpected single quotes

I was trying to make a SQL statement in PHP, to convert a string into a time(6). But I have tried everything, for the last 12 hours, and have not made an inch of progress. I have tried these statements, all yield the same error.
UPDATE scheduling SET start='03:42PM' WHERE activityid=2;
UPDATE scheduling SET start=CONVERT(TIME(6),'03:42PM');
INSERT INTO scheduling(start) VALUES (start=CONVERT(TIME(6),'03:42PM'));
INSERT INTO scheduling(start) VALUES (start=CONVERT(TIME(6),'03:42PM'));
INSERT INTO scheduling(start) VALUES (start=CONVERT(TIME(6),'15:42'));
The error is
Syntax Error: unexpected '03:42PM'(single quoted text)"
I do not know how to fix this, the table exists, and i have sucesfully got other info using statements like SELECT activityid=2 FROM xxxxxx.scheudling
I guess I have two questions, either answer would work.
In my PHP document, how would I convert a string I get in from an Android Studio volley to a date. (I get the variable correctly, with $start=$_Post("start"), so that works, but I cant convert it into a time. I looked online, and tried everything that looked like it work work.
Conversion through SQL Code, I already tried CAST and CONVERT, neither works. My start column is type TIME(6).
I recommend testing expressions using a SELECT statement.
Firstly, the MySQL CONVERT function arguments are flipped around backwards.
The syntax is CONVERT(expr,type)
And type is supplied as a keyword, not a string literal. For example:
SELECT CONVERT('235',SIGNED)
To convert to a TIME datatype
SELECT CONVERT( '15:42' ,TIME(6)) // => 15:42:00.000000
The 'PM' part of the string literal will be ignored.
SELECT CONVERT( '03:42PM' ,TIME(6)) // => 03:42:00.000000
We can use the STR_TO_DATE function to return a TIME value from a string that contains the AM/PM indicator
SELECT STR_TO_DATE( '03:42PM' ,'%h:%i%p')
And there's no need to cast that to TIME(6), we can do this:
UPDATE scheduling
SET start = STR_TO_DATE( '03:42PM' ,'%h:%i%p')
WHERE activityid = 2
The STR_TO_DATE function is documented here:
https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_str-to-date
The format patterns for STR_TO_DATE are documented here, under DATE_FORMAT:
https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_date-format
FOLLOWUP
Demonstration:
setup
USE test;
CREATE TABLE scheduling (activityid INT PRIMARY KEY, start TIME(6));
-- 0 row(s) affected
INSERT INTO scheduling (activityid) VALUES (2);
-- 1 row(s) affected
execute the update statement in the answer above
UPDATE scheduling SET start = STR_TO_DATE( '03:42PM' ,'%h:%i%p') WHERE activityid = 2 ;
-- 1 row(s) affected
results
SELECT * FROM scheduling WHERE activityid = 2;
-- activityid start
-- ---------- ---------------
-- 2 15:42:00.000000
SECOND FOLLOWUP
Use same sql_mode setting reported by OP:
SET ##sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION' ;
Test:
SELECT STR_TO_DATE( '03:42PM' ,'%h:%i%p')
returns
(NULL)
But this more complicated expression:
SELECT TIME(STR_TO_DATE(CONCAT(CURRENT_DATE(),' ', '03:42PM' ),'%Y-%m-%d %h:%i%p'))
returns
15:42:00
The more complicated expression is a workaround to avoid behavior imposed by the STRICT_TRANS_TABLES and NO_ZERO_DATE in the sql_mode.

Using 'DATE_SUB(now(), INTERVAL 2 MONTH)' properly with Codeigniter

I'm trying to run this query with Codeigniter:
SELECT * FROM `bf_bs_history` WHERE date > DATE_SUB(now(), INTERVAL 2 MONTH)
If I enter it directly in phpMyAdmin, I get the result I want. However, running if from the code, it will not take any effect. It's not filtering at all.
The PHP line looks like this:
$this->history_model->where(array('date > ' => 'DATE_SUB(now(), INTERVAL 2 MONTH)'))->find_all();
Any idea where I go wrong?
CodeIgniter Active Record is adding backticks to your statement, which renders your clause as a string, not an executable function. You can set a second parameter to false to stop that. Also, for a function predicate like this you can simply pass in the string:
$this->history_model->where("date > DATE_SUB(now(), INTERVAL 2 MONTH)", false)->find_all();

MySQL Datetime Less Than Functionality

I have a table with some datetime columns. I need to be able to select the rows between two datetimes on the same column.
For example:
SELECT *
FROM `t_punchcards`
WHERE UserID = 'root' AND
PunchInTime > CAST('01-01-2015 00:00:00' AS DATETIME) AND
PunchInTime < CAST('03-06-2015 23:59:59' AS DATETIME);
For some strange reason, this yields no results. I have tried using BETWEEN, I've tried not casting to datetime from string (just use a raw string), I've tried <= >=. I'm very lost.
As a sidenote, removing the second constraint (AND PunchInTime < CAST('03-06-2015 23:59:59' AS DATETIME); WILL in fact yield results. Are you not allowed to do a comparison against the same column in the same query?
Here's an example of the table:
UserID(varchar) PunchInTime(datetime) PunchOutTime(datetime)
root 01-01-2015 8:02:31 01-01-2015 12:35:51
Just write this logic as:
WHERE UserID = 'root' AND
PunchInTime > DATE('2015-01-01') AND
PunchInTime < DATE('2015-03-07')
Note that I removed the time component from the second value. This is a cleaner way of making the comparison, unless you really want to treat the last few milliseconds of a day differently from the rest of the time during the day.
Note: If you are not using MySQL or you want more compliant code, the following works in more databases:
WHERE UserID = 'root' AND
PunchInTime > CAST('2015-01-01' as DATETIME) AND
PunchInTime < CAST('2015-03-07' as DATETIME)

Adding a variable interval to a variable timestamp

Ok, first of all I have very little clue how to use php, and only a slightly better understanding of sql. So if anything I do appears really, really stupid, please bear with me.
Given a user input of a date, a time and a time interval, I need to get two timestamps without timezones - a starting time, and an endtime.
e.g.
function myfunction($startdate, $starttime, $numhours){
$start = $startdate.' '.$starttime;
//I know this works for a timestamp because I have used this value in other sql statements and have been returned the correct results
$handler = $connection->prepare("SELECT TIMESTAMP :start + INTERVAL :numhours ' HOURS'");
$handler->bindParam(':start', $start);
$handler->bindParam(':numhours', $numhours);
$handler->execute();
$end = $handler->fetchAll();
This just gets me the following error:
Submitting booking.SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "$1" LINE 1: SELECT TIMESTAMP $1 + INTERVAL $2 ' HOURS'
I haven't been able to find anything that really tells me what I'm doing wrong here (which is probably a lot). Help is appreciated.
EDIT: I'm using postgreSQL, through `pgAdmin III
EDIT: $start should have the form 'YYYY-MM-DD HH:MM:SS'
The problem is, you use the literal notation with values (parameters) and not with constants.
The literal notation only accepts contants, like:
SELECT TIMESTAMP '2014-05-25 14:29:59' + INTERVAL 3 HOUR;
It cannot accept values (expressions) other than constants (and bound parameters are not constants.):
-- this will throw a syntax error
SELECT TIMESTAMP CONCAT('2014-05-25', ' ', '14:29:59');
You can use the CAST form over the literal notation here:
$handler = $connection->prepare('SELECT CAST(:start AS TIMESTAMP) + ' .
'CAST(:numhours || \' hours\' AS INTERVAL)');
$handler->bindParam(':start', $start);
$handler->bindParam(':numhours', $numhours);
$handler->execute();
EDIT: or, you can use PDO::ATTR_EMULATE_PREPARES to use real literals inside your query, but I believe postgres' own prepare functionality is better a choice.

ZF2 mysql query with interval

In my application I want to print a list with members who where online the last 5 minutes.
I tried to create this by mysql interval with the code below;
$interval = 5;
$select->where(array('member_last_online > ?' => 'SUBDATE(NOW(), INTERVAL '.$interval.' MINUTE)'));
Which produce this notice;
Notice: Attempting to quote a value without specific driver level support can introduce security vulnerabilities in a production environment. in ..zf_path..
The output from 'getSqlSTring()' is;
SELECT "members".* FROM "members" WHERE member_last_online > 'SUBDATE(NOW(), INTERVAL 5 MINUTE)'
If I execute the query below directly on the table it returns the data as accepted:
SELECT members.* FROM members WHERE member_last_online > SUBDATE(NOW(), INTERVAL 5 MINUTE)
What am I do wrong here?
You need to pass the db platform to getSqlString() to avoid the warning (so it knows how to quote values):
// assuming $db is the DB adapter instance
echo $select->getSqlString($db->getPlatform());
the solution to your main question is:
$select->where(new \Zend\Db\Sql\Predicate\Expression('SUBDATE(NOW(), INTERVAL '.$interval.' MINUTE)'));
or more readably:
use Zend\Db\Sql\Predicate\Expression;
$select->where(new Expression('SUBDATE(NOW(), INTERVAL '.$interval.' MINUTE)'));

Categories