Select Duplicated payments and invoices - sql

Few months ago we had a bug with our payments.
What happened was our system somehow made duplicated payments from salaries and invoices and sent them to bank
Salaries have the same user_salary_id in 2 different rows if they are duplicated, and invoice payments happened 2 times with different ref_id
Total of 4 salaries paid should only be 2
View from v_user_ledger_salary
user_profile_id
user_salary_id
requested_salary
user_ledger_id
1111
32500
144.00
1
1111
32500
144.00
2
2222
32600
96.00
3
2222
32600
96.00
4
2324
32603
245.00
5
45423
32606
978.49
6
11151
32610
38.65
7
20000
32611
100.12
8
But invoices have a different reference number, reference numbers are "neighbors", with the same amount, meaning they are also duplicated
View from v_user_ledger
user_profile_id
ref_type
ref_id
credit
1111
INVOICE_PAYMENT
999
100.00
1111
INVOICE_PAYMENT
1000
100.00
2222
INVOICE_PAYMENT
1049
320.50
2222
INVOICE_PAYMENT
1050
320.50
2324
INVOICE_PAYMENT
1051
666.23
45423
INVOICE_PAYMENT
1052
320.50
11151
INVOICE_PAYMENT
1053
10.64
20000
INVOICE_PAYMENT
1538
1392.93
View of t_invoice
id(same as invoice_id)
biller_id(same as user_profile_id)
234534
1111
235000
2222
235010
2324
235020
45423
235033
11151
235444
20000
View of t_invoice_payment
id(same as ref_id)
invoice_id
amount
999
234534
100.00
1049
235000
320.50
1051
235018
666.23
1052
235025
320.50
1053
235043
10.64
1538
235444
1392.93
What I need is a query that will SELECT * FROM v_user_ledger_salary WHERE rows have same user_salary_id but different user_ledger_id AND user_profile_id in (list)
This should return :
user_profile_id
user_salary_id
requested_salary
user_ledger_id
1111
32500
144.00
1
1111
32500
144.00
2
2222
32600
96.00
3
2222
32600
96.00
4
And from invoices SELECT * FROM v_user_ledger WHERE ref_id IN (SELECT id FROM t_invoice_payment WHERE invoice_id IN(SELECT id FROM t_invoice WHERE biller_id IN (list))) but ref_id needs to have same credit AND "neighbor" of the other ref_id AND user_profile_id IN (list)
This should return :
user_profile_id
ref_type
ref_id
credit
1111
INVOICE_PAYMENT
999
100.00
1111
INVOICE_PAYMENT
1000
100.00
1111
INVOICE_PAYMENT
1049
320.50
1111
INVOICE_PAYMENT
1050
320.50
What I have is the list of user_profile_id 's that might have duplicated salaries/invoices
Tables that are used t_invoice_payment, t_invoice
Views that are used v_user_ledger, v_user_ledger_salary
"Why is this a public question?" I have no idea how to search or in which words to search the answer for my problem, so please if you know how to solve this, or you have a link to other question similar to this one send it to me

Related

T-SQL How do I rollup transactions based on if a field value exists?

