zend database query limit not working as desired - mysql

i have a query to read magazine from db as
$select = $db->select()
->from('tbl_magazine',array('magazine_id','magazine_name'))
->join('tbl_magazine_issue','tbl_magazine_issue.magazine_id = tbl_magazine.magazine_id',array(null))
->join('mst_publisher','mst_publisher.publisher_id = tbl_magazine.publisher_id',array(null))
->where('tbl_magazine.is_active =?',1)
->where('mst_publisher.is_active =?',1)
->where('tbl_magazine_issue.os_select =?',2)
->where('tbl_magazine_issue.publish_status = ?',1)
->where('curdate() <= DATE(tbl_magazine.validity_till)')
->group('tbl_magazine.magazine_id')
->limit($start,$per_page);
but when i print the query i see some thing like this
SELECT `tbl_magazine`.`magazine_id`, `tbl_magazine`.`magazine_name`
FROM `tbl_magazine`
INNER JOIN `tbl_magazine_issue` ON tbl_magazine_issue.magazine_id = tbl_magazine.magazine_id
INNER JOIN `mst_publisher` ON mst_publisher.publisher_id = tbl_magazine.publisher_id
WHERE (tbl_magazine.is_active =1) AND (mst_publisher.is_active =1)
AND (tbl_magazine_issue.os_select =2) AND (tbl_magazine_issue.publish_status = 1)
AND (curdate() <= DATE(tbl_magazine.validity_till))
GROUP BY `tbl_magazine`.`magazine_id`
LIMIT 2147483647 OFFSET 8
but i have set $start as 0 and $perpage as 8
i was looking for a query with limit as "limit 0 ,8"

you are not using limit correctly.
Here is function definition from Zend_Db_Select. As you can see 1st param is count and not offset.
/**
* Sets a limit count and offset to the query.
*
* #param int $count OPTIONAL The number of rows to return.
* #param int $offset OPTIONAL Start returning after this many rows.
* #return Zend_Db_Select This Zend_Db_Select object.
*/
public function limit($count = null, $offset = null)
{
$this->_parts[self::LIMIT_COUNT] = (int) $count;
$this->_parts[self::LIMIT_OFFSET] = (int) $offset;
return $this;
}
And just to explain why you were getting those crazy values... Below is part of script that renders the SQL query. Because you had offset it was setting count to PHP_INT_MAX.
if (!empty($this->_parts[self::LIMIT_OFFSET])) {
$offset = (int) $this->_parts[self::LIMIT_OFFSET];
$count = PHP_INT_MAX;
}

Related

Selecting next related row in MySQL

I have a spatial dataset in MySQL 5.7 where I have the columns: id, deviceid, latlng_point, time. latlng_point is a geospatial Point.
What I'm trying to achieve is calculating distance from points. I'm unsure on how to approach this.
SELECT
ST_DISTANCE_SPHERE(latlng_point, i want the next latlng_point here) AS distance
FROM points
WHERE deviceid = 1
ORDER BY time DESC;
In PHP I would do something like this:
<?php
$conn = new mysqli($host,$user,$pass,$db);
$query = "SELECT latlng_point FROM points WHERE deviceid = 1...";
$latlng_array = array();
if ($result = $conn->query($query)) {
while ($row = $result->fetch_assoc()) {
$latlng_array[] = $row;
}
}
$distance = 0;
for ($i = 0; $i < count($latlng_array) - 1; $i++) {
$pt1 = $latlng_array[$i]['latlng_point'];
$pt2 = $latlng_array[$i+1]['latlng_point'];
$distance += haversine_function($pt1,$pt2);
}
echo "Distance: {$distance}";
?>
I'm trying to achieve something similar purely in MySQL.
Try this one:
SELECT SUM(ST_DISTANCE_SPHERE(p1.latlng_point, p2.latlng_point)) AS total_distance
FROM points p1
JOIN points p2 ON p2.id = (
SELECT p.id
FROM points p
WHERE p.deviceid = p1.deviceid
AND p.time > p1.time
ORDER BY p.time ASC
LIMIT 1
)
WHERE p1.deviceid = 1
The (correlated) subquery should return the id of the next point (sorted by time).
I can't tell you if it is really efficient or if it even works at all (can't test it).
However you should have an index on (deviceid, time) - Assuming that id is the primary key.

