【问题标题】:MySQL join query duplicates users in outputMySQL 连接查询在输出中重复用户
【发布时间】:2016-09-13 03:43:56
【问题描述】:

我有以下表格

ea_users

  • 身份证
  • 名字
  • 姓氏
  • 电子邮件
  • 密码
  • id_roles

ea_user_cfields

  • 身份证
  • c_id = 自定义字段 ID
  • u_id = 用户 ID
  • 数据

ea_customfields

  • 身份证
  • name = 自定义字段的名称
  • 说明

我想获取所有具有特定角色的用户,但我也想检索每个用户的所有自定义字段。这是我的软件的后端,应该显示所有 ea_users 和自定义字段。

我尝试了以下方法,但对于每个自定义字段,它都会重复相同的用户

    $this->db->join('(SELECT GROUP_CONCAT(data) AS custom_data, id AS dataid, u_id, c_id
     FROM ea_user_cfields userc
     GROUP BY id) AS tt', 'tt.u_id = ea.id','left');
    $this->db->join('(SELECT GROUP_CONCAT(name) AS custom_name, id AS customid
     FROM ea_customfields AS cf
     GROUP BY id) AS te', 'tt.c_id = te.customid','left');
    $this->db->where('id_roles', $customers_role_id);


    return $this->db->get('ea_users ea')->result_array();

【问题讨论】:

  • 尝试将 DISTINCT 添加到 SELECT。
  • 如果 ea_users 记录被 join 重复了,这意味着它在 ea_customfields 表中有多个记录,那么你需要决定是否要所有它的行,如果不是, 使用 group by ea_users.iddistinct
  • group_by 为 ea_users 工作,但我怎样才能得到多个 custom_data 等回来?例如,作为输出 custom_data[0] = "shit" custom_data[1] = "custom" custom_data[2] = "blabla"
  • 听起来你基本上是在尝试做一个支点。除非您确实需要在查询中完成,否则在等式的显示方面可能更容易实现您想要的。

标签: php mysql pivot entity-attribute-value


【解决方案1】:

你没有正确理解 join 是如何工作的问题。 没关系,当你有一对多的关系时,你在选择中有重复项。

简而言之,您的情况:引擎尝试从表“A”(ea_users)中获取数据,然后根据条件加入另一个表“B”(ea_customfields)。如果您在表之间有一对多关系(这意味着表“A”中的一条记录(假设我们在此表 A1 记录中)可以包含表“B”中的少量相关行,我们将它们称为 B1.1 , B1.2 和 B1.3 和 B1.4),在这种情况下,它将连接此记录并将连接结果放入内存中。所以在记忆中你会看到类似的东西

| FromTable A | FromTableB |
| A1        | B1.1       |
| A1        | B1.2       |
| A1        | B1.3       |
| A1        | B1.4       |

如果你在表“B”中有 10 条记录,这些记录与表“A”相关,它会在提取过程中将 10 次从表“A”中的数据复制到内存中。然后会渲染给你。

取决于连接类型的行,缺少相关记录,可以完全跳过(INNER JOIN),也可以用 NULL 填充(LEFT JOIN 或 RIGHT JOIN)等。

当您考虑 JOIN 时,请尝试想象自己,当您尝试在纸上连接几张大表时。你总是需要以某种方式标记哪些数据来自哪个表,以便以后能够对其进行操作,因此从表“A”中写入行“A1”的次数与你需要填充空白空间的次数一样是合乎逻辑的当您在表“B”中找到适当的记录时。否则你会在你的纸上写这样的东西:

| FromTable A | FromTableB |
| A1        | B1.1       |
|           | B1.2       |
|           | B1.3       |
|           | B1.4       |

是的,即使“FromTable A”列包含空数据,当您有 5-10 条记录并且您可以轻松操作它时,它看起来也不错(例如,您可以在脑海中对其进行排序 - 您只需要想象什么应该不是空白,但是对于它,你需要记住你是如何在纸上写数据的所有时间顺序)。但是让我们假设你有 100-1000 条记录。如果你仍然可以轻松地对其进行排序,让事情变得更复杂并告诉,表“A”中的值可以是空的,等等等等。这就是为什么 mysql 引擎更简单地从表中重复多次数据..

基本上,当您尝试想象如何在纸上连接巨大的表格或尝试从这些表格中选择一些内容然后在那里进行排序或其他内容时,我总是坚持使用示例,您将如何查看表格等。

GROUP_CONCAT,分组

然后,下一个错误,你不明白 GROUP_CONCAT 是如何工作的: 问题是 mysqlEngine 使用所有 where 条件将第一步结构提取到内存中,评估子查询 + 附加所有连接。加载结构时,它尝试执行 GROUPing。这意味着它将从临时表中选择与“A1”相关的所有行。然后将尝试将聚合函数应用于选定的数据。 GROUP_CONCAT 函数意味着我们想对选定的组应用连接,因此我们会看到类似“B1.1,B1.2,B1.3,B1.4”的内容。简而言之,但我希望它对理解它有所帮助。

我用谷歌搜索了表结构,所以你可以在那里写一些查询。 http://www.mysqltutorial.org/tryit/query/mysql-left-join/#1

下面是 GROUP_CONCAT 工作原理的示例,尝试在那里执行查询:

SELECT 
    c.customerNumber, c.customerName, GROUP_CONCAT(orderNumber) AS allOrders
FROM customers c
LEFT JOIN orders o ON (c.customerNumber = o.customerNumber)
GROUP BY 1,2
;

可以与之前的结果进行比较。

GROUP 在聚合函数中的强大功能,您可以使用它。例如,你可以使用“COUNT()”、“MAX()”、“GROUP_CONCAT()”或许多其他的。

或获取计数的示例(尝试执行):

SELECT c.customerName, count(*) AS ordersCount
FROM customers AS c
LEFT JOIN orders AS o ON (c.customerNumber = o.customerNumber)
GROUP BY 1
;

所以我的意见:

获取后在客户端或后端解决此问题更简单更好。因为就 mysql 引擎而言,列中重复的响应是绝对正确的。但是,当然,您也可以使用例如串联分组来解决它。但我有一种感觉,对于你的任务,它的逻辑过于复杂

附言。 “GROUP BY 1” - 表示我想使用第 1 列进行分组,因此在将数据选择到内存后,mySql 将尝试使用第一列对所有数据进行分组,最好不要在 prod 上使用这种写入格式。它与“GROUP BY c.customerNumber”相同。

PPS. 我还阅读了诸如“使用 DISTINCT”之类的 cmets。 要使用 DISTINCT 或 order 函数,您需要了解它是如何工作的,因为使用不正确,它可能会从您的选择中删除一些数据(与 GROUP 或 INNER JOINS 等相同)。乍一看,你的代码可能工作得很好,但它可能会导致逻辑错误,这是后来发现的最复杂的问题。 此外,当您具有一对多关系时(在您的特定情况下), DISTINCT 不会帮助您。你可以尝试执行查询:

SELECT 
    c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;
SELECT 
    DISTINCT(c.customerName), orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
;

结果应该是一样的。客户名称列和订单号重复。

以及如何使用不正确的查询丢失数据的示例;):

SELECT 
    c.customerName, orderNumber AS nr
FROM customers c
INNER JOIN orders o ON (c.customerNumber = o.customerNumber)
WHERE c.customerName='Alpha Cognac'
GROUP BY 1
;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-29
    • 2014-08-13
    • 2011-02-22
    • 2018-03-31
    • 2018-03-09
    相关资源
    最近更新 更多