Как объединить таблицы с разрозненными схемами в таблицу надмножества? - PullRequest
0 голосов
/ 10 июля 2020

Я не уверен, что «суперсет» - самый точный способ описать то, что я здесь пытаюсь сделать, но у меня есть две такие таблицы:

Таблица A:

a | b | c | f | h | j | o | q

Таблица B:

c | a | d | f | g | h | i | j | p | q

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

Объединенная таблица:

a | b | c | d | f | g | h | i | j | o | p | q

Я не особо беспокоюсь о порядке, так как я пытаюсь объединить промежуточные таблицы вместе. Они c и a столбцы могут быть помещены в итоговую таблицу - для меня это не имеет большого значения.

Это означает, что некоторые столбцы не будут содержать данных для половины таблицы, но вот в чем идея - Я не хочу терять информацию , и не все таблицы имеют совместимую схему. Если имена двух столбцов идентичны, это один и тот же логический столбец, и их не должно быть двух в новой таблице (я вручную проверю, что логически идентичные столбцы названы одинаково).

Я был используя что-то вроде INSERT INTO new SELECT * FROM old1 UNION ALL SELECT * FROM old2, но здесь это не работает, поскольку, забывая о UNION, это работает только при совпадении схем. В этом случае новая таблица будет иметь схему, объединяющую старые схемы. Команда MERGE, похоже, имеет аналогичные проблемы, ожидая несколько совместимых схем.

Есть ли способ сделать это автоматически? В каждой из таблиц около 70 столбцов, порядок не совпадает, и мне нужно сделать дюжину таких слияний, поэтому я пытаюсь каким-то образом автоматизировать рефакторинг с помощью SQL.

Было предложено использовать NATURAL JOIN, но похоже, что это не сработает - например, Таблица A:

a | b | c

1 | 5 | 35

Таблица B:

b | d | f

5 | 66 | 34

NATURAL JOIN дает:

a | b | c | d | f

1 | 5 | 35| 66| 34

Я хочу не естественное соединение, а чистую конкатенацию без повторяющихся столбцов:

a | b | c | d | f

1 | 5 | 35| |

| 5 | | 66|34

Ответы [ 3 ]

1 голос
/ 10 июля 2020

Даже если в таблицах указано разное количество столбцов, они все равно могут быть объединены в UNION следующим образом:

SELECT a, b, c, NULL AS d, f, NULL AS g, h, NULL AS i, j, o, NULL AS p, q
FROM tbl1
UNION ALL
SELECT a, NULL AS b, c, d, f, g, h, i, j, NULL AS o, p, q
FROM tbl2;

К сожалению, в MySQL / MariaDB вы не можете сделать это без явного ввода всех имен столбцов и позиционирования их вручную.

SAS PRO C SQL поддерживает то, что вам нужно, с помощью оператора OUTER UNION CORR. Там решение будет примерно таким:

SELECT * FROM tbl1
OUTER UNION CORR
SELECT * FROM tbl2;

К сожалению, это (пока) недоступно в MySQL / MariaDB. В конечном итоге вы можете эмулировать что-то подобное, используя хитрое использование GROUP_CONCAT и CONCAT, IFNULL и других функций, играя с INFORMATION_SCHEMA.COLUMNS, чтобы сгенерировать запрос SELECT в виде подготовленного оператора ... но я лично не стал бы go в этом направлении *. 1009 *

В любом случае - как основная c начальная точка этой ПЛОХАЯ идея ... Следующий запрос перечислит все уникальные столбцы из обеих таблиц (если база данных 'test' и таблицы 'tbl1' и 'tbl2'):

SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 'tbl1'
UNION
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 'tbl2'
ORDER BY COLUMN_NAME;

Теперь мы объединим их в одно поле, разделенное запятыми:

SELECT GROUP_CONCAT(CONCAT("NULL AS ", COLUMN_NAME)) AS columns
FROM (  SELECT COLUMN_NAME
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 'tbl1'
        UNION
        SELECT COLUMN_NAME
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 'tbl2'
        ORDER BY COLUMN_NAME
) AS grouped_columns;

, затем мы можем запустить эта «команда» через подготовленный оператор SELECT, который в этом примере напечатает начальную строку со значениями NULL:

SET @s:='';
SELECT @s:=CONCAT('SELECT ', columns) AS start_row
            FROM
            (
                SELECT GROUP_CONCAT(CONCAT('NULL AS ',COLUMN_NAME)) AS columns
                FROM (  SELECT COLUMN_NAME
                        FROM INFORMATION_SCHEMA.COLUMNS
                        WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 'tbl1'
                        UNION
                        SELECT COLUMN_NAME
                        FROM INFORMATION_SCHEMA.COLUMNS
                        WHERE TABLE_SCHEMA = 'test' AND TABLE_NAME = 'tbl2'
                        ORDER BY COLUMN_NAME
                ) AS grouped_columns
            ) AS grouped_columns;
PREPARE stmt FROM @s;
EXECUTE stmt;

Этот запрос «чудовище» создаст что-то вроде строки ловушки ...

+------+------+------+------+------+------+------+------+------+------+------+------+
| a    | b    | c    | d    | f    | g    | h    | i    | j    | o    | p    | q    |
+------+------+------+------+------+------+------+------+------+------+------+------+
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+------+------+------+------+------+------+------+------+------+------+------+------+

под которым нужно как-то придумать, как добавить записи из tbl1 и tbl2 на соответствующие позиции. Удачи. Я не буду бороться за это :) Это уже очень неэффективно, и я даже не близок к решению. Это не- go направление делать то, что ты хочешь. Просто введите имена столбцов вручную.

1 голос
/ 10 июля 2020

AFAIK, это не простой способ сделать это. Создавайте запросы UNION или настраиваемую UDF. В Access я бы использовал VBA для создания объектов запроса или записи данных в таблицу. Даже с VBA потребуется таблица «сопоставления», определяющая пары полей. Поэтому, если MariaDB имеет аналогичную функциональность, примените ту же концепцию.

Рассмотрим это для UNION:

SELECT "A" AS Src, a, b, c, Null AS d, f, Null AS g, h, Null AS i, j, o, Null AS p, q FROM TableA
UNION 
SELECT "B", a, Null, c, d, f, g, h, i, j, Null, p, q FROM TableB;
0 голосов
/ 10 июля 2020

По сути, вам, кажется, нужны full join и using:

select *
from a full join
     b
     using (a, c, f, j, h, i, j, p, q)

MariaDB не поддерживает это, но вы можете:

select *
from a left join
     b
     using (a, c, f, j, h, i, j, p, q)
union all
select *
from a right join
     b
     using (a, c, f, j, h, i, j, p, q)
where a.a is null;
...