I'm using SQL Server 2014. I have a list of financial transactions like in the below table:
PropertyID OccupancyNumber TransCode TransDesc VATCode Date Amount
1234 1111 GRNT Garage Rent GVAT 24/01/20 10.00
1234 1111 GVAT Garage VAT 24/01/20 2.00
1234 1111 RENT RENT 24/01/20 20.00
1234 1111 AMEN Amenity Charge AVAT 23/01/20 100.00
1234 1111 AVAT Amenity VAT 23/01/20 20.00
4567 2222 GRNT Garage Rent GVAT 24/01/20 15.00
4567 2222 GVAT Garage VAT 24/01/20 3.00
4567 2222 RENT RENT 24/01/20 150.00
4567 2222 AMEN Amenity Charge AVAT 23/01/20 200.00
4567 2222 AVAT Amenity VAT 23/01/20 40.00
However, I need to roll them up. If there is a VATCode against a TransCode, I need to add it to the original TransCode and remove the VAT one.
Any VAT charges occur on the same day as the related Transaction.
Also, this is per OccupancyNumber
This is the desired outcome:
PropertyID OccupancyNumber TransCode TransDesc VATCode Date Amount
1234 1111 GRNT Garage Rent GVAT 24/01/20 12.00
1234 1111 RENT RENT 24/01/20 20.00
1234 1111 AMEN Amenity Charge AVAT 23/01/20 120.00
4567 2222 GRNT Garage Rent GVAT 24/01/20 18.00
4567 2222 RENT RENT 24/01/20 150.00
4567 2222 AMEN Amenity Charge AVAT 23/01/20 240.00
How would I achieve this? Is a CTE the way to go?
Thanks.
One idea would be to use a self referencing JOIN and filter out the VAT transactions in the intial data set from Your Table.
This guesses the ON clause, but should be enough to get you there if it's not quite right:
WITH YourTable AS(
SELECT V.PropertyID,
V.OccupancyNumber,
V.TransCode,
V.TransDesc,
NULLIF(V.VATCode,'') AS VATCode,
TRY_CONVERT(date,V.[Date],3) AS [Date], --I hope [date] isn't a varchar, considering you display it in the format dd/MM/yy
V.Amount
FROM (VALUES(1234,1111,'GRNT','GarageRent','GVAT','24/01/20',10.00),
(1234,1111,'GVAT','GarageVAT','','24/01/20',2.00),
(1234,1111,'RENT','RENT','','24/01/20',20.00),
(1234,1111,'AMEN','AmenityCharge','AVAT','23/01/20',100.00),
(1234,1111,'AVAT','AmenityVAT','','23/01/20',20.00),
(4567,2222,'GRNT','GarageRent','GVAT','24/01/20',15.00),
(4567,2222,'GVAT','GarageVAT','','24/01/20',3.00),
(4567,2222,'RENT','RENT','','24/01/20',150.00),
(4567,2222,'AMEN','AmenityCharge','AVAT','23/01/20',200.00),
(4567,2222,'AVAT','AmenityVAT','','23/01/20',40.00))V(PropertyID,OccupancyNumber,TransCode,TransDesc,VATCode,[Date],Amount))
SELECT YT.PropertyID,
YT.OccupancyNumber,
YT.TransCode,
YT.TransDesc,
YT.VATCode,
YT.[Date],
YT.Amount + ISNULL(VAT.Amount,0) AS Amount
FROM YourTable YT
LEFT JOIN YourTable VAT ON YT.PropertyID = VAT.PropertyID
AND YT.VATCode = VAT.TransCode
WHERE YT.TransCode NOT LIKE '_VAT';

Return value in a table based on nearest date in another SQL

I apologize if the title is ambiguous, but I couldn't think of a better way to distill down my objective.
I have two tables: AcctProdHist ph and AcctBalHist bh that I have aliased ph and bh, respectively. AcctProdHist has the product history for an account and its productdate. AcctBalHist contains a history of the account balance by date balance changed balancedate.
I am trying to find the balance in AcctBalHist for the balancedate nearest the productdate in AcctProdHist. There are two situations:
ProductDate is between 2 BalanceDates, so I need the balance for the lesser of the two dates
ProductDate is less than the min(BalanceDate)
Here is my code for combining both tables and getting a full balance history:
select
ph.acctnbr,
ph.product,
ph.productdate,
bh.balancedate,
bh.balance
from AcctProdHist ph, AcctBalHist bh
where ((ph.acctnbr = bh.acctnbr(+)))
and ph.acctnbr in (12345,67890)
Here are the results:
ACCTNBR Product ProductDate BalanceDate Balance
12345 BYBU 7/16/2018 8/1/2018 550
12345 BYBU 7/16/2018 7/31/2018 510
12345 BYBU 7/16/2018 7/12/2018 500
12345 BYBU 7/16/2018 7/11/2018 460
12345 BYBU 7/16/2018 7/2/2018 450
67890 ABAU 7/20/2018 8/5/2018 103
67890 ABAU 7/20/2018 8/1/2018 102
67890 ABAU 7/20/2018 7/31/2018 101
67890 ABAU 7/20/2018 7/22/2018 100
In this case, the Balance I want for ACCTNBR 12345 is 500. The Balance I want for ACCTNBR 67890 is 100.
I'm guessing i'll have to do a UNION query as i'm working with two different situations, but I don't even know where to start.
Thanks!
EDIT: Adding Sample Data from the individual tables.
AcctProdHist
ACCTNBR Product ProductDate InactiveDate
12345 ATRU 5/1/2016 7/16/2018
12345 BYBU 7/16/2018
67890 ABAU 7/20/2018
AcctBalHist
ACCTNBR BalanceDate Balance
12345 1/1/2018 225
12345 2/6/2018 268
12345 4/20/2018 315
12345 6/1/2018 400
12345 7/2/2018 450
12345 7/11/2018 460
12345 7/12/2018 500
12345 7/31/2018 510
12345 8/1/2018 550
67890 7/22/2018 100
67890 7/31/2018 101
67890 8/1/2018 102
You must rank your rows. Use ROW_NUMBER for this, giving the closest balance record #1:
select
acctnbr,
product,
productdate,
balancedate,
balance,
from
(
select
ph.acctnbr,
ph.product,
ph.productdate,
bh.balancedate,
bh.balance,
row_number() over (partition by ph.acctnbr
order by case when ph.productdate >= bh.balancedate then 1 else 2 end,
abs(ph.productdate - bh.balancedate)
) as rn
from AcctProdHist ph
left join AcctBalHist bh on bh.acctnbr = ph.acctnbr
where ph.acctnbr in (12345,67890)
)
where rn = 1
order by acctnbr;

