5

假设我有一个包含很多记录(> 100'000)的表 A 和一个包含与 A 相同的列和大约相同的数据量的表 B。一个聪明的选择语句是否有可能获得表 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'
4

4 回答 4

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 引擎优化的机会并不意味着它会。

此外,“*”是一个坏主意,尤其是在工会中。但我把它留在了,因为这不是问题的重点。

于 2012-11-21T14:44:48.987 回答
2

您可以尝试下一个解决方案,它仅从表 tmp1 ('A' = 'A') 中选择

select
  *
from
  tmp1
where
  'A' = 'A'

union all

select
  *
from
  tmp2
where
  'B' = 'A'

SQL Fiddle 演示在这里 检查执行计划

计划

于 2012-11-22T13:49:58.433 回答
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 开发人员撰写的这篇研究论文 [PDF]中阅读有关此技术的更多信息。

于 2012-11-22T02:20:23.073 回答
1

如果没有更多的上下文,很难准确地说出你想要什么,但也许这样的事情可以奏效?

DECLARE @TableName nvarchar(15);
DECLARE @Query nvarchar(50);

SELECT @TableName = YourField
FROM YourTable
WHERE ...

SET @Query = 'SELECT * FROM ' + @TableName

EXEC @Query

语法可能会有所不同,具体取决于您使用的 RDBMS,更具体地说,您正在尝试完成什么,但可能是朝着正确方向的推动。

于 2012-11-21T13:42:29.930 回答