【问题标题】:Union 5 or more Tables with the same structure and primary key联合5个或更多具有相同结构和主键的表
【发布时间】:2022-07-30 22:20:57
【问题描述】:

SQL 5.7.37

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

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

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


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

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

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

表 1:

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

表 2:

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

表 3:

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

表 4:

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

结果(我需要的表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 命令添加更多表?

【问题讨论】:

  • UNION 的使用次数基本上没有任何限制,但我认为联合所有非 PK 列并提供重新编号以形成新的 PK 可能会更好。什么版本的mysql?
  • @CaiusJard 我会重复“不存在的地方”行吗?这就是我迷路的地方。
  • 请注意,这些表的某些行与其他表完全相同。我不确定这是否重要。 - 你希望它们进行重复数据删除吗?
  • @CaiusJard 是的,不希望它根据唯一的标题列 SQL 5.7.37 复制欺骗
  • 如果源表不是太大,则使用 UNION DISTINCT。否则使用具有完整表定义并根据 UNIQUE 约束的单独 CREATE TABLE,以及 5 个单独的 INSERT IGNORE。

标签: mysql sql phpmyadmin


【解决方案1】:

一个想法可能是仅在聚合所有表后才推迟生成每个唯一值,只要 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

查看演示here


编辑:如何为此任务从 MySQL 表中选择除一个以外的所有字段

two interesting ways这样做:

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

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

ALTER TABLE temp_Table1
DROP Id;

2) 第二种方法使用prepared statement,它允许您将查询构建为字符串。这有助于本练习,因为我们可能希望从查询中的INFORMATION_SCHEMA.COLUMNS 表中检索所有列名,然后删除我们不感兴趣的字段名,从而将列名列表传递给原始查询。

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;

请注意,除了使用ROW_NUMBER 窗口函数而不是自我更新的全局变量之外,此代码完全复制了本文的第一个代码。

这个解决方案做了一些假设,根据这些假设,它应该被仔细快速修复:

  • 表正好是 4:为了更改这个数量,有必要在每个新表的正确位置复制以下代码:
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>
  • 当前表名是Table1Table2Table3Table4,数据库名是test:当我们查找特定表的字段名时,这些引用应该被替换(按表名和数据库名过滤):
SELECT '
   (SELECT CONCAT ...
    FROM ...
    WHERE cols.TABLE_NAME = <table_name> AND cols.TABLE_SCHEMA = <db_name>),
'FROM <table_name>
  • 要删除的字段名称是'Id',它是所有表的第一列:如果名称不同,则需要在删除此列时更改其名称。此外,如果这不是第一列,则需要在此处进行一些调整:
# 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引起的异常)

注意 1:每个表的四个 SELECT 组的生成可以通过循环使用 @987654342 中包含的表名上的变量来自动生成(this page 底部的一个简单示例) @。但是我不会冒险走这条路,因为很难处理要使用准备好的语句评估的字符串文本和来自另一个表 (INFORMATION_SCHEMA.COLUMNS cols) 的计算值的 CONCAT。

注意 2:无法在 sql fiddles 中看到此代码的效果,因为无法访问 INFORMATION_SCHEMA db 表。该代码已在 MySQL 8.0 数据库上进行了离线测试。


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

总体上没有完美的解决方案,尽管有些可能会解决您的问题。

ps:我们非常欢迎任何改进这篇文章的建议。

【讨论】:

  • 有没有办法在不指定所有列的情况下做到这一点?
  • 是的,有几种方法可以做到这一点:前者使用准备好的语句(更复杂但高效),后者使用选择所有列的临时表,然后应用更改删除您不想要的列的表。我会尽快相应地更新答案,尽管我可能需要数据库的名称
  • 请注意,要求是由一个特定的列来区分,而不是所有的列
  • @CaiusJard 我以为他的意思是Notice the tables have some rows that are exactly the same as other tables. 在不同的表中有一些行的所有列都相同。您能否仔细检查这一点以了解@MaeWebb?
  • 我在“工作”尝试中从 EXISTS 中推断出“标题不同”。还有后一篇评论给我,似乎表明标题是唯一的歧视者。但我同意值得仔细检查,因为我认为任何现有的样本数据都不能确定
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
相关资源
最近更新 更多