mysql array selecting and sum values

I own an array that three records containing:
value datetime
2 03/03/2015 14:34:00
4 03/03/2015 14:36:00
5 03/03/2015 13:34:00
I want to select the records that are on time 14 and sum them. In the above example would be 4 + 2 = 6
How can I do this?
$sql ="SELECT amperagem, data FROM tomada WHERE date(data) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)";//
mysql_select_db('localiza');
$retval = mysql_query( $sql, $conn );
$num_rows = mysql_num_rows($retval);
while($row = mysql_fetch_array($retval, MYSQL_BOTH)){
$hour= substr(($row['data']),11, 2);
The sql is as simple as:
select sum(value) from tomada where hour(datetime) = 14;
You can actually get yourself the value through a single SQL query:
Be sure to read up on http://www.w3schools.com/sql/func_datepart.asp
So to get all records with a datetime containing 14:XX:XX
SELECT value FROM tomada WHERE HOUR(data) = 14;
Now simply get the rows like you did and retrieve the 'value' per row and add them up
$res = mysql_query( $sql, $conn );
$returnObjects = array();
if ($res == null) {
return $returnObjects;
}
while(!is_null($row = mysql_fetch_row($res))){
$returnObjects[] = $row;
}
$returnArray = mysql_fetch_array($returnObjects);
$sum = 0;
for($row = 0, $size = count($returnArray); $row < $size; $row++){
$sum += $returnArray[$row][0]; //Note 0 is the value you need
}
return $sum;
Note it can be done in less lines of codes with less steps but I find this helps reading what i'm doing. Also some additional checks if certain objects are NULL or values are invalid is recommended.

Eloquent: Nested query to revert order in limited result

The following function (in my User Model) gives me the correct result for my chat system. Almost... I need to revert the order of the results.
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take($limit)->get();
return $res;
}
It returns an object and I need an object as result. I tried already to convert the result to an array, revert the order and then convert the array back to object. This didn't work.
What I need is a nested query like the following raw SQL query:
SELECT *
FROM (
SELECT *
FROM chatmessages
WHERE (
receipient = '422'
AND sender = '22'
)
OR (
receipient = '22'
AND sender = '422'
)
ORDER BY created_at DESC
LIMIT 0 , 20
)faketable
ORDER BY created_at ASC
There are a few articles with nested queries, but I don't find a similar case and it would be good if someone could do this in Eloquent without the use of Raw queries... It must be possible.
Try this..
use take() and skip(),offset()
get 4 items from offset 3/4th:
Chatmessage::take(4)->offset(3)->get();
Or this (get 10 items from 8rd row):
Chatmessage::take(10)->skip(2)->get();
public function getChatConv($cp, $page=1){
$limit = $page * 20;
$user = Authek::curUser();
$res = Chatmessage::where('receipient',$cp)->where('sender',$user->id)
->orWhere('receipient',$user->id)->where('sender',$cp)
->orderBy('created_at','desc')->take(3)->skip(2)->get();
return $res;
}

How to set sql_mode in Zend Framework 2?

