Compare Two Different Fields In Oracle SQL - sql

I have a requirement in which I have two main fields Amount CR and Amount DR.
Now the requirement is that this both amounts have different values like Trx Number, Bank Name ETC but have a common Reference Number.
There is only one record for every Refrence Number with a CR Amount, DR Amount respectivly.
For detaila see the table below:
Transaction Number
Bank Name
Reference Number
CR Amount
DR Amount
1
XYZ
1234
1000
2
ABC
1234
1000
3
DEF
1111
1000
4
TEST
1111
2300
So basically I want to compare CR and DR Amount based on the Reference Number. In the example Reference Number 1234 is ok and Reference Number 1111 should be listed.
How can I achieve this by an Oracle query?

Knowing that there is exactly one record with dr and one with cr amount you can make a self join over the reference number.
The 2 Trransactions for a Reference Number will be listed in one row:
select * from table t1
inner join table t2 on t1.referencenumber = t2.referencenumber
and t1.cr_amount is not null
and t2.dr_amount is not null
where t1.cr_amount <> t2.dr_amount

Add two analytical aggregated functions calculating the sum of CRand DB per the reference_number and compare them
case when
sum(cr_amount) over (partition by reference_number) =
sum(dr_amount) over (partition by reference_number) then 'Y' else 'N' end is_equ
This identifies the rows with reference_number where the sum is not equal.
In an additional query simple filter only the rows where the not equal sum.
with test as (
select a.*,
case when
sum(cr_amount) over (partition by reference_number) =
sum(dr_amount) over (partition by reference_number) then 'Y' else 'N' end is_equ
from tab a)
select
TRANSACTION_NUMBER, BANK_NAME, REFERENCE_NUMBER, CR_AMOUNT, DR_AMOUNT
from test
where is_equ = 'N'
TRANSACTION_NUMBER BANK REFERENCE_NUMBER CR_AMOUNT DR_AMOUNT
------------------ ---- ---------------- ---------- ----------
3 DEF 1111 1000
4 TEST 1111 2300

You can aggregate and use a case expression:
select reference_number,
sum(cr_amount), sum(db_amount),
(case when sum(cr_amount) = sum(db_amount)
then 'same' else 'different'
end)
from t
group by reference_number;

Related

For the same ID in one column of SQL, how do I write a Case statement for the row with the greater value in the another column?

In SQL, for the same set of ID, I want to create the column Action with New where the Contract Number is the largest and Old for the smaller values.
For example, for ID = 123, the largest value in Contract Number is 10, so that row will be marked as New and the others as Old. The only columns in the SQL database are ID and Contract Number, so I'm assuming I should use a CASE WHEN statement to create the Action column, but not sure how to formulate it
Sample table:
ID
Contract Number
123
10
123
5
123
3
456
3
456
2
Expected output:
ID
Contract Number
Action
123
10
Keep
123
5
Old
123
3
Old
456
3
Keep
456
2
Old
You could use a case expression together with a windowed aggregate:
select *,
case when
contractnumber = Max(ContractNumber) over(partition by Id) then 'Keep'
else 'Old' end Action
from sampledata;
I like using Common Table Expressions (CTEs) for this
--get the max value
;with CN as (select max(ContractNumber) as MaxContractNumber, ID
FROM [SampleDb].[dbo].[SampleDataforSO]
group by ID,ContractNumber)
--loop through and assign the relevant action item for the max value
select s.ID, s.ContractNumber,
case
when ContractNumber < max(MaxContractNumber) then 'Old'
when ContractNumber = max(MaxContractNumber) then 'Keep'
end as 'Action'
from CN
join [SampleDb].[dbo].[SampleDataforSO] s
on s.ID = cn.ID
group by s.id, ContractNumber
output:

Sum multiple rows in a joined query

