Yii: Why does this query take 15 seconds? - mysql

I am using Yii MCV with multiple dbs.
Why does it take 15 seconds to run this code?
How to improve?
I switch the db connection using a server_id integer value.
This is the class that switches the db connection.
class VillageSlaveM extends VillageM {
const UNVERIFIED = 0;
const VERIFIED = 1;
public static function model($className = __CLASS__) {
return parent::model($className);
}
public static $server_id;
public static $slave_db;
public function getDbConnection() {
self::$slave_db = Yii::app()->dbx;
if (self::$slave_db instanceof CDbConnection) {
self::$slave_db->active = false;
$config = require(Yii::app()->getBasePath() . '/config/main.php');
$connectionString = $config['components']['dbx']['connectionString'];
self::$slave_db->connectionString = sprintf($connectionString, self::$server_id);
self::$slave_db->setActive(true);
return self::$slave_db;
}
else
throw new CDbException(Yii::t('yii', 'Active Record requires a "db" CDbConnection application component.'));
}
}
This is the code that needs 15 seconds to execute, dont know why so much.
It selects the one village that has the oldest last_update_resource timestamp value.
I have set indexes for all the db table fields involved.
$criteria = new CDbCriteria();
$criteria->condition = 'last_update_resource <= ' . ($time_start - 60 * 60 * 8);
$criteria->order = 'last_update_resource asc';
$criteria->limit = 1;
VillageSlaveM::$server_id = $world_id;
$start_x = time();
$model_village = VillageSlaveM::model()->findByAttributes(array('map_type_id' => VillageM::$map_type_id['village'], 'status' => VillageM::ACTIVE), $criteria);
$stop_x = time();
$msg[] = 'start_x: ' . ($stop_x - $start_x);
ps: after this code runs, i have much more complex queries and they run instantly;

Related

Change or Update SQL Query the proper way

