Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oracle SQL-模型子句中的可变列数_Sql_Oracle - Fatal编程技术网

Oracle SQL-模型子句中的可变列数

Oracle SQL-模型子句中的可变列数,sql,oracle,Sql,Oracle,我正在研究oraclesqlmodel子句。我正在尝试使用这个model子句编写动态oraclesql,它可以适应每次运行不同数量的列。然而,我很难理解,即使使用PL/SQL,如何将其应用于动态/通用查询或过程 这是我正在工作的桌子的大致视图 OWNER||ACCOUNT_YEAR||ACCOUNT_NAME||PERIOD_1||PERIOD_2||PERIOD_3||PERIOD_4||PERIOD_5||PERIOD_6||.... ----------------------------

我正在研究oraclesqlmodel子句。我正在尝试使用这个model子句编写动态oraclesql,它可以适应每次运行不同数量的列。然而,我很难理解,即使使用PL/SQL,如何将其应用于动态/通用查询或过程

这是我正在工作的桌子的大致视图

OWNER||ACCOUNT_YEAR||ACCOUNT_NAME||PERIOD_1||PERIOD_2||PERIOD_3||PERIOD_4||PERIOD_5||PERIOD_6||....
---------------------------------------------------------------------------------------------------
 9640||     2018   ||something  1||   34   ||  444   ||   982  ||  55    ||   42   ||  65    ||        
 9640||     2018   ||something  2||   333  ||  65    ||   666  ||  78    ||   44   ||  55    ||
 9640||     2018   ||something  3||   6565 ||  783   ||   32   ||  12    ||   46   ||  667   ||
以下是我到目前为止的情况:

select OWNER, PERIOD_1, PERIOD_2, PERIOD_3, PERIOD_4, PERIOD_5, PERIOD_6, PERIOD_7, PERIOD_8, PERIOD_9, PERIOD_10, PERIOD_11, PERIOD_12, ACCOUNT_YEAR, ACCOUNT_NAME
from DATA-TABLE
where OWNER IN ('9640') and PERIOD_1 is not null 
MODEL  ignore nav 
Return UPDATED ROWS
PARTITION BY (OWNER, ACCOUNT_NAME)
DIMENSION BY (ACCOUNT_YEAR)
MEASURES (PERIOD_1,PERIOD_2, PERIOD_3, PERIOD_4, PERIOD_5, PERIOD_6, PERIOD_7, PERIOD_8, PERIOD_9, PERIOD_10, PERIOD_11, PERIOD_12)
RULES
(
          PERIOD_1[2021] = PERIOD_1[2018] * 1.05,
          PERIOD_2[2021] = PERIOD_2[2018] * 1.05,
          PERIOD_3[2021] = PERIOD_3[2018] * 1.05,
          PERIOD_4[2021] = PERIOD_4[2018] * 1.05,
          PERIOD_5[2021] = PERIOD_6[2018] * 1.05,
          PERIOD_7[2021] = PERIOD_7[2018] * 1.05,
          PERIOD_8[2021] = PERIOD_8[2018] * 1.05,
          PERIOD_9[2021] = PERIOD_9[2018] * 1.05,
          PERIOD_10[2021] = PERIOD_10[2018] * 1.05,
          PERIOD_11[2021] = PERIOD_11[2018] * 1.05,
          PERIOD_12[2021] = PERIOD_12[2018] * 1.05

)
ORDER BY ACCOUNT_YEAR asc;
正如您在“度量和规则”部分中所看到的,我目前正在将每个期间列硬编码到此查询中

我希望能够以一种灵活的方式很好地使用这个模型子句,特别是规则部分,这样我就可以有一个可以运行的查询,比如,周期1-3,或者周期5-12

我已经尝试研究过这一点,但所有示例都显示了规则的左侧,例如PERIOD_12[2021]=。。。要显式引用表中的列,而不是参数或变量,我可以简单地替换为其他内容

如果您能帮助我通过SQL或PLSQL实现这一点,我将不胜感激

如果您理解DBMS_SQL包所演示的SQL的解析、绑定和执行流,就可以了解潜在的障碍

一个游标被打开,一个SQL语句被解析一次。解析后,可以调用descripe\u COLUMNS,它明确地告诉您执行该SQL语句将返回哪些列。从那时起,您可以执行多个绑定和执行,将变量的不同值放入同一语句并重新运行。每次执行之后可能会有一次或多次回迁。bind、execute或fetch都不会影响返回的列数、名称、顺序或数据类型

更改返回列的唯一方法是解析不同的SQL语句

根据最后需要的内容,您可能能够使用复杂的数据类型(如XML或JSON)从同一语句返回具有不同内部结构的数据,甚至在同一语句返回的不同行中返回数据

如果您理解DBMS_SQL包所演示的SQL的解析、绑定和执行流,就可以了解潜在的障碍

