SQL Joins и MS Access — как объединить несколько таблиц в одну?

У меня есть четыре таблицы в Access 2010, каждая с одним и тем же первичным ключом. Я хотел бы объединить все данные во всех четырех таблицах в одну таблицу со столбцами для каждого значения, привязанного к первичному ключу во всех таблицах. Так, например:

Table1

ID   Value1

1    10       
2    7
3    4
4    12

Table 2

ID   Value2

1    33
2    8
6    19
7    4

Table 3

ID   Value3

1    99
2    99
5    99
7    99

Я хотел бы создать:

Table 4

ID  Value1  Value2  Value3

1   10      33      99
2   7       8       99
3   4
4   12
5           99
6           19      
7           4       99

Я использую MS Access и знаю, что мне нужно в основном использовать 3 соединения (левое, правое, внутреннее), чтобы получить полное соединение, но я не совсем уверен в том, как структурировать запрос.

Может ли кто-нибудь дать мне пример кода SQL, чтобы указать мне правильное направление относительно того, как получить этот результат?

Вот что у меня есть до сих пор. Это объединяет все таблицы, но похоже, что мне все еще не хватает некоторых данных. Я сделал что-то не так:

SELECT Coventry.cptcode, Coventry.[Fee Schedule], CT6002.[Fee Schedule], Medicare.[Fee Schedule], OFSP.[Fee Schedule]
FROM ((Coventry LEFT JOIN CT6002 ON Coventry.cptcode = CT6002.cptcode) LEFT JOIN Medicare ON CT6002.cptcode = Medicare.cptcode) LEFT JOIN OFSP ON Medicare.cptcode = OFSP.cptcode
UNION
SELECT Coventry.cptcode, Coventry.[Fee Schedule], CT6002.[Fee Schedule], Medicare.[Fee Schedule], OFSP.[Fee Schedule]
FROM ((Coventry RIGHT JOIN CT6002 ON Coventry.cptcode = CT6002.cptcode) RIGHT JOIN Medicare ON CT6002.cptcode = Medicare.cptcode) RIGHT JOIN OFSP ON Medicare.cptcode = OFSP.cptcode
UNION
SELECT Coventry.cptcode, Coventry.[Fee Schedule], CT6002.[Fee Schedule], Medicare.[Fee Schedule], OFSP.[Fee Schedule]
FROM ((Coventry INNER JOIN CT6002 ON Coventry.cptcode = CT6002.cptcode) INNER JOIN Medicare ON CT6002.cptcode = Medicare.cptcode) INNER JOIN OFSP ON Medicare.cptcode = OFSP.cptcode;

person Victor Brunell    schedule 03.07.2014    source источник
comment
Вы пробовали погуглить?   -  person Alexandre Santos    schedule 03.07.2014
comment
Да, но я не могу найти ничего, что точно отвечает на этот вопрос. Я нашел некоторую информацию об использовании всех трех предложений соединения, но предложение ON, следующее за предложением соединения, похоже, требует, чтобы у меня были некоторые поля, которые равны, но я хотел бы вытащить все поля, даже если нет совпадения в другая таблица, например запись 5 из примера в моем вопросе.   -  person Victor Brunell    schedule 03.07.2014


Ответы (3)


Как насчет этого?

SELECT id
, max(v1) as value1
, max(v2) as value2
, max(v3) as value3
FROM 
(
    select id
    , value1 as v1
    , iif(true,null,value1) as v2
    , iif(true,null,value1) as v3 
    from Table1

    union 

    select id, null , value2 , null  from Table2

    union 

    select id, null , null , value3 as v3 from Table3
)
group by id
order by id

Как это работает:

  • Вместо того, чтобы выполнять объединение, я помещаю все результаты в одну «таблицу» (мой подзапрос), но со значениями value1, value2 и value3 в своих собственных столбцах и устанавливаю значение null для таблиц, которые не имеют этих столбцов.

  • Операторы iif в первом запросе говорят, что я хочу, чтобы v2 и v3 имели тот же тип данных, что и v1. Это хитрый хак, но, кажется, работает (к сожалению, доступ работает с типом из первого оператора в запросе, а приведение withclng(null) не работает). Они работают, потому что результат iif должен быть того же типа, что и последние два параметра, и только последний параметр имеет тип, так что из этого следует; в то время как первый параметр, являющийся истинным, означает, что возвращаемое значение всегда будет только вторым параметром.

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

Я не уверен, как производительность сравнивается со способом, описанным в статье MS, но если вы используете доступ, я подозреваю, что у вас есть другие вещи, о которых нужно беспокоиться;).

Скрипт SQL: http://sqlfiddle.com/#!6/6f93b/2 ( Для SQL Server так как Access недоступен, но я постарался сделать его максимально похожим)

person JohnLBevan    schedule 03.07.2014
comment
Что представляют собой id, v1, v2 и v3? - person Victor Brunell; 05.07.2014
comment
@Caulibrot: они определены в подзапросе. Они представляют Value1, Value2 и Value3 соответственно; Я назвал их v# вместо value# для краткости и во избежание путаницы с фактическими именами столбцов; то есть, чтобы вы могли легко сказать, где я извлекаю данные непосредственно из таблицы, а где я делаю что-то немного другое (в этом случае извлекаю значение поля или использую null, где исходная таблица не содержит этот столбец). - person JohnLBevan; 05.07.2014