I'm not even sure how to ask this. So here goes. I have two tables I am joining, and am needing to sum one column of data, easy enough, but the data that needs to be summed is dependent on a certain character associated with the same job number from a different table.
Table1
JobNumber CostType Amount
1 A 10
1 B 20
1 C 50
1 C 50
3 C 75
Table 2
JobNumber Status Value
1 A 100
2 I 50
3 A 75
Okay, so some of the jobs will have multiple lines for CostType 'C'. I'm trying to display all JobNumbers with the total of any amounts for CostType C, BUT only for jobs that have the Status of 'A'. Here's my query so far:
SELECT Table1.JobNumber
,Table1.Amount
,Table2.Value
FROM DB.Table1, DB.Table2
WHERE Table1.JobNumber = Table2.JobNumber and Table1.CostType = 'C' and Table2.Status = 'A'
GROUP BY Table1.JobNumber, Table1.Amount, Table2.Value
ORDER BY Table1.JobNumber ASC
It's giving me the list of job numbers, their amounts, and the contract value, and only for CostType 'C' and with the Status of 'A'. But each separate CostType 'C' amount has its own row. Is there a way to combine them and display the total Amount along with the Value for each JobNumber, like this?
JobNumber CostTypeCTotal Value
1 100 100
3 75 75
Hmmm . . . Try aggregating table1 before joining the tables:
SELECT t2.JobNumber, t1.c_Amount, t2.Value
FROM DB.Table2 t2
(SELECT JobNumber,
SUM(CASE WHEN CostType = 'C' THEN amount END) as c_amount
FROM DB.Table1 t1
GROUP BY JobNumber
HAVING SUM(CASE WHEN CostType = 'A' THEN 1 ELSE 0 END) > 0
) t1 JOIN
ON t1.JobNumber = t2.JobNumber;
Note: Learn to use proper, explicit, standard, readable JOIN syntax. Never use commas in the FROM clause. Such use of , is archaic syntax that has been out of date since the 1990s.

SQL aggregate rows with same id , specific value in secondary column

I'm looking to filter out rows in the database (PostgreSQL) if one of the values in the status column occurs. The idea is to sum the amount column if the unique reference only has a status equals to 1. The query should not SELECT the reference at all if it has also a status of 2 or any other status for that matter. status refers to the state of the transaction.
Current data table:
reference | amount | status
1 100 1
2 120 1
2 -120 2
3 200 1
3 -200 2
4 450 1
Result:
amount | status
550 1
I've simplified the data example but I think it gives a good idea of what I'm looking for.
I'm unsuccessful in selecting only references that only have status 1.
I've tried sub-queries, using the HAVING clause and other methods without success.
Thanks
Here's a way using not exists to sum all rows where the status is 1 and other rows with the same reference and a non 1 status do not exist.
select sum(amount) from mytable t1
where status = 1
and not exists (
select 1 from mytable t2
where t2.reference = t1.reference
and t2.status <> 1
)
SELECT SUM(amount)
FROM table
WHERE reference NOT IN (
SELECT reference
FROM table
WHERE status<>1
)
The subquery SELECTs all references that must be excluded, then the main query sums everything except them
select sum (amount) as amount
from (
select sum(amount) as amount
from t
group by reference
having not bool_or(status <> 1)
) s;
amount
--------
550
You could use windowed functions to count occurences of status different than 1 per each group:
SELECT SUM(amount) AS amount
FROM (SELECT *,COUNT(*) FILTER(WHERE status<>1) OVER(PARTITION BY reference) cnt
FROM tc) AS sub
WHERE cnt = 0;
Rextester Demo

SQL query Splitting a column into Multiple rows divide by percentage

