假设我有一个包含大量记录的表A(> 100'000)和一个表B与A具有相同的列并且具有相同的数据量。 是否有可能使用一个聪明的select语句,我可以获得表A 或表B的所有记录的所有记录?
由于性能原因,我对目前使用的方法不太满意:
select
column1
,column2
,column3
from (
select 'A' as tablename, a.* from table_a a
union
select 'B' as tablename, b.* from table_b b
) x
where
x.tablename = 'A'
答案 0 :(得分:2)
另外,您的方法似乎是标准SQL中的唯一方法。
通过将UNION
更改为UNION ALL
,您将大大提高效果。在返回任何数据之前,UNION
必须从两个表中读取数据,然后消除重复项。
UNION ALL
不会消除重复。这样做的好坏取决于数据库引擎以及可能的转向参数。
实际上,还有另一种可能性。我不知道它会有多好用,但你可以尝试一下:
select *
from ((select const.tableName, a.*
from A cross join
(select 'A' as tableName where x.TableName = 'A')
) union all
(select const.tableName, b.*
from B cross join
(select 'B' as tableName where x.TableName = 'B')
)
) t
没有承诺。但这个想法是交叉连接到一个包含1或0行的表。这在MySQL中不起作用,因为它不允许WHERE
子句没有FROM
。在其他数据库中,您可能需要一个表名,例如dual
。当子查询不包含任何记录时,这使查询引擎有机会完全优化表的读取。当然,仅仅因为你给SQL引擎提供优化的机会并不意味着它会。
另外,“*”是一个坏主意,特别是在联盟中。但是我把它留了下来因为这不是问题的焦点。
答案 1 :(得分:2)
您可以尝试下一个解决方案,它仅从表tmp1中选择('A'='A')
select
*
from
tmp1
where
'A' = 'A'
union all
select
*
from
tmp2
where
'B' = 'A'
SQL小提琴演示here 检查执行计划
答案 2 :(得分:1)
很难在没有更多背景的情况下确切地说出你想要的东西,但也许这样的东西可以起作用?
DECLARE @TableName nvarchar(15);
DECLARE @Query nvarchar(50);
SELECT @TableName = YourField
FROM YourTable
WHERE ...
SET @Query = 'SELECT * FROM ' + @TableName
EXEC @Query
语法可能略有不同,具体取决于您使用的是哪种RDBMS,更具体地说是您要完成的任务,但可能是朝着正确的方向发展。
答案 3 :(得分:1)
执行此操作和的正确方法需要对物理表设计进行一些修改。
如果您可以为包含指标列的每个表添加一列并在该列上添加检查约束,则可以在查询中实现“分区”消除。
DDL:
create table table_a (
c1 ...
,c2 ...
,c3 ...
,table_ind char(1) not null generated always as 'A'
,constraint ck_table_ind check (table_ind = 'A')
);
create table table_b (
c1 ...
,c2 ...
,c3 ...
,table_ind char(1) not null generated always as 'B'
,constraint ck_table_ind check (table_ind = 'B')
);
create view v1 as (
select * from table_a
union all
select * from table_b
);
如果执行查询select c1,c2,c3 from v1 where table_ind = 'A'
,DB2优化器将使用检查约束来识别table_b
中的任何行都不能与table_ind = 'A'
谓词匹配,因此它将完全消除该表访问计划。
在DB2 for Linux / UNIX / Windows支持的Range Partitioning之前使用了(在某些情况下仍然如此)。您可以在2002年由一些IBM DB2开发人员撰写的research paper [PDF]中阅读有关此技术的更多信息。