Создайте объединение всех идентификаторов из всех таблиц, таким образом вы получите столбец идентификаторов в таблице 4. Затем инкапсулируйте это объединение в подзапрос (id) и создайте левое соединение между этим подвыбором (родительское объединение) и table1, table2, table3 (дочернее объединение). Затем выберите то, что вам нужно...

SELECT 
ids.id, 
t1.Value1, 
t2.Value2, 
t3.Value3
FROM ((
(select id from table1 
union 
select id from table2
union 
select id from table3)  AS ids 
LEFT JOIN Table1 AS t1 ON ids.id = t1.ID) 
LEFT JOIN Table2 AS t2 ON ids.id = t2.ID) 
LEFT JOIN Table3 AS t3 ON ids.id = t3.ID;
person nelucon    schedule 04.07.2014
comment
Это намного лучше, чем использование max, но вы можете объяснить, что вы делаете. - person Conrad Frix; 04.07.2014
comment
Я имел в виду, что вы должны отредактировать свой ответ, чтобы объяснить, что вы делаете. И, в частности, почему это лучше принятого ответа. - person Conrad Frix; 08.07.2014
comment
Технически это должно дать тот же результат, потому что Id является первичным ключом. Но это может быть быстрее, потому что не требует какой-либо агрегатной функции. - person nelucon; 08.07.2014

Вот вариант ответа JohnLBevan, который будет работать, если [Value1], [Value2] и [Value3] имеют разные типы. Например, с

Таблица 1

ID  Value1
--  ------
 1      10
 2       7
 3       4
 4      12

Таблица 2

ID  Value2      
--  ------------
 1  thirty-three
 2  eight       
 6  nineteen    
 7  four        

Таблица3

ID  Value3    
--  ----------
 1  1999-01-01
 2  1999-01-01
 5  1999-01-01
 7  1999-01-01

запрос

SELECT ID, MAX(v1) AS Value1, MAX(v2) AS Value2, MAX(v3) as Value3
FROM (
        SELECT 0 AS ID, 0 AS v1, '' AS v2, #2001-01-01# AS v3
        FROM (SELECT COUNT(*) FROM Table1)
    UNION ALL
        SELECT ID, Value1 AS v1, NULL AS v2, NULL AS v3
        FROM Table1
    UNION ALL
        SELECT ID, NULL AS v1, Value2 AS v2, NULL AS v3
        FROM Table2
    UNION ALL
        SELECT ID, NULL AS v1, NULL AS v2, Value3 AS v3
        FROM Table3
)
WHERE ID > 0
GROUP BY ID

возвращается

ID  Value1  Value2        Value3    
--  ------  ------------  ----------
 1      10  thirty-three  1999-01-01
 2       7  eight         1999-01-01
 3       4                          
 4      12                          
 5                        1999-01-01
 6          nineteen                
 7          four          1999-01-01

Пояснение:

Первая часть подзапроса UNION создает одну строку с фиктивными значениями, чтобы гарантировать, что результат UNION имеет правильные типы столбцов.

SELECT 0 AS ID, 0 AS v1, '' AS v2, #2001-01-01# AS v3
FROM (SELECT COUNT(*) FROM Table1)

возвращается

ID  v1  v2  v3        
--  --  --  ----------
 0   0      2001-01-01

Остальная часть подзапроса UNION добавляет фактические строки из каждой таблицы.

    SELECT 0 AS ID, 0 AS v1, '' AS v2, #2001-01-01# AS v3
    FROM (SELECT COUNT(*) FROM Table1)
UNION ALL
    SELECT ID, Value1 AS v1, NULL AS v2, NULL AS v3
    FROM Table1
UNION ALL
    SELECT ID, NULL AS v1, Value2 AS v2, NULL AS v3
    FROM Table2
UNION ALL
    SELECT ID, NULL AS v1, NULL AS v2, Value3 AS v3
    FROM Table3

давая нам

ID  v1  v2            v3        
--  --  ------------  ----------
 0   0                2001-01-01
 1  10                          
 2   7                          
 3   4                          
 4  12                          
 1      thirty-three            
 2      eight                   
 6      nineteen                
 7      four                    
 1                    1999-01-01
 2                    1999-01-01
 5                    1999-01-01
 7                    1999-01-01

Внешний запрос агрегации исключает первую строку с фиктивными значениями (WHERE ID > 0), дает нам одну строку на каждый идентификатор (GROUP BY ID) и использует функцию MAX() для возврата значения, если оно есть (или Null, если нет).

ID  Value1  Value2        Value3    
--  ------  ------------  ----------
 1      10  thirty-three  1999-01-01
 2       7  eight         1999-01-01
 3       4                          
 4      12                          
 5                        1999-01-01
 6          nineteen                
 7          four          1999-01-01
person Gord Thompson    schedule 03.07.2014
comment
Это похоже на то, что мне нужно, но я не совсем понимаю синтаксис. Почему вы используете MAX и почему вы выбираете 0 в качестве идентификатора? И откуда берутся значения v1, v2, v3? - person Victor Brunell; 05.07.2014
comment
Если у меня есть несколько таблиц с одинаковым именем для значения, могу ли я изменить v1 на что-то вроде table1.v1, а v2 на table2.v2? - person Victor Brunell; 05.07.2014