MySQL Query INNER JOIN with aliases

核能气质少年 提交于 2019-12-12 05:38:49

问题


I have two tables: users and users_info

users looks like this:

+----+----------+-------+
| id | slug     | name  |
+----+----------+-------+
|  1 | theploki | Kris  |
+----+----------+-------+

and users_info looks like this:

+----+--------+----------+---------------+
| id | parent | info_key | info_val      |
+----+--------+----------+---------------+
|  1 | 1      | email    | kris@kris.com |
+----+--------+----------+---------------+
|  2 | 1      | age      | 28            |
+----+--------+----------+---------------+

I want to SELECT a user who has user_info email = 'kris@kris.com'
- and -
return ALL user_info values and users values

Here's the result I'm looking for:

+----+----------+-------+---------------+-----+
| id | slug     | name  | email         | age |
+----+----------+-------+---------------+-----+
|  1 | theploki | Kris  | kris@kris.com | 28  |
+----+----------+-------+---------------+-----+

So far the closest I've gotten is with this query:

SELECT users.*, users_info.* FROM users
INNER JOIN users_info on users_info.parent = users.id
where users.id = (SELECT users_info.parent FROM users_info
    WHERE users_info.parent = users.id
    AND users_info.info_val = 'kris@kris.com')

And it returns this result:

+----+----------+-------+----+--------+----------+---------------+
| id | slug     | name  | id | parent | info_key | info_val      |
+----+----------+-------+----+--------+----------+---------------+
|  1 | theploki | Kris  |  1 |  1     | email    | kris@kris.com |
+----+----------+-------+----+--------+----------+---------------+
|  1 | theploki | Kris  |  2 |  1     | age      | 28            |
+----+----------+-------+----+--------+----------+---------------+

Obviously I don't need the id of the users_info result and I want each info_key to be the "alias" (or column name) and each info_val to be the value for that "alias".


回答1:


For this case, you can do it like this;) Just a simple table pivot.

select
    users.id,
    users.slug,
    users.name,
    max(if(users_info.info_key = 'email', users_info.info_val, null)) as email,
    max(if(users_info.info_key = 'age', users_info.info_val, null)) as age
from users
inner join users_info
on users.id = users_info.parent
group by users.id

SQLFiddle DEMO HERE

If you have a dynamic info_key, you will need a dynamic sql to do this, here I give you a sample.

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(if(users_info.info_key = ''',
      users_info.info_key,
      ''', users_info.info_val, null)) as ',
      users_info.info_key
    )
  ) INTO @sql
FROM users
inner join users_info
on users.id = users_info.parent
;

SET @sql = CONCAT('select users.id, users.slug, users.name, ', @sql, ' FROM users
inner join users_info group by users.id having email = \'kris@kris.com\'');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

SQLFiddle DEMO HERE




回答2:


This utilizes a change in the schema to support casting of data coming back. And it hinges on the use of a stored procedure.

The maximum value of group_concat is governed by your setting for the following variable (which is usually defaulted rather low, like 1K):

set session group_concat_max_len = 20000;

Embed that call at the top of your stored proc under BEGIN. The manual page is here. The value can be huge. For instance, at least 4GB.

Schema

drop table if exists users;
create table users
(
    id int auto_increment primary key,
    slug varchar(100) not null,
    name varchar(100) not null
    -- other indexes here like uniqueness, etc (btw none added)
);

drop table if exists users_info;
create table users_info
(
    id int auto_increment primary key,
    parent int not null,
    info_key varchar(100) not null,
    info_val varchar(100) not null,
    datatype varchar(100) not null, -- see http://stackoverflow.com/a/8537070/ (DATA TYPES)
    -- other indexes here (btw none added)
    -- FK below:
    foreign key `ui_2_users_9283` (parent) references users(id) -- I guess
);

Load Test data;

-- delete from users; -- note truncate disallowed on parent with an FK (so delete !)
insert users(slug,name) values 
('theploki','Kris'),
('Yoda','Yoda');
-- select * from users;

-- truncate table users_info;
insert users_info(parent,info_key,info_val,datatype) values 
(1,'email','kris@kris.com','char(100)'),
(1,'age','28','unsigned'),
(2,'birthdate','1996-02-14','date'),
(2,'email','yoda@starwars.com','char(100)'),
(2,'networth','102504.12','decimal(12,2)'),
(2,'age','910','unsigned');

Stored Procedure:

drop procedure if exists fetchInfoKeysByEmailAddr;
DELIMITER $$
create procedure fetchInfoKeysByEmailAddr(emailAddr varchar(100))
BEGIN
    set @parentid=-1;
    select parent into @parentid 
    from users_info 
    where info_key='email' and info_val=emailAddr;

    if @parentid>0 then
        -- http://stackoverflow.com/a/8537070/ (DATA TYPES)

        SELECT GROUP_CONCAT(concat('cast("',info_val,'" as ',datatype,') as ',info_key)
        ORDER BY info_key SEPARATOR ',')  into @tail
        FROM users_info
        where parent=@parentid
        GROUP BY parent;

        set @final:=concat("select id,slug,name,",@tail,' from users where id=',@parentid);

        PREPARE stmt1 FROM @final;
        EXECUTE stmt1;
        DEALLOCATE PREPARE stmt1;
    end if;
END$$
DELIMITER ;

Test:

call fetchInfoKeysByEmailAddr('x');
-- user info does not exist, empty (todo: change accordingly)

call fetchInfoKeysByEmailAddr('kris@kris.com');
+----+----------+------+-----+---------------+
| id | slug     | name | age | email         |
+----+----------+------+-----+---------------+
|  1 | theploki | Kris |  28 | kris@kris.com |
+----+----------+------+-----+---------------+

call fetchInfoKeysByEmailAddr('yoda@starwars.com');
+----+------+------+-----+------------+-------------------+-----------+
| id | slug | name | age | birthdate  | email             | networth  |
+----+------+------+-----+------------+-------------------+-----------+
|  2 | Yoda | Yoda | 910 | 1996-02-14 | yoda@starwars.com | 102504.12 |
+----+------+------+-----+------------+-------------------+-----------+

Due to the cast call embedded in the select, the data is brought back in its native, anticipated data type. Which means you can work on it directly.



来源:https://stackoverflow.com/questions/38042927/mysql-query-inner-join-with-aliases

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!