Illuminate database query with date_sub - mysql

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.

Related

How to write the below eloquent query code using raw query in laravel?

My eloquent query:
$data = (new Model())->whereDate('start_date', '<=', Carbon::today())
->whereDate('end_date', '>=', Carbon::today())
->count();
The raw query I have tried:
$data = (new Model())->select(DB::raw("COUNT(id) as countData where date(start_date) <= NOW() and date(end_date) >= NOW()"))->get();
How can I write my eloquent query as raw query. The below raw query gives me syntax violation error;
This is because of how you are constructing your query. If you inspect the generated SQL statement you'll see that the SQL FROM clause is actually on the end of your statement and not at all where it should be.
You'll want to split your WHERE clauses out and use either where or whereRaw. For example:
$data = (new Model)->select(DB::raw('COUNT(id) as countData'))
->whereRaw('date("start_date") <= NOW() AND date("end_date") >= NOW()')
->get();
Your question was a little unclear but if you want to do what you did with eloquent in raw query, be noted that the select function of DB in raw by itself, so simply write your query inside select function:
$tableName = (new Model)->getTable();
$data = DB::select("
SELECT COUNT(`id`) as `countData` FROM `$tableName`
WHERE DATE(`start_date`) <= NOW() AND DATE(`end_date`) >= NOW()
")->first();
Alternatively you may use whereRaw function of your Model:
$data = Model::whereRaw(
"DATE(`start_date`) <= NOW() AND DATE(end_date) >= NOW()"
)->count();

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

Allowing user input once per minute

I have trouble getting this to work.
The thing is that a user is only allowed to create a comment once per minute. As simple as that....
$checkLastComment = Comment::where('user_id', '=', 1)
->where('created_at', '<', 'CURDATE() + INTERVAL 1 MINUTE')->count();
You should use here rather:
$checkLastComment = Comment::where('user_id', '=', $id)
->where('created_at', '>=', 'DATE_SUB(NOW(), INTERVAL 1 MINUTE)')->first();
if ($checkLastComment) {
// user not allowed to add new comment
}
3 issues in your code:
You need greater than >, not less than < operator
You need raw condition
You can't use curdate() since it's time part is 00:00:00 always.
So here's what you want:
$notAllowedToComment = Comment::where('user_id', '=', 1)
->where('created_at', '>', DB::raw('now() - INTERVAL 1 MINUTE'))->count();

Zend Db Sql Where

Hi how can I do a query like this in zf2 with zend\db\sql?
Query:
SELECT * FROM table WHERE field = $field AND data > SUBDATE(NOW(), INTERVAL 1 DAY)
In ZF2
$select = $this->sql->select();
$select->from(self::MYTABLE)
->where(array('fiels' => $field))
->where(array('data > ' => 'SUBDATE(NOW(), INTERVAL '.$lifetime.' SECOND'));
$statement = $this->sql->prepareStatementForSqlObject($select);
return $statement->execute()->current();
change the line
->where(array('data > ' => 'SUBDATE(NOW(), INTERVAL '.$lifetime.' SECOND'));
to
->where(array('data > ?' => 'SUBDATE(NOW(), INTERVAL '.$lifetime.' SECOND'));
From the code snippet, it's seen you had missed the place holder for the parameter(?), include a question mark, I had mentioned the existing line of code and the modified code for quick reference
There's no parameter there so it doesn't need to be an array. Assuming you know $lifetime is a safe, integer value, try:
->where('data > SUBDATE(NOW(), INTERVAL '.$lifetime.' SECOND)');

Convert MySQL fetch array query to redbean PHP

I have 2 buttons which execute a post operations and set a hidden variable which is used to set the MySQL query to filter the database according to date
if result = today
$query = "SELECT id,customer_name,CAST( `register_date` AS DATE ) AS dateonly,status,
DATE_FORMAT(book_date, '%m/%d/%y') FROM table WHERE book_date
BETWEEN (CURDATE() - INTERVAL 1 DAY) AND CURDATE()";
if result = week
$query = "SELECT id,customer_name,CAST( `register_date` AS DATE ) AS dateonly,status,
DATE_FORMAT(book_date, '%m/%d/%y') FROM table
WHERE book_date BETWEEN (CURDATE() - INTERVAL 7 DAY) AND CURDATE()";
I then want to use something like
$result=mysql_query($query);
while ($mytable=mysql_fetch_array($result))
{
loop and display all the information in array in a table
}
But I need the red bean equivalent of this.
The easiest way is to just paste the $query inside the sql function:
$results=R::getAll($query);
foreach($results as $row){
echo $row['id'];
}
The next way is to manually build the query.... which may just make it look sloppier in my opinion:
$results=R::$f->begin()->select('id, customer_name, CAST( register_date AS DATE ) AS dateonly,status, DATE_FORMAT(book_date, '%m/%d/%y')')->from('table')->where('book_date BETWEEN (CURDATE() - INTERVAL 1 DAY) AND CURDATE())->get();
The final way is to grab results via redbean and handle them manually:
$results=R::find('table','book_date BETWEEN (CURDATE() - INTERVAL 7 DAY) AND CURDATE()');
Then loop through the results, configuring data along the way in php.
I always use this when I have to access a lot of data from mysql:
while ($row = mysqli_fetch_array($query)) { #converts query into array
$array[] = $row;
}
$array will be a multidimensional array. $array[x][column_name] will get you your data, x being the row which you want to access it from. Hope this helped.