2016-02-18 75 views
0

我想在in子句中創建一個獨佔或語句。例如在WHERE子句中的幾個IN子句中創建XOR

WHERE ACCOUNT IN (1,2,3) XOR ACCOUNT IN (3,4) XOR ACCOUNT IN (5,6) 

我能找到的唯一參考資料並不方便使用IN子句。 TIA。

編輯 - 澄清:

DDL:

CREATE TABLE EXAMPLE 
(
    CONTRACT VARCHAR2(1), 
    ID_NUMBER NUMBER, 
    ACCOUNT  NUMBER, 
    AMOUNT_1 NUMBER, 
    AMOUNT_2 NUMBER 
); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('A', 1, 100, 5, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('A', 2, 101, NULL, 5); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('A', 3, 200, 2, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('B', 4, 100, 7, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('B', 5, 100, 3, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('B', 6, 101, NULL, 10); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('B', 7, 200, 2, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('C', 8, 200, 10, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('C', 9, 200, 5, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('C', 10, 201, NULL, 15); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('C', 11, 300, 6, NULL); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('C', 12, 301, NULL, 6); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('D', 13, 100, NULL, -5); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('D', 14, 100, NULL, 5); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('D', 15, 300, 7, 3); 

INSERT INTO EXAMPLE (CONTRACT, ID_NUMBER, ACCOUNT, AMOUNT_1, AMOUNT_2) 
VALUES ('D', 16, 200, NULL, 4); 

我的查詢:

SELECT * FROM 
(
    SELECT 
    A.CONTRACT, 
    COUNT(NVL(ID_NUMBER,1)) AS ID_NUMBER_COUNT, 
    LISTAGG(ID_NUMBER, ', ') WITHIN GROUP(ORDER BY CONTRACT) AS ID_NUMBERS, 
    SUM(NVL(AMOUNT_1,0)) AS AMOUNT_1_SUM,         
    SUM(NVL(AMOUNT_2,0)) AS AMOUNT_2_SUM          
    FROM EXAMPLE A 
    WHERE 1=1 
     AND NOT (NVL(AMOUNT_1,0) = NVL(AMOUNT_2,0)) 
    GROUP BY CUBE(CONTRACT,ACCOUNT) 
) A 
WHERE 1=1 
    AND NVL(A.AMOUNT_1_SUM,0) = NVL(A.AMOUNT_2_SUM,0) 
    AND CONTRACT IS NOT NULL 

魔方功能可能沒有必要在這個例子中,但我實際的表有幾更多的描述符列需要跨組合進行搜索。

如果您對上表運行查詢,而沒有任何IN子句來限制帳戶,那麼您將不會收到實際的偏移量記錄羣(應該說明,如果它們在相同的位置列,其他明智的偏移量將發生在兩個總計金額相等的列上)。

的,我旨在捕獲記錄的真實總體是:

-On合同A,ID號碼1和2

-On合同B,ID號4,5和6

-On合同C,所有的ID號

-On合同d,所有的ID號

當前標準的可以捕捉整個續所有ID號碼查詢反對C和D,但是合同A和B中有記錄,除非賬號有限,否則這些記錄不會作爲有效結果返回。

- 將賬戶限制在IN(100,101)將產生A和B的ID號碼,我打算捕獲。需要注意的是,我的全部人口中有大約20種賬戶組合必須進行搜索。

- 兩個不同合約之間永遠不會發生抵消。我通過使用GROUPING_ID在整個人口的查詢中處理這個問題,然後只是排除Contract字段爲空的任何地方。

- 作爲最後的手段,我可​​以使用UNION語句,但希望不使用它。

- 我現在唯一想做的其他事情是在運行查詢之前在某處定義帳戶集合,然後爲每個集合運行一個FOR循環。

謝謝!

回答

1

A XOR B等效爲(A AND NOT B) OR (B AND NOT A)這將使您的查詢是這樣的:

WHERE (ACCOUNT IN (1,2,3) AND ACCOUNT NOT IN (3,4,5,6)) 
OR (ACCOUNT IN (3,4) AND ACCOUNT NOT IN (1,2,3,5,6)) 
OR (ACCOUNT IN (5,6) AND ACCOUNT NOT IN (1,2,3,3,4)) 

然而,問題並沒有真正爲ACCOUNT不能出現在多個3有多個值,以便(除了感套),你似乎在測試相當於A XOR NOT A這將永遠是真實的(當ACCOUNT <> 3)。

鑑於此,上述邏輯將簡化爲:

WHERE ACCOUNT IN (1,2,4,5,6) 

編輯 - 繼澄清的問題的:

甲骨文設置

我改名爲Amount_1Amount_2列到CreditDebit

CREATE TABLE EXAMPLE(CONTRACT, ID_NUMBER, ACCOUNT, CREDIT, DEBIT) AS 
SELECT 'A', 1, 100, 5, NULL FROM DUAL UNION ALL 
SELECT 'A', 2, 101, NULL, 5 FROM DUAL UNION ALL 
SELECT 'A', 3, 200, 2, NULL FROM DUAL UNION ALL 
SELECT 'B', 4, 100, 7, NULL FROM DUAL UNION ALL 
SELECT 'B', 5, 100, 3, NULL FROM DUAL UNION ALL 
SELECT 'B', 6, 101, NULL, 10 FROM DUAL UNION ALL 
SELECT 'B', 7, 200, 2, NULL FROM DUAL UNION ALL 
SELECT 'C', 8, 200, 10, NULL FROM DUAL UNION ALL 
SELECT 'C', 9, 200, 5, NULL FROM DUAL UNION ALL 
SELECT 'C', 10, 201, NULL, 15 FROM DUAL UNION ALL 
SELECT 'C', 11, 300, 6, NULL FROM DUAL UNION ALL 
SELECT 'C', 12, 301, NULL, 6 FROM DUAL UNION ALL 
SELECT 'D', 13, 100, NULL, -5 FROM DUAL UNION ALL 
SELECT 'D', 14, 100, NULL, 5 FROM DUAL UNION ALL 
SELECT 'D', 15, 300, 7, 3  FROM DUAL UNION ALL 
SELECT 'D', 16, 200, NULL, 4 FROM DUAL UNION ALL 
SELECT 'E', 17, 100, 3, NULL FROM DUAL UNION ALL 
SELECT 'E', 18, 200, NULL, 4 FROM DUAL; 

CREATE OR REPLACE TYPE TransactionObj AS OBJECT(
    ID_NUMBER INT, 
    ACCOUNT INT, 
    VALUE  INT 
); 
/

CREATE OR REPLACE TYPE TransactionTable AS TABLE OF TransactionObj; 
/

CREATE OR REPLACE FUNCTION getMaxZeroSum(
    Transactions TransactionTable 
) RETURN TransactionTable 
AS 
    zeroSumTransactions TransactionTable := Transactiontable(); 
    bitCount INT; 
    valueSum INT; 
    maxBitCount INT := 0; 
    valueMax INT := 0; 
BEGIN 
    IF Transactions IS NULL OR Transactions IS EMPTY THEN 
    RETURN zeroSumTransactions; 
    END IF; 
    FOR i IN 1 .. POWER(2, Transactions.COUNT) - 1 LOOP 
    bitCount := 0; 
    valueSum := 0; 
    FOR j IN 1 .. Transactions.COUNT LOOP 
     IF BITAND(i, POWER(2, j - 1)) > 0 THEN 
     valueSum := valueSum + Transactions(j).VALUE; 
     bitCount := bitCount + 1; 
     END IF; 
    END LOOP; 
    IF valueSum = 0 AND bitCount > maxBitCount THEN 
     maxBitCount := bitCount; 
     valueMax := i; 
    END IF; 
    END LOOP; 
    IF maxBitCount > 0 THEN 
    zeroSumTransactions.EXTEND(maxBitCount); 
    bitCount := 0; 
    FOR j IN 1 .. Transactions.COUNT LOOP 
     IF BITAND(valueMax, POWER(2, j - 1)) > 0 THEN 
     bitCount := bitCount + 1; 
     zeroSumTransactions(bitCount) := transactions(j); 
     END IF; 
    END LOOP; 
    END IF; 
    RETURN zeroSumTransactions; 
END; 
/

查詢

SELECT zs.Contract, 
     LISTAGG(t.ID_NUMBER, ',') WITHIN GROUP (ORDER BY ID_NUMBER) AS ids, 
     LISTAGG(t.ACCOUNT, ',') WITHIN GROUP (ORDER BY ID_NUMBER) AS accounts 
FROM (
      SELECT CONTRACT, 
       getMaxZeroSum(CAST(COLLECT(TransactionObj(ID_NUMBER, ACCOUNT, NVL(CREDIT, 0) - NVL(DEBIT, 0))) AS TransactionTable)) AS Transactions 
      FROM EXAMPLE 
      WHERE NVL(CREDIT, 0) <> NVL(DEBIT, 0) 
      GROUP BY CONTRACT 
     ) zs, 
     TABLE(zs.Transactions) (+) t 
GROUP BY Contract; 

輸出

CONTRACT IDS   ACCOUNTS 
-------- -------------- -------------------- 
A  1,2   100,101    
B  4,5,6   100,100,101   
C  8,9,10,11,12 200,200,201,300,301 
D  13,14,15,16 100,100,300,200 
E  NULL   NULL 

getMaxZeroSum功能幾乎肯定能夠提高審議通過,以排除項目的最少數量的訂單交易排除了所有的二分之一,然後在找到零和時立即返回(但是,我去了一個簡單的t o編寫函數來演示如何在高性能函數上完成)。但是,無論你寫什麼,我都看不到一種不是O(n(2^n))的方式,其中n是給定合同的交易次數。

+0

我應該澄清 - 對不起。我正在尋找多個不同賬戶之間的餘額(彼此抵消)(總和爲零)。我需要限制帳戶1和2之間可能存在的偏移量,因此我要查找偏移量的帳戶集合,但是當帳戶3被添加時,它不再等於零。我有一組已知的帳戶,可能存在抵消,這些都是我的where子句中需要的。 – keyoung1

+0

@ keyoung1這不是你在問題中提出的問題 - 我建議你澄清一下你的問題,並用一個示例表(包括DDL和DML語句),期望的結果以及到目前爲止嘗試過的內容給出一個最簡單的工作示例。 – MT0

+0

@ keyoung1您的問題描述與計算困難的子集總和問題(除非帳戶餘額可以受到限制)具有強烈的相似性 - 期望需要爲可行的性能應用合適的啓發式。 – collapsar