SQL:行和列之间的数量差异

时间:2016-02-04 06:38:30

标签: sql database oracle

我有一张桌子 使用以下数据

Table X             
Seq_no    A    Claim    Payment     Balance (to be calculated)
1        abc    100     10          90 (100-10)
2        abc            50          40 (90-50)
3        abc            20          20 (40-20)
1        xyz    150     10          140 (150-10)
1        qwe    200     10          190 (200-10)

我需要计算列余额。

我正在尝试使用以下查询 SQL>

Select
Seq_no, a, Claim, Payment, 
CASE 
    When Seq_no =1
    then (claim-Payment) 
    Else ( lag(Balance)- Payment over (order by Balance))
END as  Balance
from table X

但是我收到了错误

ORA-00904: "Balance": invalid identifier
00904. 00000 -  "%s: invalid identifier"

我认为这是因为Balance不是现有的列名。

是否有正确的方法来实现结果。?

更新 *我错过了重要的一部分。 *

我拥有的数据格式如下:

Table X             
Seq_no    A    Claim    Payment    
1        abc    100     10         
2        abc    100     50         
3        abc    100     20         
1        xyz    150     10          
1        qwe    200     10          

我需要以下格式的结果。

Table X             
Seq_no    A    Claim    Payment     Balance (to be calculated)
1        abc    100     10          90 (100-10)
2        abc            50          40 (90-50)
3        abc            20          20 (40-20)
1        xyz    150     10          140 (150-10)
1        qwe    200     10          190 (200-10)

已完成Seq_no计算,以使索赔列为重复索赔的情况为空,我已经想到了这一点。

3 个答案:

答案 0 :(得分:2)

在创建之前,您无法参考平衡。

您可以使用“运行总和”来实现您想要达到的目标。

请注意,我用A进行了分区,因为您希望每个A都有另一个余额。

Select
    Seq_no, a, Claim, Payment, 
    sum(nvl(claim,0) - payment) over (partition by A order by seq_no) as Balance
from X;

结果:

SEQ_NO  A   CLAIM   PAYMENT BALANCE
1       abc 100     10      90
2       abc         50      40
3       abc         20      20
1       qwe 200     10      190
1       xyz 150     10      140

编辑:对于较新的数据集,您只需要在seq = 1时将nvl函数替换为大小写:

Select
    Seq_no, 
    a, 
    case when seq_no=1 then claim else 0 end as Claim, 
    Payment, 
    sum(case when seq_no=1 then claim else 0 end - payment) 
          over (partition by A order by seq_no) as Balance
from X;

答案 1 :(得分:2)

要将balance用作列标识符,您必须深入一级,即将现有查询作为子查询

工作演示:

SQL> WITH sample_data AS(
  2  SELECT 1 Seq_no, 'abc' A, 100 Claim, 10 Payment FROM dual UNION ALL
  3  SELECT 2 Seq_no, 'abc' A, NULL Claim, 50 FROM dual UNION ALL
  4  SELECT 3 Seq_no, 'abc' A, NULL Claim, 20 FROM dual UNION ALL
  5  SELECT 1 Seq_no, 'xyz' A, 150 Claim, 10 FROM dual UNION ALL
  6  SELECT 1 Seq_no, 'qwe' A, 200 Claim, 10 FROM dual
  7  )
  8  -- end of sample_data mimicking real table
  9  SELECT seq_no, A, claim, payment,
 10    CASE
 11      WHEN lag(balance) OVER(PARTITION BY A ORDER BY seq_no) IS NULL
 12      THEN balance
 13      ELSE lag(balance) OVER(PARTITION BY A ORDER BY seq_no) - payment
 14    END balance
 15  FROM
 16    (SELECT seq_no, A, claim, payment,
 17      CASE
 18        WHEN seq_no = 1
 19        THEN claim     - payment
 20        ELSE lag(claim - payment) OVER(PARTITION BY A ORDER BY seq_no) - payment
 21      END balance
 22    FROM sample_data
 23    );

    SEQ_NO A        CLAIM    PAYMENT    BALANCE
