连接多个表会导致重复的行

nor*_*zig 10 sql-server-2005 join sql-server t-sql

我得到的行比我期望的查询返回的行多。

我相信这与我的 join 语句有关。

有多个表,其中包含不同的信息。 Person保存有关此人的主要信息,但不保存地址、电话或电子邮件。这是因为最初的设计师希望桌子能够容纳多个电话号码、电子邮件和地址。

SELECT (person.FirstName + ' ' + person.LastName) as FullName
    ,ISNULL(Person.isClient, '')
    ,ISNULL(Person.UDF1, '')
    ,ISNULL(Address.City, '')
    ,ISNULL(Address.state, '')
    ,PersonAddress.Person
    ,PersonAddress.Address
    ,ISNULL(Phone.PhoneNumber, 'N/A')
    ,Email.Email
    ,Person.Website
FROM Person
    left join PersonAddress on Person.ID = PersonAddress.Person
    left join Address on PersonAddress.Address = Address.ID
    left join PersonPhone on Person.ID = PersonPhone.Person
    left join Phone on PersonPhone.Person = Phone.ID
    left join Email with (nolock) on Person.ID = Email.Person
WHERE (
        isclient = 'prospect'
        or isclient = 'client'
        )
    and Address is not null
    and name like '%Mike%'
ORDER BY isClient asc;
Run Code Online (Sandbox Code Playgroud)

对于这个例子,我得到了 6 行“Mike Worths”。其中 3 份副本有一封电子邮件,三份副本有另一封电子邮件。

对于“Mike Pamstein”,我收到两行相同的电子邮件。

我需要结果只包含每个人的一个唯一行。

我想放弃第二封电子邮件。

Han*_*non 7

大概,您希望为每个唯一的人/地址/电子邮件/网站组合看到一个条目。如果是这样,试试这个:

SELECT (person.FirstName + ' ' + person.LastName) as FullName
    , ISNULL(Person.isClient, '')
    , ISNULL(Person.UDF1, '')
    , ISNULL([Address].City, '')
    , ISNULL([Address].[state], '')
    , PersonAddress.Person
    , PersonAddress.[Address]
    , ISNULL(Phone.PhoneNumber, 'N/A')
    , Email.Email
    , Person.Website
FROM dbo.Person
    LEFT JOIN dbo.PersonAddress ON Person.ID = PersonAddress.Person
    LEFT JOIN dbo.[Address] ON PersonAddress.[Address] = [Address].ID
    LEFT JOIN dbo.PersonPhone ON Person.ID = PersonPhone.Person
    LEFT JOIN dbo.Phone ON PersonPhone.Person = Phone.ID
    LEFT JOIN dbo.Email WITH (NOLOCK) ON Person.ID = Email.Person
WHERE (
        isclient = 'prospect'
        or isclient = 'client'
        )
    and [Address] is not null
    and name like '%Mike%'
GROUP BY (person.FirstName + ' ' + person.LastName)
    , ISNULL(Person.isClient, '')
    , ISNULL(Person.UDF1, '')
    , ISNULL([Address].City, '')
    , ISNULL([Address].state, '')
    , PersonAddress.Person
    , PersonAddress.[Address]
    , ISNULL(Phone.PhoneNumber, 'N/A')
    , Email.Email
    , Person.Website
ORDER BY isClient asc;
Run Code Online (Sandbox Code Playgroud)

最后的GROUP BY子句确保子句中每个唯一的列组合只返回一行GROUP BY。这应该可以防止结果中显示重复的行。

有几点需要注意:

  1. 始终在FROM子句上使用模式限定符。 FROM Person应该FROM dbo.Person-> 如果您将来引入新模式,这将消除任何混淆,并防止查询优化器必须为您的用户寻找默认模式。

  2. 对于未来的可维护性,你可能想的名字列一样的,不管他们是在哪个表。因此,举例来说,而不是ID在列People表被命名ID,它被命名PersonAddress表,我想它命名PersonID两个表。这可以防止连接中的混淆(读取错误),例如dbo.Person LEFT JOIN dbo.Address ON Person.ID = Address.Person.

  3. Person它们应该以它们包含的项目集合命名,而不是像 那样命名表,以复数形式命名。所以,Person变成People,并且Address变成Addresses。这消除了混淆 -> 该Address表实际上包含单个地址还是多个地址?

  4. WITH (NOLOCK)应该避免像瘟疫一样,除非您完全理解读取已被其他事务修改但尚未提交的行的后果。来自 MSDN:

在 READ UNCOMMITTED 级别运行的事务不会发出共享锁以防止其他事务修改当前事务读取的数据。READ UNCOMMITTED 事务也不会被排他锁阻塞,排他锁会阻止当前事务读取已被其他事务修改但未提交的行。设置此选项后,可以读取未提交的修改,称为脏读。数据中的值可以更改,行可以在事务结束之前在数据集中出现或消失。此选项与在事务中的所有 SELECT 语句中的所有表上设置 NOLOCK 的效果相同。这是隔离级别中限制最少的。

在 SQL Server 中,您还可以最大程度地减少锁定争用,同时使用以下任一方法保护事务免受未提交数据修改的脏读影响:

READ COMMITTED 隔离级别,其中 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON。

快照隔离级别。