MySQL 使用别名查询 INNER JOIN

MySQL Query INNER JOIN with aliases

我有两个表: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 一个 useruser_info 电子邮件 = 'kris@kris.com'
- 和 -
return 所有 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')

它 return 是这个结果:

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

显然我不需要 users_info 结果的 id 并且我希望每个 info_key 是 "alias" (或列名)并且每个 info_val 成为 "alias".

的值

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

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

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

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

由于 cast 调用嵌入在 select 中,数据将返回其原始的预期数据类型。这意味着您可以直接处理它。