Mysql 有没有一种更有效的方法可以在不使用联合和重复语句的情况下构建查询?

Mysql 有没有一种更有效的方法可以在不使用联合和重复语句的情况下构建查询?,mysql,Mysql,我有一个查询,基本上可以找到符合特定产品资格的所有成员及其家庭。每个家庭成员都列在一行中,即使他们属于同一个家庭。我们使用MySQL是值得注意的 例如,实际返回的所有数据的小样本: ID | familyid | firstname | lastname | address | city | relationship ==============================================================================

我有一个查询,基本上可以找到符合特定产品资格的所有成员及其家庭。每个家庭成员都列在一行中,即使他们属于同一个家庭。我们使用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
上表中的合成数据来自两个表:

主要信息包含有关成员和地址的所有一般信息 家庭信息包含配偶和子女的姓名和性别信息 为简单起见,假设这些表中包含的唯一信息如下:

成员 家庭 我们系统中的成员可以在单独的表中选择多个产品。不幸的是,我知道构建上述语句的唯一方法是从本质上选择相同的信息两次,然后执行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在这件事上只是索尔吗

注: 表格按原样规范化,因此请不要建议对其进行改进。此特定查询仅用于每月自动生成一次的单个报告,并且很少根据需要生成。我只是想找出是否有可能避免使用模式的Union语句。无法修改架构。

您似乎在使用成员表存储家庭的主要成员和家庭地址,并使用家庭表存储家庭中其他人的信息。这似乎不是很好的设计和名称选择不是很好

建议:使用新的第三种类型将所有主要成员也添加到族表中:

然后,您可以对r查询使用简单联接:

您甚至可以从成员表中删除firstname和lastname列,或者重命名这些表以更好地反映它们的使用情况

第二种情况将写成:

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'
                         ) ;
您似乎正在使用“成员”表存储家庭的主要成员和家庭地址,并使用“家庭”表存储家庭中其他人的信息。这似乎不是很好的设计和名称选择不是很好

建议:使用新的第三种类型将所有主要成员也添加到族表中:

然后,您可以对r查询使用简单联接:

您甚至可以从成员表中删除firstname和lastname列,或者重命名这些表以更好地反映它们的使用情况

第二种情况将写成:

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'
                         ) ;

正如我在评论中提到的,我认为使用更灵活的模式更可行,如下所示:


家庭表将假定一个家庭,其中包含来自成员的成员。这些成员之间的关系将在成员映射中处理。家长成员id是可选的,在这种情况下,将指示户主。

正如我在评论中提到的,我认为使用更灵活的模式更可行,如下所示:


家庭表将假定一个家庭,其中包含来自成员的成员。这些成员之间的关系将在成员映射中处理。家长成员id是可选的,在这种情况下表示户主。

我不太确定我是否完全理解这个问题。因为我不知道预期的结果集是什么。 然而,将家庭概念与个人成员分开是一个好主意。要做到这一点,家庭应该是一个表和家庭成员一个单独的表称为成员。 这可能是显而易见的,但只是为了清楚起见

创建表格族 家庭ID, 姓, 家庭住址, . . .

创建表成员 会员身份证 是_Primary/1还是0/, Member_Name/*可根据需要扩展到任意多个列,例如-名、姓…*/, 成员信息/*性别、年龄*/

然后,您的联接应构造为:族联接通道,用于获取具有某种查看模式的族,并将单个成员构造为:族联接通道联接成员

这是我第一次对Stackoverflow发表评论。我不太知道如何突出显示语法。
... 我发现这会使它更精致

我不太确定我是否完全理解这个问题。因为我不知道预期的结果集是什么。 然而,将家庭概念与个人成员分开是一个好主意。要做到这一点,家庭应该是一个表和家庭成员一个单独的表称为成员。 这可能是显而易见的,但只是为了清楚起见

创建表格族 家庭ID, 姓, 家庭住址, . . .

创建表成员 会员身份证 是_Primary/1还是0/, Member_Name/*可根据需要扩展到任意多个列,例如-名、姓…*/, 成员信息/*性别、年龄*/

