Почему объединения нескольких таблиц создают повторяющиеся строки?

Скажем, у меня есть три таблицы A, B и C. Каждый из них имеет два столбца: первичный ключ и некоторый другой фрагмент данных. Каждый из них имеет одинаковое количество строк. Если я JOIN A и B на первичном ключе, я должен получить то же количество строк, что и в любом из них (в отличие от A.rows * B.rows).

Теперь, если я JOIN A JOIN B с C, почему я получаю дубликаты строк? Я несколько раз сталкивался с этой проблемой, и я не понимаю ее. Похоже, что он должен давать тот же результат, что и JOIN ing A и B, поскольку он имеет такое же количество строк, но вместо этого создаются дубликаты.

Запросы, которые дают такие результаты, относятся к формату

SELECT *
FROM M
    INNER JOIN S
        on M.mIndex = S.mIndex
    INNER JOIN D
        ON M.platformId LIKE '%' + D.version + '%'
    INNER JOIN H
        ON D.Name = H.Name
        AND D.revision = H.revision

Вот схемы для таблиц. H содержит историческую таблицу, содержащую все, что было когда-либо в D. Существует множество M строк для каждого D и одного S для каждого M.

Таблица M

    [mIndex] [int] NOT NULL PRIMARY KEY,
    [platformId] [nvarchar](256) NULL,
    [ip] [nvarchar](64) NULL,
    [complete] [bit] NOT NULL,
    [date] [datetime] NOT NULL,
    [DeployId] [int] NOT NULL PRIMARY KEY REFERENCES D.DeployId,
    [source] [nvarchar](64) NOT NULL PRIMARY KEY

Таблица S

[order] [int] NOT NULL PRIMARY KEY,
[name] [nvarchar](64) NOT NULL,
[parameters] [nvarchar](256) NOT NULL,
[Finished] [bit] NOT NULL,
[mIndex] [int] NOT NULL PRIMARY KEY,
[mDeployId] [int] NOT NULL PRIMARY KEY,
[Date] [datetime] NULL,
[status] [nvarchar](10) NULL,
[output] [nvarchar](max) NULL,
[config] [nvarchar](64) NOT NULL PRIMARY KEY

Таблица D

[Id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[branch] [nvarchar](64) NOT NULL,
[revision] [int] NOT NULL,
[version] [nvarchar](64) NOT NULL,
[path] [nvarchar](256) NOT NULL

Таблица H

[IdDeploy] [int] IDENTITY(1,1) NOT NULL,
[name] [nvarchar](64) NOT NULL,
[version] [nvarchar](64) NOT NULL,
[path] [nvarchar](max) NOT NULL,
[StartDate] [datetime] NOT NULL,
[EndDate] [datetime] NULL,
[Revision] [nvarchar](64) NULL,

Я не публиковал таблицы и запрос изначально, потому что меня больше интересует понимание этой проблемы для себя и избежания ее в будущем.

Ответ 1

Если одна из таблиц M, S, D или H имеет более одной строки для данного Id (если только столбец Id не является Первичным ключом) то запрос приведет к "повторяющимся" строкам. Если в таблице есть несколько строк для Id, то в состоянии JOIN должны быть включены другие столбцы, которые однозначно идентифицируют строку.

Ссылки

Связанный вопрос на форуме MSDN

Ответ 2

Если у вас есть связанные таблицы, у вас часто есть отношения "один ко многим" или "многие ко многим". Поэтому, когда вы присоединяетесь к TableB каждой записи в TableA, многие из них имеют несколько записей в TableB. Это нормально и ожидается.

Теперь вам понадобятся только определенные столбцы, и все они одинаковы для всех записей, тогда вам нужно будет сделать какую-то группу по отдельности или удалить, чтобы удалить дубликаты. Рассмотрим пример:

TableA
Id Field1
1  test
2  another test

TableB
ID Field2 field3
1  Test1  something
1  test1  More something
2  Test2  Anything

Поэтому, когда вы присоединяетесь к ним и выбираете все файлы, которые вы получаете:

select * 
from tableA a 
join tableb b on a.id = b.id

a.Id a.Field1        b.id   b.field2  b.field3
1    test            1      Test1     something
1    test            1      Test1     More something
2    another test 2  2      Test2     Anything

Это не дубликаты, потому что значения поля 3 отличаются друг от друга, хотя в ранних полях повторяются значения. Теперь, когда вы выбираете только определенные столбцы, одинаковое количество записей объединяется вместе, но поскольку столбцы с различной информацией не отображаются, они выглядят как дубликаты.

select a.Id, a.Field1,  b.field2
from tableA a 
join tableb b on a.id = b.id

a.Id a.Field1       b.field2  
1    test           Test1     
1    test           Test1 
2    another test   Test2

Это похоже на дубликаты, но это не из-за нескольких записей в TableB.

Обычно вы исправляете это, используя агрегаты и группу, используя различные или путем фильтрации в предложении where для удаления дубликатов. Как вы решаете это, зависит от того, что такое ваше бизнес-правило и как создана ваша база данных и какие данные там есть.

Ответ 3

Хорошо в этом примере вы получаете дубликаты, потому что вы соединяете как D, так и S на M. Я предполагаю, что вы должны присоединиться к D.id на S.id, как показано ниже:

SELECT *
FROM M
INNER JOIN S
    on M.Id = S.Id
INNER JOIN D
    ON S.Id = D.Id
INNER JOIN H
    ON D.Id = H.Id

Ответ 4

Это может показаться действительно базовым ответом "DUH", но убедитесь, что столбец, который вы используете для поиска в объединенном файле, на самом деле полон уникальных значений!

Сегодня я заметил, что PowerQuery не выдаст вам ошибку (например, в PowerPivot) и с радостью разрешит вам запустить слияние Many-Many. Это приведет к созданию нескольких строк для каждой записи, которая соответствует не уникальному значению.