---------- --- ---------- ---------- ----------
         1 abc        100         10         90
         2 abc                    50         40
         3 abc                    20         20
         1 qwe        200         10        190
         1 xyz        150         10        140

SQL>

UPDATE :如果您的claim值始终不为null,那么您可以执行运行差异:

SQL> WITH sample_data AS(
  2    SELECT 1 Seq_no, 'abc' A, 100 Claim, 10 Payment FROM dual UNION ALL
  3    SELECT 2 Seq_no, 'abc' A, 100 Claim, 50 FROM dual UNION ALL
  4    SELECT 3 Seq_no, 'abc' A, 100 Claim, 20 FROM dual UNION ALL
  5    SELECT 4 Seq_no, 'abc' A, 100 Claim, 10 FROM dual UNION ALL
  6    SELECT 5 Seq_no, 'abc' A, 100 Claim, 10 FROM dual UNION ALL
  7    SELECT 1 Seq_no, 'xyz' A, 150 Claim, 10 FROM dual UNION ALL
  8    SELECT 1 Seq_no, 'qwe' A, 200 Claim, 10 FROM dual
  9    )
 10  -- end of sample_data mimicking real table
 11  SELECT Seq_no,
 12    a,
 13    Claim,
 14    Payment,
 15    CASE
 16      WHEN seq_no = 1
 17      THEN claim     - payment
 18      ELSE SUM(claim - payment) over (partition BY A order by seq_no) - claim*(seq_no-1)
 19    END balance
 20  FROM sample_data;

    SEQ_NO A        CLAIM    PAYMENT    BALANCE
---------- --- ---------- ---------- ----------
         1 abc        100         10         90
         2 abc        100         50         40
         3 abc        100         20         20
         4 abc        100         10         10
         5 abc        100         10          0
         1 qwe        200         10        190
         1 xyz        150         10        140

7 rows selected.

SQL>

答案 2 :(得分:-1)

发生错误是正确的,因为Balance不是表中的列。您需要添加该列。

更大的问题是,遗憾的是,使用单个SQL语句计算运行总计是不可能的。 SQL不太适合这样的操作。它可以使用游标在存储过程中完成,但最终结果是程序性的而不是基于集合的,因此性能会很差。以下是如何在SQL Server中执行此操作(作为示例,未经测试):

DECLARE PaymentCursor CURSOR FOR SELECT A, Claim, Payment FROM X ORDER BY A, Seq_no
OPEN PaymentCursor

DECLARE @A VARCHAR(16)
DECLARE @Claim MONEY
DECLARE @Payment MONEY
DECLARE @Balance MONEY
DECLARE @PreviousA VARCHAR(16)

SET @PreviousA = ''

FETCH NEXT FROM PaymentCursor INTO @A, @Claim, @Payment
WHILE @@FETCH_STATUS = 0
BEGIN
    IF @A <> @PreviousA
    BEGIN
        SET @Balance = 0
        SET @PreviousA = @A
    END

    SET @Balance = @Balance + @Claim - @Payment

    UPDATE X SET Balance = @Balance WHERE CURRENT OF PaymentCursor

    FETCH NEXT FROM PaymentCursor INTO @A, @Claim, @Payment
END

CLOSE PaymentCursor
DEALLOCATE PaymentCursor

如果您的应用程序确实需要知道与每个事务关联的帐户余额,则更好的方法是每次插入行时计算余额。将插入逻辑包装在存储过程中:

CREATE PROCEDURE InsertAndComputeBalance
    @A VARCHAR(16),
    @Claim MONEY,
    @Payment MONEY
AS
    DECLARE @MAX_Seq_no INTEGER = (SELECT MAX(Seq_no) FROM X WHERE A = @A)

    INSERT INTO X
    SELECT @MAX_Seq_no + 1, @A, @Claim, @Payment, Balance + @Claim - @Payment FROM X
    WHERE A = @A AND Seq_no = @MAX_Seq_no

理论上你可以从一个触发器调用类似的逻辑,这样每次插入一行时都会自动执行,但触发器可能是邪恶的,所以要非常小心地使用这种方法。