联合 5 个或更多具有相同结构和主键的表

Union 5 or more Tables with the same structure and primary key

SQL 5.7.37

我正在使用此代码将 2 sql table 合并为第三个 table。但现在我需要做同样的事情,有 5 个或更多 tables.

create table table3 as
    select *
    from table1
    union all
    select *
    from table2
    where not exists (select 1 from table1 where table1.title = table2.title);

如何向此命令添加更多 table?


这是一组示例数据和所需的结果:

请注意 table 有 一些 行与其他 table 完全相同。我不确定这是否重要。

结构: ID(主键,自增),Title(唯一,索引),DESC,URL

Ex. Table 1
ID, Title, DESC, URL
 1 Bob thisisbob bob.com
 2 Tom thisistom tom.com
 3 Chad thisischad chad.com

Ex. Table 2
ID, Title, DESC, URL
 1 Chris thisischris chris.com
 2 Chad thisischad chad.com
 3 Dough thisisdough doug.com

Ex. Table 3
ID, Title, DESC, URL
 1 Morgan thisismorgan morgan.com
 2 Jerome thisisjerome jerome.com
 3 Mike thisismike mike.com

Ex. Table 4
ID, Title, DESC, URL
 1 Chris thisischris chris.com
 2 Chad thisischad chad.com
 3 Luke thisisluke luke.com

Result:
What I need in Table 5
ID, Title, DESC, URL
 1 Bob thisisbob bob.com
 2 Tom thisistom tom.com
 3 Chad thisischad chad.com
 4 Chris thisischris chris.com
 5 Dough thisisdough doug.com
 6 Morgan thisismorgan morgan.com
 7 Jerome thisisjerome jerome.com
 8 Mike thisismike mike.com
 9 Luke thisisluke luke.com

如何向我的 union sql 命令添加更多 table?

一个想法可能是 postpone 仅在您聚合了所有 table 之后才生成每个唯一值,只要 UNION_ALLUNION,你会用这种方式执行一次 DISTINCT 操作,而不是五次。

SET @cnt = 0;

SELECT 
    (@cnt := @cnt + 1) AS rowNumber,
    distinct_aggr_tables.* 
FROM 
    ( 
    SELECT DISTINCT * FROM (
        SELECT `Title`, `DESC`, `url` FROM Table1
        UNION ALL
        SELECT `Title`, `DESC`, `url` FROM Table2
        UNION ALL
        SELECT `Title`, `DESC`, `url` FROM Table3
        UNION ALL
        SELECT `Title`, `DESC`, `url` FROM Table4
        ) aggr_tables
    ) distinct_aggr_tables

这里是对应的fiddle:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ea1fa1f668e769cc5b1824dcfb9feb40.

这个解决方案对你有用吗?


编辑:如何select除MySQLtable中的一个以外的所有字段用于此任务

two interesting ways个这样做:

1) 第一种方法将每个 table 复制到 不同的临时 tables,然后使用ALTER 语句删除我们不感兴趣的列,因此将这些 table 与此代码的第一个版本一起使用。

    # for each table
    CREATE TEMPORARY TABLE temp_Table1 AS
    SELECT * FROM Table1;

    ALTER TABLE temp_Table1
    DROP Id;

