如何使用查询构建器基于内部连接关系编写具有不同的自连接查询?

How to write this self joined query with distinct based on the inner join relationship using the query builder?

我有一个实体 Place 可以包含许多不同类型的地方,特别是在这种情况下“城市”和“州”,其中每个“城市”可以包含对相同 table 指向父“状态”。

我有另一个实体 OfficePlace 存在多对一关系,但由于域限制,办公室只能链接到“城市”,而不会链接到“城市” “状态”。

我必须编写一个查询来获取我们拥有一个或多个办事处的所有州,但仅限于那些州。

在简单的 SQL 中,查询很容易解决:

SELECT DISTINCT states.*
         FROM offices o
            INNER JOIN places cities ON cities.id = o.place_id
            INNER JOIN places states ON cities.parent_place_id = states.id
WHERE p.place_type = 'city'

这行得通,我们得到了我们需要的东西。

但是我需要(我更愿意)使用查询生成器编写查询。这是“基本”查询,但我们需要有条件地应用更多过滤器,这将使使用 QB much 更清晰和易于管理。

目前我们使用的是Native Query,但这意味着我们需要在调用em::createNativeQuery()之前操作SQL字符串,这很麻烦。

$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('states')->distinct()
           ->from(PartnerOffice::class, 'o')
           ->innerJoin('o.place', 'p')
           ->innerJoin('p.parentPlace', 'states')
           ->where("p.placeType = 'city'");

但这给出了一个错误:

SELECT DISTINCT': Error: Cannot select entity through identification variables without choosing at least one root entity alias

我们怎样才能让这个查询起作用?

感谢和问候。

我认为您发布了一个不完整的基本 sql 查询 (WHERE AND),并且您创建了一个别名 p.place_type,该别名未在上面的查询中定义。 但是,根据您的陈述,我认为您可以尝试以下查询,

$qb->selectRaw('DISTINCT s.states')
           ->from('offices as o')
           ->join('places as c', 'c.id', '=', 'o.place_id')
           ->join('places as s', 's.id', '=', 'c.parent_place_id')
           ->where('c.placeType', 'city')->get();

默认情况下,->join()指的是queryBuilder中的inner join。

来源:

  1. https://laravel.com/docs/8.x/queries#inner-join-clause

如果没有看到实体(至少对我而言),很难找出您应该构建的查询。例如:你有 mappedBy 字段吗?但我认为您的问题是您没有从 PartnerOffice 获取任何信息,而它是您的根条目(来自)。将您的 ->select() 更改为 ->addSelect 但这将为每条记录获取整个 PartnerOffice 实体,并且您的 distinct 可能无法按预期工作,或者使用 Place 实体作为您的根条目并翻转逻辑。

让我们假设您的实体的关系是双向的。像这样的事情应该可以做到(但您可能需要为真实的实体和字段名称更改它)。

$this->getEntityManager()->createQueryBuilder()
    ->distinct()
    ->from(Place::class, 'p')
    ->join('p.cities', 'c')
    ->join(PartnerOffice::class, 'po', Join::WITH, 'po.place = p')
    ->andWhere('p.placeType = \'state\'');