I am working on a database project using the Spring JDBC API with MySQL and Angular. I want to update the SQL query efficiently and get back that data from the DB. Right now i'm using StringBuilder to manually update the SQL query and getting the data back. Is there a better way to do this?
The initial state of the query is,
private String SQL_MZGTE = "SELECT * FROM `protein` WHERE `auroc_for_mixture_0` >= 0.65";
Receiving the name and dataset name from the controller (#RequestBody). This is what i'm doing at the repository level,
#Override
public void filter(Params[] params) {
if (!(params.length == 0)) {
var datasetList = new ArrayList<String>();
var SQL = new StringBuilder("SELECT * FROM `protein` WHERE `auroc_for_mixture_0` >= 0.65");
SQL.append(" AND ");
for (var dataset : params) {
if (!datasetList.contains(dataset.getOfDataset())) {
SQL.append("`dataset`").append(" = ").append("'").append(dataset.getOfDataset()).append("'").append(" OR ");
datasetList.add(dataset.getOfDataset());
}
}
SQL.replace(SQL.lastIndexOf("OR"), SQL.lastIndexOf("OR") + 2, "AND");
SQL.append("(");
for (var omicsFeature : params) {
SQL.append("`feature_type`").append(" = ").append("'").append(omicsFeature.getOmicsFeature()).append("'").append(" OR ");
}
this.SQL_MZGTE = SQL.substring(0, SQL.lastIndexOf("OR") - 1) + ")";
}
System.out.println("PARAMETERS --> " + Arrays.toString(params));
System.out.println("SQL --> " + this.SQL_MZGTE);
}
This is what i'm receiving through the #RequestBody,
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Params {
private String omicsFeature;
private String ofDataset;
}
From another endpoint i'm getting this as the #RequestBody,
#Getter
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class CancerTypeParam {
private String cancerType;
}
The goal is to combine omicsFeature as a union (or) if from the frontend multiple omicsFeature are being selected and same for the cancerType as well, but the there should be an intersection (and) between these two, so the query should be something like this,
SELECT * FROM `TABLE_NAME` WHERE `auroc_for_mixture_0` >= 0.65 AND `dataset` = 'DATASET_NAME' AND (`feature_type` = 'ft1' OR `feature_type` = 'ft2') AND (`cancer_type` = 'ct1' OR `cancer_type` = 'ct2');
And when the checkbox is unchecked at the frontend, the query should be changed or updated as well. If i'm unchecking the cancer_type 'ct1' the query should be like this,
SELECT * FROM `TABLE_NAME` WHERE `auroc_for_mixture_0` >= 0.65 AND `dataset` = 'DATASET_NAME' AND (`feature_type` = 'ft1' OR `feature_type` = 'ft2') AND (`cancer_type` = 'ct2');
These are just 2 constraints, there are many more in the DB. How can I do this efficiently?
Thanks in Advance!

A circular reference has been detected (configured limit: 1) Serializer SYMFONY [duplicate]

I am using Doctrine 2 and Zend framework since a few days.
I am generating my entities across yaml files.
Now I met an issue to convert my entities Doctrine into Json format (in order to use it through AJAX).
Here is the code used :
$doctrineobject = $this->entityManager->getRepository('\Entity\MasterProduct')->find($this->_request->id);
$serializer = new \Symfony\Component\Serializer\Serializer(array(new Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer()), array('json' => new Symfony\Component\Serializer\Encoder\JsonEncoder()));
$reports = $serializer->serialize($doctrineobject, 'json');
below is the return I get :
Fatal error: Maximum function nesting level of '100' reached, aborting! in /Users/Sites/library/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php on line 185
the issue seems to be the same than here :
http://comments.gmane.org/gmane.comp.php.symfony.symfony2/2659
but there is not proper solution proposed.
Any idea how can I do it ?
Cheers
I solved the same problem by writing my own GetSetNormalizer my class. Defined static variable in a class for branching
class LimitedRecursiveGetSetMethodNormalizer extends GetSetMethodNormalizer
{
public static $limit=2;
/**
* {#inheritdoc}
*/
public function normalize($object, $format = null)
{
$reflectionObject = new \ReflectionObject($object);
$reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
$attributes = array();
foreach ($reflectionMethods as $method) {
if ($this->isGetMethod($method)) {
$attributeName = strtolower(substr($method->name, 3));
$attributeValue = $method->invoke($object);
if (null !== $attributeValue && !is_scalar($attributeValue) && LimitedRecursiveGetSetMethodNormalizer::$limit>0) {
LimitedRecursiveGetSetMethodNormalizer::$limit--;
$attributeValue = $this->serializer->normalize($attributeValue, $format);
LimitedRecursiveGetSetMethodNormalizer::$limit++;
}
$attributes[$attributeName] = $attributeValue;
}
}
return $attributes;
}
/**
* Checks if a method's name is get.* and can be called without parameters.
*
* #param ReflectionMethod $method the method to check
* #return Boolean whether the method is a getter.
*/
private function isGetMethod(\ReflectionMethod $method)
{
return (
0 === strpos($method->name, 'get') &&
3 < strlen($method->name) &&
0 === $method->getNumberOfRequiredParameters()
);
}
}
And usage
LimitedRecursiveGetSetMethodNormalizer::$limit=3;
$serializer = new Serializer(array(new LimitedRecursiveGetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$response =new Response($serializer->serialize($YOUR_OBJECT,'json'));
JMSSerializerBundle seems to handle circular references fine.

Mysql functions in zend 2

How to query below in zend 2
select * from states st where TRIM(LOWER(st.state_name))='noida'
Any help is appreciated.
Thanks
/* DB Adapter get and SQL object create */
$adapter = GlobalAdapterFeature::getStaticAdapter();
$sql = new \Zend\Db\Sql\Sql($adapter);
/* Select object create */
$select = new \Zend\Db\Sql\Select();
$select->from('states');
$select->where->addPredicate(
new \Zend\Db\Sql\Predicate\Expression(
'TRIM(LOWER(state_name)) = ?',
'noida'
)
);
/* Select object convert to string and execute */
$queryString = $sql->getSqlStringForSqlObject($select);
$result = $adapter->query($queryString, Adapter::QUERY_MODE_EXECUTE);
Use following:
$resultStates=$this->states->select()->where('TRIM(LOWER(st.state_name))=?','noida')
->query()
->fetchAll();
For details refer Here and Here.
In you model file just use below code here I am using module profile.
Profile/Model/Common.php
namespace Profile\Model;
use Zend\Db\Sql\Sql;
use Zend\Db\Adapter\Adapter;
use Zend\Db\ResultSet\ResultSet;
use Zend\Db\Sql\Select;
class Common
{
protected $dbConfig;
protected $adapter;
public function __construct($dbConfig)
{
$this->adapter = new Adapter($dbConfig);
}
public function getStateList()
{
$sql = "select * from states st where TRIM(LOWER(st.state_name))='noida'";
$statement = $this->adapter->query($sql);
$results = $statement->execute();
$resultSet = new ResultSet();
$resultSet->initialize($results);
$list = $resultSet->toArray();
return $list; // This will return a list of array
}
}
Profile/Controller/IndexController
namespace Profile\Controller;
use Profile\Model\Common;
class IndexController extends AbstractActionController
{
protected $dbConfig = array(
'driver' => DRIVER,
'database' => DB,
'username' => DBUSER,
'password' => DBPASS
);
public function init(){
$ssOrder = new Container(__CLASS__);
//SET OPTIONS
}
public function indexAction()
{
$plist = new Common($this->dbConfig);
$resultList = $plist->getStateList(); // This will give you state list
}
}
Good Luck

Getting the last User ID in Zend Framework

Using MySQL query browser, I manually made a table called users and input some date in the fields. I set the primary key to id and set it to auto increment. There are 3 rows, the highest id is 3.
I then made the following class in the method directory to call upon the data in the table etc.
class Application_Model_DbTable_User extends Zend_Db_Table_Abstract
{
protected $_name = 'user';
public function getLatestUserId()
{
$id = $this->getAdapter()->lastInsertId();
return $id;
}
}
In the controller I do the following which gets the value generated by the method and lets the view access it:
$usersDbModel = new Application_Model_DbTable_User();
$lastUserId = $usersDbModel->getLatestUserId();
$this->view->lastUserId = $lastUserId;
In the view I then echo it to display it to the user:
echo $this->lastUserId;
However, even though my last id in the users table is 3. It displays 0.
I have also tried:
public function getLatestUserId()
{
$sql = 'SELECT max(id) FROM user';
$query = $this->query($sql);
$result = $query->fetchAll();
return $result;
}
But this just throws out a server error.
What have I done wrong?
Am I missing something?
Is there another way of doing this?
The answer was:
$sql = 'SELECT max(id) FROM user';
$query = $this->getAdapter()->query($sql);
$result = $query->fetchAll();
return $result[0]['max(id)'];
If you queried like "SELECT id FROM USERS SORT BY id DESC LIMIT 0,1"
You would get nothing but the id of the newest user, no?
if you use a select statement like
SELECT max(id) FROM USERS;
If you haven't tried having a function in the controller try something like this.
class IndexController extends Zend_Controller_Action {
public function init() {
}
public function indexAction() {
}
public function getLatestUserId()
{
$bootstrap = $this->getInvokeArg('bootstrap');
$resource = $bootstrap->getPluginResource('db');
$db = $resource->getDbAdapter();
$sqlrequest = "select max(id) as Idmax from user";
$rows = $db->fetchAll($sqlrequest);
foreach ($rows as $row)
echo $maxId = $row['Idmax'];
return $maxId;
}
}
and your bootstrap file would look something like
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initConfig()
{
$config = new Zend_Config($this->getOptions());
Zend_Registry::set('config', $config);
return $config;
}
}
Hopefully something like that works, hope it helped
The problem is that Mysql doesn't really support lastInsertId() it will in some cases return the last id of an auto incrementing primary key.
Several solutions have been presented here and most will return the last id of the primary key, which may or may not be what you really need.
This method will also do the same using the select() object.
public function getlastInsertId(){
$select = $this->select();
$select->from($this->_name, "id");
$select->order('id DESC');
$select->limit(0, 1);
$result = $this->fetchAll($select)->current();
return $result->id;
}
I think that over time you may find these kinds of methods unreliable if what you're really after is the last id you inserted. Over time as records are added and deleted the database may or may not fill in vacant id's depending on which database you are currently using and how that database is set up.
In most cases we need the id of the last item inserted very soon after inserting it, usually to update a view script. In this instance I usually just make the whole row object part of the return.
Here is an example of what I mean:
/**this method will save or update a record depending on data present in the array*/
public function saveUser(array $data) {
/**cast data array as std object for convience*/
$dataObject = (object) $data;
/**if id exists as array and has a non null value*/
if (array_key_exists('id', $data) && isset($data['id'])) {
/**find row if updating*/
$row = $this->find($dataObject->id)->current();
} else {
/**create new row*/
$row = $this->createRow();
}
$row->first_name = $dataObject->first_name;
$row->last_name = $dataObject->last_name;
$row->email = $dataObject->email;
/**save or update row*/
$row->save();
/**return the $row you just built or you can return $result = $row->save(); and get*/
/** just the primary key. Save() will throw an exception if table is marked read only*/
return $row;
}

AMFPHP complex sql Internal Server Error gateway.php

OK, I am new to AMFPHP. I can do simple sql statements with no problem.
Here are my classes of Schools and Locations:
class Schools {
public $id;
public $district_id;
public $school_name;
// explicit actionscript class
var $_explicitType = "components.Schools";
}
class Locations {
public $id;
public $school_id;
public $school_address;
public $icon_id;
// explicit actionscript class
var $_explicitType = "components.Locations";
}
This simple sql statement works with no issues:
/**
* Retrieves schools data
* #returns id, district_id, school_name
*/
public function getSchools() {
//connect to the database.
$mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);
mysql_select_db(DATABASE_NAME);
//retrieve all rows
$query = "SELECT * FROM schools ORDER BY school_name";
$result = mysql_query($query);
$ret = array();
while ($row = mysql_fetch_object($result)) {
$tmp = new Schools();
$tmp->id = $row->id;
$tmp->district_id = $row->district_id;
$tmp->school_name = $row->school_name;
$ret[] = $tmp;
}
mysql_free_result($result);
return $ret;
}
However, I am having a hard time making a "somewhat" complex sql statement work properly.
/**
* Retrieves schools with locations data
* #returns school_name, school_address
*/
public function getSchoolsLocations() {
//connect to the database.
$mysql = mysql_connect(DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD);
mysql_select_db(DATABASE_NAME);
//retrieve all schools with their locations
$query = "SELECT schools.school_name AS SNAME, school_address AS SLOC FROM schools, locations WHERE schools.id = locations.school_id";
$result = mysql_query($query);
$ret = array();
while ($row = mysql_fetch_object($result)) {
$tmp = new Schools();
$tmp->id = $row->id;
$tmp->school_name = $row->SNAME;
$ret[] = $tmp;
$tmp2 = new Locations();
$tmp2->school_id = $row->school_id;
$tmp2->school_address = $row->SLOC;
$ret2[] = $tmp2;
array_splice($ret, count($ret), 0, $ret2);
}
mysql_free_result($result);
return $ret;
}
BTW, SELECT schools.school_name AS SNAME, school_address AS SLOC FROM schools, locations WHERE schools.id = locations.school_id works perfectly in mysql.
It doesn't look like you are declaring $ret2.
FWIW: you might get better performance with your query if you format it as a join. Just a thought.