Home > OS >  Minimizing repeated joins in MySQL to horizontally constructed table
Minimizing repeated joins in MySQL to horizontally constructed table

Time:02-16

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 JOINs, 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.

  • Related