然后,您的联接应构造为:族联接通道,用于获取具有某种查看模式的族,并将单个成员构造为:族联接通道联接成员



这是我第一次对Stackoverflow发表评论。我不太知道如何突出显示。。。。我发现这会使它更精致

因此family.type只能有2个值,1或0?@ypercube-目前是的,但如果需要更多值,我可以使用CASE语句。您能更改架构的结构吗?@njk-是的,但不确定这样做是否有意义。一个成员可以有20多个依赖项,就像主成员可以拥有的通道一样,这些是动态的,不应包含在主表中。不确定上下文,但关系没有真正规范化。因此family.type只能有2个值,1或0?@ypercube-目前是的,但如果我需要更多,我可以使用CASE语句。您能更改架构的结构吗?@njk-是的,但不确定这样做是否有意义。一个成员可以有20多个依赖项,就像主成员可以拥有的通道一样,这些是动态的,不应该包含在主表中。不确定上下文,但关系没有真正规范化。1这些名称是示例,我没有给出表的实际名称。谢谢你花时间回答,但我看不出你的建议如何回答整个问题。实际的成员表包含大量的实际信息。它是我们整个系统中最重要的表。“family”表只是将任何其他人员链接到该成员,最终与您描述的新family表完全相同。我建议将数据从成员复制到family,然后从两个表(如firstname和lastname)中的成员中删除这些列。这就解决了这个问题,因为您不必再进行联合了。由于给定的示例场景,即使新表存在,联合也会被取消。一个成员拥有一个家庭并不意味着他的家庭拥有家庭级产品。会员可以选择多种产品,有些是会员身份的,有些是家庭身份的。我很好奇你为什么说这似乎设计得不太好。它被适当地规范化,并且是最有意义的。也许我应该在我的原始问题中提供一个注释,原始表无论如何都不能被修改,因为数百页都在使用它们。它实际上看起来根本没有标准化。特别是如果地址在多个表中重复。1名称是示例,我没有给出表的实际名称。谢谢你花时间回答,但我看不出你的建议如何回答整个问题。实际的成员表包含大量的实际信息。它是我们整个系统中最重要的表。“family”表只是将任何其他人员链接到该成员,最终与您描述的新family表完全相同。我建议将数据从成员复制到family,然后从两个表(如firstname和lastname)中的成员中删除这些列。这就解决了这个问题,因为您不必再进行联合了。由于给定的示例场景,即使新表存在,联合也会被取消。一个成员拥有一个家庭并不意味着他的家庭拥有家庭级产品。会员可以选择多种产品,有些是会员身份的,有些是家庭身份的。我很好奇你为什么说这似乎设计得不太好。它被适当地规范化,并且是最有意义的。也许我应该在我的原始问题中提供一个注释,原始表无论如何都不能被修改,因为数百页都在使用它们。它实际上看起来根本没有标准化。特别是当地址在多个表中重复时。谢谢您的书写。这类似于ypercube编写的内容,我的评论回复来得有点晚,但在此设置中不可能修改模式。在未来的项目中,我可能会考虑类似的活动,但不确定它是否提供了更多的灵活性,但我可能是错的。谢谢你的写作。这类似于ypercube所写的内容,我的评论回复来得有点晚,但修改模式不是p
在这种情况下可能发生。在未来的项目中,我可能会考虑类似的活动,但不确定它是否提供了更多的灵活性,但我可能是错的。谢谢。正如我的说明所述,修改模式是不可能的。除了这个单独的报告之外,这个模式在其他所有测试用例中都运行良好。我会考虑未来的项目,但希望解决当前的问题。谢谢。正如我的说明所述,修改模式是不可能的。除了这个单独的报告之外,这个模式在其他所有测试用例中都运行良好。我会考虑未来的项目,但要解决当前的问题。
INSERT INTO family
  (family_id, type, firstname, lastname)
SELECT family_id, 2, firstname, lastname
FROM member ;
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 ;
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'
                         ) ;