SQL:搜索具有给定值的列的列表(在一行内)

时间:2010-01-26 15:26:41

标签: sql oracle

我有一个包含多个列的表格。有没有办法让查询回答问题:“对于特定的_id(主键),此行中的哪些字段的值为10”?

编辑:

澄清:表格设置正确。我正在进行的查询是一些手动查询,因为我跟踪了一些不正确的数据。该表已针对代表 浩大 多数查询运行的自动查询进行了优化,以便最快。 (并且有超过9500万行,每一点优化都很重要)

我意识到我的问题是要求做一些SQL不打算做的事情。我只是希望有一些技巧可以得到我想要的东西。

为后代编辑:

在我们的系统中,我们有许多不同的用户帐户。一个帐户是我们用于所有只读查询的帐户(这是我大部分时间都使用的帐户)。它没有相关的表格,所以当我根据自己的情况调整答案时,我不得不做出以下改变:

USER_TAB_COLUMNS必须变为ALL_TAB_COLUMNS,我必须在查询中添加OWNER = '[OWNER]'

3 个答案:

答案 0 :(得分:3)

这不是正常的数据库功能。但是,你不是第一个要求这个或类似的人。

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

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

  1. 您要搜索的表的名称
  2. 该表的主键名称 柱
  3. 您想要的主键值
  4. 限制
  5. 您要搜索的值。
  6. 很粗糙,所以你可能需要编辑它来整理输出或使程序更灵活。

    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;用他们选择的价值。

对于手动查询,它工作正常。但是,这样做的表现可能不好,所以不要按原样对大型数据集运行。