1

我有一个查询,基本上可以找到符合特定产品条件的所有成员及其家人。每个家庭成员都列在单独的行上,即使他们属于同一个家庭。我们使用注意的 MySQL。

例如(实际返回的所有数据的小样本):

ID  | familyid |  firstname | lastname | address      | city      | relationship
==============================================================================
1   |    1     |   john     |  davis   | 942 James Ln | cityplace | primary
2   |    1     |   suzy     |  davis   | 942 James Ln | cityplace | spouse
3   |    2     |   andrew   |  smith   | 444 A Rd     | new york  | primary
4   |    3     |   Mike     |  lewis   | 123 Street   | dallas    | primary
5   |    3     |   Donna    |  lewis   | 123 Street   | dallas    | child
1   |    3     |   Jamie    |  lewis   | 123 Street   | dallas    | child

上表中的(组成)数据来自两个表:

  1. 主要信息(包含有关会员和地址的所有一般信息)
  2. 家庭信息(包含配偶和子女的姓名和性别信息)

为简单起见,我们假设这些表中包含的唯一信息如下:

成员

ID | familyid | firstname | lastname | address | city
-----------------------------------------------------

家庭

ID | familyid | firstname | lastname | type | gender
------------------------------------------------------

我们系统中的成员可以拥有他们选择的多个产品(信息位于单独的表格中)。不幸的是,我知道构建上述语句的唯一方法是本质上选择相同的信息两次,然后执行 UNION ALL 语句,如下面的示例语句:

SELECT r.* FROM (
  SELECT familyid, firstname, lastname, address, city, 'primary'
  FROM member
 UNION ALL
  SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, IF(b.type = 1, 'spouse', 'child')
  FROM member a
  JOIN family b ON (a.familyid = b.familyid)
) r

这已经有点乱了,但是当我开始将其他几个表加入已经收集的“名称”信息时,“真正的”问题就出现了。例如,我可以在整个查询中添加一个 join 语句,以仅显示选择了 HBO 电视服务的成员或家庭:

after last statement: JOIN channels c ON (r.familyid = c.familyid)

这将清除之前选择的声明中没有 HBO 的所有成员,但是如果我想选择 HBO 的子部分(例如,那些只有会员版本的人与全家庭版本的人)怎么办。这些随后的查询基本上迫使我进行两次完全相同的调用,只返回略有不同的信息。

SELECT r.* FROM (
  SELECT familyid, firstname, lastname, address, city, 'primary'
  FROM member m
  JOIN channels c ON (m.familyid = c.familyid)
  WHERE c.name = "HBO" and c.memtype = "member only"
 UNION ALL
  SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, IF(b.type = 1, 'spouse', 'child')
  FROM member a
  JOIN family b ON (a.familyid = b.familyid)
  JOIN channels c ON (b.familyid = c.familyid)
  WHERE c.name = "HBO" and c.memtype = "family"
) r

有没有办法更好地优化这些语句?MySQL 在这件事上只是 SOL 吗?

注意:表格按原样进行了标准化,因此请不要建议对其进行改进。此特定查询仅针对每月根据需要自动生成一次的单个报告(非常罕见)。我只是想找出是否有可能避免使用模式的 Union 语句。无法修改架构。

4

3 回答 3

2

您似乎正在使用该member表来存储家庭的“主要”成员和家庭地址,并使用该family表来存储有关家庭中其他人的信息。这似乎设计得不是很好,名字也不是很好选择。

建议:将所有“主要”成员也添加到family表中,并添加一个新的第三个type

INSERT INTO family
  (family_id, type, firstname, lastname)
SELECT family_id, 2, firstname, lastname
FROM member ;

然后您可以使用简单的连接进行r查询:

SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, 
       CASE b.type 
         WHEN 0 THEN 'child'
         WHEN 1 THEN 'spouse'
         WHEN 2 THEN 'primary'
       END AS type
FROM member a
  JOIN family b 
    ON a.familyid = b.familyid ;

您甚至可以从表中删除firstnamelastnamemember(并且可能重命名表以更好地反映它们的使用情况)。


第二种情况可以写成:

SELECT a.familyid, b.firstname, b.lastname, a.address, a.city, 
       CASE b.type 
         WHEN 0 THEN 'child'
         WHEN 1 THEN 'spouse'
         WHEN 2 THEN 'primary'
       END AS type
FROM member a
  JOIN family b 
    ON a.familyid = b.familyid
  JOIN channels c 
    ON b.familyid = c.familyid
WHERE c.name = 'HBO' AND ( b.type <> 2 AND c.memtype = 'family'  
                        OR b.type  = 2 AND c.memtype = 'member only'
                         ) ;
于 2013-01-11T21:55:52.383 回答
2

正如我在评论中提到的那样,我认为使用如下更灵活的模式(简化)会更可行:

family表将假设一个家庭,其中包含来自 的成员member。这些成员之间的关系将在member_map. 是可选的parent_member_id,在这种情况下将指示户主。

于 2013-01-11T22:13:08.933 回答
0

我不太确定我是否完全理解这个问题。(因为我不知道预期的结果集是什么)。但是,最好将家庭概念与个人成员分开。为此,Family 应该是一个表,而家庭成员应该是一个名为 Members 的单独表。(这可能很明显,但只是为了清楚起见)

创建表 Family(Family_ID、Family_Name、Family_Address、...)

创建表 MEmbers ( Member_ID Is_Primary / 1 or 0 /, Member_Name /* 根据需要扩展为尽可能多的列 - first name, last name ... */, Member_Info /* as Gender, Age ... */ )

然后,您的加入应构造为:家庭加入频道,用于获取具有某种观看模式的家庭和个人成员:家庭加入频道加入会员。

(这是我对 Stackoverflow 的第一条评论。我不太清楚如何语法高亮...)。我会发现它会更精致。

于 2013-01-11T22:29:14.857 回答