我有以下表格
ea_users
ea_user_cfields
ea_customfields
我希望获得具有特定角色的所有用户,但我还希望检索每个用户的所有自定义字段。这适用于我的软件的后端,其中应显示所有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();
答案 0 :(得分:0)
你不能正确理解联接是如何工作的问题。 没关系,当你有一对多的关系时,你在选择中有重复。
简言之,你的情况是:引擎尝试从表“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时,试着想象自己,当你尝试在纸上加入几张大桌子时。 U总是需要以某种方式标记哪些数据来自哪个表以便以后能够使用它,因此它非常合乎逻辑地从表“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”的内容。用几句话说,但我希望它能帮助你理解它。
我用googled表结构,所以你可以在那里写一些查询。 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引擎的响应中,列中的重复是完全正确的。但是,当然,你也可以使用例如连接的分组来解决它。但我觉得,对于你的任务来说,它过于复杂的逻辑<强> PS。强> “GROUP BY 1” - 意味着我想使用第1列进行分组,因此在将数据选入内存之后,mySql将尝试使用第一列对所有数据进行分组,最好不要在prod上使用这种格式。它与“GROUP BY c.customerNumber”相同。
PPS。我也读过“使用DISTINCT”等评论。 要使用DISTINCT或顺序函数,您需要了解它是如何工作的,因为它的使用不正确,它可以从您的选择中删除一些数据(与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
;