我有一个包含多个列的表格。有没有办法让查询回答问题:“对于特定的_id(主键),此行中的哪些字段的值为10”?
编辑:
澄清:表格设置正确。我正在进行的查询是一些手动查询,因为我跟踪了一些不正确的数据。该表已针对代表 浩大 多数查询运行的自动查询进行了优化,以便最快。 (并且有超过9500万行,每一点优化都很重要)
我意识到我的问题是要求做一些SQL不打算做的事情。我只是希望有一些技巧可以得到我想要的东西。
为后代编辑:
在我们的系统中,我们有许多不同的用户帐户。一个帐户是我们用于所有只读查询的帐户(这是我大部分时间都使用的帐户)。它没有相关的表格,所以当我根据自己的情况调整答案时,我不得不做出以下改变:
USER_TAB_COLUMNS
必须变为ALL_TAB_COLUMNS
,我必须在查询中添加OWNER = '[OWNER]'
。
答案 0 :(得分:3)
这不是正常的数据库功能。但是,你不是第一个要求这个或类似的人。
解决方案需要两件事。第一个是数据字典; Oracle数据库不支持Reflection,但它附带了一组视图,这些视图为我们提供了有关数据库对象的元数据。在这种情况下,我们需要user_tab_columns
,它将为我们提供给定表的列。第二件事是动态SQL;这是在运行时组装SQL查询然后执行它的能力。有几种方法可以做到这一点,但通常参考游标就足够了。
以下代码是概念验证。它需要四个参数:
很粗糙,所以你可能需要编辑它来整理输出或使程序更灵活。
create or replace procedure search_cols
(tname in user_tables.table_name%type
, pk_col in user_tab_columns.column_name%type
, pk in number
, val in number )
is
firstcol boolean := true;
stmt varchar2(32767);
result varchar2(32767);
rc sys_refcursor;
begin
stmt := 'select ';
<< projection >>
for lrec in ( select column_name from user_tab_columns
where table_name = tname
and column_name != pk_col
and data_type = 'NUMBER'
order by column_id )
loop
if not firstcol then
stmt := stmt || chr(10) || '||'',''||';
else
firstcol := false;
end if;
stmt := stmt || ' case when '|| lrec.column_name||' = '|| val ||
' then '''|| lrec.column_name || ''' else null end';
end loop projection;
stmt := stmt || chr(10)|| ' from '||tname||' where '|| pk_col || ' = '|| pk;
-- dbms_output.put_line(stmt);
open rc for stmt;
fetch rc into result;
close rc;
dbms_output.put_line(tname || '::' || val || ' found in '||result);
end search_cols;
/
如您所见,动态SQL很难阅读。调试起来比较困难:)所以最好有一种方法来显示最终的陈述。
无论如何,结果如下:
SQL> set serveroutput on size unlimited
SQL> exec search_cols('T23', 'ID', 111, 10)
T23::10 found in ,COL_B,COL_C,
PL/SQL procedure successfully completed.
SQL> exec search_cols('T23', 'ID', 222, 10)
T23::10 found in COL_A,,,
PL/SQL procedure successfully completed.
SQL>
答案 1 :(得分:1)
听起来您的数据库没有正确规范化。也就是说,您可以在子查询中使用UNPIVOT命令来执行您要执行的操作。
答案 2 :(得分:1)
我的解决方案将使用字典表(USER_TAB_COLUMNS)动态获取表中所有NUMBER列的名称和动态SQL,因为在这里我看不出如何避免它。
DECLARE
CURSOR cur_columns IS
select COLUMN_NAME from USER_TAB_COLUMNS
where TABLE_NAME='<MY_TABLE>' and DATA_TYPE='NUMBER';
query_text VARCHAR2(1000);
result_value NUMBER;
BEGIN
-- Iterate through each NUMBER column of the table
FOR rec_col IN cur_columns LOOP
-- In my line of primary key <MY_ID>, check if the current column has
-- the wanted value.
query_text :=
'SELECT count(1) FROM <MY_TABLE> WHERE <TABLE_ID> = <MY_ID> AND '
|| rec_col.COLUMN_NAME || ' = <MY_VALUE>'; -- < the "magic" is here
EXECUTE IMMEDIATE query_text INTO result_value;
IF result_value > 0 THEN
DBMS_OUTPUT.PUT_LINE('Got a match for column ' ||
rec_col.COLUMN_NAME || '.');
END IF;
END LOOP;
END;
显然,你需要更换所有&lt;变量&gt;用他们选择的价值。
对于手动查询,它工作正常。但是,这样做的表现可能不好,所以不要按原样对大型数据集运行。