Stored Procedure Select from 3 tables

I have three tables in my database Sales, SalesPeople and Appliances.
Sales
SaleDate EmployeeID AppID Qty
---------- ---------- ----- -----------
2010-01-01 1412 150 1
2010-01-05 3231 110 1
2010-01-03 2920 110 2
2010-01-13 1412 100 1
2010-01-25 1235 150 2
2010-01-22 1235 100 2
2010-01-12 2920 150 3
2010-01-14 3231 100 1
2010-01-15 1235 300 1
2010-01-03 2920 200 2
2010-01-31 2920 310 1
2010-01-05 1412 420 1
2010-01-15 3231 400 2
SalesPeople
EmployeeID EmployeeName CommRate BaseSalary SupervisorID
---------- ------------------------------ ----------- ----------- ------------
1235 Linda Smith 15 1200 1412
1412 Anne Green 12 1800 NULL
2920 Charles Brown 10 1150 1412
3231 Harry Purple 18 1700 1412
Appliances
ID AppType StoreID Cost Price
---- -------------------- ------- ------------- -------------
100 Refrigerator 22 150 250
110 Refrigerator 20 175 300
150 Television 27 225 340
200 Microwave Oven 22 120 180
300 Washer 27 200 325
310 Washer 22 280 400
400 Dryer 20 150 220
420 Dryer 22 240 360
How can I obtain this result? (That displays the profitability of each of the salespeople ordered from the most profitable to the least. Gross is simply the sum of the quantity of items sold multiplied by the price. Commission is calculated from the gross minus the cost of those items (i.e. from
qty*(price-cost)). Net profit is the total profit minus commission.)
Name Gross Commission Net Profit
------------- ----- ---------- ---------
Charles Brown 2380 83.5 751.5
Linda Smith 1505 83.25 471.75
Harry Purple 990 65.7 299.3
Anne Green 950 40.2 294.8
My attempt:
CREATE PROC Profitability AS
SELECT
sp.EmployeeName, (sum(s.Qty) * a.Price) as [Gross],
[Gross] - a.Cost, as [Commision],
SOMETHING as [Net Profit]
FROM
Salespeople sp, Appliances a, Sales s
WHERE
s.AppID = a.ID
AND sp.EmployeeID = s.EmployeeID
GROUP BY
sp.EmployeeName
GO
EXEC Profitability
Simple rule: Never use commas in the FROM clause. Always use explicit JOIN syntax.
In addition to fixing the JOIN syntax, your query needs a few other enhancements for the aggregation functions:
SELECT sp.EmployeeName, sum(s.Qty * a.Price) as Gross,
SUM(s.Qty * (a.Price - a.Cost)) * sp.CommRate / 100.0 as Commission,
SUM(s.Qty * (a.Price - a.Cost)) * (1 - sp.CommRate / 100.0) as NetProfit
FROM Sales s JOIN
Salespeople sp
ON sp.EmployeeID = s.EmployeeID JOIN
Appliances a
ON s.AppID = a.ID
GROUP BY sp.EmployeeName sp.CommRate
ORDER BY NetProfit DESC;

