Как соединить таблицы с несколькими деталями в таблицу заголовков - PullRequest
1 голос
/ 27 февраля 2011

Мне нужно объединить одну таблицу заголовков и шесть таблиц подробностей в один результат.Для демонстрации я создал очень простой пример:

DECLARE @MyHeader TABLE (HeaderPK int, Name varchar(100), Total smallmoney)
INSERT INTO @MyHeader (HeaderPK, Name, Total) 
    SELECT 1, 'ABC Company', 1600

DECLARE @MyDetail1 TABLE (Detail1PK int, HeaderFK int, Detail1Description varchar(100), Detail1Amount smallmoney)
INSERT INTO @MyDetail1 (Detail1PK, HeaderFK, Detail1Description, Detail1Amount) 
    SELECT 1, 1, 'Detail 1A', 100
    UNION SELECT 2, 1, 'Detail 1B', 300

DECLARE @MyDetail2 TABLE (Detail2PK int, HeaderFK int, Detail2Description varchar(100), Detail2AmountA smallmoney, Detail2AmountB smallmoney)
INSERT INTO @MyDetail2 (Detail2PK, HeaderFK, Detail2Description, Detail2AmountA, Detail2AmountB) 
    SELECT 1, 1, 'Detail 2A', 100, 100
    UNION SELECT 2, 1, 'Detail 2B', 200, 200
    UNION SELECT 3, 1, 'Detail 3C', 300, 300

-- Returns 2 Rows, Expected 2
SELECT 
    MyHeader.*
    ,MyDetail1.*
FROM
    @MyHeader MyHeader
    FULL JOIN @MyDetail1 MyDetail1 ON MyHeader.HeaderPK = MyDetail1.HeaderFK
ORDER BY
    MyDetail1.Detail1PK

-- Returns 6 Rows, Expected 3
SELECT 
    MyHeader.*
    ,MyDetail1.*
    ,MyDetail2.*
FROM
    @MyHeader MyHeader
    FULL JOIN @MyDetail1 MyDetail1 ON MyHeader.HeaderPK = MyDetail1.HeaderFK
    FULL JOIN @MyDetail2 MyDetail2 ON MyHeader.HeaderPK = MyDetail2.HeaderFK

Примечания:

  • MSSQL 2008R2
  • Каждая таблица сведений будет содержать приблизительно от 0 до 15 записей..

В этом примере таблицы с подробной информацией имеют схожие структуры.В производственной системе они очень разные.

Ответы [ 3 ]

2 голосов
/ 28 февраля 2011

Это даст вам то, что вы хотите, основываясь на слоте вверх по поиску подробных таблиц.Честно говоря, я показываю этот код только потому, что он немного интересен.Обычно нет веских причин для выполнения этого типа запроса.

Для эффективности передачи, вероятно, было бы лучше не повторять данные в столбцах заголовка:

Header
Detail1
Detail2
Detail3
etc

SQL-запрос

DECLARE @MyHeader TABLE (HeaderPK int, Name varchar(100), Total smallmoney)
INSERT INTO @MyHeader (HeaderPK, Name, Total) 
    SELECT 1, 'ABC Company', 1600

DECLARE @MyDetail1 TABLE (Detail1PK int, HeaderFK int, Detail1Description varchar(100), Detail1Amount smallmoney)
INSERT INTO @MyDetail1 (Detail1PK, HeaderFK, Detail1Description, Detail1Amount) 
    SELECT 1, 1, 'Detail 1A', 100
    UNION SELECT 2, 1, 'Detail 1B', 300

DECLARE @MyDetail2 TABLE (Detail2PK int, HeaderFK int, Detail2Description varchar(100), Detail2AmountA smallmoney, Detail2AmountB smallmoney)
INSERT INTO @MyDetail2 (Detail2PK, HeaderFK, Detail2Description, Detail2AmountA, Detail2AmountB) 
    SELECT 1, 1, 'Detail 2A', 100, 100
    UNION SELECT 2, 1, 'Detail 2B', 200, 200
    UNION SELECT 3, 1, 'Detail 3C', 300, 300

DECLARE @MyDetail3 TABLE (Detail3PK int, HeaderFK int, Detail3Description sysname)
INSERT INTO @MyDetail3 (Detail3PK, HeaderFK, Detail3Description) 
    SELECT 1, 1, 'Detail 3A'
    UNION SELECT 2, 1, 'Detail 3B'

-- Returns 6 Rows, Expected 3
SELECT 
    MyHeader.*
    ,MyDetail1.*
    ,MyDetail2.*
    ,MyDetail3.*
FROM
    @MyHeader MyHeader
    LEFT JOIN
    (SELECT *, RN=ROW_NUMBER() over (order by Detail1PK) FROM @MyDetail1) MyDetail1
    FULL JOIN
    (SELECT *, RN=ROW_NUMBER() over (order by Detail2PK) FROM @MyDetail2) MyDetail2
    ON MyDetail1.HeaderFK = MyDetail2.HeaderFK AND MyDetail1.RN = MyDetail2.RN
    FULL JOIN
    (SELECT *, RN=ROW_NUMBER() over (order by Detail3PK) FROM @MyDetail3) MyDetail3
    ON COALESCE(MyDetail1.HeaderFK, MyDetail2.HeaderFK) = MyDetail3.HeaderFK
       AND COALESCE(MyDetail1.RN, MyDetail2.RN) = MyDetail3.RN
    ON MyHeader.HeaderPK = COALESCE(MyDetail1.HeaderFK, MyDetail2.HeaderFK)

Вы расширяете подробные таблицы, добавляя больше в COALSECE, поэтому шестая подробная таблица будет иметь вид

    FULL JOIN
    (SELECT *, RN=ROW_NUMBER() over (order by Detail6PK) FROM @MyDetail6) MyDetail6
    ON COALESCE(MyDetail1.HeaderFK, MyDetail2.HeaderFK, MyDetail3.HeaderFK,
        MyDetail4.HeaderFK, MyDetail5.HeaderFK) = MyDetail6.HeaderFK
       AND COALESCE(MyDetail1.RN, MyDetail2.RN, MyDetail3.RN, MyDetail4.RN, MyDetail5.RN) = MyDetail6.RN
0 голосов
/ 27 февраля 2011

Когда вы сопоставляете @ MyDetail1 с @MyHeader, вы получаете две строки. Если вы сопоставите @ MyDetail2 с @MyHeader, вы получите три строки. Однако в последнем соединении вы не сообщаете системе, как сопоставить две строки в @ MyDetail1 с тремя строками в @ MyDetail2, и поэтому система предполагает, что вы хотите сопоставить каждую строку @ MyDetail1 с каждой строкой @ MyDetail2, получающейся в результате 2 х 3 строки или 6 строк.

Решения:

  1. Если предполагается, что @ MyDetail1 и @ MyDetail2 напрямую связаны друг с другом, значит, отсутствует соединение.
  2. Если вам нужны только @ MyDetail1, а затем @ MyDetail2, используйте два предложения Select.
0 голосов
/ 27 февраля 2011

Каждое соединение выполняется для каждой строки. Таким образом, если вы объединяете таблицу с 1 строкой в ​​таблице с соответствующими 2 строками, вы получаете 2 строки. Если вы объедините это в таблице с 3 строками, вы получите 6 рядов и т. Д.

Одним из решений является выполнение нескольких операторов SQL, по одному для каждой подробной таблицы. Клиент может перейти к следующему результату с эквивалентом SqlDataReader.NextResult().

Другим вариантом является union all различные утверждения вместе; это приведет к слишком большому количеству столбцов, но количество строк будет уменьшено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...