Sql-Server

連接多個表會導致重複行

  • July 29, 2017

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

我相信這與我的加入聲明有關。

有多個表,其中包含不同的資訊。 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;

對於此範例,我得到 6 行“Mike Worths”。其中 3 份有一封電子郵件,三份有另一封電子郵件。

對於“Mike Pamstein”,我收到兩行相同的電子郵件。

我需要結果只包含每個人的一個唯一行。

我想刪除第二封電子郵件。

大概,您希望為每個獨特的人/地址/電子郵件/網站組合看到一個條目。如果是這樣,試試這個:

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;

最後的GROUP BY子句確保子句中每個唯一的列組合只返回一行GROUP BY。這應該可以防止結果中顯示重複的行。

有幾點需要注意:

  1. 始終在FROM子句上使用模式限定符。 FROM Person應該FROM dbo.Person-> 如果您將來引入新模式,這將消除任何混亂,並防止查詢優化器不得不為您的使用者尋找預設模式。
  2. 為了將來的可維護性,您可能希望將列命名為相同,而不管它們在哪個表中。因此,例如,不是表中的IDPeople被命名ID,而是Person在表中被命名Address,我將它命名PersonID兩張桌子。這可以防止在連接中出現混淆(讀取錯誤),例如dbo.Person LEFT JOIN dbo.Address ON Person.ID = Address.Person.
  3. 而不是像命名表一樣Person,它們應該以它們包含的項目集合命名,複數形式。所以,Person變成PeopleAddress變成Addresses。這消除了混淆 ->Address表實際上包含單個地址還是多個地址?
  4. WITH (NOLOCK)除非您完全理解讀取已被其他事務修改但尚未送出的行的後果,否則應該像瘟疫一樣避免這種情況。來自 MSDN:

在 READ UNCOMMITTED 級別執行的事務不會發出共享鎖,以防止其他事務修改目前事務讀取的數據。READ UNCOMMITTED 事務也不會被排他鎖阻塞,排他鎖會阻止目前事務讀取已修改但未由其他事務送出的行。設置此選項時,可以讀取未送出的修改,稱為臟讀。可以更改數據中的值,並且可以在事務結束之前在數據集中出現或消失行。此選項與在事務中的所有 SELECT 語句中的所有表上設置 NOLOCK 具有相同的效果。這是隔離級別中限制最少的。

在 SQL Server 中,您還可以最大限度地減少鎖定爭用,同時保護事務免受未送出數據修改的髒讀,使用以下任一方法:

> > READ_COMMITTED_SNAPSHOT 數據庫選項設置為 ON 的 READ COMMITTED 隔離級別。 > > > SNAPSHOT 隔離級別。 > > >

您可以像這樣使用子查詢在您的聯接中返回一條記錄嗎?

   ...
   FROM dbo.Person
   LEFT JOIN (SELECT MAX(AddressID) AS AddressID, Person FROM  dbo.PersonAddress GROUP BY Person) PersonAddress ON Person.ID = PersonAddress.Person
   LEFT JOIN dbo.[Address] ON PersonAddress.[Address] = [Address].ID

在這種情況下,我只是使用 MAX 將其強制為一個人,但您可以使用其他邏輯將其降低為每人一條記錄,並以這種方式消除重複項。

引用自:https://dba.stackexchange.com/questions/103262