這是一個有趣的問題。您想要動態更改列的編號和名稱。這是不可能用「普通」SQL創建的。我嘗試使用PIPELINED FUNCTION創建示例。
我首先創建表:
CREATE TABLE PERSON (ID int, NAME varchar2(30));
CREATE TABLE CUSTOMFIELDS (CFID int, CFTable varchar2(30), CFFieldName varchar2(30), CFFieldType varchar2(30));
CREATE TABLE CUSTOMFIELDVALUE (CFVID int, CFID int, CFFieldName varchar2(100), CFFieldValue varchar2(100));
INSERT INTO PERSON(id, name) values(1001, 'Clark Kent');
INSERT INTO CUSTOMFIELDS(CFID, CFTable, CFFieldName, CFFieldType) values(100, 'PERSON', 'AGE', 'INTEGER');
INSERT INTO CUSTOMFIELDS(CFID, CFTable, CFFieldName, CFFieldType) values(200, 'PERSON', 'WEIGHT', 'INTEGER');
...我放了一些數據:
INSERT INTO CUSTOMFIELDVALUE (CFVID, CFID, CFFieldName, CFFieldValue) values(100, 100, 1001, 44);
INSERT INTO CUSTOMFIELDVALUE (CFVID, CFID, CFFieldName, CFFieldValue) values(101, 200, 1001, 200);
然後,我創建的對象類型:
CREATE TYPE CustomFieldType AS OBJECT
(
row_id number,
fieldType varchar2(200),
person_id number,
fieldValue1 varchar2(2000),
fieldValue2 varchar2(2000),
fieldValue3 varchar2(2000),
fieldValue4 varchar2(2000),
fieldValue5 varchar2(2000)
)
/
CREATE TYPE CustomFieldTypeSet AS TABLE OF CustomFieldType
/
而且還創造了管道函數:
CREATE OR REPLACE
FUNCTION GET_PERSON_FIELDS(person_id_in IN NUMBER
,field_names_in IN VARCHAR2) RETURN CustomFieldTypeSet
PIPELINED
IS
-- constructor CustomFieldType()
l_header_row CustomFieldType := CustomFieldType(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
l_data_row CustomFieldType := CustomFieldType(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
l_tablen BINARY_INTEGER;
l_tab DBMS_UTILITY.uncl_array;
l_num_of_field_values PLS_INTEGER := 5;
l_counter PLS_INTEGER := 1;
l_position PLS_INTEGER;
l_field_names_in VARCHAR2(2000) := field_names_in;
TYPE type_header_hash IS TABLE OF PLS_INTEGER INDEX BY VARCHAR2(200);
l_header_hash type_header_hash;
BEGIN
-- 1) check, what fields you can display
IF (l_field_names_in IS NULL) THEN
<<get_all_fields>>
FOR cur_all_fields IN (SELECT DISTINCT flds.CFFIELDNAME
FROM CUSTOMFIELDS flds
,CUSTOMFIELDVALUE cfv
WHERE cfv.CFID = flds.CFID
AND flds.CFTable = 'PERSON') LOOP
l_field_names_in := l_field_names_in ||
cur_all_fields.CFFIELDNAME ||
',';
END LOOP get_all_fields;
END IF;
-- 2) generate header (function RTRIM prevent ORA-00931 exception!)
DBMS_UTILITY.comma_to_table(list => RTRIM(l_field_names_in, ','), tablen => l_tablen, tab => l_tab);
l_header_row.row_id := 1;
l_header_row.fieldType := 'HEADER';
<<header_cursor>>
FOR i IN 1..l_tablen LOOP
IF (i = 1) THEN
l_header_row.fieldValue1 := l_tab(i);
l_header_hash(l_tab(i)) := i;
ELSIF (i = 2) THEN
l_header_row.fieldValue2 := l_tab(i);
l_header_hash(l_tab(i)) := i;
ELSIF (i = 3) THEN
l_header_row.fieldValue3 := l_tab(i);
l_header_hash(l_tab(i)) := i;
ELSIF (i = 4) THEN
l_header_row.fieldValue4 := l_tab(i);
l_header_hash(l_tab(i)) := i;
ELSIF (i = 5) THEN
l_header_row.fieldValue5 := l_tab(i);
l_header_hash(l_tab(i)) := i;
END IF;
END LOOP header_cursor;
-- 3) print data to SQL (over pipe)...
PIPE ROW(l_header_row);
FOR cur_persons IN (SELECT ID
FROM PERSON
WHERE ID = COALESCE(person_id_in, ID)) LOOP
l_data_row.row_id := NULL;
l_data_row.person_id := NULL;
l_data_row.fieldType := NULL;
l_data_row.fieldValue1 := NULL;
l_data_row.fieldValue2 := NULL;
l_data_row.fieldValue3 := NULL;
l_data_row.fieldValue4 := NULL;
l_data_row.fieldValue5 := NULL;
l_data_row.fieldType := 'DATA';
FOR cur_data IN (SELECT p.ID AS person_id
,cfv.CFID
,flds.CFTABLE
,flds.CFFIELDNAME
,cfv.CFFIELDVALUE
FROM PERSON p
,CUSTOMFIELDS flds
,CUSTOMFIELDVALUE cfv
WHERE p.ID = cur_persons.ID
AND p.ID = cfv.CFFIELDNAME
AND cfv.CFID = flds.CFID) LOOP
l_data_row.person_id := cur_persons.ID;
l_position := NULL;
IF (l_header_hash.EXISTS(cur_data.CFFIELDNAME)) THEN
l_position := l_header_hash(cur_data.CFFIELDNAME);
END IF;
IF (l_position = 1) THEN
l_data_row.fieldValue1 := cur_data.CFFIELDVALUE;
ELSIF (l_position = 2) THEN
l_data_row.fieldValue2 := cur_data.CFFIELDVALUE;
ELSIF (l_position = 3) THEN
l_data_row.fieldValue3 := cur_data.CFFIELDVALUE;
ELSIF (l_position = 4) THEN
l_data_row.fieldValue4 := cur_data.CFFIELDVALUE;
ELSIF (l_position = 5) THEN
l_data_row.fieldValue5 := cur_data.CFFIELDVALUE;
END IF;
END LOOP;
l_counter := l_counter + 1;
l_data_row.row_id := l_counter;
PIPE ROW(l_data_row);
END LOOP;
RETURN;
END GET_PERSON_FIELDS;
比你可以使用SQL來獲取樣本數據(注意:防止異常ORA-22905,你必須設置會話變量「ALTER SESSION SET CURSOR_SHARING = EXACT;」):
SELECT * FROM TABLE(GET_PERSON_FIELDS(1001,'AGE,WEIGHT'));
這裏是輸出:
ROW_ID FIELDTYPE PERSON_ID FIELDVALUE FIELDVALUE FIELDVALUE
------ ---------- --------- ---------- ---------- ----------
1 HEADER AGE
2 DATA 1001 44
在第一列是首部,其中存儲關於字段名和報頭之後信息被存儲的數據。您可以使用這些sql語句的組合:
SELECT * FROM TABLE(GET_PERSON_FIELDS(1001,'AGE,WEIGHT'));
SELECT * FROM TABLE(GET_PERSON_FIELDS(1002,'AGE,GENDER'));
SELECT * FROM TABLE(GET_PERSON_FIELDS(1001,NULL));
SELECT * FROM TABLE(GET_PERSON_FIELDS(NULL,NULL));
- 第一個參數是PERSON_ID
- 第二個參數是你想看到的輸出
- 如果第一個參數是NULL的項目列表中,你可以看到列表所有的人
- 如果第二個參數是NULL,你可以看到所有的參數
- 腳本是不完整的名單,並有一定的侷限性:
- 在對象CustomFieldType你不能動態地改變區域的數量(我的意思是fieldValue1,fieldValue2 ...)
- 在功能GET_PERSON_FIELDS的身體,你可以看到,動態問題也是IF語句(IF(l_position = 1) THEN l_data_row.fieldValue1,IF(l_position = 2)THEN l_data_row.fieldValue1)...
最後,當我進入一些示例數據,如下所示:
INSERT INTO PERSON(id, name) values(1002, 'Lois Lane');
INSERT INTO CUSTOMFIELDS(CFID, CFTable, CFFieldName, CFFieldType) values(300, 'PERSON', 'GENDER', 'VARCHAR');
INSERT INTO CUSTOMFIELDS(CFID, CFTable, CFFieldName, CFFieldType) values(400, 'PERSON', 'SINGLE', 'VARCHAR');
INSERT INTO CUSTOMFIELDVALUE (CFVID, CFID, CFFieldName, CFFieldValue) values(102, 100, 1002, 45);
INSERT INTO CUSTOMFIELDVALUE (CFVID, CFID, CFFieldName, CFFieldValue) values(103, 300, 1002, 'FEMALE');
INSERT INTO CUSTOMFIELDVALUE (CFVID, CFID, CFFieldName, CFFieldValue) values(104, 400, 1002, 'YES');
...和ra n這個SQL命令:
SELECT * FROM TABLE(GET_PERSON_FIELDS(NULL,NULL));
...輸出是這樣的:
ROW_ID FIELDTYPE PERSON_ID FIELDVALUE FIELDVALUE FIELDVALUE FIELDVALUE
------ ---------- --------- ---------- ---------- ---------- ----------
1 HEADER AGE GENDER SINGLE WEIGHT
2 DATA 1001 44 200
3 DATA 1002 45 FEMALE YES
從我學到了什麼(上SO),這就是所謂的實體 - 屬性 - 值(EAV)模型。關於SO的各種參考文獻,或[Wikipedia](http://en.wikipedia.org/wiki/Entity-attribute-value_model)... – pascal 2010-08-25 20:36:06
您正在使用哪個版本的Oracle? Oracle 11引入了PIVOT,它允許您以最小的努力將行轉換爲列 – 2010-08-25 21:09:42
此示例看起來有點不妥 - CustomFieldValue表中的第三個值名爲cfFieldName,您的示例顯示1001,Person.ID? – 2010-08-26 12:10:34