MySQL查询INNER JOIN与别名

时间:2016-06-26 20:34:08

标签: mysql join inner-join entity-attribute-value

我有两张表:usersusers_info

users看起来像这样:

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

users_info看起来像这样:

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

我想SELECT user user_info发送电子邮件=' kris@kris.com'
- 和 -
返回所有user_info值和users

这是我正在寻找的结果:

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

到目前为止,我最接近的是查询:

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

并返回此结果:

+----+----------+-------+----+--------+----------+---------------+
| 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            |
+----+----------+-------+----+--------+----------+---------------+

显然,我不需要id结果的users_info,我希望每个info_key成为"别名" (或列名称),并且每个info_val都是"别名"的值。

2 个答案:

答案 0 :(得分:1)

对于这种情况,您可以这样做;)只是一个简单的表格枢轴。

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

如果你有动态info_key,你需要一个动态的sql来做这个,在这里我给你一个样本。

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

答案 1 :(得分:1)

这利用架构中的更改来支持投射返回的数据。它取决于存储过程的使用。

group_concat的最大值取决于您对以下变量的设置(通常默认默认值较低,如1K):

set session group_concat_max_len = 20000;

将该通话嵌入BEGIN下存储过程的顶部。手册页为here。价值可能很大。例如,至少4GB。

模式

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

加载测试数据;

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

存储过程:

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 ;

测试:

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 |
+----+------+------+-----+------------+-------------------+-----------+

由于select中嵌入了cast调用,数据将以其本机预期数据类型返回。这意味着你可以直接使用它。