【问题标题】:dynamic table name in select statementselect语句中的动态表名
【发布时间】:2013-06-30 00:04:56
【问题描述】:

我在 oracle 9 数据库中有一系列历史记录表。 History_table_00 包含上个月的数据,History_table_01 包含上个月的数据,History_table_02 包含上个月的数据。下个月,History_table_02 将自动重命名为 history_table_03,history_table_01 重命名为 history_table_02,history_table_00 重命名为 history_table_01,并且将创建一个新的 history_table_00 来收集最新的历史记录(我真的希望我说得通)。

无论如何,我需要编写一个选择语句来动态选择所有历史表。我希望这不会太复杂,因为它们都共享相同的名称,只是附加了序号,这样我就可以发现表名:

select table_name from all_tables where table_name like 'HISTORY_TABLE_%';

我对每个表的标准查询是:

select id, name, data_column_1, data_column_2 from history_table_%;

我必须做些什么来完成编写一个总是从所有历史表中选择而不需要我每个月都进入并添加新表的 sql 语句的目标?感谢你们提供的任何东西。

【问题讨论】:

  • 这里正确的解决方案是只拥有一个表,并添加一个'Year'列作为主键的第一部分。
  • @Joel Coehoorn - 也许这是一个穷人的分区解决方案。你永远不知道。
  • @haki 在主键前添加年份将具有与大多数用例的单独表相似的性能特征
  • Joel,我同意绝对建议更改表命名和归档,但他最初的问题仍然很有趣。有时,当有人获得不是他们设计的数据库时,他们会遇到这种情况。请参阅我的回答,了解如何实现。
  • @JoelCoehoorn,我同意你的看法。我希望这只是我必须处理的一张桌子。但是,无论出于何种原因,这就是它的创建方式。不幸的是,我坚持下去了。

标签: sql oracle dynamic dynamic-sql


【解决方案1】:

最好的办法是执行一个动态 SQL 语句,为数据库中存在的每个表构建一个大型查询。试试下面的 SQL 查询。 (请原谅我的格式,我不知道如何在这里换行)

DECLARE @table VARCHAR(255)
      , @objectID INT
      , @selectQuery VARCHAR(MAX)

SELECT @objectID = MIN(object_id)
  FROM sys.tables
 WHERE name LIKE 'history_table_%'

WHILE @objectID IS NOT NULL
BEGIN
  SELECT @table = name
    FROM sys.tables
   WHERE object_id = @objectID
   ORDER BY object_id

  SELECT @selectQuery = ISNULL(@selectQuery + ' UNION ALL ', '') + 'select id, name, data_column_1, data_column_2 FROM ' + @table

  SELECT @objectID = MIN(object_id)
    FROM sys.tables
   WHERE name LIKE 'tblt%'
     AND object_id > @objectID
END 

SELECT @selectQuery
--EXEC (@selectQuery)

【讨论】:

  • 感谢@SeanPrice,还不知道我的解决方案是什么。我会试一试你的。
【解决方案2】:

您可以使用参考光标,但我不推荐它。 是这样的

create table tab_01 as select 1 a , 10 b from dual;
create table tab_02 as select 2 a , 20 b from dual;
create table tab_03 as select 3 a , 30 b from dual;

create or replace function get_all_history
return sys_refcursor
as
   r sys_refcursor;
   stmt varchar2(32000);
   cursor c_tables is
           select  table_name
           from    user_tables
           where   table_name like 'TAB_%';
begin
   for x in c_tables loop
           stmt := stmt || ' select * from ' || x.table_name ||' union all';
   end loop;
   stmt := substr(stmt , 1 , length(stmt) - length('union all'));
   open r for stmt;
   return r;
end;
/

SQL> select get_all_history() from dual;

GET_ALL_HISTORY()
--------------------
CURSOR STATEMENT : 1

CURSOR STATEMENT : 1

         A          B
---------- ----------
         1         10
         2         20
         3         30

【讨论】:

    【解决方案3】:

    我建议您定义一个视图,您可以在其中使用 union all 从所有历史记录表中进行选择 每次重命名表时,您也会修改视图。

    create OR replace view history_data as 
    SELECT id, name, data_column_1, data_column_2  FROM history_table_01
    union all 
    SELECT id, name, data_column_1, data_column_2  FROM history_table_02
    union all 
    SELECT id, name, data_column_1, data_column_2  FROM history_table_03
    ;
    

    然后你就可以简单地 SELECT * FROM history_data;

    您可以借助以下语句动态构建视图:

    SELECT 'SELECT id, name, data_column_1, data_column_2  FROM ' || table_name || ' union all ' 
    FROM  user_tables 
    WHERE table_name like 'HISTORY_TABLE_%'     
    

    【讨论】:

    • 感谢@schurik。但是,每个月都必须手动修改视图,这正是我想要避免的。
    • 如何重命名/创建表?,您可以在该过程中集成 create veiw 语句。
    • 这是个好建议。我会向拥有该流程的人提出这个问题,看看他能做什么。
    【解决方案4】:

    一个可能的解决方案:

        CREATE OR REPLACE PROCEDURE GET_HIST_DETAILS IS
    
    DECLARE
    
    QUERY_STATEMENT VARCHAR2(4000) := NULL;
    CNT             NUMBER;
    
    BEGIN 
    
    select COUNT(table_name) INTO CNT from all_tables where table_name like 'HISTORY_TABLE_%';
    
    FOR loop_counter IN 1..CNT
    
    LOOP
    
     IF LOOP_COUNTER <> CNT THEN
     {  
     QUERY_STATEMENT := QUERY_STATEMENT || 'select id, name, data_column_1, data_column_2 from history_table_0' || loop_counter || ' UNION';
     }
     ELSE
     {
       QUERY_STATEMENT := QUERY_STATEMENT || 'select id, name, data_column_1, data_column_2 from history_table_0' || loop_counter ;
     } 
    
    EXECUTE_IMMEDIATE QUERY_STATEMENT;
    
    END LOOP;
    
    
    END GET_DETAILS;
    

    PS:我没有安装Oracle,所以没有测试它的语法错误。

    【讨论】:

      猜你喜欢
      • 2016-09-10
      • 2013-07-28
      • 2021-01-28
      • 2011-01-23
      • 2017-09-26
      • 2013-11-27
      • 1970-01-01
      • 1970-01-01
      • 2011-07-09
      相关资源
      最近更新 更多