Eloquent Joins的复杂条件

时间:2014-07-04 13:18:50

标签: mysql join laravel group-by eloquent

我希望使用Eloquent从我们的数据库输出一些数据,但数据库可能会有所不同。

以下是我的数据库表格的说明

网络服务标签:

----------------------------------------------------------
| id | webservice_tag        | webservice_name | blog_id |
==========================================================
| 1  | TEST, TESSST, TES     | Test Service    | 1       |
----------------------------------------------------------
| 2  | OPTION, OPT, EXAMPLE  | Example Service | 1       |
----------------------------------------------------------
| 3  | ANOTHER, ANO, THER    | Another Service | 1       |
----------------------------------------------------------

博文:

----------------------------------------------------------
| id | title                         | blog_id | tag     |
==========================================================
| 1  | Blog Title 1                  | 1       | THER    |
----------------------------------------------------------
| 2  | Blog Title 2                  | 1       | TES     |
----------------------------------------------------------
| 3  | Blog Title 3                  | 1       | ANOTHER |
----------------------------------------------------------

所以在这里,我们有两个表。博客帖子和网络服务标签。

我们的博客文章中包含来自众多不同提供商的大量网络服务。例如,测试服务,示例服务和其他服务。但是,这些Web服务非常不一致;他们将通过标签组合发送,并且不保证两个帖子是相同的。

因此,我们创建了一个名为Webservice Tags的表,用于记录这些出现的每一个。通过这种方式,我们可以确定(在示例中)博客标题3是由另一个服务发送的,博客标题2是由测试服务等发送的。

我正在开发报告,以显示我们从每个网络服务获得的帖子数量。因此,对于每篇博文,我需要识别Web服务并获取与之关联的Web服务名称。我们有多个博客,每个博客都有自己的网络服务(有些可能会共享标签),因此需要将此报告与每个博客隔离开来。

这是Eloquent中的查询:

$query = DB::table('blog_posts')
    ->join('webservice_tags', function($join) use ($blog) {
        $join->on('blog_posts.tag', '=', 'webservice_tags.webservice_tag')
        ->where('webservice_tags.blog_id', '=', $blog->id);
    })
    ->addSelect('webservice_tags.webservice_name AS name')
    ->addSelect(DB::raw("COUNT(blog_posts.id) AS count"))
    ->where('blog_posts.blog_id', '=', $blog->id)
    ->groupBy('webservice_tags.webservice_name')
    ->get();

此查询很好,而Webservice发送了一致的标记。但是现在每个Web服务都有不同的标记,而且这个报告需要平均计算它们。

这是我的修正案,但它没有按预期运作:

->join('webservice_tags', function($join) use ($blog) {
    $join->on('blog_posts.tag', 'LIKE', DB::raw('CONCAT("%", webservice_tags.webservice_tag, "%")'))
    ->where('webservice_tags.blog_id', '=', $blog->id);
})

我没有通过此方法在查询中获得任何匹配。

该查询旨在将任何字段连接到SELECT,其中blog_posts.tag位于逗号分隔的webservice_tags列表中。

有没有办法更有效地做到这一点?

关于blog_id关系的解释

此系统管理多个博客以及这些博客中的帖子。 Web服务可能适用于一个博客,但不适用于另一个博客,这就是blog_id与webservices以及各个帖子相关联的原因。有一些事项,比如与Web服务相关的佣金百分比,Test Webservice可能为博客A提供10%,为博客B提供12%,因此它们基本上是分开的。

1 个答案:

答案 0 :(得分:1)

最快/原油解决方案:

您没有看到任何结果的原因是:

$join->on('blog_posts.tag', 'LIKE', DB::raw('CONCAT("%", webservice_tags.webservice_tag, "%")'))

转换为:

INNER JOIN `webservice_tags`
   ON `blog_posts`.`tag` LIKE CONCAT("%", webservice_tags.webservice_tag, "%") 

这会尝试匹配blog_posts的值tag%ANOTHER, ANO, THER%

所以,让我们说blog_posts.tag是"另一个"而webservice_tags.webservice_tag是"另一个,另一个,那个"。数据库正在尝试匹配以下值:

,而不是匹配" ANOTHER"
ANOTHER    !=    FOOANOTHER, ANO, THER
ANOTHER    !=    ANOTHER, ANO, THERBAR
ANOTHER    !=    FOOANOTHER, ANO, THERBAR

这就是为什么你没有得到任何结果的原因。您将需要交换列,如下所示:

