I have two tables; members
and telephone
.
A member can have multiple telephone rows. Each telephone row can have flags set to indicate what type of phone number it is.
I need a result set that is a single row per member, containing all or some of the different telephone rows.
This is a concatenation of LEFT JOINS
from different parts of the same table.
The database tables both have full indexes on all numeric columns.
Table structures
(probably not that important but here for completeness sake)
members:
CREATE TABLE `members` (
`mem_id` smallint(6) NOT NULL AUTO_INCREMENT,
...
`reg` varchar(10) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'No Reg',
PRIMARY KEY (`mem_id`)
) ENGINE=MyISAM AUTO_INCREMENT=968 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
Telephone:
CREATE TABLE `telephone` (
`tel_id` int(8) NOT NULL AUTO_INCREMENT,
`mem_id` int(8) NOT NULL,
`tel_name` varchar(64) CHARACTER SET utf8 NOT NULL,
`tel_num` varchar(24) COLLATE utf8mb4_unicode_ci NOT NULL,
`main` char(1) CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`player` char(1) CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`home` char(1) CHARACTER SET utf8 NOT NULL DEFAULT 'N' COMMENT 'Y= Home Phone',
PRIMARY KEY (`tel_id`),
KEY `memid` (`mem_id`),
KEY `main` (`main`),
KEY `player` (`player`),
KEY `home` (`home`),
KEY `telnum` (`tel_num`)
) ENGINE=MyISAM AUTO_INCREMENT=2237 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
My Query so far:
Aim of the query is to return a single row for each member detailing (amongst other data unshown) all their associated telephone numbers and being able to differenciate between each number:
The WHERE
conditionals are only applied to the members
table only.
SELECT members.mem_id, ... ,
thome.tel_num as thome, tmob.tel_num as tmob,
twork.tel_num as twork
FROM members
LEFT JOIN telephone thome FROM telephone ON thome.mem_id = members.mem_id AND thome.home = 'Y'
LEFT JOIN telephone tmob FROM telephone ON tmob.mem_id = members.mem_id AND tmob.main = 'Y' AND tmob.tel_num LIKE '07%'
LEFT JOIN telephone twork FROM telephone ON twork.mem_id = members.mem_id AND twork.player = 'Y'
WHERE ...
Example data:
Members table:
memid | mname | reg
---------------------------------
22 | george | 1456436456
Telephone table:
tel_id | mem_id | tel_name | tel_num | main | player | home
-----------------------------------------------------------------------
1 | 22 | Home phone | 01234 456463 | N | N | Y
12 | 22 | Work phone | 01334 457893 | Y | N | N
19 | 22 | Mobile num | 07564 456333 | N | Y | N
23 | 22 | Home phone | 01987 657353 | N | N | N
Desired Result:
memid | mname | reg | twork | tmob | thome | etc...
-------------------------------------------------------------------
22 | george | 1456436456 | 01334...| 07564...| 01234...| ...
Notes:
I'm aware that the flag columns (
main
,player
, etc.) could be ENUMsI'm also aware the tables could be InnoDB and have foreign keys setup.
(I'm not at all against either of these things it's just a case of getting round to them)
None of the telephone numbers are required so while I am aware
INNER JOIN
is more efficient; it can't be used in this case.GROUP_CONCAT
doesn't work on a per-column basis as each column is selected by differet criteria (flags). Maybe it can work on a broader setting but eachtel_num
value would need to be renamed (I think I could use some guidance on usingGROUP_CONCAT
in this situation).Flags are not mutually exclusive: a telephone row could be
| Y | Y | Y
. But each member only has one of each flag.All the phone numbers are active, some will have no flags and so not need to be returned.
Question:
Can I improve my SQL query to not need three seperate LEFT JOIN
s?
Other questions I've looked at with similar issues: