How can I perform this in a single query - sql

In SQL Server 2008, I have something similar to this:
Accounts:
aaa | 01/01/2010 | 15.00
bbb | 01/01/2010 | 20.00
ccc | 01/01/2010 | 10.00
ddd | 02/01/2010 | 30.00
eee | 04/01/2010 | 25.00
fff | 05/01/2010 | 1.00
Transactions:
aaa | 01/01/2010 | 15.00
aaa | 02/01/2010 | 20.00
aaa | 03/01/2010 | 5.00
bbb | 01/01/2010 | 15.00
bbb | 04/01/2010 | 5.00
ccc | 04/01/2010 | 10.00
ddd | 05/01/2010 | 25.00
I need the results to be something like:
Jan-10 Feb-10 Mar-10 Apr-10 May-10 Accts tot Trans Tot
Jan-10 15 20 5 5 0 45 45
Feb-10 15 0 0 15 0 30 30
Mar-10 0 0 0 0 0 0 0
Apr-10 0 0 0 0 0 25 25
May-10 0 0 0 0 0 0 0
I hope that makes sense... Each cell is the sum of the transactions for accounts created in a certain month. So accounts created in January will have a transaction total each month for the year. The first column is the account date, the row is the transaction date, and the cells are sums anytime an account created in that month has a transaction in the corresponding month.
Right now I do a join on the tables, then loop through and calculate each cell.
The loop is killing my processing time.

I don't think that you'll find a query to return that. Many years ago I was an expert in sqlServer but I've forgot many things about transact-sql, what I did when I had those kind of problems was to create a temporary table with the columns needed based on a query and start inserting the information, using cursors to loop the information. At the end just a "select * from #temp". Hope this idea helps...

Related

Calculation of interest on balance due

I have 3 tables Invoice, Payments and InterestRate. For purposes of simplicity I am representing each of the tables as follows.
The Invoice table has the following columns (the InvNum is the primary key)
+--------------+-------------+---------------+
| InvNum | InvDate | InvAmt |
+--------------+-------------+---------------+
| 123 | 2017-03-09| 15000 |
| 456 | 2017-01-13| 20000 |
+--------------+-------------+---------------+
The Payment table is as follows (the InvNum in the payments table is a foreign key which links to the Invoice table above
+--------------+------------+----------+
| payment_date | InvNum | amount |
+--------------+------------+----------+
| 2017-08-10 | 123 | 5000 |
| 2017-08-15 | 456 | 3000 |
| 2017-09-15 | 123 | 3000 |
| 2017-10-11 | 456 | 4000 |
| 2017-10-16 | 123 | 3500 |
| 2017-11-10 | 123 | 3000 |
| 2017-11-15 | 456 | 2500 |
+--------------+------------+----------+
My InterestRate looks something like this
+--------------+-------+
| ResetDate | IntRate|
+--------------+--------+
| 2016-01-01| 10 |
| 2017-06-08| 10.25 |
| 2017-11-03| 10.67 |
| 2018-05-03| 10.83 |
| 2018-07-29| 10.72 |
| 2019-01-01| 10.67 |
+------------+----------+
What I need is to calculate the interest for each Invoice after considering each payment and the final balance due on the invoice (in case it has not been fully paid). The final table should look something like this
InvNum IntFrom IntTo Amount IntRate Interest Payment Balance
123 2017-03-09 2017-06-07 15000 10 xxx 0 15000
123 2017-06-08 2017-08-10 15000 10.25 yyy 5000 10000
123 2017-08-11 2017-09-15 10000 10.25 zzz 3000 7000
123 2017-09-16 2017-10-16 7000 10.25 aaa 3500 3500
123 2017-10-17 2017-11-02 3500 10.67 bbb 0 3500
123 2017-11-03 2017-11-15 3500 10.67 ccc 2500 1000
123 2017-11-16 2018-05-02 1000 10.67 ddd 0 1000
123 2018-05-03 2018-07-28 1000 10.83 eee 0 1000
123 2018-07-29 2018-12-31 1000 10.72 fff 0 1000
123 2019-01-01 (today) 1000 10.67 ggg 0 1000
I have not shown the results of Inv#456. But the desired solution needs to group each of the invoices in the invoice table.
So in effect the interest calculation takes into account the payments made against each invoice (listed in the Payments table) as well as the changing interest rates (listed in the InterestRate table). Of equal importance is the fact that the interest on the balance due on each invoice (1000 for inv#123) needs to calculated till the date of running the query.
One of the challenges I am also facing is how can I capture the above results in a table. Would it mean dropping and recreating the table every time you run the query (since everytime the date changes the interest amount on the balance due will undergo a change). So unless only the interest on the balance per invoice can be updated, it would (should?) necessarily mean that the table has to be dropped and re-created. Alternatively, the results have to be viewed through a view query.
I hope I have been able to articulate my problem satisfactorily.
Thanks a lot for your time and attention to helping me out

Finding the the difference in day from previous record and partitioning by category

I have the following table:
+----------+------------+----------------+
| Customer | Date | DesiredDayDiff |
+----------+------------+----------------+
| aaa | 12/09/2018 | 0 |
| aaa | 18/09/2018 | 6 |
| aaa | 25/09/2018 | 13 |
| aaa | 27/09/2018 | 15 |
| aaa | 28/09/2018 | 16 |
| bbb | 07/09/2018 | 0 |
| bbb | 11/09/2018 | 4 |
| bbb | 11/09/2018 | 4 |
+----------+------------+----------------+
I need to be able to calculate the difference of day from previous record, for that particular customer.
I believe there is added functionality in SQL server 2012+ that allows some sort of window functioning?? If this can be done using a window function, this would be a bonus as it hopefully, allows my query to be a lot more tidy.
I couldn't find a similar thread where the solution partitions by another category (in this instance above, it's the customer)
If I follow your narrative and diff from the previous row, LAG works for that:
declare #t table (Customer char(3), Date date, DesiredDayDiff int)
insert into #t(Customer,Date,DesiredDayDiff) values
('aaa','20180912',0),
('aaa','20180918',6),
('aaa','20180925',13),
('aaa','20180927',15),
('aaa','20180928',16),
('bbb','20180907',0),
('bbb','20180911',4),
('bbb','20180911',4)
select
*,
COALESCE(DATEDIFF(day,LAG(Date) OVER (PARTITION BY Customer ORDER By Date),Date),0)
from
#t
Results:
Customer Date DesiredDayDiff
-------- ---------- -------------- -----------
aaa 2018-09-12 0 0
aaa 2018-09-18 6 6
aaa 2018-09-25 13 7
aaa 2018-09-27 55 2
aaa 2018-09-28 66 1
bbb 2018-09-07 0 0
bbb 2018-09-11 4 4
bbb 2018-09-11 4 0
To match your "desired" column, I have to use FIRST_VALUE instead.

How to scale creating extra columns in an SQL statement instead of creating extra rows using GROUP BY?

The schema of the database is mentioned here: http://sqlzoo.net/wiki/Guest_House
The request is for each day of the week beginning 2016-11-14 show how many guests are checking out that day by floor number.
The final table should be like this one (the naming of the columns is of little importance of course):
+------------+-----+-----+-----+
| i | 1st | 2nd | 3rd |
+------------+-----+-----+-----+
| 2016-11-14 | 5 | 3 | 4 |
| 2016-11-15 | 6 | 4 | 1 |
| 2016-11-16 | 2 | 2 | 4 |
| 2016-11-17 | 5 | 3 | 6 |
| 2016-11-18 | 2 | 3 | 2 |
| 2016-11-19 | 5 | 5 | 1 |
| 2016-11-20 | 2 | 2 | 2 |
+------------+-----+-----+-----+
In the attempt to implement it using SQL for the middle column (2nd) resulted in this script:
select ADDDATE(booking_date, nights) as checkout, count(distinct guest_id) as '2nd'
from booking
where CAST(room_no as char) like '2%'
and ADDDATE(booking_date, nights) >= '2016-11-14'
group by checkout
order by checkout
LIMIT 7
The issue is that this script produces only one column at a time
A scalable version is this, but this is only per row:
select ADDDATE(booking_date, nights) as checkout,
SUBSTR(CAST(room_no as char), 1, 1) as floor, count(distinct guest_id) as 'guest count'
from booking
where ADDDATE(booking_date, nights) >= '2016-11-14'
group by checkout, floor
order by checkout, floor
LIMIT 21
and the formatted output of this approach is not ideal to be presented:
checkout floor guest count
2016-11-14 1 5
2016-11-14 2 3
2016-11-14 3 4
2016-11-15 1 6
2016-11-15 2 4
2016-11-15 3 1
2016-11-16 1 2
2016-11-16 2 2
2016-11-16 3 4
2016-11-17 1 5
2016-11-17 2 3
2016-11-17 3 6
2016-11-18 1 2
2016-11-18 2 3
2016-11-18 3 2
2016-11-19 1 5
2016-11-19 2 5
2016-11-19 3 1
2016-11-20 1 2
2016-11-20 2 2
2016-11-20 3 2
Twice as many columns versus twice as many rows == not much difference on scaling. And there is a much smaller limit for the number of columns.
Go for more rows, then use pivoting for displaying as columns. (See the [pivot-table] tag)

Updating a field based on multiple criteria

For reference, this isn't a homework assignment, but something I am doing at work. Unfortunately, my SQL skills are not very good, so I'm not sure how to go about doing this. I know there are some great SQL guys here, so I was hoping you could help me out!
At the very least, any guidance on the general structure of the query would be awesome! The primary issue I have is that the heart of this query will have to be run multiple times (ie. A loan has multiple rows that have Payoff = 1)
Thanks!
Suppose you have the following tables:
Payoffs:
P_ID | DebtID (FK) | LoanID | Payoff | PayoffAmount
=====|=============|========|========|=============
1 | 1 | 10 | 1 | 0.00
2 | 2 | 10 | 1 | 0.00
3 | 3 | 27 | 0 | 0.00
4 | 4 | 14 | 1 | 0.00
5 | 5 | 10 | 0 | 0.00
Debts:
DebtID | CurrAccountBal
=======|===============
1 | 2375.00
2 | 1000.00
3 | 2300.00
4 | 2400.00
5 | 500.00
Given a LoanID, if Payoffs.Payoff = "1", then take the amount in Debts.CurrAccountBalance and insert it into Payoffs.PayoffAmount.
This should work in most (if not all) database engines:
UPDATE Payoffs
SET PayoffAmount =
(SELECT CurrAccountBal
FROM Debts
WHERE Payoffs.DebtID = Debts.DebtID
)
WHERE Payoff = 1
AND LoanID = [insert desired LoanID here];

Query to use GROUP BY multiple columns

I have a table full of patients/responsible parties/insurance carrier combinations (e.g. patient Jim Doe's responsible party is parent John Doe who has insurance carrier Aetna Insurance). For each of these combinations, they have a contract that has multiple payments. For this particular table, I need to write a query to find any parent/RP/carrier combo that has multiple contract dates in the same month. Is there anyway to do this?
Example table:
ContPat | ContResp | ContIns | ContDue
------------------------------------------------------
53 | 13 | 27 | 2012-01-01 00:00:00.000
53 | 13 | 27 | 2012-02-01 00:00:00.000
53 | 15 | 27 | 2012-03-01 00:00:00.000
12 | 15 | 3 | 2011-05-01 00:00:00.000
12 | 15 | 3 | 2011-05-01 00:00:00.000
12 | 15 | 3 | 2011-06-01 00:00:00.000
12 | 15 | 3 | 2011-07-01 00:00:00.000
12 | 15 | 3 | 2011-08-01 00:00:00.000
12 | 15 | 3 | 2011-09-01 00:00:00.000
In this example, I would like to generate a list of all the duplicate months for any Patient/RP/Carrier combinations. The 12/15/3 combination would be the only row returned here, but I'm working with thousands of combinations.
Not sure if this is possible using a GROUP BY or similar functions. Thanks in advance for any advice!
If all you care about is multiple entries in the same calendar month:
SELECT
ContPat,
ContResp,
ContIns,
MONTH(ContDue) as Mo,
YEAR(ContDue) as Yr,
COUNT(*) as 'Records'
FROM
MyTable
GROUP BY
ContPat,
ContResp,
ContIns,
MONTH(ContDue),
YEAR(ContDue)
HAVING
COUNT(*) > 1
This will show you any Patient/Responsible Party/Insurer/Calendar month combination with more than one record for that month.