将 MySQL 中的重复连接最小化为水平构建的 table
Minimizing repeated joins in MySQL to horizontally constructed table
我有一个 table,其中包含联系人(此处已简化):
create table contacts
(
id_c char(36) not null primary key,
first_name varchar(255) not null,
last_name varchar(255) not null,
user_id1_c char(36) not null,
user_id2_c char(36) not null,
user_id3_c char(36),
user_id4_c char(36),
user_id5_c char(36),
user_id6_c char(36),
user_id7_c char(36) null,
user_id8_c char(36) null,
user_id9_c char(36) null,
user_id10_c char(36) null,
user_id11_c char(36) null,
user_id12_c char(36) null,
user_id13_c char(36) null,
user_id14_c char(36),
user_id15_c char(36) null,
user_id16_c char(36) null
)
engine = MyISAM
charset = utf8;
和包含用户信息的相关 table:
create table users (
id char(36) not null primary key,
first_name varchar(255),
last_name varchar(255)
)
对于报告,我希望能够获得关联用户的名称,在 MySQL 中,这是一个 JOIN
语句。
SELECT contacts.*,
CONCAT_WS(' ', u1.first_name, u1.last_name) AS `some_user`,
CONCAT_WS(' ', u2.first_name, u2.last_name) AS `some_other_user`,
FROM contacts
LEFT JOIN users u1 ON u1.id = user_id1_c
LEFT JOIN users u2 ON u2.id = user_id2_c
...
在查询中,我将能够毫无问题地获取名称(在您对设计进行批评的评论之前,这是我得到的结构,因此构建一个更抽象的中间人用户table 不在我的薪酬等级之内)。我认识到将 16 个连接附加到查询上很慢,即使它的索引很好。所以我的问题变成了:
有没有更直接的方法来解决这个问题?我考虑过做一个 LEFT JOIN users WHERE users.id IN (user_id1_c,...)
,但我无法组织一个 GROUP BY
或一个 distinct 来检索与该列匹配的名称,只是 user
映射到 联系人记录中的某些列。
我是注定要重复LEFT JOIN
s,还是有更好的方法。如果重要的话,我目前使用的是 MySQL5.7 版本,不过我认为我们可能很快就会将公司升级到版本 8。
SELECT
c.*,
MAX(CASE WHEN u.id = c.user_id1 THEN u.whole_name END) AS user_name_1,
MAX(CASE WHEN u.id = c.user_id2 THEN u.whole_name END) AS user_name_2,
MAX(CASE WHEN u.id = c.user_id3 THEN u.whole_name END) AS user_name_3,
...
FROM
contacts
LEFT JOIN
(
SELECT
id,
CONCAT_WS(' ', first_name, last_name) AS whole_name
FROM
users
)
AS u
ON u.id IN (c.user_id1, c.user_id2, c.user_id3, ...)
GROUP BY
c.id
真的,您应该根据联系人 table 中 select 的 所有内容 进行分组,但是 MySQL 5.7 是宽松的,允许您仅按唯一标识符进行分组。
我有一个 table,其中包含联系人(此处已简化):
create table contacts
(
id_c char(36) not null primary key,
first_name varchar(255) not null,
last_name varchar(255) not null,
user_id1_c char(36) not null,
user_id2_c char(36) not null,
user_id3_c char(36),
user_id4_c char(36),
user_id5_c char(36),
user_id6_c char(36),
user_id7_c char(36) null,
user_id8_c char(36) null,
user_id9_c char(36) null,
user_id10_c char(36) null,
user_id11_c char(36) null,
user_id12_c char(36) null,
user_id13_c char(36) null,
user_id14_c char(36),
user_id15_c char(36) null,
user_id16_c char(36) null
)
engine = MyISAM
charset = utf8;
和包含用户信息的相关 table:
create table users (
id char(36) not null primary key,
first_name varchar(255),
last_name varchar(255)
)
对于报告,我希望能够获得关联用户的名称,在 MySQL 中,这是一个 JOIN
语句。
SELECT contacts.*,
CONCAT_WS(' ', u1.first_name, u1.last_name) AS `some_user`,
CONCAT_WS(' ', u2.first_name, u2.last_name) AS `some_other_user`,
FROM contacts
LEFT JOIN users u1 ON u1.id = user_id1_c
LEFT JOIN users u2 ON u2.id = user_id2_c
...
在查询中,我将能够毫无问题地获取名称(在您对设计进行批评的评论之前,这是我得到的结构,因此构建一个更抽象的中间人用户table 不在我的薪酬等级之内)。我认识到将 16 个连接附加到查询上很慢,即使它的索引很好。所以我的问题变成了:
有没有更直接的方法来解决这个问题?我考虑过做一个 LEFT JOIN users WHERE users.id IN (user_id1_c,...)
,但我无法组织一个 GROUP BY
或一个 distinct 来检索与该列匹配的名称,只是 user
映射到 联系人记录中的某些列。
我是注定要重复LEFT JOIN
s,还是有更好的方法。如果重要的话,我目前使用的是 MySQL5.7 版本,不过我认为我们可能很快就会将公司升级到版本 8。
SELECT
c.*,
MAX(CASE WHEN u.id = c.user_id1 THEN u.whole_name END) AS user_name_1,
MAX(CASE WHEN u.id = c.user_id2 THEN u.whole_name END) AS user_name_2,
MAX(CASE WHEN u.id = c.user_id3 THEN u.whole_name END) AS user_name_3,
...
FROM
contacts
LEFT JOIN
(
SELECT
id,
CONCAT_WS(' ', first_name, last_name) AS whole_name
FROM
users
)
AS u
ON u.id IN (c.user_id1, c.user_id2, c.user_id3, ...)
GROUP BY
c.id
真的,您应该根据联系人 table 中 select 的 所有内容 进行分组,但是 MySQL 5.7 是宽松的,允许您仅按唯一标识符进行分组。