2010-01-26 8 views
7

Mam tabelę z wielu kolumn. Czy istnieje sposób, aby utworzyć zapytanie, które odpowiada na pytanie: "Dla konkretnego _id (klucz podstawowy), jakie pola w tym wierszu mają wartość 10"?SQL: Wyszukaj listę kolumn o danej wartości (w wierszu)

EDYTOWANIE:

Wyjaśnienie: Tabela została poprawnie skonfigurowana. Zapytanie, które robię, to ręczne sprawdzanie, ponieważ wyśledziłem niepoprawne dane. Tabela została zoptymalizowana tak, aby była najszybsza dla zautomatyzowanych zapytań, które reprezentują większość uruchomionych zapytań. (A z ponad 95 milionami wierszy, każda optymalizacja jest ważna)

Zdaję sobie sprawę, że moje pytanie wymaga zrobienia czegoś, czego SQL nie był zamierzony. Mam tylko nadzieję, że jest jakaś sztuczka, aby uzyskać to, czego chcę.

EDIT dla potomności:

W naszym systemie, mamy wiele różnych kont użytkowników. Jedno konto jest używane dla wszystkich zapytań tylko do odczytu (jest to ten, którego używam przez większość czasu). To nie jest właścicielem tabel w pytaniu, więc kiedy dostosowania odpowiedź na moją sytuację, musiałem wprowadzić następujące zmiany:

USER_TAB_COLUMNS musiał stać ALL_TAB_COLUMNS i musiałem dodać OWNER = '[OWNER]' do zapytania.

Odpowiedz

3

To nie jest normalna funkcja bazy danych. Jednak nie jesteś pierwszą osobą, która o to poprosiła lub coś podobnego.

Rozwiązanie wymaga dwóch rzeczy. Pierwszy to słownik danych; baza danych Oracle nie obsługuje Reflection, ale jest dostarczana z zestawem widoków, które dostarczają nam metadanych dotyczących naszych obiektów bazy danych. W tym przypadku potrzebujemy user_tab_columns, co da nam kolumny dla danej tabeli. Drugą rzeczą jest dynamiczny SQL; jest to możliwość złożenia zapytania SQL w środowisku wykonawczym, a następnie jego wykonania. Jest kilka sposobów na zrobienie tego, ale zwykle ref kursy są wystarczające.

Poniższy kod jest dowodem koncepcji. To trwa cztery parametry:

  1. nazwę tabeli, którą chcesz wyszukać
  2. nazwa klucza podstawowego kolumnie tej tabeli za
  3. podstawowa wartość klucza chcesz ograniczyć przez
  4. wartość, którą chcesz szukać.

Jest on już "szorstki" i może być konieczne edytowanie go w celu uporządkowania danych wyjściowych lub zwiększenia elastyczności programu.

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; 
/

Jak widać dynamiczny SQL jest trudny do odczytania. Trudniej jest debugować :) Więc dobrze jest mieć środki, aby pokazać końcowe oświadczenie.

W każdym razie, oto wyniki:

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

Tak, to było coś, o czym wiedziałem, że musi tam być. Dzięki! –

1

Wygląda na to, że twoja baza danych nie jest prawidłowo znormalizowana. Powiedział, że prawdopodobnie możesz użyć polecenia UNPIVOT wewnątrz podzapytania, aby zrobić to, co próbujesz zrobić.

+0

Czy podasz więcej szczegółów na temat programu univot? Konkretnie, czy rozpakowywanie działa w wyroczni? –

+0

@ David Oneill: 'UNPIVOT' (i' PIVOT' w tym przypadku) są obsługiwane tylko w Oracle 11g +. Wszystko, co jest wcześniejsze, wymaga użycia instrukcji "CASE" lub "DECODE" - sprawdź znaczniki sql, pivot i/lub ranking na SO. –

1

Moje rozwiązaniem byłoby użyć tabel słownikowych (USER_TAB_COLUMNS), aby pobrać dynamicznie nazwy wszystkich kolumn numer z tabeli i dynamiczny SQL, bo tu nie mam zobacz, jak można tego uniknąć.

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; 

Oczywiście, trzeba wymienić wszystkie zmienne <> z wybranej wartości.

W przypadku ręcznego wysyłania zapytań działa poprawnie. Jednak wydajność tego jest prawdopodobnie zła, więc nie należy go uruchamiać w stosunku do dużych zestawów danych w obecnej postaci.

+0

Tak, to było coś, o czym wiedziałem, że musi tam być. Dzięki! –

Powiązane problemy