我有两张桌子; members
和telephone
。
成员可以有多个电话行。每个电话行可以设置标志以指示它是什么类型的电话号码。
我需要一个每个成员单行的结果集,包含所有或部分不同的电话行。
这是来自同一表的不同部分的LEFT JOINS
的串联。
数据库表在所有数字列上都有完整索引。
(可能不那么重要,但为了完整起见,这里)
成员:
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
电话:
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
查询的目的是为每个成员详细说明(在未示出的其他数据中)返回所有相关电话号码并且能够在每个号码之间进行区分的单行:
WHERE
conditionals仅适用于members
表。
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 ...
会员表:
memid | mname | reg
---------------------------------
22 | george | 1456436456
电话表:
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
期望的结果:
memid | mname | reg | twork | tmob | thome | etc...
-------------------------------------------------------------------
22 | george | 1456436456 | 01334...| 07564...| 01234...| ...
main
,player
等)可能是ENUM(我完全不反对这些事情中的任何一个,这只是一个绕过它们的情况)
INNER JOIN
效率更高;它不能用于这种情况。GROUP_CONCAT
不能按列进行操作,因为每个列都是由不同的标准(标志)选择的。也许它可以在更广泛的设置上工作,但每个tel_num
值都需要重命名(我想我可以在这种情况下使用GROUP_CONCAT
的一些指导)。| Y | Y | Y
。但每个成员只有一个标志。题:
LEFT JOIN
s?我在类似问题上看过的其他问题:
在不改变当前架构的情况下,我没有看到改进当前查询的方法,除了尝试使用索引等进行调整之外。但是,我可以对您的表设计进行建议更改,这样可以更简单查询所需的输出。考虑将telephone
表重构为以下内容:
tel_id | mem_id | tel_name | tel_num | type | active
-----------------------------------------------------------------------
1 | 22 | Home phone | 01234 456463 | 1 | 1
12 | 22 | Work phone | 01334 457893 | 2 | 1
19 | 22 | Mobile num | 07564 456333 | 3 | 1
23 | 22 | Home phone | 01987 657353 | 1 | 0
现在,如果您想要一个可以检索每个成员的活动主页,工作和移动号码的查询,则以下内容应该足够:
SELECT
m.memid,
m.mname,
m.reg,
GROUP_CONCAT(t.tel_name ORDER BY t.type) names,
GROUP_CONCAT(t.tel_num ORDER BY t.type) numbers
FROM members m
LEFT JOIN telephone t
ON m.mem_id = t.mem_id AND
t.active = 1
GROUP BY
m.memid;
我假设每个成员总是有一些家庭,工作和移动号码的条目,即使该条目应为null或空字符串。我还假设您只想报告活动数字。如果您需要报告所有数字,那么组连接解决方案就会变得不那么有吸引力了,因为您可能会找回一个很长的CSV字符串,并且可能不太直观如何阅读它。
编辑:
我们也可以GROUP_CONCAT
电话名称标签。这意味着除了CSV数字列表之外,结果集中的每条记录还将具有相应的电话号码类型。