【问题标题】:SQL - match records from one table to another table based on several columnsSQL - 根据几列将记录从一个表匹配到另一个表
【发布时间】:2011-09-29 18:52:29
【问题描述】:

我有两张桌子:
爱好

   +-------+-------+-------+-------+  
   | name  |hobby1 |hobby2 |hobby3 |  
   +-------+-------+-------+-------+   
   | kris  | ball  | swim  | dance |  
   | james | eat   | sing  | sleep |  
   | amy   | swim  | eat   | watch |  
   +-------+-------+-------+-------+

tblavailable_hobby

+----------------+ 
| available_hobby|
+----------------+
| ball           | 
| dance          | 
| swim           | 
| eat            | 
| watch          | 
+----------------+ 

sql 查询应该获取 tblhobby 中的所有列,并将其与 tblavailable_hobby 匹配。如果所有爱好都与 available_hobby 匹配,则选择此人

查询应该产生

+--------+ 
| name   |
+--------+
| kris   | 
| amy    | 
+--------+

请帮忙

感谢您的回答。我继承了这个数据库,目前无法对其进行规范化。但是,我想为这个问题添加另一个转折点。假设:

   +-------+-------+-------+-------+  
   | name  |hobby1 |hobby2 |hobby3 |  
   +-------+-------+-------+-------+   
   | kris  | ball  | swim  | dance |  
   | james | eat   | sing  | sleep |  
   | amy   | swim  | eat   | watch | 
   | brad  | ball  |       | dance |
   +-------+-------+-------+-------+

我想买

+--------+ 
| name   |
+--------+
| kris   | 
| amy    | 
| brad   |
+--------+

我将如何处理它?

【问题讨论】:

  • 你的表的设计违反了3nf,你需要规范化tblHobby。
  • @JonH 它违反了1NF。 :)
  • @Shef:你错了,所有表似乎都在 1NF 中。评论this article:“许多作者误解了重复组的概念,并用它来声称某个表违反了 1NF。有些人 [错误地] 认为一组列(通常名称相似)相邻放置在一个表中相互连接,并且具有相同的数据类型构成一个'重复组'。”
  • @JonH:3NF 不是很有用。他们应该以 5NF 为目标。
  • @onedaywhen 对不起,伙计,但你是误解它的人。这不是文章描述的情况,这里的重复组违反了 1NF,因为它是与人相关的同一类型的数据。在这种情况下,这个人可以在没有爱好、有一个或两个爱好的情况下站立得很好。并非所有三列都是必需的。如果您参考文章中的示例,则所有列都是必需的。如果您仔细观察,列之间交换了相同的爱好,因此hobby_1 没有谈论一种爱好,而是重复列以适应值。

标签: mysql sql ms-access


【解决方案1】:

糟糕的数据库设计,但是,假设你必须忍受它:

SELECT h.name
    FROM tblhobby h
        INNER JOIN tblavailable_hobby ah1
            ON h.hobby1 = ah1.available_hobby
        INNER JOIN tblavailable_hobby ah2
            ON h.hobby2 = ah2.available_hobby
        INNER JOIN tblavailable_hobby ah3
            ON h.hobby3 = ah3.available_hobby

编辑:回答以下 cmets 中提出的扭曲。

SELECT h.name
    FROM tblhobby h
        LEFT JOIN tblavailable_hobby ah1
            ON h.hobby1 = ah1.available_hobby
        LEFT JOIN tblavailable_hobby ah2
            ON h.hobby2 = ah2.available_hobby
        LEFT JOIN tblavailable_hobby ah3
            ON h.hobby3 = ah3.available_hobby
    WHERE (h.hobby1 IS NULL OR ah1.available_hobby IS NOT NULL)
        AND (h.hobby2 IS NULL OR ah2.available_hobby IS NOT NULL)
        AND (h.hobby3 IS NULL OR ah3.available_hobby IS NOT NULL)

