shipmonk/doctrine-mysql-index-hints

为Doctrine定制的SQL遍历器,允许在无需原生查询的情况下使用MySQL索引提示

3.1.0 2024-08-08 14:12 UTC

This package is auto-updated.

Last update: 2024-09-13 15:22:29 UTC


README

此库提供了一种简单的方法,通过自定义SqlWalker将MySQL的索引提示集成到Doctrine查询语言编写的SELECT查询中。无需再使用原生查询。

安装

composer require shipmonk/doctrine-mysql-index-hints

简单使用

$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();

它将产生以下SQL

SELECT u0_.id AS id_0
FROM user u0_ FORCE INDEX (IDX_FOO)
WHERE u0_.id = 1

查看使用的实体(将表名和索引名放入公共常量中,以便将其绑定在一起并轻松引用是有意义的)

#[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';

    // ...
}

组合多个提示

您可能需要向MySQL提供一个可能的索引列表,或者提示它不要使用某些索引。如你所见,提示连接表同样简单。

->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),
])

产生以下SQL

FROM user u0_
JOIN account a1_ IGNORE INDEX (IDX_3, IDX_4) USE INDEX (IDX_1, IDX_2) ON (...)

多次提示表连接

您可能只需要提示某些表的特定连接。只需将指定它的DQL别名作为第三个参数添加即可。

->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
])

产生以下SQL

FROM user u0_
JOIN account a1_ USE INDEX (IDX_1) ON (...)
JOIN account a2_ ON (...)

高级使用说明

  • 它甚至适用于DQL中不存在但存在于SQL中的表!
    • 例如,当选择子类时,来自类表继承的父表。
  • 任何无效使用都会在运行时进行检查
    • 检查表名是否存在,因此您不能意外地交换tableNameindexName参数或使用不存在的DQL别名
    • 忘记的提示或无效的参数也会进行检查
    • 由于这些检查不能被任何静态分析工具捕获,因此建议为每个查询编写一个测试。

与优化器提示结合使用

从3.0.0版本开始,您可以将此库与shipmonk/doctrine-mysql-optimizer-hints结合使用。

$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();