I have a table which contains contacts (simplified here):
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;
and a related table containing user information:
create table users (
id char(36) not null primary key,
first_name varchar(255),
last_name varchar(255)
)
For a report, I would want to be able to get the names of the associated users, and in MySQL this is a JOIN
statement.
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
...
In the query, I would be able to get the names no problem (and before you go rushing to the comments with criticism for the design, this is the structure I got, so building a more abstract middleman user table isn't really within my pay grade). I recognize that appending 16 joins onto a query is slow even if it is well indexed. So my question becomes the following:
Is there a more direct way to address this problem? I have considered doing a LEFT JOIN users WHERE users.id IN (user_id1_c,...)
, but I cannot organize a GROUP BY
or a distinct such that I retrieve the names matched to the column, just that a user
maps to some column in a contact record.
Am I doomed to repeated LEFT JOIN
s, or is there a better way. If it matters, I am currently in version MySQL5.7, though I think we might be upgrading our company to version 8 sometime soon.
CodePudding user response:
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
Really you should group by everything that you select from the contacts table, but MySQL 5.7 is lax and allows you to group by only the unique identififer.