【讨论】:

  • 如果其中一个爱好是空的,我怎么还能选人?
  • 你会做一个左外连接。所以只需将上面示例中的所有INNER JOINs 更改为LEFT OUTER JOIN 这意味着:包含左侧表格中的所有数据,无论它是否与右侧表格中的任何数据匹配。
  • 嗨,抱歉,附加查询不起作用。仍然产生克里斯和艾米
  • @KrisAdidarma 附加查询假定您的“空”值为 NULL。您的“空”值是NULL 还是空字符串''
  • 那是另一回事。数据库正在使用 access 2003,我试图确定它是空的还是空的。我仍然无法弄清楚。我认为访问没有区别。你有这方面的经验吗?
【解决方案2】:

我知道这并不能直接回答您的问题,并且其他人指出您的表格设计存在问题。它应该是这样的:

Table: Person
Id    Name
-------------
1     Kris
2     James
3     Amy

table: PersonHobby (Join table)
PersonId HobbyId
----------------
1        1 -- Kris likes to ball
1        2 --   "           dance
1        3 --   "           swim
2        4  -- James likes to eat

Table: Hobby
Id   Name
--------------
1    Ball
2    Dance
3    Swim
4    Eat
etc.

此设计使用Join or Junction table 的概念,允许您在数据之间建立多对多关系。在这种情况下,人和爱好。

然后你像这样查询数据:

SELECT * 
FROM Person p 
JOIN PersonHobby AS ph on p.Id = ph.PersonId
JOIN Hobby       AS h  on h.Id = ph.HobbyId

WHERE ... -- filter as you need to

我的示例中的 PersonH​​obbies 表采用 Persons 表和 Hobbies 表,并启用 Persons 和 Hobbies 之间的关系。我知道这对你来说可能看起来像是更多的工作......额外的表格,额外的列。但请相信我们,这种设计将在不久的将来让您的生活变得更加简单。事实上,通过尝试找出一个比针对当前数据库的查询要简单得多的查询,您已经感受到了设计的痛苦。

我想制作一个 WHERE 过滤器来满足您的要求,但我不太明白您想要什么。你能更详细地解释一下吗?

【讨论】:

  • 在解决 1NF 问题时,您忽略了能够容纳所呈现的数据 - 即分配给不“可用”的人的爱好。在您的模型中(虽然我没有对好处提出异议),查询连接实际上可能会变得不同,或者您可以有一个相当冗长的 WHERE 子句,因为然后您添加回“可用”爱好和为一个人链接的所有爱好需要“可用”的爱好。即该人不能与任何“不可用”的爱好相关联。
  • 嗨,保罗,乔的回答是我总体上想要的,但我已经编辑了我的问题,因为现在我在其中一个专栏中缺少一个爱好。因此,我需要修改 joe 的查询。这是未规范化数据库的结果,我知道,但我需要想办法解决它。也许你能想出我们的解决方案。谢谢
  • @Kris:我对您继承的数据模型表示诚挚的哀悼。不幸的是,我建议的解决方案一直贯穿应用程序,并且确实会改变行为。例如,用户将无法输入不存在的爱好。它必须首先创建,或者在检测到不存在的爱好时即时创建。等等。这将需要重做大部分逻辑......
  • @PaulSasik 谢谢。很多逻辑和编程都涉及到这一点。实际上,数据库比爱好问题要复杂一些,但它是这样呈现的,因为它更容易理解。感谢您的支持
【解决方案3】:

您可以使用查询将现有表转换为“虚拟表”,我认为它应该更易于使用。将此 SQL 语句另存为 qryHobbiesUnion。

SELECT [name] AS person, hobby1 AS hobby
FROM tblhobby
WHERE (((hobby1) Is Not Null))
UNION
SELECT [name], hobby2
FROM tblhobby
WHERE (((hobby2) Is Not Null))
UNION
SELECT [name], hobby3
FROM tblhobby
WHERE (((hobby3) Is Not Null));

我将“名称”括在方括号中,因为它是reserved word。我将 [name] 别名为 person 以避免稍后在子查询中使用 qryHobbiesUnion 时出现方括号问题。

我假设爱好的任何“空”值都将为 Null。如果空格也可以是空字符串 (""),请将 WHERE 子句更改为如下模式:

WHERE Len(hobby1 & "") > 0