一个游标被打开,一个SQL语句被解析一次。解析后,可以调用descripe\u COLUMNS,它明确地告诉您执行该SQL语句将返回哪些列。从那时起,您可以执行多个绑定和执行,将变量的不同值放入同一语句并重新运行。每次执行之后可能会有一次或多次回迁。bind、execute或fetch都不会影响返回的列数、名称、顺序或数据类型

更改返回列的唯一方法是解析不同的SQL语句


根据最终需要,您可能可以使用复杂的数据类型(如XML或JSON)从同一语句返回具有不同内部结构的数据,甚至在同一语句返回的不同行中返回数据。

首先,您应该尝试通过将表结构更改为更简单的格式来避免动态列。如果垂直存储数据而不是水平存储数据,SQL会简单得多——使用多行而不是多列

如果您不能更改数据结构,您仍然希望使模型查询尽可能简单,因为使用MODEL子句是一件非常痛苦的事情。使用UNPIVOT将表从列转换为行,运行简化的模型查询,然后根据需要将结果转换回

如果您真的需要纯SQL语句中的动态列,那么您需要使用Gary Myers建议的高级数据类型,或者使用下面的Method4解决方案

示例模式 为了使示例完全可复制,这里是我使用的示例数据,以及我不得不稍微修改的模型查询,以仅引用6个变量和新表名

create table data_table
(
    owner number,
    account_year number,
    account_name varchar2(100),
    period_1 number,
    period_2 number,
    period_3 number,
    period_4 number,
    period_5 number,
    period_6 number
);

insert into data_table
select 9640,     2018   ,'something  1',   34   ,  444   ,   982  ,  55    ,   42   ,  65   from dual union all
select 9640,     2018   ,'something  2',   333  ,  65    ,   666  ,  78    ,   44   ,  55   from dual union all
select 9640,     2018   ,'something  3',   6565 ,  783   ,   32   ,  12    ,   46   ,  667  from dual;

commit;
模型查询:

select OWNER, PERIOD_1, PERIOD_2, PERIOD_3, PERIOD_4, PERIOD_5, PERIOD_6, ACCOUNT_YEAR, ACCOUNT_NAME
from DATA_TABLE
where OWNER IN ('9640') and PERIOD_1 is not null 
MODEL  ignore nav 
Return UPDATED ROWS
PARTITION BY (OWNER, ACCOUNT_NAME)
DIMENSION BY (ACCOUNT_YEAR)
MEASURES (PERIOD_1,PERIOD_2, PERIOD_3, PERIOD_4, PERIOD_5, PERIOD_6)
RULES
(
          PERIOD_1[2021] = PERIOD_1[2018] * 1.05,
          PERIOD_2[2021] = PERIOD_2[2018] * 1.05,
          PERIOD_3[2021] = PERIOD_3[2018] * 1.05,
          PERIOD_4[2021] = PERIOD_4[2018] * 1.05,
          PERIOD_5[2021] = PERIOD_5[2018] * 1.05,
          PERIOD_6[2021] = PERIOD_6[2018] * 1.05

)
ORDER BY ACCOUNT_YEAR, ACCOUNT_NAME asc;
结果:

OWNER  PERIOD_1  PERIOD_2  PERIOD_3  PERIOD_4  PERIOD_5  PERIOD_6  ACCOUNT_YEAR  ACCOUNT_NAME
-----  --------  --------  --------  --------  --------  --------  ------------  ------------
9640       35.7     466.2    1031.1     57.75      44.1     68.25          2021  something  1
9640     349.65     68.25     699.3      81.9      46.2     57.75          2021  something  2
9640    6893.25    822.15      33.6      12.6      48.3    700.35          2021  something  3
OWNER  ACCOUNT_YEAR  ACCOUNT_NAME  PERIOD_CODE  QUANTITY
-----  ------------  ------------  -----------  --------
 9640          2018  something  1  P1                 34
 9640          2018  something  1  P2                444
 9640          2018  something  1  P3                982
...
UNPIVOT方法 本例使用静态代码来演示语法,但如果必要,也可以通过创建临时表的PL/SQL使其更加动态

create table unpivoted_data as
select *
from data_table
unpivot (quantity for period_code in (period_1 as 'P1', period_2 as 'P2', period_3 as 'P3', period_4 as 'P4', period_5 as 'P5', period_6 as 'P6'));
对于未插入的数据,MODEL子句更简单。不列出每个时段的规则,只需按时段代码进行分区:

select *
from unpivoted_data
where OWNER IN ('9640')
    and (OWNER, ACCOUNT_YEAR, ACCOUNT_NAME) in
    (
        select owner, account_year, account_name
        from unpivoted_data
        where period_code = 'P1'
            and quantity is not null
    )
