可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.