Metaprogramming oracle sql select statement

匿名 (未验证) 提交于 2019-12-03 09:02:45

问题:

Let's imagine I have a query like the following:

SELECT   CASE WHEN ONE = 1 THEN 1 ELSE 0 END,   CASE WHEN JUST_ONE = 1 THEN 1 ELSE 0 END,   CASE WHEN ANOTHER_ONE = 1 THEN 1 ELSE 0 END,    CASE WHEN TWO = 1 THEN 1 ELSE 0 END,   CASE WHEN JUST_TWO = 1 THEN 1 ELSE 0 END,   CASE WHEN ANOTHER_TWO = 1 THEN 1 ELSE 0 END    -- 20 more things like that where changes only columns name FROM   SOME_TABLE; 

As you can see, the only difference between these two groups is that in the first one I use columns that have 'ONE' and in the second the ones that have 'TWO' and I have around 30 groups like this in my actual query so I wonder if there is a way to shorten it somehow?

回答1:

Since they are different columns, you must explicitly mention them separately in the SELECT list. You cannot do it dynamically in pure SQL.

I would suggest, using a good text editor, it would hardly take a minute or two to write the entire SQL.

You could use DECODE which will have some less syntax instead of CASE expression which is verbose.

For example,

  DECODE(ONE, 1, 1, 0) AS col1,    DECODE(JUST_ONE, 1, 1, 0) AS col2,   DECODE(ANOTHER_ONE, 1, 1, 0) AS col3,   DECODE(TWO, 1, 1, 0) AS col4,   DECODE(JUST_TWO, 1, 1, 0) AS col5,   DECODE(ANOTHER_TWO, 1, 1, 0) as col6 

I would suggest to stick to SQL, and not use PL/SQL. They are not the same, they are different engines. PL --> Procedural Language.

But if you insist, then you could use a cursor for loop to loop through all the columns in [DBA|ALL|USER]_TAB_COLS. You could use a SYS_REFCURSOR to see the data. First you will have to build the dynamic SQL.



回答2:

Below is an example of dynamically creating query. You can put this in e.g. cursor variable if you want more queries created.

select 'SELECT ' || listagg('CASE WHEN '||column_name||' = 1 THEN 1 ELSE 0 END ' || column_name,',') within group(order by column_name) || ' FROM YOUR_TABLE_NAME' from cols  where data_type in ('NUMBER') and table_name = 'YOUR_TABLE_NAME'; 

You can use COLS view to get all column names and their datatypes for all your tables.



回答3:

Oracle SQL metaprogramming is possible by combining Oracle Data Cartridge with the ANY types.

Even using an existing package for metaprogramming, building a query inside a query is complicated and should be avoided when possible. The other answers are generally better, even though they may require an extra step and are not "pure" SQL.

If you really need to do everything in a single SQL statement, try my open source project, Method4. After installing it, create a sample schema:

create table some_table(     one number, just_one number, another_one number,     two number, just_two number, another_two number);  insert into some_table values(1,1,1,2,2,2); 

Run this query that builds the real SELECT statement based on the data dictionary:

select * from table(method4.dynamic_query(     q'[         --Find columns that match pattern and aggregate into SELECT list.         select             'SELECT'||chr(10)||             rtrim(listagg('  CASE WHEN '||column_name||' = 1 THEN 1 ELSE 0 END '||column_name||',', chr(10))                 within group (order by order_by1, order_by2), ',')||chr(10)||             'FROM some_table' sql_statement         from user_tab_columns         join         (             --Column names that might match the pattern [null|JUST_|ANOTHER]SPELLED_NUMBER.             select prefix||spelled_number possible_column_names                 ,order_by1, order_by2             from             (                 --Numbers 1-10.                 select upper(to_char(to_date(level, 'j'), 'jsp')) spelled_number                     ,level order_by1                 from dual                 --Increase this number up to the maximum possible number.                 connect by level <= 10             )             cross join             (                 --Possible prefixes.                 select null prefix, 1 order_by2 from dual union all                 select 'JUST_' prefix, 2 order_by2 from dual union all                 select 'ANOTHER_' prefix, 3 order_by2 from dual             )         ) column_names             on user_tab_columns.column_name = column_names.possible_column_names         where table_name = 'SOME_TABLE'     ]' )); 

The same query will return different columns based on the table:

       ONE   JUST_ONE ANOTHER_ONE        TWO   JUST_TWO ANOTHER_TWO ---------- ---------- ----------- ---------- ---------- -----------          1          1           1          0          0           0 

That's some seriously complicated coding to avoid typing a few lines. This is the kind of solution that a manager would dream up when he first hears that hard-coding is always bad.

This literally answers the question about metaprogramming an Oracle SQL SELECT statement. There are a few rare cases where this approach is a life-saver. But 99.9% of the time it's better to do things the simple way even if it is a bit less automated.



回答4:

The generative approach (in SQL limited with manuall post processing) could be

with src as ( select 'ONE' col_name from dual union all select 'TWO' col_name from dual ) select  'CASE WHEN '||col_name||' = 1 THEN 1 ELSE 0 END,'||chr(10)|| 'CASE WHEN JUST_'||col_name||' = 1 THEN 1 ELSE 0 END,'||chr(10)|| 'CASE WHEN ANOTHER_'||col_name||' = 1 THEN 1 ELSE 0 END,'||chr(10) from src; 

giving

CASE WHEN ONE = 1 THEN 1 ELSE 0 END, CASE WHEN JUST_ONE = 1 THEN 1 ELSE 0 END, CASE WHEN ANOTHER_ONE = 1 THEN 1 ELSE 0 END,  CASE WHEN TWO = 1 THEN 1 ELSE 0 END, CASE WHEN JUST_TWO = 1 THEN 1 ELSE 0 END, CASE WHEN ANOTHER_TWO = 1 THEN 1 ELSE 0 END, 

Note that you provide the list of your column names and gets the expanded syntax. Typically you need to remove the last delimiter and add the wrapping SELECT frame.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!