MODEL  ignore nav 
Return UPDATED ROWS
PARTITION BY (OWNER, ACCOUNT_NAME, PERIOD_CODE)
DIMENSION BY (ACCOUNT_YEAR)
MEASURES (QUANTITY)
RULES
(
    QUANTITY[2021] = QUANTITY[2018] * 1.05
)
ORDER BY ACCOUNT_YEAR, ACCOUNT_NAME, PERIOD_CODE;
结果:

OWNER  PERIOD_1  PERIOD_2  PERIOD_3  PERIOD_4  PERIOD_5  PERIOD_6  ACCOUNT_YEAR  ACCOUNT_NAME
-----  --------  --------  --------  --------  --------  --------  ------------  ------------
9640       35.7     466.2    1031.1     57.75      44.1     68.25          2021  something  1
9640     349.65     68.25     699.3      81.9      46.2     57.75          2021  something  2
9640    6893.25    822.15      33.6      12.6      48.3    700.35          2021  something  3
OWNER  ACCOUNT_YEAR  ACCOUNT_NAME  PERIOD_CODE  QUANTITY
-----  ------------  ------------  -----------  --------
 9640          2018  something  1  P1                 34
 9640          2018  something  1  P2                444
 9640          2018  something  1  P3                982
...
SQL中的动态SQL 如果您真的需要在一个查询中完成这一切,我的开源软件包可以提供帮助。一旦包装完成 如果已安装,则通过传入将生成要运行的查询的查询来调用它

此查询返回与上一个模型查询相同的结果,但将根据表中的列自动进行调整

