英文:
Combine SELECT from TABLE with all_tab_columns
问题
以下是翻译的内容:
- 寻找
ALL_TAB_COLUMNS
中列(或列的列表)的存在。 - 如果列存在,应将其包括在SELECT列列表中。
- 执行构建的SELECT语句并返回结果。
表:
CREATE TABLE SCHEMA.TEST
(
id int PRIMARY KEY,
name varchar(20),
address varchar(20)
);
下面的查询仅输出感兴趣的列,但不包含列值。
SELECT c.* FROM
(SELECT column_name FROM all_tab_columns where
owner = 'SCHEMA' AND table_name = 'TEST' and column_name IN ('ID', 'NAME')) c
** 更新 **
要求是以列名作为输入并返回每个列的值。由于存在列名在请求之前可能由于拼写错误或删除而不匹配表中的列名的可能性,最简单的方法是使用包含所有列的SELECT语句。如果查询由于列不存在而失败,则从列表中删除该列并重新执行SELECT语句。如果存储过程/函数循环检查错误并删除列,则逻辑的复杂性会增加。
另一个解决方案是将输入列与all_tab_columns表进行比较,并创建具有存在的列的SELECT查询。我已经编写了以下语句 -
DECLARE
colname varchar2(4000);
BEGIN
SELECT listagg(column_name, ',') WITHIN GROUP (ORDER BY column_name) INTO colname FROM all_tab_columns
where owner = 'SCHEMA' AND table_name = 'TEST' and column_name IN ('ID', 'NAME', 'ADD') ;
IF colname IS NOT NULL THEN
EXECUTE IMMEDIATE 'SELECT ' || colname || ' FROM SCHEMA.TEST WHERE ID = 1';
END IF;
END;
乍一看,上述解决方案存在三个问题 -
- 它最多可以累积多少输入列。
- 如何在JDBC中使用它,结果集将始终为空。这是由于EXECUTE IMMEDIATE引起的。
- 我不确定如何将主键(PK)集成到解决方案中。PK的类型可以是数据库支持的任何类型。
英文:
I would like to combine below into a single statement -
- Look for the existence of a column (or list of columns) in
ALL_TAB_COLUMNS
. - If a column exists, it should be included in the SELECT column list.
- Execute the constructed
SELECT
statement and return the result.
Table:
CREATE TABLE SCHEMA.TEST
(
id int PRIMARY KEY,
name varchar(20),
address varchar(20)
);
The query below only outputs the columns of interest, but does not also contain the column value.
SELECT c.* FROM
(SELECT column_name FROM all_tab_columns where
owner = 'SCHEMA' AND table_name = 'TEST' and column_name IN ('ID', 'NAME')) c
** Update **
The requirement is to get the name of the column(s) as input and return the values for each column. There is a possibility that the column name in the table does not match due to a typo or dropped before the request was made. The simplest approach is to use the SELECT statement with all columns. If the query fails because the column does not exist, remove the column from the list and re-execute the SELECT statement. The complexity of the logic increases if the stored procedure/function loops to check for an error and remove the column.
Another solution is to check the input column against the all_tab_columns table and create a SELECT query with the columns present. I had written the statement as below -
DECLARE
colname varchar2(4000);
BEGIN
SELECT listagg(column_name, ',') WITHIN GROUP (ORDER BY column_name) INTO colname FROM all_tab_columns
where owner = 'SCHEMA' AND table_name = 'TEST' and column_name IN ('ID', 'NAME', 'ADD') ;
IF colname IS NOT NULL THEN
EXECUTE IMMEDIATE 'SELECT ' || colname || 'FROM SCHEMA.TEST WHERE ID = 1';
END IF;
END;
At first glance, there are three problems with the above solution -
- How many input columns it can accumulate at most.
- How to use it with JDBC, the result set will always be empty. This is due to EXECUTE IMMEDIATE.
- I am not sure how to use PK into the solution. The type of PK can be any type supported in the database.
答案1
得分: 4
您可以使用多态表函数(Polymorphic Table Function)来完成此任务。
但是,要求不太明确,因为您可以在构建SQL查询时指定所需的列:您的应用程序应能够预先指定现有列或处理结果中不存在的列。在这两种情况下,此逻辑可以转换为查询组合任务。
create or replace package pkg_projection as
function describe(tab in out dbms_tf.table_t, cols varchar2 default null)
return dbms_tf.describe_t
;
end pkg_projection;
/
create or replace package body pkg_projection as
function describe(tab in out dbms_tf.table_t, cols varchar2 default null)
return dbms_tf.describe_t
as
begin
if cols is null then
return null;
end if;
for i in 1..tab.column.count() loop
/*标记要传递的列*/
tab.column(i).pass_through := '',' || upper(cols) || ',' like '%,' || trim(both '"' from upper(tab.column(i).description.name)) || ',%';
end loop;
return null;
end;
end pkg_projection;
/
create or replace function f_projection(tab table, cols varchar2 default null)
return table pipelined
row polymorphic using pkg_projection;
/
给定这个示例数据:
create table t(
id,val1,val2
)
as
select level, -level, lpad(level, 3, ''0'')
from dual
connect by level < 4
它返回以下结果:
select *
from f_projection(t, ''ID,VAL')
ID
---
1
2
3
select *
from f_projection(t, ''ID,VAL2')
ID | VAL2
---|-----
1 | 001
2 | 002
3 | 003
select *
from f_projection(t, null)
ID | VAL1 | VAL2
---|------|----
1 | -1 | 001
2 | -2 | 002
3 | -3 | 003
英文:
You may use Polymorphic Table Function for this task.
However, the requirement is not clear, because you may specify the required columns when you build a SQL query: your application should be able either to specify existing columns upfront or handle absent columns in the result. In both cases this logic may be translated into the query composition task.
create or replace package pkg_projection as
function describe(tab in out dbms_tf.table_t, cols varchar2 default null)
return dbms_tf.describe_t
;
end pkg_projection;
/
create or replace package body pkg_projection as
function describe(tab in out dbms_tf.table_t, cols varchar2 default null)
return dbms_tf.describe_t
as
begin
if cols is null then
return null;
end if;
for i in 1..tab.column.count() loop
/*Mark columns to be propagated*/
tab.column(i).pass_through := ',' || upper(cols) || ',' like '%,' || trim(both '"' from upper(tab.column(i).description.name)) || ',%';
end loop;
return null;
end;
end pkg_projection;
/
create or replace function f_projection(tab table, cols varchar2 default null)
return table pipelined
row polymorphic using pkg_projection;
/
Given this sample data:
create table t(
id,val1,val2
)
as
select level, -level, lpad(level, 3, '0')
from dual
connect by level < 4
it returns below results:
select *
from f_projection(t, 'ID,VAL')
ID
--
1
2
3
select *
from f_projection(t, 'ID,VAL2')
ID | VAL2
---|-----
1 | 001
2 | 002
3 | 003
select *
from f_projection(t, null)
ID | VAL1 | VAL2
---|------|----
1 | -1 | 001
2 | -2 | 002
3 | -3 | 003
答案2
得分: 1
你无法在纯SQL中完成这个任务。
你可以使用PL/SQL来动态构建SQL语句和游标:
DECLARE
v_schema VARCHAR2(30) := USER;
v_table VARCHAR2(30) := 'TEST';
v_columns identifier_list := identifier_list('ID', 'NAME');
v_found_columns identifier_list;
v_sql CLOB;
v_cursor INTEGER;
v_dummy NUMBER;
v_value VARCHAR2(4000);
v_row PLS_INTEGER := 0;
BEGIN
SELECT column_name
BULK COLLECT INTO v_found_columns
FROM all_tab_columns
WHERE owner = v_schema
AND table_name = v_table
AND column_name MEMBER OF v_columns;
IF v_found_columns IS EMPTY THEN
RETURN;
END IF;
v_sql := EMPTY_CLOB() || 'SELECT "' || v_found_columns(1) || '"';
FOR i IN 2 .. v_found_columns.COUNT LOOP
v_sql := v_sql || ', "' || v_found_columns(i) || '"';
END LOOP;
v_sql := v_sql || ' FROM "' || v_schema || '"."' || v_table || '"';
DBMS_OUTPUT.PUT_LINE(v_sql);
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, v_sql, DBMS_SQL.NATIVE);
FOR i IN 1 .. v_found_columns.COUNT LOOP
DBMS_SQL.DEFINE_COLUMN(v_cursor, i, v_value, 4000);
END LOOP;
v_dummy := DBMS_SQL.EXECUTE(v_cursor);
LOOP
EXIT WHEN DBMS_SQL.FETCH_ROWS(v_cursor) <= 0;
v_row := v_row + 1;
DBMS_OUTPUT.PUT_LINE('Row ' || v_row);
FOR i IN 1 .. v_found_columns.COUNT LOOP
DBMS_SQL.COLUMN_VALUE(v_cursor, i, v_value);
DBMS_OUTPUT.PUT_LINE(' ' || RPAD(v_found_columns(i), 30, ' ') || ' = ' || v_value);
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(v_cursor);
END;
/
这里使用了类型:
CREATE TYPE identifier_list IS TABLE OF VARCHAR2(30);
如果你有这个表:
CREATE TABLE TEST(id, name) AS
SELECT 1, 'abc' FROM DUAL;
那么输出结果如下:
SELECT "ID", "NAME" FROM "FIDDLE_KLYCWRFYONMLIIJLPZYP"."TEST"
Row 1
ID = 1
NAME = abc
英文:
You cannot do this in pure SQL.
You can use PL/SQL and dynamically build the SQL and a cursor:
DECLARE
v_schema VARCHAR2(30) := USER;
v_table VARCHAR2(30) := 'TEST';
v_columns identifier_list := identifier_list('ID', 'NAME');
v_found_columns identifier_list;
v_sql CLOB;
v_cursor INTEGER;
v_dummy NUMBER;
v_value VARCHAR2(4000);
v_row PLS_INTEGER := 0;
BEGIN
SELECT column_name
BULK COLLECT INTO v_found_columns
FROM all_tab_columns
WHERE owner = v_schema
AND table_name = v_table
AND column_name MEMBER OF v_columns;
IF v_found_columns IS EMPTY THEN
RETURN;
END IF;
v_sql := EMPTY_CLOB() || 'SELECT "' || v_found_columns(1) || '"';
FOR i IN 2 .. v_found_columns.COUNT LOOP
v_sql := v_sql || ', "' || v_found_columns(i) || '"';
END LOOP;
v_sql := v_sql || ' FROM "' || v_schema || '"."' || v_table || '"';
DBMS_OUTPUT.PUT_LINE(v_sql);
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, v_sql, DBMS_SQL.NATIVE);
FOR i IN 1 .. v_found_columns.COUNT LOOP
DBMS_SQL.DEFINE_COLUMN(v_cursor, i, v_value, 4000);
END LOOP;
v_dummy := DBMS_SQL.EXECUTE(v_cursor);
LOOP
EXIT WHEN DBMS_SQL.FETCH_ROWS(v_cursor) <= 0;
v_row := v_row + 1;
DBMS_OUTPUT.PUT_LINE('Row ' || v_row);
FOR i IN 1 .. v_found_columns.COUNT LOOP
DBMS_SQL.COLUMN_VALUE(v_cursor, i, v_value);
DBMS_OUTPUT.PUT_LINE(' ' || RPAD(v_found_columns(i), 30, ' ') || ' = ' || v_value);
END LOOP;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(v_cursor);
END;
/
Which uses the type:
CREATE TYPE identifier_list IS TABLE OF VARCHAR2(30);
Then if you have the table:
CREATE TABLE TEST(id, name) AS
SELECT 1, 'abc' FROM DUAL;
Then the output is:
> lang-none
> SELECT "ID", "NAME" FROM "FIDDLE_KLYCWRFYONMLIIJLPZYP"."TEST"
> Row 1
> ID = 1
> NAME = abc
>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论