从随机数量的结构相同的表中选择

Mar*_*ged 6 oracle oracle-11g-r2 plsql

我正在沿着不利的设计路线进行编程。我对架构没有影响,因为它属于现成的标准软件。

为简单起见,假设该软件存储多个用户的地址簿。每个表都分配有相应用户的名称。

这些表是这样创建的:

CREATE TABLE
    NAME_OF_USER
    (
        SURNAME VARCHAR(100),
        TOWN VARCHAR(100)
    )
Run Code Online (Sandbox Code Playgroud)

所以所有表都有这些列:

存在相当于两个地址簿的两个表:

  • 唐老鸭
  • 雏菊鸭

通讯录已经填写如下:

INSERT
INTO
    DONALDDUCK
    (
        SURNAME,
        TOWN
    )
    VALUES
    (
        'Dagobert Duck',
        'Entenhausen'
    )
Run Code Online (Sandbox Code Playgroud)

为了检索我可以运行的完整地址列表

Select * from donaldduck
Select * from daisyduck
Run Code Online (Sandbox Code Playgroud)

但是如果有人为 GOOFY 添加地址簿怎么办?我将不得不更改每个新创建的地址簿的查询,在本例中添加 aSELECT * FROM GOOFY作为第三行。

为了使这更加复杂,还有其他具有不同设置的表(它们正在存储其他信息,这些信息与我列出给定地址簿中的所有条目的任务完全无关)。所以我不能简单地遍历所有现有的表,因为这会导致处理我在查询中不需要的表。

有没有办法说“从包含名为城镇的列的所有表中选择 *”?这应该通过使用普通 SQL 或 PL/SQL 来解决,因为我想避免使用高级语言编写代码,就像我认为我需要遵循 jsapkota 的建议来做。

Bal*_*app 12

我并不为公开写这样的东西而自豪。

样本+数据:

CREATE TABLE donaldduck ( SURNAME VARCHAR(100), TOWN VARCHAR(100) );
CREATE TABLE daisyduck  ( SURNAME VARCHAR(100), TOWN VARCHAR(100) );
CREATE TABLE goofy      ( something number(1), SURNAME VARCHAR(100), TOWN VARCHAR(100) );

INSERT INTO DONALDDUCK ( SURNAME, TOWN ) VALUES ( 'Dagobert Duck', 'Entenhausen' );
INSERT INTO daisyduck ( SURNAME, TOWN ) VALUES ( 'Daisy Duck', 'Entenhausen' );
INSERT INTO goofy ( SOMETHING, SURNAME, TOWN ) VALUES (   1, 'Goofy Dog', 'Entenhausen' );
commit;
Run Code Online (Sandbox Code Playgroud)

使用流水线函数的类型和 PL/SQL:

create type t_NAME_OF_USER as object
(
    SURNAME VARCHAR(100),
    TOWN VARCHAR(100)
);
/

create type t_name_of_user_tab IS TABLE OF t_NAME_OF_USER;
/

CREATE OR REPLACE FUNCTION get_surname_town RETURN t_name_of_user_tab PIPELINED AS
  rc sys_refcursor;
  query clob;
  l_surname varchar2(100);
  l_town varchar2(100);
begin
  for t in (
    select table_name from user_tables ut
    where
      'SURNAME' in (select column_name from user_tab_columns utc where utc.table_name = ut.table_name) and
      'TOWN'  in (select column_name from user_tab_columns utc where utc.table_name = ut.table_name)
    )
  loop
    open rc for 'select surname, town from ' || t.table_name ;
    loop
      fetch rc into l_surname, l_town;
      exit when rc%notfound;
      pipe row(t_NAME_OF_USER(l_surname, l_town));
    end loop;
  end loop;
end;
/
Run Code Online (Sandbox Code Playgroud)

选择:

select * from table(get_surname_town);

SURNAME                        TOWN
------------------------------ --------------------
Daisy Duck                     Entenhausen
Dagobert Duck                  Entenhausen
Goofy Dog                      Entenhausen
Run Code Online (Sandbox Code Playgroud)


kev*_*kio 9

我认为人们很难理解你的问题,因为表格结构太糟糕了,似乎是为了让你头疼。正如Balazs Papp 所指出的,几乎没有什么可以实现扩展或看起来不像被黑客攻击的东西。

然而,有一些解决方案可以在 PL/SQL 中完成。一个流水线表函数最终会看起来像一个视图。它不会扩展到大量用户,但不会看起来像黑客。

另一种解决方案涉及交易执行速度以换取数据的新鲜度。许多数据由 10% 的活动记录和 90% 不太可能更改的存档数据组成。如果您的数据只需每天或每几个小时更新一次,您可以将下面的伪代码实现为打包过程并调用作业来刷新表。

---伪代码,不打算编译--

CREATE TABLE ADDRESS_BOOK(
ID NUMBER(9) NOT NULL,
NAME_OF_USER VARCHAR2(250) NOT NULL,
SURNAME VARCHAR(100) NOT NULL,
TOWN VARCHAR(100) NOT NULL);
Run Code Online (Sandbox Code Playgroud)
  • 创建一个序列来填充 ID 列
  • 在 ID 上创建主键
  • 创建一个触发器,当插入的值为空时,自动从序列中插入ID

--创建你的包

CREATE PACKAGE PKG_ADDRESS_BOOK
AS
PROCEDURE REFRESH;
END PKG_ADDRESS_BOOK;

CREATE OR REPLACE PACKAGE BODY PKG_ADDRESS_BOOK
 IS
PROCEDURE REFRESH
CURSOR the_tables IS
select table_name  
from user_tab_cols 
where column_name ='TOWN';

BEGIN
--clear out old data
DELETE FROM ADDRESS_BOOK;
FOR items in the_tables LOOP
EXECUTE IMMEDIATE('INSERT INTO ADDRESS_BOOK '||
'SELECT null,'
||items.table_name
||','
||'items.table_name.surname,items.table_name.town '
||'FROM '
||items.table_name);

END LOOP;

END REFRESH;

END PKG_ADDRESS_BOOK;
Run Code Online (Sandbox Code Playgroud)

这一切都基于数据正在缓慢变化的想法。优化将包括添加一种仅更改更改数据的方法。