select * from table(method4.dynamic_query(
    q'[
        --Generate the MODEL query.
        select
            replace(replace(q'<
                select OWNER, #PERIOD_COLUMN_LIST#, ACCOUNT_YEAR, ACCOUNT_NAME
                from DATA_TABLE
                where OWNER IN ('9640') and PERIOD_1 is not null 
                MODEL  ignore nav 
                Return UPDATED ROWS
                PARTITION BY (OWNER, ACCOUNT_NAME)
                DIMENSION BY (ACCOUNT_YEAR)
                MEASURES (#PERIOD_COLUMN_LIST#)
                RULES
                (
                #RULES#
                )
                ORDER BY ACCOUNT_YEAR, ACCOUNT_NAME asc
            >', '#PERIOD_COLUMN_LIST#', period_column_list)
            , '#RULES#', rules) sql_statement
        from
        (
            --List of columns.
            select
                listagg(column_name, ', ') within group (order by column_id) period_column_list,
                listagg(column_name||'[2021] = '||column_name||'[2018] * 1.05', ','||chr(10)) within group (order by column_id) rules
            from user_tab_columns
            where table_name = 'DATA_TABLE'
                and column_name like 'PERIOD%'
        )
    ]'
));

首先,您应该尝试通过将表结构更改为更简单的格式来避免动态列。如果垂直存储数据,SQL会简单得多 d水平方向-使用多行而不是多列

如果您不能更改数据结构,您仍然希望使模型查询尽可能简单,因为使用MODEL子句是一件非常痛苦的事情。使用UNPIVOT将表从列转换为行,运行简化的模型查询,然后根据需要将结果转换回

如果您真的需要纯SQL语句中的动态列,那么您需要使用Gary Myers建议的高级数据类型,或者使用下面的Method4解决方案

示例模式 为了使示例完全可复制,这里是我使用的示例数据,以及我不得不稍微修改的模型查询,以仅引用6个变量和新表名

create table data_table
(
    owner number,
    account_year number,
    account_name varchar2(100),
    period_1 number,
    period_2 number,
    period_3 number,
    period_4 number,
    period_5 number,
    period_6 number
);

insert into data_table
select 9640,     2018   ,'something  1',   34   ,  444   ,   982  ,  55    ,   42   ,  65   from dual union all
select 9640,     2018   ,'something  2',   333  ,  65    ,   666  ,  78    ,   44   ,  55   from dual union all
select 9640,     2018   ,'something  3',   6565 ,  783   ,   32   ,  12    ,   46   ,  667  from dual;

commit;
模型查询:

select OWNER, PERIOD_1, PERIOD_2, PERIOD_3, PERIOD_4, PERIOD_5, PERIOD_6, ACCOUNT_YEAR, ACCOUNT_NAME
from DATA_TABLE
where OWNER IN ('9640') and PERIOD_1 is not null 
MODEL  ignore nav 
Return UPDATED ROWS
PARTITION BY (OWNER, ACCOUNT_NAME)
DIMENSION BY (ACCOUNT_YEAR)
MEASURES (PERIOD_1,PERIOD_2, PERIOD_3, PERIOD_4, PERIOD_5, PERIOD_6)
RULES
(
          PERIOD_1[2021] = PERIOD_1[2018] * 1.05,
          PERIOD_2[2021] = PERIOD_2[2018] * 1.05,
          PERIOD_3[2021] = PERIOD_3[2018] * 1.05,
          PERIOD_4[2021] = PERIOD_4[2018] * 1.05,
          PERIOD_5[2021] = PERIOD_5[2018] * 1.05,
          PERIOD_6[2021] = PERIOD_6[2018] * 1.05

)
ORDER BY ACCOUNT_YEAR, ACCOUNT_NAME asc;
结果:

OWNER  PERIOD_1  PERIOD_2  PERIOD_3  PERIOD_4  PERIOD_5  PERIOD_6  ACCOUNT_YEAR  ACCOUNT_NAME
-----  --------  --------  --------  --------  --------  --------  ------------  ------------
9640       35.7     466.2    1031.1     57.75      44.1     68.25          2021  something  1
9640     349.65     68.25     699.3      81.9      46.2     57.75          2021  something  2
9640    6893.25    822.15      33.6      12.6      48.3    700.35          2021  something  3
OWNER  ACCOUNT_YEAR  ACCOUNT_NAME  PERIOD_CODE  QUANTITY
-----  ------------  ------------  -----------  --------
 9640          2018  something  1  P1                 34
 9640          2018  something  1  P2                444
 9640          2018  something  1  P3                982
...
UNPIVOT方法 本例使用静态代码来演示语法,但如果必要,也可以通过创建临时表的PL/SQL使其更加动态

create table unpivoted_data as
select *
from data_table
unpivot (quantity for period_code in (period_1 as 'P1', period_2 as 'P2', period_3 as 'P3', period_4 as 'P4', period_5 as 'P5', period_6 as 'P6'));
对于未插入的数据,MODEL子句更简单。不列出每个时段的规则,只需按时段代码进行分区:

select *
from unpivoted_data
where OWNER IN ('9640')
    and (OWNER, ACCOUNT_YEAR, ACCOUNT_NAME) in
    (
        select owner, account_year, account_name
        from unpivoted_data
        where period_code = 'P1'
            and quantity is not null
    )
MODEL  ignore nav 
Return UPDATED ROWS
PARTITION BY (OWNER, ACCOUNT_NAME, PERIOD_CODE)
DIMENSION BY (ACCOUNT_YEAR)
MEASURES (QUANTITY)
RULES
(
    QUANTITY[2021] = QUANTITY[2018] * 1.05
)
ORDER BY ACCOUNT_YEAR, ACCOUNT_NAME, PERIOD_CODE;
结果:

OWNER  PERIOD_1  PERIOD_2  PERIOD_3  PERIOD_4  PERIOD_5  PERIOD_6  ACCOUNT_YEAR  ACCOUNT_NAME
-----  --------  --------  --------  --------  --------  --------  ------------  ------------
9640       35.7     466.2    1031.1     57.75      44.1     68.25          2021  something  1
9640     349.65     68.25     699.3      81.9      46.2     57.75          2021  something  2
9640    6893.25    822.15      33.6      12.6      48.3    700.35          2021  something  3
OWNER  ACCOUNT_YEAR  ACCOUNT_NAME  PERIOD_CODE  QUANTITY
-----  ------------  ------------  -----------  --------
 9640          2018  something  1  P1                 34
 9640          2018  something  1  P2                444
 9640          2018  something  1  P3                982
...
SQL中的动态SQL 如果您真的需要在一个查询中完成这一切,我的开源软件包可以提供帮助。一旦包装完成 如果已安装,则通过传入将生成要运行的查询的查询来调用它

此查询返回与上一个模型查询相同的结果,但将根据表中的列自动进行调整

select * from table(method4.dynamic_query(
    q'[
        --Generate the MODEL query.
        select
            replace(replace(q'<
                select OWNER, #PERIOD_COLUMN_LIST#, ACCOUNT_YEAR, ACCOUNT_NAME
                from DATA_TABLE
                where OWNER IN ('9640') and PERIOD_1 is not null 
                MODEL  ignore nav 
                Return UPDATED ROWS
                PARTITION BY (OWNER, ACCOUNT_NAME)
                DIMENSION BY (ACCOUNT_YEAR)
                MEASURES (#PERIOD_COLUMN_LIST#)
                RULES
                (
                #RULES#
                )
                ORDER BY ACCOUNT_YEAR, ACCOUNT_NAME asc
            >', '#PERIOD_COLUMN_LIST#', period_column_list)
            , '#RULES#', rules) sql_statement
        from
        (
            --List of columns.
            select
                listagg(column_name, ', ') within group (order by column_id) period_column_list,
                listagg(column_name||'[2021] = '||column_name||'[2018] * 1.05', ','||chr(10)) within group (order by column_id) rules
            from user_tab_columns
            where table_name = 'DATA_TABLE'
                and column_name like 'PERIOD%'
        )
    ]'
));