一个想法可能是仅在聚合所有表后才推迟生成每个唯一值,只要 UNION_ALL 比 UNION 快得多,并且您将改为执行一次 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>
- 当前表名是
Table1、Table2、Table3、Table4,数据库名是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:我们非常欢迎任何改进这篇文章的建议。