doushun1870 2016-09-12 17:28
浏览 17

MySQL连接查询在输出中复制用户

I have the following tables

ea_users

  • id
  • first_name
  • last_name
  • email
  • password
  • id_roles

ea_user_cfields

  • id
  • c_id = custom field ID
  • u_id = user ID
  • data

ea_customfields

  • id
  • name = name of custom field
  • description

I want to get all users which have a certain role, but I also want to retrieve all the custom fields per user. This is for the backend of my software where all the ea_users and custom fields should be shown.

I tried the following, but for each custom field, it duplicates the same user

    $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();
  • 写回答

1条回答 默认 最新

  • dtncv04228 2016-09-12 18:45
    关注

    the problem that u did not understand properly how join works. its ok, that u have duplicates in select when u have relation one to many.

    in few words your case: engine tries to fetch data from table "A" (ea_users) then JOIN according to the conditions another table "B" (ea_customfields). If u have one to many relation between tables (it means that one record from table "A" (lets say that we have in this table A1 record) can contain few related rows in table "B", lets call them as B1.1, B1.2 and B1.3 and B1.4), in this case it will join this records and put join result in memory. So in memory u would see something like

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

    if u have 10 records in table "B", which related to the table "A" it would put 10 times in memory copy of data from table "A" during fetching. And then will render it to u.

    depending on join type rows, with missing related records, can be skipped at all (INNER JOIN), or can be filled up with NULLs (LEFT JOIN or RIGHT JOIN), etc.

    When u think about JOINs, try to imagine yourself, when u try to join on the paper few big tables. U would always need to mark somehow which data come from which table in order to be able to operate with it later, so its quite logically to write row "A1" from table "A" as many times as u need to fill up empty spaces when u find appropriate record in table "B". Otherwise u would have on your paper something like:

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

    Yes, its looks ok even when column "FromTable A" contains empty data, when u have 5-10 records and u can easily operate with it (for example u can sort it in your head - u just need to imagine what should be instead of empty space, but for it, u need to remember all the time order how did u wrote the data on the paper). But lets assume that u have 100-1000 records. if u still can sort it easily, lets make things more complicated and tell, that values in table "A" can be empty, etc, etc.. Thats why for mysql engine simpler to repeat many times data from table..

    Basically, I always stick to examples when u try to imagine how would u join huge tables on paper or will try to select something from this tables and then make sorting there or something, how would u look through the tables, etc.

    GROUP_CONCAT, grouping

    Then, next mistake, u did not understand how GROUP_CONCAT works: The thing is that mysqlEngine fetch on the first step structure into memory using all where conditions, evaluating subqueries + appends all joins. When structure is loaded, it tried to perform GROUPing. It means that it will select from temporary table all rows related to the "A1". Then will try to apply aggregation function to selected data. GROUP_CONCAT function means that we want to apply concatenation on selected group, thus we would see something like "B1.1, B1.2, B1.3, B1.4". Its in few words, but I hope it will help a little to understand it.

    I googled table structure so u can write some queries there. http://www.mysqltutorial.org/tryit/query/mysql-left-join/#1

    and here is example how GROUP_CONCAT works, try to execute there query:

    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
    ;
    

    can compare with results with previous one.

    power of GROUP in aggregation functions which u can use with it. For example, u can use "COUNT()", "MAX()", "GROUP_CONCAT()" or many many others.

    or example of fetching of count (try to execute it):

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

    so my opinion:

    simpler and better to solve this issue on client side or on backend, after fetching. because in term of mysql engine response with duplication in column is absolutely correct. BUT of course, u can also solve it using grouping with concatenations for example. but I have a feeling that for your task its overcomplicating of logic

    PS. "GROUP BY 1" - means that I want to group using column 1, so after selecting data into memory mySql will try to group all data using first column, better not to use this format of writing on prod. Its the same as "GROUP BY c.customerNumber".

    PPS. Also I read comments like "use DISTINCT", etc. To use DISTINCT or order functions, u need to understand how does it work, because of incorrect usage it can remove some data from your selection, (same as GROUP or INNER JOINS, etc). On the first look, you code might work fine, but it can cause bugs in logic, which is the most complicated to find out later. Moreover DISTINCT will not help u, when u have one-to-many relation(in your particular case). U can try to execute queries:

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

    the result should be the same. Duplication in customer name column and orders numbers.

    and example how to loose data with incorrect query ;):

    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
    ;
    
    评论

报告相同问题?

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?