2) 第二种方法使用 准备语句 ,它允许您将查询构建为字符串。这可以帮助这个练习,因为我们可能想要在查询中从 INFORMATION_SCHEMA.COLUMNS table 中检索所有列名,然后删除我们不感兴趣的字段名,因此传递列列表原始查询的名称。

    SET @sql = CONCAT(
        'CREATE OR REPLACE VIEW AllTables AS ', 
        'SELECT
            ROW_NUMBER() OVER(ORDER BY Title ASC) AS rowNumber,
            distinct_aggr_tables.* 
         FROM 
            (',
            'SELECT DISTINCT 
                * 
            FROM 
                (
                SELECT ',
                    (SELECT CONCAT(REPLACE(GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'), 'Id`,', ''), '` ')
                     FROM INFORMATION_SCHEMA.COLUMNS cols
                     WHERE cols.TABLE_NAME = 'Table1' AND cols.TABLE_SCHEMA = 'test'),
                'FROM 
                    Table1
                UNION ALL
                SELECT ',
                    (SELECT CONCAT(REPLACE(GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'), 'Id`,', ''), '` ')
                     FROM INFORMATION_SCHEMA.COLUMNS cols
                     WHERE cols.TABLE_NAME = 'Table2' AND cols.TABLE_SCHEMA = 'test'),
                'FROM 
                    Table2
                UNION ALL
                SELECT ',
                    (SELECT CONCAT(REPLACE(GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'), 'Id`,', ''), '` ')
                     FROM INFORMATION_SCHEMA.COLUMNS cols
                     WHERE cols.TABLE_NAME = 'Table3' AND cols.TABLE_SCHEMA = 'test'),
                'FROM 
                    Table3
                UNION ALL
                SELECT ',
                    (SELECT CONCAT(REPLACE(GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'), 'Id`,', ''), '` ')
                     FROM INFORMATION_SCHEMA.COLUMNS cols
                     WHERE cols.TABLE_NAME = 'Table4' AND cols.TABLE_SCHEMA = 'test'),
                'FROM 
                    Table4

            ) aggr_tables
        ) distinct_aggr_tables;'
    );
                 
    PREPARE stmt FROM @sql;
    EXECUTE stmt;

    SELECT * FROM AllTables;

请注意,这段代码完全重现了 post 的第一段代码,除了使用 ROW_NUMBER window 函数而不是自我更新的全局变量。

这个解决方案做了一些假设,根据这些假设应该仔细quick-fixed:

  • table正好是 4:为了改变这个数量,有必要在每个新的 table 的正确位置复制以下代码:

       SELECT ',
          (SELECT CONCAT(REPLACE(GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'), 'Id`,', ''), '` ')
           FROM INFORMATION_SCHEMA.COLUMNS cols
           WHERE cols.TABLE_NAME = <new_table_name> AND cols.TABLE_SCHEMA = 'test'),
      'FROM 
          <new_table_name>
    
  • 当前 table 名称为 Table1Table2Table3Table4,数据库名称为 test:当我们查找特定 table 的字段名称时,应替换这些引用(按 table 名称和数据库名称过滤):

     SELECT '
         (SELECT CONCAT ...
          FROM ...
          WHERE cols.TABLE_NAME = <table_name> AND cols.TABLE_SCHEMA = <db_name>),
    'FROM 
         <table_name>
    
  • 要删除的字段名称是'Id',它是所有table的第一列:如果名称不同,则需要更改其名称在删除此列期间。此外,如果这不是第一列,则需要在此处进行一些调整:

      # COLUMN_NAME: 
      # ['Id', 'Title', 'DESC', 'url']
      #
      # GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'):
      # 'Id`,`Title`,`DESC`,`url'
      # 
      # REPLACE(GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'), 'Id`,', '')
      # '`Title`,`DESC`,`url'
      # 
      # CONCAT(REPLACE(GROUP_CONCAT(COLUMN_NAME SEPARATOR '`,`'), 'Id`,', ''), '` ')
      # '`Title`,`DESC`,`url`'
    

(加反引号是为了避免DESC造成的异常)

Note1: 四个SELECT groups for each table的生成可能是自动化的(最下面的一个简单例子this page) 通过循环使用 INFORMATION_SCHEMA.TABLES 中包含的 table 个名称的变量。然而,我不会冒险走那条路,因为使用准备好的语句和来自另一个 table (INFORMATION_SCHEMA.COLUMNS cols).[= 的计算值的 CONCAT 来处理要评估的字符串文本变得很困难。 34=]

Note2:无法在 sql fiddle 内看到此代码的效果,因为无法访问 INFORMATION_SCHEMA db tables。该代码已在 MySQL 8.0 数据库上进行了离线测试。


第一种方法可能会占用大量内存,而如果在修复中谨慎处理以定制您的数据库,第二种方法可能会更有效。

总的来说没有完美的解决方案,但有些可能会解决您的问题。

ps:非常欢迎任何建议的改进 post。