从Oracle / Mysql中的通用数据创建[物化]视图

时间:2010-06-18 00:51:19

标签: sql mysql oracle view pivot

我有一个包含3个表的通用数据模型

CREATE TABLE Properties 
(
  propertyId int(11) NOT NULL AUTO_INCREMENT,
  name varchar(80) NOT NULL
)
CREATE TABLE Customers
(
  customerId int(11) NOT NULL AUTO_INCREMENT,
  customerName varchar(80) NOT NULL
)
CREATE TABLE PropertyValues
(
  propertyId int(11) NOT NULL,
  customerId int(11) NOT NULL,
  value varchar(80) NOT NULL
)
INSERT INTO Properties VALUES (1, 'Age');
INSERT INTO Properties VALUES (2, 'Weight');
INSERT INTO Customers VALUES (1, 'Bob');
INSERT INTO Customers VALUES (2, 'Tom');
INSERT INTO PropertyValues VALUES (1, 1, '34');
INSERT INTO PropertyValues VALUES (2, 1, '80KG');
INSERT INTO PropertyValues VALUES (1, 2, '24');
INSERT INTO PropertyValues VALUES (2, 2, '53KG');

我想要做的是创建一个视图,其中包含属性中所有ROWS的列,并在Customers中包含条目。列值从PropertyValues填充。 例如

 customerId Age   Weight
 1          34    80KG
 2          24    53KG

我认为我需要一个存储过程来执行此操作,也许需要一个物化视图(表“属性”中的条目很少更改)。 有什么提示吗?

1 个答案:

答案 0 :(得分:2)

使用动态SQL生成视图很简单:

create or replace procedure gen_view
as
    cols_stmt varchar2(32767);
    from_stmt varchar2(32767);
    subq_name varchar2(30);
begin
    for r in ( select * from properties
               order by propertyid )
    loop
        subq_name := 'pv_'||trim(to_char(r.propertyid)); 
        cols_stmt := cols_stmt || ', '|| subq_name ||'.value as '||r.name;
        from_stmt := from_stmt || ' left join ( select value, customerid from propertyvalues where propertyid = '
            ||trim(to_char(r.propertyid))||') '||subq_name
            ||' on '||subq_name||'.customerid = customers.customerid';
    end loop;
    execute immediate 'create or replace view eav_view as select customers.customerid, customers.customername'
                        || cols_stmt
                        || ' from customers '
                        || from_stmt;
end gen_view;
/

这是有效的:

SQL> exec gen_view

PL/SQL procedure successfully completed.

SQL> select * from eav_view
  2  /

CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
         1
Bob
34
80KG

         2
Tom
24
53KG


SQL>

让我们为一些客户创建一个新属性并为其插入值...

SQL> insert into properties values (3, 'FavouriteIceCream')
  2  /

1 row created.

SQL> insert into propertyvalues values (3, 1, 'Cherry Garcia')
  2  /

1 row created.

SQL> exec gen_view

PL/SQL procedure successfully completed.

SQL> select * from eav_view
  2  /

CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
         1
Bob
34
80KG
Cherry Garcia

         2
Tom
24
53KG

SQL>
  

“我想我需要存储   这样做的程序,也许是一个   物化视图(中的条目)   表“属性”很少改变。“

问题是,属性 会发生变化,而我猜你不会对发生这种情况进行监督。因此,您会发现将更改应用于物化视图非常困难。这很重要,因为改变物化视图的投影需要丢弃它。因此,如果不中断服务,这很难做到。类似的考虑适用于常规视图,但中断几乎为零。

如果您确实希望将视图语句转换为物化视图,请注意Oracle在物化视图方面似乎不喜欢ANSI-92语法(它推出了ORA-12054)。我不确定为什么会这样,但是当我改用旧的连接技术时,问题就消失了,这很烦人,因为外部连接语法很笨拙。

无需重新创建数据库对象的解决方案是在函数中使用动态SQL,该函数返回Ref Cursor,映射到JDBC ResultSet:

create or replace function get_eav_view
    return sys_refcursor
as
    cols_stmt varchar2(32767);
    from_stmt varchar2(32767);
    subq_name varchar2(30);
    return_value sys_refcursor;
begin
    for r in ( select * from properties
               order by propertyid )
    loop
        subq_name := 'pv_'||trim(to_char(r.propertyid)); 
        cols_stmt := cols_stmt || ','|| subq_name ||'.value as '||r.name;
        from_stmt := from_stmt || ' left join ( select value, customerid from propertyvalues where propertyid = '
            ||trim(to_char(r.propertyid))||') '||subq_name
            ||' on '||subq_name||'.customerid = customers.customerid';
    end loop;
    open return_value for
            'select customers.customerid, customers.customername'
                    || cols_stmt
                    || ' from customers '
                    || from_stmt;
    return return_value;
end get_eav_view;
/

这将始终返回最新预测:

SQL> var rc refcursor
SQL> exec :rc := get_eav_view

PL/SQL procedure successfully completed.

SQL> print rc

CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
         1
Bob
34
80KG
Cherry Garcia

         2
Tom
24
53KG


SQL>     

现在,如果我们添加一个新属性,它会立即被提取:

SQL>  insert into properties values (4, 'StarSign')
   2  /

1 row created.

SQL>  insert into propertyvalues values (4, 2, 'Aries')
  2  /

1 row created.

SQL> exec :rc := get_eav_view

PL/SQL procedure successfully completed.

SQL> print rc

CUSTOMERID
----------
CUSTOMERNAME
--------------------------------------------------------------------------------
AGE
--------------------------------------------------------------------------------
WEIGHT
--------------------------------------------------------------------------------
FAVOURITEICECREAM
--------------------------------------------------------------------------------
STARSIGN
--------------------------------------------------------------------------------
         1
Bob
34
80KG
Cherry Garcia


         2
Tom
24
53KG

Aries


SQL>