2010-01-26 37 views
7

我有很多列的表。有没有办法做出一个查询来回答这个问题:“对于一个特定的_id(主键),该行中的哪些字段的值为10”?SQL:搜索列的列表与给定值(行内)

编辑:

澄清:该表设置正确。我正在查询的是一些手动查询,因为我追查了一些不正确的数据。该表已被优化以最快的为代表的广大多数的查询运行自动查询。 (以及超过95万行,优化的每一点有很重要)

我意识到,我的问题是问做一些SQL并没有打算这样做。我只是希望有一些技巧可以得到我想要的。

编辑后人:

在我们的系统,我们有很多不同的用户帐户。一个帐户是我们用于所有只读查询的帐户(这是我大部分时间使用的帐户)。它并不拥有有问题的表格,所以当我适应了答案,我的情况,我不得不做出如下变化:

USER_TAB_COLUMNS必须成为ALL_TAB_COLUMNS,我不得不添加OWNER = '[OWNER]'到查询。

回答

3

这不是数据库功能正常的片。然而,你不是第一个问过这个问题的人,或者类似的东西。

的解决方案需要两件事情。首先是数据字典; Oracle数据库不支持Reflection,但它带有一组视图,它们为我们提供了有关数据库对象的元数据。在这种情况下,我们需要user_tab_columns,它会给我们给定表的列。第二件事是动态SQL;这是在运行时组装SQL查询并执行它的能力。有几种方法可以做到这一点,但通常使用ref游标就足够了。

以下代码是概念验证。它有四个参数:

  1. 您要搜索
  2. 该表的主键 列的名称表的名称
  3. 要 主键值由
  4. 值您限制想要搜索。

现在已经过时了,所以您可能需要编辑它以整理输出或使程序更加灵活。

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> 
+0

是的,这是我知道的事情必须在那里。谢谢! – 2010-01-27 05:19:11

1

这听起来像你的数据库是没有正确归。也就是说,你可以在子查询中使用UNPIVOT命令来做你想做的事情。

+0

你会提供更多关于unpivot的细节吗?具体来说,在oracle中做unpivot工作吗? – 2010-01-26 16:13:59

+0

@David Oneill:Oracle 11g +仅支持'UNPIVOT'(和“PIVOT”)。之前的任何事情都需要使用'CASE'或'DECODE'语句 - 检查SO上的sql,主键和/或排名标签。 – 2010-01-26 16:36:40

1

我的解决方案将使用字典表(USER_TAB_COLUMNS)从你的桌子,和动态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; 

很显然,你需要用自己选择的值来代替所有<变量>。

对于手动查询,它工作正常。但是,这样做的性能可能很差,因此不要按照原样对大型数据集运行它。

+0

是的,这是我知道的事情必须在那里。谢谢! – 2010-01-27 05:20:05