Alex Poole有一個很好的評論 - 如果您已經知道哪些列包含T/F
數據,則不需要按數據類型進行查詢,因爲您已經知道按名稱檢查哪些列。您可以按列名查詢。
我會提供一個答案,如果你做的話不是已經知道哪些列需要檢查,但也會包含遠低於靜態sql的示例。我不確定你的第二個要求到底是什麼意思,所以我會添加幾個不同角度的例子。正如Gordon Linoff在評論中提到的,這些將使用EXECUTE IMMEDIATE
。
這個第一個例子解釋了你的要求,你事先不知道表(否則你可以檢查它的CHAR
列,並直接查詢),但要檢查是否至少有一行有一個T
爲每個給出TABLE
的CHAR
列(跨行)。
該塊將TABLE_NAME
作爲參數,然後構建一個動態查詢,檢查每個COLUMN
在表中是否至少有一個條目的值爲T
。
首先創建具有不同數據類型的試驗表格包括一些CHAR
:
CREATE TABLE HETEROGENEOUS (
CHAR_COL_1 CHAR(10),
NUMBER_COL_1 NUMBER,
CHAR_COL_2 CHAR(10),
TIMESTAMP_COL_1 TIMESTAMP,
CHAR_COL_3 CHAR(10)
);
然後添加一些測試數據。這第一個負載有三列中的兩列,至少有一個值爲T
,所以測試失敗。
INSERT INTO HETEROGENEOUS VALUES ('Chewbacca', 1, 'VOLTRON', SYSTIMESTAMP, 'Gundam');
INSERT INTO HETEROGENEOUS VALUES ('T', 1, 'Frodo', SYSTIMESTAMP, 'Starscream');
INSERT INTO HETEROGENEOUS VALUES ('X', 1, 'Bombadil', SYSTIMESTAMP, 'T');
然後運行該塊。此塊計算CHAR
的列數,然後執行動態查詢來算多少列至少有一個排在每個CHAR
列T
值和T
列的計數與CHAR
列的計數比較:
DECLARE
V_TABLE_NAME VARCHAR2(128) := 'HETEROGENEOUS';
V_SQL_TEXT VARCHAR2(32000);
V_REQUIRED_COLUMN_COUNT NUMBER := 0;
V_OK_COLUMN_COUNT NUMBER := 0;
BEGIN
EXECUTE IMMEDIATE
UTL_LMS.FORMAT_MESSAGE('SELECT COUNT(*) FROM USER_TAB_COLUMNS WHERE TABLE_NAME = ''%s'' AND DATA_TYPE = ''CHAR''',V_TABLE_NAME)
INTO V_REQUIRED_COLUMN_COUNT;
SELECT 'SELECT ' ||LISTAGG('(SELECT COALESCE(MIN(1),0) FROM '||V_TABLE_NAME||' WHERE TRIM('||
COLUMN_NAME||') = ''T'' AND ROWNUM = 1)','+')
WITHIN GROUP (ORDER BY COLUMN_ID) || ' FROM DUAL'
INTO V_SQL_TEXT
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = V_TABLE_NAME
AND DATA_TYPE = 'CHAR' GROUP BY TABLE_NAME;
EXECUTE IMMEDIATE V_SQL_TEXT INTO V_OK_COLUMN_COUNT;
IF V_OK_COLUMN_COUNT < V_REQUIRED_COLUMN_COUNT
THEN
DBMS_OUTPUT.PUT_LINE(UTL_LMS.FORMAT_MESSAGE('Required at least: %s columns to have 1+ T values but only found: %s',TO_CHAR(V_REQUIRED_COLUMN_COUNT),TO_CHAR(V_OK_COLUMN_COUNT)));
ELSE
DBMS_OUTPUT.PUT_LINE(UTL_LMS.FORMAT_MESSAGE('All: %s CHAR columns have at least one T value',TO_CHAR(V_REQUIRED_COLUMN_COUNT)));
END IF;
END;
/
結果:
Required at least: 3 columns to have 1+ T values but only found: 2
然後添加其他行,以獲得最後的需要T
值:
INSERT INTO HETEROGENEOUS VALUES ('Deckard', 1, 'T', SYSTIMESTAMP, 'Megatron');
,並再次運行:
All: 3 CHAR columns have at least one T value
靜態SQL當量(如果你已經知道你的表/列)爲:
SELECT (SELECT COALESCE(MIN(1), 0) FROM HETEROGENEOUS
WHERE TRIM(CHAR_COL_1) = 'T' AND ROWNUM = 1) +
(SELECT COALESCE(MIN(1), 0) FROM HETEROGENEOUS
WHERE TRIM(CHAR_COL_2) = 'T' AND ROWNUM = 1) +
(SELECT COALESCE(MIN(1), 0) FROM HETEROGENEOUS
WHERE TRIM(CHAR_COL_3) = 'T' AND ROWNUM = 1)
FROM DUAL;
如果您的要求反而是找到ROW
S其中至少一個CHAR
列的值爲T
,方法相同,但動態查詢不同。
第二個例子會發現所有其中至少一個CHAR
柱具有T
值的行(和剛打印出來):
DECLARE
V_TABLE_NAME VARCHAR2(128) := 'HETEROGENEOUS';
V_SQL_TEXT VARCHAR2(32000);
TYPE REFCURSOR IS REF CURSOR;
V_REFCURSOR REFCURSOR;
V_ROWID VARCHAR2(64);
BEGIN
SELECT 'SELECT ROWID FROM '||V_TABLE_NAME||' WHERE 1 = ANY ('||LISTAGG('DECODE(TRIM('||COLUMN_NAME||'),''T'',1,0) ',',') WITHIN GROUP (ORDER BY COLUMN_ID)||')'
INTO V_SQL_TEXT
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = V_TABLE_NAME
AND DATA_TYPE = 'CHAR'
GROUP BY TABLE_NAME;
OPEN V_REFCURSOR FOR V_SQL_TEXT;
LOOP
FETCH V_REFCURSOR INTO V_ROWID;
EXIT WHEN V_REFCURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(UTL_LMS.FORMAT_MESSAGE('RowId: %s',V_ROWID));
END LOOP;
CLOSE V_REFCURSOR;
END;
/
運行它給出了具有T在任何CHAR
三排列:
RowId: AAGKHPAFJAABL49AAB
RowId: AAGKHPAFJAABL49AAC
RowId: AAGKHPAFJAABL49AAD
或可替換地得到單列已經在他們的CHAR
列NO T
值,通過切換從ANY
到ALL
:
WHERE 1 = ANY
WHERE 1 <> ALL
給出一個行:
RowId: AAGKHPAFJAABL49AAA
靜態eqivalent(如果你已經知道你的表,並不需要使用的數據類型)是:
SELECT ROWID
FROM HETEROGENEOUS
WHERE 1 = ANY (DECODE(TRIM(CHAR_COL_1), 'T', 1, 0),
DECODE(TRIM(CHAR_COL_2), 'T', 1, 0),
DECODE(TRIM(CHAR_COL_3), 'T', 1, 0));
你會需要使用'立即執行'。 –
這聽起來像一個非常奇怪的任務或一個非常糟糕的數據模型。通常人們確切地知道要使用哪些列。爲什麼我要從產品表中選擇所有行,其中一個char列等於'T'(所有「布爾」列包含true,我想呢?)?這樣的結果可能告訴我什麼(例如,所有有庫存或女性用品,或者對環境友好或者適合未成年人)?似乎沒有道理。除此之外,動態構建查詢,例如使用PL/SQL。 –
我有一個表格,具有訪問某些操作的權限。每一行都包含與一種附件和列定義有關的一種類型的權限,可以對所選的一種進行哪種操作。 Appart來自具有真/假值的列,列中也有字符串,日期或數字值。我的任務是檢查哪些用戶擁有特權,可以使用附件。即有一個。PNG文件,用戶有用戶和管理員權限,我需要檢查他是否可以打開和刪除那種文件。 – micnow21