How to select top 4 records of amount from 2 tables [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 8 years ago.
Improve this question
I have 2 tables one contain document detail as below :
Table1 : contains customer and total document amount.
DocEntry CustID CustName City DocAmount
1 GF002 Raffy N London 120.00
2 GF025 Jhon Liverpool 50.00
3 GF120 Keng London 125.25
4 GF055 Tung L. London 30.00
5 GF020 Lee H. Manchester 60.00
Table2 : contains item and item price of each document.
DocEntry LineNum ItemID ItemName ItemPrice Qty LineAmount
1 0 I0001 Mouse 6.00 5 30.00
1 1 I0002 Key Broad 6.00 5 30.00
1 2 I0200 Monitor 60.00 1 60.00
2 0 I0501 Ext.HDD1 50.00 1 50.00
3 0 I0665 Printer 125.00 1 125.00
4 0 I0002 Key Broad 6.00 4 24.00
4 1 I0001 Mouse 6.00 1 6.00
5 0 I0050 ODD 12.00 1 12.00
5 1 I0001 Mouse 6.00 8 48.00
I would like to select the top 3 of documents from Table1 which have highest DocAmount and in the top 3 selected have to show line detail from Table2
the result should be :
Row DocEntry CustID CustName DocAmount ItemID ItemName ItemPrice Qty LineAmount
1 3 GF120 Keng 125.25 I0665 Printer 125.00 1 125.00
2 1 GF002 Raffy N 120.00 I0001 Mouse 6.00 5 30.00
3 1 GF002 Raffy N 120.00 I0002 Key Broad 6.00 5 30.00
4 1 GF002 Raffy N 120.00 I0200 Monitor 60.00 1 60.00
5 5 GF020 Lee H. 60.00 I0050 ODD 12.00 1 12.00
5 5 GF020 Lee H. 60.00 I0001 Mouse 6.00 8 48.00
select Table2.DocEntry, CustID, CustName, DocAmount, ItemID, ItemName,
ItemPrice, Qty, LineAmount
from (select top 3 * from Table1 order by DocAmount desc) TopDocs
join Table2 on TopDocs.DocEntry=Table2.DocEntry
order by DocAmount desc
SQL Fiddle here.

Incremental count in SQL Server 2005

I am working with a Raiser's Edge database using SQL Server 2005. I have written SQL that will produce a temporary table containing details of direct debit instalments. Below is a small table containing the key variables for the question I'm going to ask, with some fictional data:
Donor_ID Instalment_ID Instalment_Date Amount
1234 1111 01/01/2011 £5.00
1234 1112 01/02/2011 £0.00
1234 1113 01/03/2011 £5.00
1234 1114 01/04/2011 £5.00
1234 1115 01/05/2011 £0.00
1234 1116 01/06/2011 £0.00
2345 2111 01/01/2011 £0.00
2345 2112 01/02/2011 £5.00
2345 2113 01/03/2011 £5.00
2345 2114 01/04/2011 £0.00
2345 2115 01/05/2011 £0.00
2345 2116 01/06/2011 £0.00
As you will see, some of the values in the Amount column are £0.00. This can occur when a donor has insufficient funds in their account, for example.
What I'd like to do is write a SQL query that will create a field containing an incremental count of consecutive £0.00 payments that resets after a non-£0.00 payment or after a change in Donor_ID. I have reproduced the above data below, with the field I'd like to see.
Donor_ID Instalment_ID Instalment_Date Amount New_Field
1234 1111 01/01/2011 £5.00
1234 1112 01/02/2011 £0.00 1
1234 1113 01/03/2011 £5.00
1234 1114 01/04/2011 £5.00
1234 1115 01/05/2011 £0.00 1
1234 1116 01/06/2011 £0.00 2
2345 2111 01/01/2011 £0.00 1
2345 2112 01/02/2011 £5.00
2345 2113 01/03/2011 £5.00
2345 2114 01/04/2011 £0.00 1
2345 2115 01/05/2011 £0.00 2
2345 2116 01/06/2011 £0.00 3
To help clarify what I'm looking for, I think what I'm looking to do would be similar to a winning streak field on a list of a football team's results. For example:
Opponent Score Winning_Streak
Arsenal 1-0 1
Liverpool 0-0
Swansea 3-1 1
Chelsea 2-1 2
Fulham 4-0 3
Stoke 0-0
Man Utd 1-3
Reading 2-1 1
I've considered various options, but have made no progress. Unless I've missed something obvious, I think that a solution more advanced than my current SQL programming level might be required.
If I am thinking about this problem correctly, I believe that you want a row number when the Amount is 0.00 pounds.
Select 0 as As InsufficientCount
, Donor_ID
, Installment_ID
, Amount
From [Table]
Where Amount > 0.00
Union
Select Row_Number() Over (Partition By Donor_ID Order By Installment_ID)
, Donor_ID
, Installment_ID
, Amount
From [Table]
Where Amount = 0.00
This union select should only give you 'ranks' where the Amount equals 0.
Am calling your new field streakAmount
ALTER TABLE instalments ADD streakAmount int NULL;
Then, to update the value:
UPDATE instalments
SET streakAmount =
(SELECT
COUNT(*)
FROM
instalments streak
WHERE
streak.donor_id = instalments.donor_id
AND
streak.instalment_date <= instalments.instalment_date
AND
(streak.instalment_date >
-- find previous instalment date, if any exists
COALESCE(
(
SELECT
MAX(instalment_date)
FROM
instalments prev
WHERE
prev.donor_id = instalments.donor_id
AND
prev.amount > 0
AND
prev.instalment_date < instalments.instalment_date
)
-- otherwise min date
, cast('1753-1-1' AS date))
)
)
WHERE
amount = 0;
http://sqlfiddle.com/#!6/a571f/18