【问题标题】:Inner Join one table to another table multiple times内部多次将一个表连接到另一个表
【发布时间】:2023-03-18 00:13:02
【问题描述】:

假设我有三张桌子:

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

可能有许多答案条件。

基本上,我要问的问题是编写查询的最佳方法是什么是您对来自同一个表的多行具有相同列的条件...

对不起,如果这令人困惑,请随时提出任何问题,我会尽力回答他们......

谢谢。

【问题讨论】:

  • 对不起,如果这写得不好或措辞不好,这里已经过了午夜,我的清晰度很差,但我想,因为在其他地方是合理的时间,也许有人可以提供帮助。我之前问过一个类似的问题,但是随着我的理解的增加,我想知道这是否仍然是正确的答案,也许多个 UNION 选择就是答案……您可以在此处查看上一个问题的答案:stackoverflow.com/questions/9833056/…跨度>
  • 啊。这很大程度上取决于(IME)的性能。有时,精心设计的联合会更快。
  • 什么是临界点?如果想根据用户对 10 个甚至 20 个甚至 100 个问题的回答来获取用户?哪个提供最佳性能?也许它没有那么简单?...
  • 我确实有一个想法,也许你可以告诉我它是否正确......如果我有一个包含来自用户的所有列的视图(例如称为 vw_users)会更快吗表以及每个问题的列(例如问题 1、问题 2),其中包含用户对每个问题的回答,这样我就可以使用简单的 Where 而无需连接...
  • 好的,在这一点上我是在推测而不是给出一个硬性规定,但如果你在做类似 'WHERE Table1.FieldA = something OR Table1.FieldB = 别的​​东西'。您将能够使用两个单独的查询计划,而不是试图提出一个总体的查询计划。但是,如果同一字段的值总是不同的问题,ORs 可能也会这样做。不幸的是,这种情况可能需要进行实验。

标签: sql sql-server join


【解决方案1】:

您的基本查询看起来不错。随着您变得更加详细,您会根据需要如何组合条件来构建 WHERE 子句。

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

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

如果只有特定的一组答案是可以接受的,情况就会变得更加复杂,所以如果“红色”和“狗”或“紫色”和“猫”是可以接受的,它看起来更像这样:

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

【讨论】:

  • 认为是这样(我知道它有效),但仅仅因为某些东西有效并不意味着它是做某事的最佳或最有效的方式。感谢您确认这是正确的方法:D
  • 如果没有提供其他答案,我将在早上(我的时间)将您的答案标记为答案我只是想看看是否有其他人有其他方法及其背后的推理......再次感谢花时间回答。我很确定你是对的。
  • @Neaox 哦,我明白了。我认为你更像是一个初学者,而不是现在很明显你是。您在上面的评论中是对的:有时工会可以更快。它取决于 (IME) 诸如索引、记录大小以及 OR 条件是否是同一列或不相关列的不同值的问题。
  • +1 不错的查询;我从来没有在一个声明中看到过两个 CTE,也不知道这是可能的。
【解决方案2】:

还可以选择使用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 子句,这很容易编写/阅读/理解/维护(请参阅上面代码块的最后三行)。

更新:

对于性能分析,比较这两个查询:

执行查询后,点击结果上方的“查看执行计划”链接,查看 SQL 在幕后做了什么

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

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

【讨论】:

  • 在性能方面这将如何与多连接方法相提并论?感谢您花时间回答我真的很感激。
  • @Neaox 我不确定哪个查询更有效,但看看我的答案更新是否有帮助
猜你喜欢
  • 2010-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-14
  • 1970-01-01
相关资源
最近更新 更多