2010-01-26 37 views
7

Tôi có một bảng với nhiều cột. Có cách nào để tạo truy vấn trả lời câu hỏi: "Đối với một _id cụ thể (khóa chính), (các) trường nào trong hàng này có giá trị là 10" không?SQL: Tìm kiếm danh sách các cột có giá trị đã cho (trong một hàng)

CHỈNH SỬA:

Làm rõ: Bảng được thiết lập đúng cách. Truy vấn tôi đang thực hiện là một số truy vấn thủ công khi tôi theo dõi một số dữ liệu không chính xác. Bảng đã được tối ưu hóa để nhanh nhất cho các truy vấn tự động đại diện cho các thuộc tính phổ biến nhất của các truy vấn chạy. (Và với hơn 95 triệu hàng, mỗi bit tối ưu hóa quan trọng)

Tôi nhận ra rằng câu hỏi của tôi là yêu cầu làm điều gì đó mà SQL không định làm. Tôi chỉ hy vọng rằng có một số mẹo để có được những gì tôi muốn.

CHỈNH SỬA cho hậu thế:

Trong hệ thống của chúng tôi, chúng tôi có nhiều tài khoản người dùng khác nhau. Một tài khoản là tài khoản chúng tôi sử dụng cho tất cả các truy vấn chỉ đọc (đây là tài khoản tôi sử dụng hầu hết thời gian). Nó không sở hữu các bảng được đề cập, vì vậy khi tôi thích ứng với câu trả lời của mình, tôi phải thực hiện thay đổi sau:

USER_TAB_COLUMNS phải trở thành ALL_TAB_COLUMNS và tôi phải thêm OWNER = '[OWNER]' vào truy vấn.

Trả lời

3

Đây không phải là một chức năng cơ sở dữ liệu bình thường. Tuy nhiên bạn không phải là người đầu tiên đã yêu cầu điều này, hoặc một cái gì đó như nó.

Giải pháp yêu cầu hai điều. Đầu tiên là từ điển dữ liệu; cơ sở dữ liệu Oracle không hỗ trợ Reflection nhưng nó đi kèm với một tập các khung nhìn cung cấp cho chúng ta siêu dữ liệu về các đối tượng cơ sở dữ liệu của chúng ta. Trong trường hợp này, chúng ta cần user_tab_columns, sẽ cung cấp cho chúng ta các cột cho một bảng nhất định. Điều thứ hai là SQL động; đây là khả năng lắp ráp một truy vấn SQL khi chạy và sau đó thực hiện nó. Có một vài cách để làm điều này, nhưng thường ref con trỏ là đủ.

Mã sau đây là bằng chứng về khái niệm. Phải mất bốn thông số:

  1. tên của bảng mà bạn muốn tìm kiếm
  2. tên của cột khóa chính của bảng mà
  3. giá trị khóa chính mà bạn muốn hạn chế bởi
  4. giá trị bạn muốn tìm kiếm.

Rất khó để bạn có thể cần chỉnh sửa để gọn gàng đầu ra hoặc làm cho chương trình linh hoạt hơn.

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

Như bạn thấy, SQL động khó đọc. Khó gỡ lỗi hơn :) Vì vậy, bạn nên có phương tiện để hiển thị câu lệnh cuối cùng.

Dù sao, đây là kết quả:

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

Vâng, đây là thứ mà tôi biết phải ở ngoài đó. Cảm ơn! –

1

Có vẻ như cơ sở dữ liệu của bạn không được chuẩn hóa đúng cách. Điều đó nói rằng, bạn có thể có thể sử dụng lệnh UNPIVOT bên trong một truy vấn phụ để làm những gì bạn đang cố gắng làm.

+0

Bạn có cung cấp thêm thông tin chi tiết về việc hủy hợp lệ không? Cụ thể, không trục trặc làm việc trong oracle? –

+0

@David Oneill: 'UNPIVOT' (và' PIVOT' cho vấn đề đó) chỉ được hỗ trợ trong Oracle 11g +. Bất kỳ điều gì sớm hơn yêu cầu sử dụng các câu lệnh 'CASE' hoặc' DECODE' - hãy kiểm tra các thẻ sql, pivot và/hoặc xếp hạng trên SO. –

1

Giải pháp của tôi sẽ sử dụng bảng từ điển (USER_TAB_COLUMNS) để lấy động tên của tất cả các cột NUMBER từ bảng của bạn, và Dynamic SQL, bởi vì ở đây tôi không xem cách người ta có thể tránh nó.

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; 

Rõ ràng, bạn cần phải thay thế tất cả các biến số <> bằng giá trị đã chọn.

Để truy vấn thủ công, nó hoạt động tốt. Tuy nhiên, hiệu suất của điều này có thể là xấu, do đó, không chạy nó chống lại bộ lớn dữ liệu như vậy.

+0

Vâng, đây là thứ mà tôi biết phải ở ngoài đó. Cảm ơn! –

Các vấn đề liên quan