https://github.com/shipmonk-rnd/doctrine-mysql-index-hints
Custom SQL walker for Doctrine allowing usage of MySQL index hints without need of native queries
https://github.com/shipmonk-rnd/doctrine-mysql-index-hints
doctrine mysql optimization
Last synced: 3 months ago
JSON representation
Custom SQL walker for Doctrine allowing usage of MySQL index hints without need of native queries
- Host: GitHub
- URL: https://github.com/shipmonk-rnd/doctrine-mysql-index-hints
- Owner: shipmonk-rnd
- Created: 2021-08-17T12:42:54.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2025-06-16T14:39:58.000Z (7 months ago)
- Last Synced: 2025-10-04T16:02:07.579Z (3 months ago)
- Topics: doctrine, mysql, optimization
- Language: PHP
- Homepage:
- Size: 261 KB
- Stars: 21
- Watchers: 5
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
## MySQL index hints for Doctrine
This library provides a simple way to incorporate [MySQL's index hints](https://dev.mysql.com/doc/refman/8.0/en/index-hints.html)
into SELECT queries written in [Doctrine Query Language](https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/reference/dql-doctrine-query-language.html)
via [custom SqlWalker](https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/cookbook/dql-custom-walkers.html#modify-the-output-walker-to-generate-vendor-specific-sql).
No need for native queries anymore.
### Installation:
```sh
composer require shipmonk/doctrine-mysql-index-hints
```
### Simple usage:
```php
$result = $em->createQueryBuilder()
->select('u.id')
->from(User::class, 'u')
->andWhere('u.id = 1')
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(UseIndexHintHandler::class, [IndexHint::force(User::IDX_FOO, User::TABLE_NAME)])
->getResult();
```
Which produces following SQL:
```mysql
SELECT u0_.id AS id_0
FROM user u0_ FORCE INDEX (IDX_FOO)
WHERE u0_.id = 1
```
See the used entity (it makes sense to put table names and index names into public constants to bind it together and reference it easily):
```php
#[ORM\Table(name: self::TABLE_NAME)]
#[ORM\Index(name: self::IDX_FOO, columns: ['id'])]
#[ORM\Entity]
class User
{
public const TABLE_NAME = 'user';
public const IDX_FOO = 'IDX_FOO';
// ...
}
```
### Combining multiple hints:
You might need to give MySQL a list of possible indexes or hint it not to use some indices.
As you can see, hinting joined tables is equally simple.
```php
->from(User::class, 'u')
->join('u.account', 'a')
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(UseIndexHintHandler::class, [
IndexHint::use(Account::IDX_1, Account::TABLE_NAME),
IndexHint::use(Account::IDX_2, Account::TABLE_NAME),
IndexHint::ignore(Account::IDX_3, Account::TABLE_NAME),
IndexHint::ignore(Account::IDX_4, Account::TABLE_NAME),
])
```
Produces this SQL:
```mysql
FROM user u0_
JOIN account a1_ IGNORE INDEX (IDX_3, IDX_4) USE INDEX (IDX_1, IDX_2) ON (...)
```
### Hinting table joined multiple times:
You might need to hint only specific join of certain table. Just add which DQL alias specifies it as third argument.
```php
->from(User::class, 'u')
->join('u.account', 'a1')
->join('u.anotherAccount', 'a2')
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(UseIndexHintHandler::class, [
IndexHint::use(Account::IDX_1, Account::TABLE_NAME, 'a1'), // alias needed
])
```
Produces this SQL:
```mysql
FROM user u0_
JOIN account a1_ USE INDEX (IDX_1) ON (...)
JOIN account a2_ ON (...)
```
### Advanced usage notes
- Subselects are also supported
- It works even for tables that are not present in the DQL, but are present in SQL!
- For example parent table from [class table inheritance](https://www.doctrine-project.org/projects/doctrine-orm/en/2.9/reference/inheritance-mapping.html#class-table-inheritance) when selecting children
- Any invalid usage is checked in runtime
- Table name existence is checked, so you just cannot swap `tableName` and `indexName` parameters by accident or use non-existing DQL alias
- Forgotten hint or invalid arguments are also checked
- Since those checks cannot be caught by any static analysis tool, it is recommended to have a test for every query
### Combining with optimizer hints:
Since 3.0.0, you can combine this library with [shipmonk/doctrine-mysql-optimizer-hints](https://github.com/shipmonk-rnd/doctrine-mysql-optimizer-hints):
```php
$result = $em->createQueryBuilder()
->select('u.id')
->from(User::class, 'u')
->andWhere('u.id = 1')
->getQuery()
->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, HintDrivenSqlWalker::class)
->setHint(OptimizerHintsHintHandler::class, ['MAX_EXECUTION_TIME(1000)'])
->setHint(UseIndexHintHandler::class, [IndexHint::force(User::IDX_FOO, User::TABLE_NAME)])
->getResult();
```