Archive for the ‘MySQL’ Category

Posted by 6bytes at 15 April 2011

Category: MySQL

Tags: , ,

Sometimes I need to insert some values into the database but only when a certain condition is met. The best example is a newsletter sign up. If someone has already signed up to your newsletter don’t add his email address again. Of course we can run one query to check if this email address already exists and if not run another query to save it in our database.
If for any reason you need to do the whole operation in one query things get a little more complicated.

Two queries

Lets say we want to insert some data only when field `email` does not equal [email protected]
First, select data:

SELECT `id` FROM `table` WHERE `email` = '[email protected]';

if this email address already exists in our database we can let our users know about it. If no records were returned we can continue to insert new data:

INSERT INTO `table` (`email`) VALUES ('[email protected]');

One query

In a way we combine those two queries above into one:

INSERT INTO `table` (`email`)
    SELECT '[email protected]' FROM DUAL WHERE NOT EXISTS (
        SELECT `id` FROM `table` WHERE `email` = '[email protected]' LIMIT 1
    );

If the subquery doesn’t return any rows the insert will be carried out. In case of subquery finding this given email address condition is not met and data doesn’t get inserted.

Posted by 6bytes at 7 April 2010

Category: MySQL

Tags: , , ,

Is your MySQL really slow without actually being too busy?
Does your scripts take very long time to connect to the database?
Do you have lots of RAM, strong CPU, just a little traffic and despite that a very long database response time?
Are you seeing connections with “unauthenticated user” while running show processlist in MySQL console?

mysql> show processlist;
+------+----------------------+-----------+----------+----------+------+-------+--------------+
| Id   | User                 | Host      | db      | Command   | Time | State | Info         |
+------+----------------------+-----------+----------+----------+------+-------+--------------+
| 2047 | unauthenticated user | localhost | myDB    | Connect   |   81 |       | NULL         |
| 2049 | unauthenticated user | localhost | myDB    | Connect   |   81 |       | NULL         |
| 2050 | unauthenticated user | localhost | myDB    | Connect   |   76 |       | NULL         |
...
+------+----------------------+-----------+----------+----------+------+-------+--------------+
131 rows in set (0.00 sec)

If you answered yes to any of the above questions your MySQL might have a problem with resolving connection’s host name.
When attempt is made for a new connection, MySQL tries to resolve the host name for that request. It takes the IP address and resolves it to a host name (using gethostbyaddr()). It then takes that host name and resolves it back to the IP address (using gethostbyname()) and compares to ensure it is the original IP address.
This might considerably increase connection time and slow down your whole application or produce show processlist result as above. You can easily solve this problem by disabling DNS host name lookups. In order to do this you need to run your mysqld with –skip-name-resolve option or add it to your my.cnf file like that:

[mysqld]
skip-name-resolve

After that running show processlist will result in “Host” column displaying only IP addresses instead of host names and the connection speed should be much faster.

Just keep in mind that also you have to change allowed hosts for your database users to proper IP addresses.
 

Posted by 6bytes at 30 March 2010

Category: MySQL, PHP

Tags: , , , ,

The bug

If you’re running PHP 5.1.6 and just started using PDO for your database connection, it’s likely you’ll run into quite an annoying bug.
Lets test a simple query partly taken from PHP documentation.

$calories = 150;
$colour = 'red';
$sth = $myPDO->prepare('SELECT name, colour, calories
    FROM fruit
    WHERE calories < :calories AND colour = :colour');
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);
$sth->execute();
if ($sth->rowCount() >= 1) {
    // iterate through results
}

Everything seems great. The problem is that $sth->rowCount() will always return 0. No matter how many results your query returns, the value of rowCount() will always be 0.

Full bug report can be found here: http://bugs.php.net/40822.

Solution

Upgrade PHP :)

If that’s not an option read on.

Lets create our own simple PDO and PDOStatement classes.

class myPDO extends PDO {
	function __construct($name_host, $username='', $password='', $driverOptions=array()) {
		parent::__construct($name_host, $username, $password, $driverOptions);
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array($this)));
	}
}

class myPDOStatement extends PDOStatement {
	public $db;

	protected function __construct($db) {
		$this->db = $db;
	}
}

Above will not fix anything. This is just a start so we can overload some PDO methods to apply the fix.

To fix the bug we need to tell MySQL to use the buffered versions of the MySQL API by setting attribute MYSQL_ATTR_USE_BUFFERED_QUERY to true.
For some reason, still unknown to me setting this option like that

class myPDO extends PDO {
	function __construct($name_host, $username='', $password='', $driverOptions=array()) {
		parent::__construct($name_host, $username, $password, $driverOptions);
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array($this)));
		$this->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
	}
}

will not work.

After many hours of tearing my hair out I found out that all you need to do is this:

class myPDO extends PDO {
	function __construct($name_host, $username='', $password='', $driverOptions=array()) {
		$driverOptions[PDO::MYSQL_ATTR_USE_BUFFERED_QUERY] = true;
		parent::__construct($name_host, $username, $password, $driverOptions);
		$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('myPDOStatement', array($this)));
	}
}

You have to set MYSQL_ATTR_USE_BUFFERED_QUERY before instantiating a connection. Setting it after the connection was made will not work.

When this is done we need to modify our myPDOStatement class.

class myPDOStatement extends PDOStatement {
	public $db;
	// other attributes
	private $foundRows; // will hold number of affected rows

	protected function __construct($db) {
		$this->db = $db;
	}

	public function execute($array = null) {
		if ($array === null) {
			$result = parent :: execute();
		} else {
			$result = parent :: execute($array);
		}

		// fix for PHP 5.1.6 rowCount error
		$this->foundRows = $this->db->query("SELECT FOUND_ROWS()")->fetchColumn();

		return $result;
	}

	public function rowCount() {
		return $this->foundRows;
	}
}

After above changes our rowCount() method will return proper values.
 

Posted by 6bytes at 25 June 2009

Category: Apache, MySQL, PHP

Tags: , , ,

If your PDO scripts are crashing Apache after installing xampp 1.7 for windows all you need to do is:

All should be good now.