确定 WHERE 子句的哪个版本返回正确的行后,保存查询并在另一个查询中使用它。

SELECT sub.person
FROM
    [SELECT qh.person, qh.hobby, ah.available_hobby
    FROM
        qryHobbiesUnion AS qh
        LEFT JOIN tblavailable_hobby AS ah
        ON qh.hobby = ah.available_hobby
        ]. AS sub
GROUP BY sub.person
HAVING (((Count(sub.hobby))=Count([sub].[available_hobby])));

使用您的第二组示例数据,该查询返回您想要的 3 个人名:amy;布拉德;还有克里斯。

如果 tblhobby 包含一个人的所有爱好字段为空的行,则此查询将不包括该人的姓名。这对我来说很有意义,因为您的意图似乎是确定爱好选择在 tblavailable_hobby 中都匹配的人。因此,没有爱好选择的人没有匹配项。如果你想要不同的行为,这可能会变得更丑陋。 :-)

【讨论】:

  • “我将“名称”括在方括号中,因为...”——但是为什么在不需要括号的情况下使用这么多括号,例如(((hobby3) Is Not Null))?!
  • @onedaywhen 我使用了查询设计器,它带有括号(IMO)。请注意,它还在 HAVING 子句中添加了方括号。那些“特征”曾经让我烦恼,我会丢弃那些无关紧要的东西。从那以后,我变得更加自满,并且不经常清理 SQL。而且我认为它有时可能会帮助 Access 初学者向他们展示当查询设计器转换 SQL 时 SQL 的外观。
【解决方案4】:

确实,您必须了解有关关系数据库的更多信息。你的设计不好。你应该有一张与人共处的桌子和一张有爱好的桌子。然后你应该有一个表格,通过一个 ID 关联这两个表格。

您的表格应如下所示

表格:人 列:PID(INT,主键),名称

表:爱好 列:HID(INT,主键),爱好

表格:PeoplesHobbies 列:ID、PID、HID

那么您的查询将如下所示

select * from people `p` inner join PeoplesHobbies `ph` on p.PID = ph.PID inner join on Hobbies `h` on ph.HID = h.HID where p.NAME = 'JOHN'

【讨论】:

  • 虽然我相信原始设计需要工作,但它确实表达了人们需要与不“可用”的爱好相关联,而您的解决方案却没有。 1NF(阵列)的原始设计有问题。将爱好视为自然键并不意味着需要将其替换为代理键 - 它仍然可以被视为使用自然键标准化。
  • 谢谢。不幸的是,这是我继承的祖父数据库。
【解决方案5】:
SELECT name
FROM tblhobby AS h
WHERE EXISTS
        ( SELECT *
          FROM tblavailable_hobby AS ah1
          WHERE h.hobby1 = ah1.available_hobby
        )
  AND EXISTS
        ( SELECT *
          FROM tblavailable_hobby AS ah2
          WHERE h.hobby2 = ah2.available_hobby
        )
  AND EXISTS
        ( SELECT *
          FROM tblavailable_hobby AS ah3
          WHERE h.hobby3 = ah3.available_hobby
        )

【讨论】:

    【解决方案6】:

    借用乔的answer

    SELECT h.name
        FROM tblhobby h
            LEFT JOIN tblavailable_hobby ah1
                ON h.hobby1 = ah1.available_hobby
            LEFT JOIN tblavailable_hobby ah2
                ON h.hobby2 = ah2.available_hobby
            LEFT JOIN tblavailable_hobby ah3
                ON h.hobby3 = ah3.available_hobby
    WHERE (h.hobby1 IS NULL OR ah1.available_hobby IS NOT NULL)
        AND (h.hobby2 IS NULL OR ah2.available_hobby IS NOT NULL)
        AND (h.hobby3 IS NULL OR ah3.available_hobby IS NOT NULL)
    

    ypercube 的answer 可以类似地扩展。

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 1970-01-01
      • 1970-01-01
      • 2011-09-24
      • 2020-05-04
      • 1970-01-01
      • 1970-01-01
      • 2017-03-15
      相关资源
      最近更新 更多