How to get percentage of a column and then inserting it as rows
Col1 item TotalAmount**
1 ABC 5558767.82
2 ABC 4747605.5
3 ABC 667377.69
4 ABC 3844204
6 CTB 100
7 CTB 500.52
I need to create a new column percentage for each item which is I have done as :-
Select item, (totalAmount/select sum(totalAmount) from table1) as Percentage
From table1
Group by item
Col1 item TotalAmount percentage
1 ABC 5558767.82 38
2 ABC 4747605.5 32
3 ABC 667377.69 5
4 ABC 3844204 26
6 CTB 100 17
7 CTB 500.52 83
Now, the complex part I have to calculate another amount by multiplying this percentage to an amount from another table say table2
ii) update the Total amount column by spilt the total amount column of table 1 into 2 rows – 1st row of the new Calculate PledgeAmount and 2nd row – (totalAmount – PledgeAmount)
*Select t1.percentage * t2.new amount as [PledgeAmount]
From table 1 join table2 where t1.item=t2.item*
. e.g. for col1 Amount of 5558767.82 will split into two rows.
Final Result sample for :-
Col1 item TotalAmount Type
1 ABC 363700.00 Pledge
1 ABC 5195067.82 Unpledge
....
I am using Temporary table to do calculations.
One of the way I think is to calculate the Pledged and Unpledged amount as new column and Pivot it but its huge table with hundreds of columns it will not perform fast.
Any other efficient way?
You can use a windowing function to solve this problem -- first in a sub-query calculate the total and then in the main query the percent:
Select *, (totalAmount/total_for_item)*100 as percent_of_total
from (
SELECT t.*,
SUM(totalAmount) OVER (PARTITION BY item) as total_for_item
FROM table t
) sub
First, let's get the total amount per item:
SELECT item, SUM( totalAmount ) as sumTotal
INTO #totalperitem
FROM table1
GROUP BY item
Now it's easy to get to the percentages:
SELECT t1.Col1,
t1.item,
t1.totalAmount,
t1.totalAmount/tpi.sumTotal*100 AS percentage
FROM table1 t1
INNER JOIN #totalperitem tpi on ...
Tricky part: Separate rows with/without match in table2. Can be done with a WHERE NOT EXISTS, or, my preference, with a single outer join:
SELECT t1.item,
CASE WHEN tpledged.item IS NULL
THEN "Unpledged"
ELSE "Pledged"
END,
SUM( t1.totalAmount ) AS amount
FROM table1 t1
LEFT OUTER JOIN table2 tpledged ON t1. ... = tpledged. ...
GROUP BY t1.item,
CASE WHEN tpledged.item IS NULL
THEN "Unpledged"
ELSE "Pledged"
END
The basic trick is to create an artificial column from the presence/absence of records in table2 and to also group by that artificial column.

DB2 SQL filter query result by evaluating an ID which has two types of entries

After many attempts I have failed at this and hoping someone can help. The query returns every entry a user makes when items are made in the factory against and order number. For example
Order Number Entry type Quantity
3000 1 1000
3000 1 500
3000 2 300
3000 2 100
4000 2 1000
5000 1 1000
What I want to the query do is to return filter the results like this
If the order number has an entry type 1 and 2 return the row which is type 1 only
otherwise just return row whatever the type is for that order number.
So the above would end up:
Order Number Entry type Quantity
3000 1 1000
3000 1 500
4000 2 1000
5000 1 1000
Currently my query (DB2, in very basic terms looks like this ) and was correct until a change request came through!
Select * from bookings where type=1 or type=2
thanks!
select * from bookings
left outer join (
select order_number,
max(case when type=1 then 1 else 0 end) +
max(case when type=2 then 1 else 0 end) as type_1_and_2
from bookings
group by order_number
) has_1_and_2 on
type_1_and_2 = 2
has_1_and_2.order_number = bookings.order_number
where
bookings.type = 1 or
has_1_and_2.order_number is null
Find all the orders that have both type 1 and type 2, and then join it.
If the row matched the join, only return it if it is type 1
If the row did not match the join (has_type_2.order_number is null) return it no matter what the type is.
A "common table expression" [CTE] can often simplify your logic. You can think of it as a way to break a complex problem into conceptual steps. In the example below, you can think of g as the name of the result set of the CTE, which will then be joined to
WITH g as
( SELECT order_number, min(type) as low_type
FROM bookings
GROUP BY order_number
)
SELECT b.*
FROM g
JOIN bookings b ON g.order_number = b.order_number
AND g.low_type = b.type
The JOIN ON conditions will work so that if both types are present then low_type will be 1, and only that type of record will be chosen. If there is only one type it will be identical to low_type.
This should work fine as long as 1 and 2 are the only types allowed in the bookings table. If not then you can simply add a WHERE clause in the CTE and in the outer SELECT.