I have installed the latest version of CI 2.1.3
Now after running a query, I am getting a very slow response time for something very simple such as:
function Bash(){
$sql = “SELECT * FROM Contacts”;
$stmt = sqlsrv_query( $conn, $sql );
if( $stmt === false) {
die( print_r( sqlsrv_errors(), true) );
}
after querying a remote database. (Sql server 2008)
When I run this same query in a simple PHP script against the same remote database. I get results instantly.
a) Has anyone else experienced this problem with the sqlsrv drivers in codeigniter?
If so, how did you solve it?
Here is my connection string:
$db['default']['hostname'] = "xxxxx,1433";
$db['default']['username'] = "xx";
$db['default']['password'] = "xxxxxx-xx";
$db['default']['database'] = "xxxxxxxxx";
$db['default']['dbdriver'] = "sqlsrv";
$db['default']['dbprefix'] = '';
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE;
$db['default']['cache_on'] = TRUE;
$db['default']['cachedir'] = '';
$db['default']['char_set'] = 'utf8';
$db['default']['dbcollat'] = 'utf8_general_ci';
$db['default']['swap_pre'] = '';
$db['default']['autoinit'] = TRUE;
$db['default']['stricton'] = FALSE;
UPDATE:
I have found the following from running the profiler.
DATABASE: database QUERIES: 1 (Hide)
0.0659 select * from Contacts
Loading Time: Base Classes 0.0428
Controller Execution Time ( Welcome / AzureBash ) 58.2173
Total Execution Time 58.2602
It seems as though the query is executing in 0.06 secs but the controller is taking a minute to load.
No idea why this is happening.
Solution
The active records interface for the latest SQLSRV drivers are buggy.
So, download and overwrite the existing interface with these (overwrite your sqlsrv folder in the database folder in CI):
http://www.kaweb.co.uk/blog/mssql-server-2005-and-codeigniter/
Note: These have been tested with SQL Azure and works.
$query->num_rows(); does not work with these drivers, so I suggest you use count instead. Or create your own wrapper.
In addition date is now a date object type in your result set.
I hope this helps.
Solution 2
If for whatever reason you find a bug that makes this completely unusable. Revert back to the sqlsrv interface originally provided. You will find what is causing the problem is the way the original interface are executing the query, thus, create a database helper class; use $sql = $this->db->last_query(); to get the query you was about to execute and then within the database_helper class execute it yourself:
function MakeDbCall ($sql)
{
$serverName = "xxxxx-xxxx-xxx,1433"; //serverName\instanceName
$connectionInfo = array( "Database"=>"xxx", "UID"=>"xx", "PWD"=>"xxxxx","ConnectionPooling" => "1");
$conn = sqlsrv_connect($serverName,$connectionInfo);
$stmt = sqlsrv_query($conn, $sql);
while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
$result_array[] = $row;
}
return $result_array;
}
Create one for row_array.
You should be able to call this function directly, from anywhere in your app. Whilst taking advantage of the way active_records constructs your query.
Not an ideal solution, but until codeigniter sort their SQLSRV class, there is not a lot we can do.
Adding an answer to this after the answer has already been accepted because I found a different solution. I was having the same problem ... looping through the result set was very very slow. i opened system/database/drivers/sqlsrv/sqlsrv_driver.php and found the connection function. i noticed that is was using the SQLSRV_CURSOR_STATIC option. i changed this to SQLSRV_CURSOR_CLIENT_BUFFERED and my slowness problems went away. See documentation for this here:
http://msdn.microsoft.com/en-us/library/hh487160(v=sql.105).aspx
I honestly have no idea what the sql server driver for php is doing, however, given the speed up, etc i can guess that the driver might be using a cursor by default. this seems like an awful idea. i also am assuming that by choosing client_buffered the data for the query would b e read without a cursor and accessed in memory on the client as if it were a cursor. If this is the case, bad things might happen if you try to execute a query that has many many rows to read. Perhaps another option (SQLSRV_CURSOR_FORWARD?) can be used to read data without a cursor - but i'm sure the methods used to access the query will be more limited (e.g. not using result_array())
-Don
Solution
The active records interface for the latest SQLSRV drivers are buggy.
So, download and overwrite the existing interface with these (overwrite your sqlsrv folder in the database folder in CI):
http://www.kaweb.co.uk/blog/mssql-server-2005-and-codeigniter/
Note: These have been tested with SQL Azure and works.
$query->num_rows(); does not work with these drivers, so I suggest you use count instead. Or create your own wrapper.
In addition date is now a date object type in your result set.
Solution 2
If for whatever reason you find a bug that makes this completely unusable. Revert back to the sqlsrv interface originally provided. You will find what is causing the problem is the way the original interface are executing the query, thus, create a database helper class; use $sql = $this->db->last_query(); to get the query you was about to execute and then within the database_helper class execute it yourself:
function MakeDbCall ($sql)
{
$serverName = "xxxxx-xxxx-xxx,1433"; //serverName\instanceName
$connectionInfo = array( "Database"=>"xxx", "UID"=>"xx", "PWD"=>"xxxxx","ConnectionPooling" => "1");
$conn = sqlsrv_connect($serverName,$connectionInfo);
$stmt = sqlsrv_query($conn, $sql);
while( $row = sqlsrv_fetch_array( $stmt, SQLSRV_FETCH_ASSOC) ) {
$result_array[] = $row;
}
return $result_array;
}
Create one for row_array.
You should be able to call this function directly, from anywhere in your app. Whilst taking advantage of the way active_records constructs your query.
Not an ideal solution, but until codeigniter sort their SQLSRV class, there is not a lot we can do.
What is your connection string? You can specify the "network protocol" explicitly, which somtimes can affect speed.
http://www.connectionstrings.com/articles/show/define-sql-server-network-protocol
"Provider=sqloledb;Data Source=190.190.200.100,1433;Network Library=DBMSSOCN;Initial Catalog=pubs;User ID=myUsername;Password=myPassword;"
By specifying the IP address, the port number (1433) and the Network Library, you are providing a very granular connection string.
Your details may vary of course.
Alot of times, you don't need this. But I've been on a few client trips where this was the magic dust.
You might want to turn db_debug to FALSE which should save time debugging the database.
Also, would suggest to turn cache_on to FALSE and specify cachedir and use $this->db->cache_on(); for queries that are less dynamic, i.e. does not change frequently.
For speed up fetch up to 3 times please use "MultipleActiveResultSets"=>'0' in your sqlsrv_connect connection options.
Ex:
$db = sqlsrv_connect('127.0.0.1', array('Database'=>'dbname','UID'=> 'sa','PWD'=> 'pass',"CharacterSet" =>"UTF-8","ConnectionPooling" => "1"
,"MultipleActiveResultSets"=>'0'
));
Related
PHP 5.4.14
SQL Server 2012/SQL Client 2012
Windows 2008 R2
I have a function (simplified version follows) that I call to run a SQL query. It works correctly: connects to DB; runs query and obtains a valid resource. The problem is that the resource that gets returned is null...
function squeal($query) {
$serverName = "XXXXXXXX\SQLEXPRESS";
$uid = "private";
$pwd = "private";
$connectionInfo = array( "UID"=>$uid, "PWD"=>$pwd, "Database"=>"DBname");
/* Connect using SQL Server Authentication. */
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if( $conn === false ) {
echo "Unable to connect.</br>";
die( print_r(sqlsrv_errors(), true));
}
/* Run query */
$result = sqlsrv_query( $conn, $query, array(), array("Scrollable"=>"buffered"));
if( $result === false ) {
echo "Error in executing query.</br>";
die( print_r(sqlsrv_errors(), true));
}
/* check resource exists for debug (still fails without these lines) */
echo("Resource=".intval($result)."</br>");
echo("Has rows=".sqlsrv_has_rows($result)."</br>");
return $result;
}
$tsql = "SELECT id FROM mytable";
$fred = squeal($tsql);
echo("Resource=".intval($fred)."</br>");
echo("Has rows=".sqlsrv_has_rows($fred)."</br>");
It gives the following output...
Resource=8
Has rows=1
Resource=8
Warning: sqlsrv_has_rows(): 8 is not a valid ss_sqlsrv_stmt resource in <path> on line 85
Has rows=
SQL is working correctly and returns a valid resource. On return from the function it knows it has been passed resource #8 (in this instance) but it is empty. I use a similar method for MySQL that works perfectly. My whole intranet app relies on being able to call a function to run a query and get a resource back.
Does the resource 'die' on leaving the function in sqlsvr/ODBC? surely not.
I have spent a couple of days now scouring google for answers but can only get SQL server to work outside of a function. I would appreciate any suggestions
Cheers
I found this a fascinating problem, and while searching for a possible cause I found this topic which actually sounds plausible.
In short: you open connection resource $conn within function scope. As such it makes sense that it is destroyed when leaving that scope. All resources stemming from that connection resource as such are automatically no longer valid, so that explains why the query resource also dies upon exiting the scope. If you look at it this way it makes sense too - how can a derived resource continue to exist without its logical parent resource. We're just spoilt by PHP that it usually doesn't care about this, but in C#/Java etc. it's plausible behaviour.
If this assumption is correct, the simplest solution is to put global $conn; at the beginning of your squeal function so it cannot get out of scope. Depending on the rest of the code you could implement a more elegant (and less potentially conflicting) solution, the implementation isn't really kosher anyway that it connects within a function and then expects followups to survive outside.
In certain functions I may need to do a couple of queries like so:
$user = & JFactory::getUser();
$db = & JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('id');
$query->from($db->quoteName('#__users'));
$query->where('username='.$db->quote($response->username));
$db->setQuery($query);
$user_id = $db->loadResult();
if ($user_id == "")
{
//do something
}
$query1 = $db->getQuery(true);
$query1->select('app_id');
$query1->from($db->quoteName('#__app_ids'));
$query1->where('app_descr='.$db->quote($this->app_descr).' AND app_valid=TRUE');
$db->setQuery($query1);
$app_id = $db->loadResult();
I find if I don't change query to query1 I can't get this to work for the subsequent queries. Outside of Joomla I've never had to do this as I close the mysql connection use the same variable as long as it is in the right order, all is well.
Two questions:
Is this right? Or is there a better way to do this?
Do I need to check for mysql failure of loadResult? How would I go about this. Looking at the Joomla core often I see nothing but sometimes there is a mix of things to handle this.
1) It should work with the same variable name, since you are getting a new query object since your method parameter is set to true. Try calling $query->clear(); just after getting query object
$query = $db->getQuery(true);
$query->clear();
$query->select('app_id');
2) In Joomla 3 it should be something like
try
{
$db->setQuery($query);
$user_id = $db->loadResult();
}
catch (RuntimeException $e)
{
$e->getMessage();
}
And in Joomla 2.5
if ($db->getErrorNum()) {
JError::raiseWarning(500, $db->getErrorMsg());
}
Also, change
$user = & JFactory::getUser();
$db = & JFactory::getDBO();
to
$user = JFactory::getUser();
$db = JFactory::getDBO();
Objects are returned by reference anyway in PHP 5, and it will throw a warning since php 5.3+
Well, on Joomla the JFactory::getDBO() always returns the same instance of connection,(at the atual version today) so each time you set the SQL you, in true, are rewriting the previous SQL. In resume this code need multiple connections, and this way dont support, you will need to write your own version of JDatabaseDriver, maybe your own version of a global static array to control your own pool of mysqli connections. PHP is poor about pool connections, isn't as JAVA or .NET, to quick code you will need to think the logic and to try to use only one connection each time, one opened query each time, always think that the JFactory::getDBO() isn't thread safe, take care about this. You never can start one transaction inside one php without commit ou rollback, it causes problems on execution of other pages.
How can I check if a db-connection is open in PHP?
How can I get a reference to this connection?
How can I determine if it is MySQL, Postgres or something else?
Ideally you would know the variable the connection is saved into. Without this it becomes a hassle to find it. It means you would have to loop through every variable and test each one, but doing this for any reason can be a pain because it wont find variables outside of the current scope. You can loop the current variables using:
foreach(get_defined_vars() as $x){
// Code
}
To check if a variable is a valid connection you can use the below code, it should return an integer:
if(mysql_thread_id($connection){
// Connection exists
}
Once you know its a connection you can use the following to return the type of connection, eg, mysql.
echo get_resource_type($connection);
You'd have to iterate over every defined variable and figure out if it's a resource handle or not. Try get_defined_vars().
Probably the best solution to the problem is to not lose track of the DB connection in the first place.
Consider integrating an ORM into your project such as Doctrine or Propel, or if you want something a little more lightweight, consider a DBAL such as Zend_Db. All of these solutions encapsulate the database connections in class instances, ensuring that they are always available to your code if necessary.
In general terms, I don't think it's really possible, not at least with some sort of complicate workaround. You can, for instance, loop through all defined variables:
foreach(get_defined_vars() as $variable){
if( is_resource($variable) && get_resource_type($variable)=='mysql link' ){
// Found a connection!
var_dump($variable);
}
}
Though, in fact, a connection remains available even if assigned to a local variable out of current scope:
function foo(){
$conn = mysql_connect('localhost', 'test', 'test');
}
foo();
$res = mysql_query('SELECT NOW() AS what_is_the_time');
if( $row = mysql_fetch_assoc($res) ){
var_dump($row);
}
The problem is that PHP doesn't really care about variables when talking about connections:
// One connection and zero variables!
mysql_connect('localhost', 'test', 'test');
$res = mysql_query('SELECT NOW() AS what_is_the_time');
if( $row = mysql_fetch_assoc($res) ){
var_dump($row);
}
You can try and find information about current connection:
var_dump(mysql_thread_id());
// int(2696)
... though many of the functions will just attempt to stablish a new connection if there isn't one so must take care of warnings...:
Warning: mysql_thread_id(): Access denied for user ''#'localhost' (using password: NO)
bool(false)
... unless your configuration actually allows a succesful credential-less connnection attempt.
I'm pretty sure that, whatever you want to do, can be done in an entirely different way :)
Is it possible to display all the query strings in Magento? I really like to see what queries are executed.
Thanks
In Varien_Db_Adapter_Pdo_Mysql
Magento 1.4 : lib/varien/Db/Adapter/Pdo/Mysql.php
set
protected $_debug = true;
protected $_logAllQueries = true;
and (if nor already there) create the folder defined in
protected $_debugFile = 'var/debug/sql.txt';
Give read / write permission
I'm not 100% sure this will catch every query, but most run through the query method Zend_Db_Adapter_Abstract query method in
lib/Zend/Db/Adapter/Abstract.php
With that in mind, you could temporarily add some debugging statements (to a copy you make in app/code/local/Mage to be safe)
public function query($sql, $bind = array())
{
// connect to the database if needed
$this->_connect();
// is the $sql a Zend_Db_Select object?
if ($sql instanceof Zend_Db_Select) {
if (empty($bind)) {
$bind = $sql->getBind();
}
$sql = $sql->assemble();
}
echo "{$sql}\n<br />\n";
var_dump($bind);
If you need to catch them all, you'd be better off doing this at the MySQL level (which isn't always possible depending on your host/IT situation)
Activate the Zend SQL Profiler with the following node in your local.xml
<resources>
<default_setup>
<connection>
<profiler>1</profiler>
Then you can access the profiler somewhere in your code and retrieve a lot of informations about all executed queries:
$profiler = Mage::getSingleton('core/resource')->getConnection('core_write')->getProfiler();
To simply output all queries:
print_r($profiler->getQueryProfiles());
You can add these two lines at the end of index.php to see all queries at the bottom of each page. Be aware that this will break AJAX requests that return a JSON response, so you might consider logging the queries instead of printing them, with this code (again, add it at the end of index.php):
$profiler = Mage::getSingleton('core/resource')->getConnection('core_write')->getProfiler();
Mage::log(print_r($profiler->getQueryProfiles(), true), null, 'queries.log', true);
Then you will find all queries in var/log/queries.log
Don't forget to remove the lines again after you finished debugging!
The queries will vary significantly depending on your activities. If you have some control over your MySQL server, try turning on query logging:
set global general_log = on
Then you can get the SQL log to see queries. As a word or warning, Magento tends to execute dozens of queries on every page load, and hundreds to save an object.
Thanks,
Joe
$collection->printLogQuery(true);
Turn on the MySQL logging will sure log all queries transactions. Here is how you can turn on the logging on.To turn on the logging to log to a file. Place the following in the my.cnf file or my.ini file if on windows, and restart MySQL.
log = /path/to/your/logfile.log
Then if you want to log to the table mysql.general_log, run these queries:
SET GLOBAL log_output = 'TABLE';
SET GLOBAL general_log = 'ON';
Run these if you want to log to the file:
SET GLOBAL log_output = "FILE";
SET GLOBAL general_log = 'ON';
I'm using Zend Framework 1.7.2, MySQL and the MySQLi PDO adapter. I would like to call multiple stored procedures during a given action. I've found that on Windows there is a problem calling multiple stored procedures. If you try it you get the following error message:
SQLSTATE[HY000]: General error: 2014
Cannot execute queries while other
unbuffered queries are active.
Consider using
PDOStatement::fetchAll().
Alternatively, if your code is only
ever going to run against mysql, you
may enable query buffering by setting
the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
attribute.
I found that to work around this issue I could just close the connection to the database after each call to a stored procedure:
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
//If on windows close the connection
$db->closeConnection();
}
This has worked well for me, however, now I want to call multiple stored procedures wrapped in a transaction. Of course, closing the connection isn't an option in this situation, since it causes a rollback of the open transaction. Any ideas, how to fix this problem and/or work around the issue.
More info about the work around
Bug report about the problem
I has same errors when called queries like this(variables to use in next query)
$db->query("SET #curr = 0.0;");
To fix this I've changed my config file to
'database' => array(
'adapter' => 'mysqli',
This pattern of preparing, executing and then closing each $sql statement that calls a stored procedure does work.
public function processTeams($leagueid,$raceid,$gender)
{
$db = Zend_Db_Table::getDefaultAdapter();
$sql = $db->prepare(sprintf("CALL addScoringTeams(%d,%d,'%s')",$leagueid,$raceid,$gender));
$sql->execute();
$sql->closeCursor();
$this->logger->info(sprintf("CALL addScoringTeams(%d,%d,'%s')",$leagueid,$raceid,$gender));
$sql1 = $db->prepare(sprintf("CALL updateScoringTeamTotals(%d)",$raceid));
$sql1->execute();
$sql1->closeCursor();
$this->logger->info(sprintf("CALL updateScoringTeamTotals(%d)",$raceid));
$sql2 = $db->prepare(sprintf("CALL updateScoringTeamClasses(%d,'%s')",$raceid,$gender));
$sql2->execute();
$sql2->closeCursor();
$this->logger->info(sprintf("CALL updateScoringTeamClasses(%d,'%s')",$raceid,$gender));
}
You can use prepare statement . No need to change driver
$sql = "CALL procedure()";
$stmt = $this->db->createStatement();
$stmt->prepare($sql);
$result = $stmt->execute();