Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex select query for SQL Server

Tags:

sql

sql-server

I have a temp table in SQL server like below

╔═══════╦═════════╦══════╦═════════╗
║Serial ║ Account ║ Due  ║ Balance ║
║1      ║ A1      ║ 1000 ║ 3100    ║
║2      ║ A1      ║ 1000 ║ 3100    ║
║3      ║ A1      ║ 1000 ║ 3100    ║
║4      ║ A1      ║ 1000 ║ 3100    ║
║1      ║ A2      ║ 100  ║ 3100    ║
║2      ║ A2      ║ 100  ║ 3100    ║    
║1      ║ B1      ║ 1000 ║ 1100    ║
║2      ║ B1      ║ 1000 ║ 1100    ║
║1      ║ B2      ║ 100  ║ 1100    ║
║2      ║ B2      ║ 100  ║ 1100    ║
╚═══════╩═════════╩══════╩═════════╝

I want to identify the rows which due would be collected. A1 and A2 Due will be collected from 3100 and B1 and B2 due will be collected from 1100.

Firstly I have used cumulative Due as following

╔═══════╔═════════╦══════╦════════════╦═════════╦
║Serial ║ Account ║ Due  ║ Cumulative ║ Balance ║
║1      ║ A1      ║ 1000 ║  1000      ║    3100 ║
║2      ║ A1      ║ 1000 ║  2000      ║    3100 ║
║3      ║ A1      ║ 1000 ║  3000      ║    3100 ║
║4      ║ A1      ║ 1000 ║  4000      ║    3100 ║
║1      ║ A2      ║ 100  ║   100      ║    3100 ║
║2      ║ A2      ║ 100  ║   200      ║    3100 ║
║1      ║ B1      ║ 1000 ║  1000      ║    1100 ║
║2      ║ B1      ║ 1000 ║  2000      ║    1100 ║
║1      ║ B2      ║ 100  ║   100      ║    1100 ║
║2      ║ B2      ║ 100  ║   200      ║    1100 ║
╚═══════╚═════════╩══════╩════════════╩═════════╝

Now I want to select following rows as output

╔═══════╔═════════╦══════╦════════════╦═════════╦
║Serial ║ Account ║ Due  ║ Cumulative ║ Balance ║  
║1      ║ A1      ║ 1000 ║  1000      ║    3100 ║
║2      ║ A1      ║ 1000 ║  2000      ║    3100 ║ 
║3      ║ A1      ║ 1000 ║  3000      ║    3100 ║  
║1      ║ A2      ║ 100  ║   100      ║    3100 ║
║1      ║ B1      ║ 1000 ║  1000      ║    1100 ║
║1      ║ B2      ║ 100  ║   100      ║    1100 ║
╚═══════╚═════════╩══════╩════════════╩═════════╩

Here is where I am stuck. How can I select those rows without using cursor or loop. All I want to do this with select statement and window functions. Thanks.

Possible Solution: If the table can be updated as following then the problem would be solved.

╔═══════╔═════════╦══════╦═══════════════════╦
║Serial ║ Account ║ Due  ║ Balance Remaining ║
║1      ║ A1      ║ 1000 ║  3100             ║
║2      ║ A1      ║ 1000 ║  2100             ║
║3      ║ A1      ║ 1000 ║  1100             ║
║4      ║ A1      ║ 1000 ║   100             ║
║1      ║ A2      ║ 100  ║   100             ║
║2      ║ A2      ║ 100  ║     0             ║
║1      ║ B1      ║ 1000 ║  1100             ║
║2      ║ B1      ║ 1000 ║   100             ║
║1      ║ B2      ║ 100  ║   100             ║
║2      ║ B2      ║ 100  ║     0             ║
╚═══════╚═════════╩══════╩═══════════════════╩

The cases Balance Remaining is equal/greater than Due we update it with difference else it will remain as before. Problem is here to update rows by partitioning between A & B.

UPDATE I am providing link with new data set to express my requirement more clearly. new dataset

like image 416
Esty Avatar asked Aug 12 '15 04:08

Esty


2 Answers

At last, solved this with update query.

UPDATE A
SET
A.Balance = @Balance
, @PreBalance = @Balance
, @Balance = ( CASE WHEN (@Balance IS NULL OR @AccountType <> A.AccountType)
                    THEN 
                        CASE WHEN A.Balance - A.Due >= 0
                            THEN A.Balance
                            ELSE A.Balance + A.Due
                        END
                    ELSE 
                        CASE WHEN @Balance - A.Due >= 0 AND (@Flag = 1 OR @AccountNO <> A.AccountNO)
                              THEN @Balance
                              ELSE @Balance + A.Due
                         END
                END) - A.Due
, A.FLAG = @Flag
, @AccountNO = CASE WHEN A.Flag = 0 THEN A.AccountNO ELSE 'NoDueFoundForAcc' END
, @Flag = CASE WHEN @AccountType = A.AccountType 
                THEN 
                    CASE WHEN @PreBalance = @Balance 
                            THEN 0 
                            ELSE 1 
                        END
                ELSE 
                    CASE WHEN A.Balance - A.Due >= 0
                            THEN 1
                            ELSE 0 
                        END
                END
, @AccountType = A.AccountType
FROM #tempTable A

SELECT * FROM #tempTable A WHERE A.Flag = 1
like image 191
Esty Avatar answered Nov 15 '22 09:11

Esty


Very simple

select * from account 
where (Balance-(Select sum(ac.Due) from account ac where 
ac.SerialNo<=account.SerialNo and  ac.Account =account.Account )>0)

Update

there is no relation between A1 and A2 that say that balance 3100 is to be share between A1 and A2 and not with B1.

So you have to specify some where that a1 and a2 are on same group
there is suggested option for you
Add group no column in you table and give same no for A1 and A2, other same no for B1 and B2. Then add Priority column that specifies A1 should deduct first due then if balance left a2 will get chance

then query will be

    SELECT          *
    FROM            account
    WHERE
        ( Balance - ( SELECT
                        SUM(ac.Due)
                      FROM
                        account ac
                      WHERE
                        ( ac.GroupNo = account.GroupNo
                          AND ( ( ac.Account = account.Account
                                  AND ( ac.SerialNo <= account.SerialNo )
                                )
                                OR ac.Prioirty < account.Prioirty
                              )
                        )
                    ) > 0 )
like image 34
VISHMAY Avatar answered Nov 15 '22 09:11

VISHMAY