$join->on('webservice_tags.webservice_tag', 'LIKE', DB::raw('CONCAT("%", blog_posts.tag, "%")'))

更长的解决方案:

我要在这里抛出我的想法。在考虑这个问题时,我会想到以下几点:

  • 标签< - >当Web Service映射实际上是一组数据时,它将存储为字符串。这使得难以搜索并难以对其执行查询。我喜欢将它们分成两个或更多表。这称为Database normalization。不要问我是哪一级,我从不理解他们的技术描述。 :d
  • 由于您提到Web服务可以包含许多标记,但某些标记也可以由不同的Web服务共享,因此我们需要一个数据透视表来处理这种多对多关系。
  • 聚合时,我喜欢从上到下开始。所以我从webservices开始编写查询,然后继续写博客文章。这与你的例子相反。
  • 我现在忽略了webservices表中的blog_id列,因为我还不太清楚他们做了什么。

首先,我首先尝试将表格首先规范化为webservices,webservice_tags,tags和blog_posts,如下所示:

<强> web服务:

-------------------------
| id  | webservice_name |
=========================
| 1   | Test Service    |
-------------------------
| 2   | Example Service |
-------------------------
| 3   | Another Service |
-------------------------

<强> webservice_tags:

------------------------------------------
| id | tag_name          | webservice_id |
==========================================
| 1  | TEST              | 1             |
------------------------------------------
| 2  | TESSST            | 1             |
------------------------------------------
| 3  | TES               | 1             |
------------------------------------------
| 4  | OPTION            | 2             |
------------------------------------------
| 5  | OPT               | 2             |
------------------------------------------
| 6  | EXAMPLE           | 2             |
------------------------------------------
| 7  | ANOTHER           | 3             |
------------------------------------------
| 8  | ANO               | 3             |
------------------------------------------
| 9  | THER              | 3             |
------------------------------------------
| 10 | FOO               | NULL          |
------------------------------------------
| 11 | BAR               | NULL          |
------------------------------------------

请注意,为了便于阅读,我在上面的数据透视表中使用tag_name。我想最好使用tag_id代替。

<强>标签:

--------------------------
| id | tag_name          |
==========================
| 1  | TEST              |
--------------------------
| 2  | TESSST            |
--------------------------
| 3  | TES               |
--------------------------
| 4  | OPTION            |
--------------------------
| 5  | OPT               |
--------------------------
| 6  | EXAMPLE           |
--------------------------
| 7  | ANOTHER           |
--------------------------
| 8  | ANO               |
--------------------------
| 9  | THER              |
--------------------------
| 10 | FOO               |
--------------------------
| 11 | BAR               |
--------------------------

<强> blog_posts:

-----------------------------------------------------------
| id | title                         | blog_id | tag_name |
===========================================================
| 1  | Blog Title 1                  | 1       | THER     |
-----------------------------------------------------------
| 2  | Blog Title 2                  | 1       | TES      |
-----------------------------------------------------------
| 3  | Blog Title 3                  | 1       | ANOTHER  |
-----------------------------------------------------------

现在,要获取每个Web服务创建的博客帖子的报告,我们可以使用联接查询来执行此操作。 在这种情况下,QueryBuilder查询会提供更好的性能,因为我们只想知道聚合,而不是实际的数据库模型:

$report = DB::table('webservices')
    ->leftJoin('webservice_tags', 'webservice_tags.webservice_id', '=', 'webservices.id')
    ->leftJoin('tags', 'tags.tag_name', '=', 'webservices_tags.tag_name')
    ->leftJoin('blog_posts', 'blog_posts.tag_name', '=', 'tags.tag_name')
    ->where('blog_posts.blog_id', '=', $blog->id)
    ->select(['webservices.webservice_name', DB::raw('COUNT(`blog_posts.id`) as `num_posts`')])
    ->groupBy('webservices.id')
    ->get();

现在,您可以获得每个Web服务为所有Web服务创建的博客帖子的报告。

一个注意事项是,由于您提到某个网络服务可能会共享代码,而您正在识别网络服务&lt; - &gt;博客文章通过标签, 您无法100%准确地确定原始网络服务,因为您无法确定哪个网站服务为该帖子设置了该标记。


额外:您可能会注意到我还在Web服务标签中添加了FOO和BAR。 这有助于万一,就像你说的那样,Web服务非常不一致&#34;,新的标签将被添加到你的数据库中。 您还可以查询它们并轻松生成报告,以找出尚未映射的标记。