I'm currently having an issue with pagination in Zend Framework 2.
This code
public function findAllByCriteria(CourseSearchInput $input) {
$concatDelimiter = self::CONCAT_DELIMITER;
$select = new Select();
$where = new Where();
$having = new Having();
$select->columns(array(
'id', 'title', 'description'
));
$select->from($this->tableGateway->getTable());
$select
->join('coursedata', 'courses.id = coursedata.id', array(
'relevance' => $this->buildRelevanceExpressionFromCriteria($input)
))
;
$having
->greaterThanOrEqualTo('relevance', self::RELEVANCE_MIN);
;
$select->where($where, Predicate::OP_AND);
$select->having($having);
$select->group(array('courses.id'));
$dbAdapter = $this->tableGateway->getAdapter();
// $dbAdapter->getDriver()->getConnection()->execute('SET sql_mode = "";');
$adapter = new \Zend\Paginator\Adapter\DbSelect($select, $dbAdapter);
$paginator = new \Zend\Paginator\Paginator($adapter);
return $paginator;
}
create this SQL:
SELECT
`courses`.`id` AS `id`,
`courses`.`title` AS `title`,
`courses`.`description` AS `description`,
MATCH (coursedata.title) AGAINST ('Salsa') * 5 + MATCH (coursedata.description) AGAINST ('Salsa') * 2 AS `relevance`
FROM `courses`
INNER JOIN `coursedata` ON `courses`.`id` = `coursedata`.`id`
GROUP BY `courses`.`id`
HAVING `relevance` >= '3'
It ueses the MySQL Extensions to GROUP BY and cannot be executed, if the sql_mode is set to ONLY_FULL_GROUP_BY. So, I tried to reset the sql_mode before the statement is executed (see the commented out line above: $dbAdapter->getDriver()->getConnection()->execute('SET sql_mode = "";');). But it didn't worked. So, how can I set the sql_mode in order to execute my non-standard SQL?
This may not be the answer to the question you are asking, but I can see you are going to have an issue with your query regardless when using Paginator.
The DbSelect Adapter for the Paginator doesn't like the aggregate function in there (Group By)
The Paginator will try and use your query to build it's own query to calculate the "count" for items in the collection. This is broken due to you using an aggregate in your query, any groups etc will break the adapter.
if you check the default implementation you will see:
/**
* Returns the total number of rows in the result set.
*
* #return integer
*/
public function count()
{
if ($this->rowCount !== null) {
return $this->rowCount;
}
$select = clone $this->select;
$select->reset(Select::COLUMNS);
$select->reset(Select::LIMIT);
$select->reset(Select::OFFSET);
// This won't work if you've got a Group By in your query
$select->columns(array('c' => new Expression('COUNT(1)')));
$statement = $this->sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
$row = $result->current();
$this->rowCount = $row['c'];
return $this->rowCount;
}
this doesn't like when you are using Group BY and will give back incorrect results.
You can create your own adataper, and extend the existing DbSelect and override the count method when you are planning to use Group BY;
Off the top of my head something like this should work, but may not be the most efficient way of doing it
/**
* Returns the total number of rows in the result set.
*
* #return integer
*/
public function count()
{
if ($this->rowCount !== null) {
return $this->rowCount;
}
/**
* If the query hasn't got 'GROUP BY' just try and use the old method
*/
$stateGroup = $this->select->getRawState('group');
if( ! isset($stateGroup) || empty($stateGroup)) {
return parent::count();
}
$select = clone $this->select;
$select->reset(Select::LIMIT);
$select->reset(Select::OFFSET);
$statement = $this->sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
$this->rowCount = $result->count();
return $this->rowCount;
}

Dynamic mysql query LIMIT not working

I'm creating the parameters to LIMIT a mysql query using PHP variables. I'm outputting the query to make sure it's right, and it is. However the results completely ignore the offset parameter. However if I manually type in the LIMIT, it works fine.
$page_rows = 5;
$max = ($pagenum - 1) * $page_rows .',' .$page_rows;
$qry = "SELECT * FROM ".$wpdb->prefix."nslikethis_votes WHERE user_id = '$uid' AND active = 1 ORDER BY time DESC LIMIT " . $max;
$rs = mysql_query($qry);
$result = array();
if($rs && $rows>0){
while($lc = mysql_fetch_object($rs, "LikedContent")){
$result[] = $lc;
}
}
return $result;
Outputting $qry gives me this whether I use $max or manually enter '5,5':
SELECT * FROM wp_nslikethis_votes WHERE user_id = '1' AND active = 1 ORDER BY time DESC LIMIT 5,5
Try doing this for check:
$page_rows = "5";
$max = ($pagenum - 1) * $page_rows .',' .$page_rows;
$qry = "SELECT * FROM ".$wpdb->prefix."nslikethis_votes WHERE user_id = '$uid' AND `active` = 1 ORDER BY `time` DESC LIMIT " . $max;
//check for variables in query like $wpdb->prefix, $uid: are they fine, and try adding tilded sign as above
Hope this works for you