2010-01-26 40 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