Skip to content

Instantly share code, notes, and snippets.

@arnaud-lb
Created May 15, 2012 19:27
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save arnaud-lb/2704404 to your computer and use it in GitHub Desktop.
Save arnaud-lb/2704404 to your computer and use it in GitHub Desktop.
USE INDEX / FORCE INDEX in a Doctrine2 DQL query
<?php
use UseIndexWalker;
use Doctrine\ORM\Query
$query = $em->createQuery("SELECT f FROM Foo f WHERE f.a = 1 and f.b = 2");
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, 'UseIndexWalker');
$query->setHint(UseIndexWalker::HINT_USE_INDEX, 'some_index_name');
$query->getResult();
<?php
use Doctrine\ORM\Query\SqlWalker;
/**
* Quick hack to allow adding a USE INDEX on the query
*/
class UseIndexWalker extends SqlWalker
{
const HINT_USE_INDEX = 'UseIndexWalker.UseIndex';
public function walkFromClause($fromClause)
{
$result = parent::walkFromClause($fromClause);
if ($index = $this->getQuery()->getHint(self::HINT_USE_INDEX)) {
$result = preg_replace('#(\bFROM\s*\w+\s*\w+)#', '\1 USE INDEX (' . $index . ')', $result);
}
return $result;
}
}
@NakedFerret
Copy link

Cheers!

@multifinger
Copy link

Here is modified version of walker,
contains FIX for join-queries

http://stackoverflow.com/questions/16729011/index-hinting-in-doctrine2

@ggergo
Copy link

ggergo commented Jun 26, 2020

Here is mine that can use different indexes per DQL table aliases. Works with both createQuery and createQueryBuilder. Still a hack tho. Thx for the idea arnaud-lb!

https://github.com/ggergo/SqlIndexHintBundle
https://packagist.org/packages/ggergo/sqlindexhintbundle

@renatogcarvalho
Copy link

renatogcarvalho commented Jul 8, 2020

@ggergo Thank you for sharing your version.

The only issue I encountered is that once the query hint is added we're forced to have all the DQL aliases listed. For example, sometimes you might only want to add a hint in a JOIN clause.

Once I updated the "walkFromClause" method to the following, it worked as expected:

public function walkFromClause($fromClause): string
{
    $sql = parent::walkFromClause($fromClause);
    $hints = $this->getQuery()->getHint(self::HINT_INDEX);

    foreach ($this->getIndexHintParameters($fromClause) as $params) {
        if (!array_key_exists($params['dqlAlias'], $hints)) {
            continue;
        }
        
        $sql = $this->insertIndex($params['sqlKey'], $params['sqlTableAlias'], $hints[$params['dqlAlias']], $sql);
    }

    return $sql;
}

The difference here is the "array_key_exists()" check, which will only insert an index hint if listed.

Best,

Renato.

@ggergo
Copy link

ggergo commented Jul 9, 2020

@renatogcarvalho Thank you for noticing! I added your fix. 👍 Best regards, Gergő

@janedbal
Copy link

Another composer package that works even for tables nor present in DQL, but present in SQL: https://github.com/shipmonk-rnd/doctrine-mysql-index-hints

@MarionLeHerisson
Copy link

Thanks for the solution ! 🙏
Small fix : in example.php line 8, you can use UseIndexWalker::class instead of 'UseIndexWalker'. It avoids having hardcoded strings in code + it manages if the file is in a different namespace. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment