在WHERE子句

时间:2016-02-18 22:36:42

标签: oracle plsql oracle-sqldeveloper

我试图在in子句中创建一个独占或声明。例如

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

对于这个例子,CUBE函数似乎有些过分,但是我的实际表有几个描述符列,需要搜索组合。

如果您在上表中运行查询,没有任何IN子句来限制帐户,您将不会收到偏移记录的真实数量(如果它们在同一列中,则应说明它们仅总和为零另外,在聚合量相等的两列中都会发生偏移。

我打算捕获的真实记录是:

- 合同A,身份证号码1和2

- 合同B,身份证号码4,5和6

- 在合同C中,所有ID号

- 合约D,所有身份证号码

目前的查询可以捕获合同C和D中的所有ID号,但是合同A和B中的记录不会作为有效结果返回,除非帐户有限。

- 限制帐户到IN(100,101)将产生我想要捕获的A和B的ID号。需要注意的是,在我的完整人口中有大约20个帐户组合必须被搜索。

- 永远不会在两个不同的合同之间发生偏移。我通过使用GROUPING_ID在完整填充的查询中处理这个,然后只是排除Contract字段为空的任何地方。

- 作为最后的手段,我可​​以使用UNION语句,但不想使用它。

- 我目前唯一想做的事情就是在运行查询之前在某处定义帐户集,然后为每个集合运行一个FOR循环。

谢谢!

1 个答案:

答案 0 :(得分:1)

等同于A XOR B的{​​{1}}会使您的查询如下:

( 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之外),您似乎正在测试相当于{{1}的3这将始终为真(当A XOR NOT A时)。

鉴于此,上述逻辑将简化为:

ACCOUNT <> 3

编辑 - 在澄清问题之后

Oracle安装程序

我将WHERE ACCOUNT IN (1,2,4,5,6) Amount_1列重命名为Amount_2Credit

Debit

<强>查询

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的方式,其中O(n(2^n))是给定合约的交易数量。