web-dev-qa-db-ja.com

複数のテーブルを結合すると、行が重複する

クエリから返されると予想されるよりも多くの行を取得しています。

私はそれが私の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;

この例では、「マイクワース」の6行を取得します。コピーの3つには1つのメールがあり、3つには別のメールがあります。

「Mike Pamstein」の場合、同じメールで2つの重複した行が表示されます。

結果には、各人の一意の行を1つだけ含める必要があります。

2つ目のメールを削除します。

7
normandantzig

おそらく、個人/アドレス/メール/ウェブサイトの組み合わせごとに1つのエントリを表示したいと考えています。もしそうなら、これを試してください:

_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つの行のみが返されます。これにより、結果に重複行が表示されるのを防ぐことができます。

注意すべき点がいくつかあります。

  1. FROM句では常にスキーマ修飾子を使用してください。 _FROM Person_は_FROM dbo.Person_にする必要があります。これにより、将来新しいスキーマを導入する場合の混乱がなくなり、クエリオプティマイザーがユーザーのデフォルトスキーマを探す必要がなくなります。

  2. 将来の保守性のために、列がどのテーブルにあるかに関係なく、列に同じ名前を付けたいと思うでしょう。たとえば、IDテーブルのPeople列をID、およびPersonテーブルではAddressという名前が付けられているので、bothテーブルではPersonIDという名前を付けます。これにより、_dbo.Person LEFT JOIN dbo.Address ON Person.ID = Address.Person_などの結合での混乱(バグの読み取り)が防止されます。

  3. Personのようにテーブルに名前を付けるのではなく、複数のアイテムを含むアイテムのコレクションに基づいて名前を付ける必要があります。したがって、PersonPeopleになり、AddressAddressesになります。これにより混乱がなくなります-> Addressテーブルには実際に単一のアドレスまたは複数のアドレスが含まれていますか?

  4. WITH (NOLOCK)は、他のトランザクションによって変更されたがまだコミットされていない行の読み取りの結果を完全に理解しない限り、疫病のように回避する必要があります。 MSDNから:

READ UNCOMMITTEDレベルで実行されているトランザクションは、他のトランザクションが現在のトランザクションによって読み取られたデータを変更できないようにする共有ロックを発行しません。 READ UNCOMMITTEDトランザクションは、現在のトランザクションが変更されたが他のトランザクションによってコミットされていない行を読み取ることを妨げる排他ロックによってもブロックされません。このオプションを設定すると、ダーティリードと呼ばれるコミットされていない変更を読み取ることができます。トランザクションが終了する前に、データの値を変更したり、データセット内の行を表示または非表示にしたりできます。このオプションは、トランザクションのすべてのSELECTステートメントのすべてのテーブルにNOLOCKを設定するのと同じ効果があります。これは、分離レベルの中で最も制限が少ないものです。

SQL Serverでは、次のいずれかを使用して、コミットされていないデータ変更のダーティリードからトランザクションを保護しながら、ロックの競合を最小限に抑えることもできます。

READ_COMMITTED_SNAPSHOTデータベースオプションをONに設定したREAD COMMITTED分離レベル。

SNAPSHOT分離レベル。

7
Max Vernon

サブクエリを使用して、結合で1つのレコードを返すことができますか?

    ...
    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を使用して1人に強制していますが、他のロジックを使用して、1人あたり1レコードに減らし、重複を排除することもできます。

1
codedawg82