内部多次将一个表连接到另一个表

时间:2012-10-13 11:23:21

标签: sql sql-server join

说我有三张桌子:

User Table
{
    UserId INT,
    Username NVARCHAR
    ...
}

Questions
{
    QuestionId INT
    QuestionText NVARCHAR
}

Answers
{
    AnswerId INT,
    QuestionId INT,
    UserId INT,
    Answer NVARCHAR
}

这种结构显然过于简单,但就本例而言,它应该足够了。

选择对特定问题有特定答案的用户的最佳方式是什么,例如 - 假设表格填充了以下数据:

用户表

UserId              Username                ...
--------------------------------------------------------------------------------------------------------
1                   User1                   ... 
2                   User2                   ... 
3                   User3                   ...
4                   User4                   ...
5                   User5                   ...
6                   User6                   ...
7                   User7                   ...
8                   User8                   ...
9                   User9                   ...
10                  User10                  ...
...                 ...                     ...

etc

问题表

QuestionId              QuestionText
--------------------------------------------------------------------------------------------------------
1                       What is your favorite color?
2                       What do you prefer cats or dogs?
3                       Do you prefer if it is too hot or too cold?
4                       What is your favorite season (Summer, Autumn (Fall), Winter, Spring)?
5                       How Old Are you?
...                     ...

etc

答案表

AnswerId                QuestionId              UserId          Answer
--------------------------------------------------------------------------------------------------------
1                       1                       1               Red 
2                       1                       2               Red
3                       1                       3               Blue
4                       1                       4               Green
5                       1                       5               Black
6                       2                       6               Cats
7                       2                       1               Dogs
8                       3                       1               Too Cold
9                       4                       1               Spring
10                      5                       1               22
11                      2                       4               Dogs
12                      3                       4               Too Hot
13                      3                       3               Too Cold
14                      5                       6               46
15                      1                       8               Purple

如果我想选择喜欢狗和红色或紫色以及50岁以下的用户

最好(最有效)的方法是从用户表到答案表有多个连接(每个答案条件需要一个)

例如:

如果我想要让喜欢狗的用户和红色的用户可以使用以下MSSQL:

SELECT * 
FROM 
Users 
JOIN Answers As a1 
ON Users.UserId = a1.UserId 
JOIN Answers as a2 
ON Users.UserId = a2.UserId 
WHERE 
    (
        a1.QuestionId = 1 AND 
        a1.Answer = 'Red'
    ) AND 
    (
        a2.QuestionId = 2 AND 
        a2.Answer = 'Dogs'
    )

可能有很多答案条件。

基本上我问的问题是,编写查询的最佳方法是在同一个表中具有相同列的多行的条件...

很抱歉,如果这令人困惑,请随时提出任何问题,我会尽力回答这些问题......

感谢。

2 个答案:

答案 0 :(得分:3)

您的基本查询看起来很好。随着您的详细说明,您将根据需要合并条件的方式略微区别地构建WHERE子句。

例如,在您提供的示例中,红色或紫色是可接受的答案,您可以像这样构造WHERE子句:

WHERE (a1.QuestionId = 1 AND (a1.Answer IN ('Red','Purple')) 
  AND (a2.QuestionId = 2 AND a2.Answer = 'Dogs')

如果只接受某些答案,那就变得更复杂了,所以如果'Red'和'Dogs'或'Purple'和'Cats'都可以接受,那么看起来会更像这样:

WHERE 
  (
          (a1.QuestionId = 1 AND a1.Answer = 'Red')
      AND (a2.QuestionId = 2 AND a2.Answer = 'Dogs')
  )
  OR
  (
          (a1.QuestionId = 1 AND a1.Answer = 'Purple')
      AND (a2.QuestionId = 2 AND a2.Answer = 'Cats')
  )

如果您的情况变得更复杂,您可能需要阅读Dynamic Search Conditions in T-SQL。虽然您的条件不是动态的,但那里有很多有用的信息。

最后,由于很容易将ID与问题的答案相混淆,特别是如果它们不是很好的,人类可识别的值,它可以帮助使用CTE预先选择答案:

WITH Colors
AS   (
    SELECT *
    FROM   Answers
    WHERE  QuestionID = 1
)
,    Animals
AS   (
    SELECT *
    FROM   Answers
    WHERE  QuestionID = 2
)
SELECT   *
FROM     Users 
   JOIN  Colors
       ON  Users.UserID = Colors.UserID
   JOIN  Animals
       ON  Users.UserID = Animals.UserID
WHERE   (
        Colors.Answer = 'Red'
    AND Animals.Answer = 'Dogs'
    )
    OR  (
        Colors.Answer = 'Purple'
    AND Animals.Answer = 'Cats'
    )

答案 1 :(得分:3)

还可以使用PIVOT operator

以上建议的查询是如何编写的:

select UserId, UserName
from (
  select
    u.UserId,
    u.UserName,
    case 
      when a.QuestionId = 1 then 'Color'
      when a.QuestionId = 2 then 'Animal'
      when a.QuestionId = 3 then 'Temperature'
      when a.QuestionId = 4 then 'Season'
      when a.QuestionId = 5 then 'Age'
    end as Question,
    a.Answer
  from Users u
  join Answers a on a.UserId = u.UserId
) as SourceTable
pivot (
  max(Answer)
  for Question in (
    [Color], 
    [Animal], 
    [Temperature], 
    [Season], 
    [Age])
) as pivotTable

where Animal = 'Dogs'
  and Color in ('red', 'purple')
  and Age < 50

以下是在线测试此链接的链接:http://www.sqlfiddle.com/#!3/5c960/23

是的,查询看起来很麻烦,但你可以写一次并且(假设问题不经常改变)只需改变where子句,这很容易写/读/理解/ maintain(参见上面代码块中的最后三行)。

<强>更新

对于性能分析,请比较以下两个查询:

执行查询后,点击结果上方的&#34; 查看执行计划&#34;链接以查看SQL在幕后的内容)< / p>

我建议您在自己的数据库上运行这些查询,您可能已经创建了适当的索引,并且数据量足以产生相关结果。

我没有SQL性能专家,但我有一种预感,Ann L.'s solution可以更高效,并且可以更好地扩展到大量数据。但同样,这只是一种预感;如果您可以对您的环境进行测试,您可以看到实际结果。