【问题标题】:How to join two tables when the ON clause is variableON子句可变时如何连接两个表
【发布时间】:2012-03-27 14:55:30
【问题描述】:

为了简化描述,我将使用成员表和邮件表。邮件表的结构如下:

-------------------------------
FromMember  int  NOT NULL (FK)
ToMember    int  NOT NULL (FK)
...
-------------------------------

我想编写一个查询,它将返回特定成员的所有邮件,无论他是 FromMember 还是 ToMember。这很容易,只需在 WHERE 子句中使用 OR 子句。

但复杂之处在于我想将邮件表加入成员表以检索 other 成员的成员信息。因此,如果我是 FromMember,则成员表的联接子句将使用 ToMember 字段,如果我是 ToMember,则联接子句将使用 FromMember 字段。基本上,ON 子句需要根据特定条件加入两个不同的字段。这可能吗?

到目前为止,我的解决方案是使用 UNION ALL,如下所示:

SELECT * FROM mail INNER JOIN member ON mail.ToMember = member.MemberID
WHERE mail.FromMember = @me
UNION ALL
SELECT * FROM mail INNER JOIN member ON mail.FromMember = member.MemberID
WHERE mail.ToMember = @me

但是,我想将此查询放在视图中,因为结果集将在我的代码中的许多地方使用。这意味着我需要一个参数化视图,我认为我需要使用用户定义的表函数来实现它。

基本上,我正在寻找两个答案:
有没有更好的方法来编写该查询而不是 UNION ALL?
我应该将该查询放在 VIEW 还是 UDF 中?

如果 VIEW 是首选方法,我如何将 @me 参数传递给 VIEW。我无法将@me 参数移至 WHERE 子句,有些人在他们的回答中说过,因为 WHERE 子句取决于 ON 子句。取出 WHERE 子句然后对它们进行 ORing 将不会返回与上述 UNION ALL 查询相同的结果集。

【问题讨论】:

  • 您可以创建一个包含 UNION ALL 的视图。为什么不呢?
  • 请注意人们在他们的解决方案中建议使用“OR”:您不会使用任何索引,而是会扫描整个表。这可能会变得很痛苦,具体取决于行数和/或此查询运行的次数。使用UNION 将允许在FromMemberToMember 上命中索引(前提是它们存在)。根据我的经验,在大多数情况下,使用索引会比表扫描快得多。
  • @KM:上面的“OR”语句是否也适用于 INNER JOIN ON 子句中的 OR 子句?正如@quzary 在下面的答案中指定的那样?
  • 没关系。这样想你有一个很大的电话簿(数千页)并且你想找到一个特定的名字。您只需查找名称,即可快速获得。现在,假设您想在“名称”或“地址”字段中查找特定名称。你是怎样做的?查看每个条目,这是一个表扫描。现在假设您有两个电话簿,一个按名称排序,一个按地址排序。您可以使用联合方法:在“名称”排序的书中快速搜索。您将这些结果附加到快速搜索“地址”排序书的结果中。
  • 记住 SQL 的目标是快速结果,也就是索引使用。你经常不得不编写重复的代码(想想复制粘贴)。如果您优化 SQL 以实现高效的击键,通常会降低执行速度。

标签: sql-server database foreign-keys relational-database


【解决方案1】:

1.

SELECT * 
 FROM mail 
  INNER JOIN member 
   ON (mail.FromMember = @me and mail.ToMember = member.MemberID)
    or (mail.ToMember = @me and mail.FromMember = member.MemberID)
  1. 通常视图比 UDF 更快。

【讨论】:

  • 呃,我基本上是通过在 where 中进行 OR 来做到这一点的,甚至没有看到简化。 +1
  • 如何将@me 参数发送到视图?
  • 您不需要参数。使用 where 过滤视图。
  • 该参数需要在ON子句中使用。我不能将上述查询中的 @me 参数移动到 WHERE 子句中吗?
  • 当然可以,但这会让你的查询稍微复杂一点:SELECT mail.* , member.*, member2.memberId me FROM mail INNER JOIN member on mail.ToMember = member.MemberID 或mail.FromMember = member.MemberID INNER JOIN member2 on mail.ToMember = member2.MemberID or mail.FromMember = member2.MemberID ON (mail.FromMember = member2.MemberId and mail.ToMember = member.MemberID) or (mail.ToMember = member2.MemberId and mail.FromMember = member.MemberID) 现在您可以将其用作“where myview.me=@me”的视图
【解决方案2】:

您需要翻转查询并执行以下操作:

SELECT member.* 
FROM member
    LEFT JOIN mail AS ToMail 
        ON ToMail.ToMember = member.MemberID AND ToMail.ToMember = @me
    LEFT JOIN mail AS FromMail 
        ON FromMail.FromMember = member.MemberID AND FromMail.ToMember = @me
WHERE ToMail.ToMember IS NOT NULL OR FromMail.FromMember IS NOT NULL

【讨论】:

    【解决方案3】:

    我不相信 T-SQL 提供参数化视图。该功能是通过内联表函数处理的。这是一个可以处理您的问题的函数:

    CREATE FUNCTION fExample_GetRelatedMailMembersForMember
    (
        @MemberId INT
    )
    RETURNS TABLE
    AS
    RETURN
    SELECT
        * -- Replace this with explicit columns
    FROM mail M
    INNER JOIN member MBR
        ON MBR.MemberID IN (M.FromMember, M.ToMember)
        AND MBR.MemberID <> @MemberId
    WHERE M.MemberId = @MemberId
    

    【讨论】:

    • 邮件表不包含字段MemberID,它只包含FromMember和ToMember字段。因此,我不能拥有您定义的 WHERE 子句。我在您的解决方案中遗漏了什么吗?
    • @Cristina 我知道你已经接受了一个答案,但为了历史的缘故,我继续修复了我的答案。因为您试图加入member,但它不是您发送的成员ID,我只需将其更改为@MemberId。很高兴您找到了适合您的解决方案。 :)
    • WHERE 子句不应该是:WHERE m.FromMember = "@me" OR m.ToMember = "@me"。只是想确保我得到这个示例作为我自己的知识库!
    【解决方案4】:

    您的 UNION ALL 查询没问题。几乎按原样放在视图中;

    CREATE VIEW myView AS
    SELECT * FROM mail INNER JOIN member ON mail.ToMember = member.MemberID
    UNION ALL 
    SELECT * FROM mail INNER JOIN member ON mail.FromMember = member.MemberID
    

    然后,如上面@JotaBe 所述,使用 WHERE 子句查询视图

    SELECT * FROM myView WHERE ToMember = @me OR FromMember = @me
    

    如果需要,您还可以将查询中的视图连接在一起以创建一个链,以查看从一个成员传递到另一个成员的邮件消息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-05
      • 2010-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多