October 12, 2010 at 1:25 pm
I have 4 tables that contain account information concerning the account, charges, charge details and payments on the account.
For simplicity I have created the 4 tables below and the data that is necessary to see the problem I am having. I am a .Net developer that has been handed the SQL job of retrieving data. I like what I've been seeing with SQL however the cartesian product is giving me fits.
Scripts for creating the tables.
create table test_acc (
account varchar(15) not null ,
code varchar(5)
constraint [PK_test_acc] primary key clustered
(
account)
)
create table test_charges (
charge_number int identity(1,1) not null,
account varchar(15) not null,
cdm varchar(10) not null,
qty int not null
constraint [PK_test_charges] primary key clustered
(
charge_number
)
)
create table test_charges_detail (
charge_number int not null,
cpt4 varchar(5) not null,
amount numeric(10,2)
)
create table test_payments (
account varchar(15) not null,
amt_paid numeric (10,2) not null,
contractual numeric (10,2) not null,
write_off numeric (10,2) not null,
write_off_code varchar(10)
)
Code for loading the data
insert into test_acc (account, code)
select 'c2942020', 'NEW'
insert into test_charges ( account, cdm, qty)
select 'c2942020', '5556633', 2 union all
select 'c2942020', '5557744', 1
insert into test_charges_detail ( charge_number, cpt4, amount)
select 1, '81111', 5.00 union all
select 2, '82222', 3.00 union all
select 2, '83333', 2.00
insert into test_payments (account, amt_paid, contractual, write_off, write_off_code)
select 'c2942020', 3.00, 2.00, 0.00, null union all
select 'c2942020', 3.00, 3.00, 4.00, 'SBal'
I need to be able to get the charges from the test_charges by account, linked to the test_charges_details for the charge amount. The amt_paid, contractual, write_off and write_off_code from the test_payments table.
After several attempts the below is the best solution that I can obtain, and it shows the problem I'm having.
select ta.account, sum(qty*amount) as [Total Charges],
sum(amt_paid) as [Amt Paid],
sum(contractual) as [contractual] , sum (write_off) as [write off], write_off_code
from test_acc ta
inner join test_charges tc on tc.account = ta.account
inner join test_charges_detail tcd on tcd.charge_number = tc.charge_number
inner join test_payments tp on tp.account = ta.account
group by ta.account, write_off_code
giving the following results
accountTotal ChargesAmt Paidcontractualwrite offwrite_off_code
c294202015.009.006.000.00NULL
c294202015.009.009.0012.00SBal
when the results should be
accountTotal ChargesAmt Paidcontractualwrite offwrite_off_code
c2942020 15.00 6.00 5.00 4.00 SBal
I would appreciate any links to articles or help in this matter.
Thank you.
David K.
October 12, 2010 at 2:44 pm
I think this give the correct results, but I'm not too clear on how to handle multiple test_payments rows with non-null write_off_codes for a single account.
with charges as (
select ta.account, sum(qty*amount) as [Total Charges]
from test_acc ta
inner join test_charges tc on tc.account = ta.account
inner join test_charges_detail tcd on tcd.charge_number = tc.charge_number
group by ta.account),
payments as (
select account,
sum(amt_paid) as amt_paid,
sum(contractual) as contractual,
sum(write_off) as write_off,
max(write_off_code) as write_off_code
from test_payments
group by account)
select c.account, c.[Total Charges],
p.amt_paid as [Amt Paid],
p.contractual as [contractual],
p.write_off as [write off],
p.write_off_code
from charges c
inner join payments p on p.account = c.account;
____________________________________________________
Deja View - The strange feeling that somewhere, sometime you've optimised this query before
How to get the best help on a forum
http://www.sqlservercentral.com/articles/Best+Practices/61537October 12, 2010 at 2:56 pm
Hi David,
The problem seems to be that you are grouping by the write off code in the #test_payments table. This forces the query to give a separate row for the same account # if the write off code changes. A null write off code is still a separate grouping than the one with a value.
There are ways around this problem, but we'd need to know this business rule for how you want this to appear. If you only want a single line per account, and want it summarizing all the payments, how do we pick the appropriate write off code to show?
If you don't care about that field (recommended since showing it would likely be misleading), you can just remove it from the select and group by.
If it has to be there you need to give us a rule to explain which write_off_code to choose when an account has 2 payments with different ones.
Another option would be to "pivot" your data by having a column for each write_off_code's total amount.
October 12, 2010 at 3:01 pm
You beat me to it, but I'm posting mine anyhow, because there are some points that I wanted to address.
WITH Charges AS (
SELECT Account, Sum(qty*amount) AS Total_Charges
FROM test_charges AS tc
INNER JOIN test_charges_detail AS tcd ON tc.charge_number = tcd.charge_number
GROUP BY Account
)
, Payments AS (
SELECT Account, Sum(amt_paid) AS Amt_Paid, Sum(write_off) AS write_off, Max(write_off_code) AS write_off_code
-- Your description and sample data/results don't give enough information about what to do with multiple non-null write offs.
FROM test_payments
GROUP BY account
)
SELECT c.account, total_charges, Amt_Paid, write_off, write_off_code
FROM test_acc AS a
LEFT OUTER JOIN Charges AS c
on a.account = c.account
LEFT OUTER JOIN Payments AS p
ON a.account = p.account
WHERE IsNull(c.Account, p.Account) IS NOT NULL
The problem is that you were performing the joins and then trying to summarize and individual charges and individual payments are not correlated, so you need to summarize them separately before performing the join.
The main point that I wanted to make is that in a given time period, you may not have any charges and/or payments, so you need to do a left outer join to the main account table. The where clause filters out records that have neither charges nor payments. You can always leave this off.
Drew
J. Drew Allen
Business Intelligence Analyst
Philadelphia, PA
October 12, 2010 at 3:18 pm
As fate would have it I asked about the multiple write_off_codes, and there can be several. I ran your code on the tables and got exactly what I was looking for, or so I thought. I added another payment with a different write_off_code and again I am lost
Added new record to payments with a differenct write_off_code
insert into test_payments (account, amt_paid, contractual, write_off, write_off_code)
select 'c2942020', 3.00, 3.00, 4.00, 'pBal
got the following
accountTotal ChargesAmt Paidcontractualwrite offwrite_off_code
c294202015.006.005.004.00sBal
I'm sorry that I didn't have all the facts on the front end.
October 13, 2010 at 7:24 am
Thanks for the reply. The main purpose of the query is to find accounts that have a write_off amount. The application will not allow the users to write off an amount without a reason. Any account that does not have a write off amount should not be included in the results. After finding that I could have multiple write off's with different reasons, I have to go back to the drawing board. It is becoming increasingly obvious to me that I am not about to do this without writing a program.
Thanks to all of the individual that help the lost souls that post here, and I am grateful to all that have helped in this.
David K.
October 13, 2010 at 8:25 am
How many different possible write-off amounts are there? And are they fairly static?
October 13, 2010 at 9:20 am
There are 15 write off codes and they are defined in a table. One requires a comment because it is OTHER. So they are pretty static.
thanks.
October 13, 2010 at 11:23 am
One way to accomplish this is to do a crosstab or Pivot on write_off_code.
I just built it in to Mark's code from above
create table test_acc (
account varchar(15) not null ,
code varchar(5)
constraint [PK_test_acc] primary key clustered
(
account)
)
create table test_charges (
charge_number int identity(1,1) not null,
account varchar(15) not null,
cdm varchar(10) not null,
qty int not null
constraint [PK_test_charges] primary key clustered
(
charge_number
)
)
create table test_charges_detail (
charge_number int not null,
cpt4 varchar(5) not null,
amount numeric(10,2)
)
create table test_payments (
account varchar(15) not null,
amt_paid numeric (10,2) not null,
contractual numeric (10,2) not null,
write_off numeric (10,2) not null,
write_off_code varchar(10)
)
insert into test_acc (account, code)
select 'c2942020', 'NEW'
insert into test_charges ( account, cdm, qty)
select 'c2942020', '5556633', 2 union all
select 'c2942020', '5557744', 1
insert into test_charges_detail ( charge_number, cpt4, amount)
select 1, '81111', 5.00 union all
select 2, '82222', 3.00 union all
select 2, '83333', 2.00
insert into test_payments (account, amt_paid, contractual, write_off, write_off_code)
select 'c2942020', 3.00, 2.00, 0.00, null union all
select 'c2942020', 3.00, 3.00, 4.00, 'SBal'
insert into test_payments (account, amt_paid, contractual, write_off, write_off_code)
select 'c2942020', 3.00, 3.00, 2.00, 'pBal'
;
with charges as (
select ta.account, sum(qty*amount) as [Total Charges]
from test_acc ta
inner join test_charges tc on tc.account = ta.account
inner join test_charges_detail tcd on tcd.charge_number = tc.charge_number
group by ta.account),
payments as (
select account,
sum(amt_paid) as amt_paid,
sum(contractual) as contractual,
sum(write_off) as write_off,
(write_off_code) as write_off_code
from test_payments
where write_off>0
group by account, write_off_code)
select account,[Total Charges],[Amt Paid],contractual,[pBal],[SBal],[pBal]+[SBal] AS [Total Write Off]
from
(
select c.account, c.[Total Charges],
p.amt_paid as [Amt Paid],
p.contractual as [contractual],
p.write_off as [write off],
p.write_off_code
from charges c
inner join payments p on p.account = c.account) derived
PIVOT
(SUM([write off])
FOR write_off_code IN ([pBal],[SBal])) AS pvt
Obviously I just added the two sample write_off_code's. You would have to include the other possible ones to get them subtotaled out. I also include a write_off>0 in the CTE since you said you wanted to look only at accounts with write offs. If a negative write off is possible, that would have to change to <>.
The other options are to just arbitrarily pick a code to display and not group by it, or to not even show the code and just sum all the write offs.
October 13, 2010 at 1:28 pm
Wow, after looking at the results of your modification of Mark's code. I believe this is exactly what is needed. the 15 codes are not unmanagable as this could be placed in a view and the dates changed as necessary.
Thanks so much. I'm upside down with joy.
David
:hehe:
Viewing 10 posts - 1 through 9 (of 9 total)
You must be logged in to reply to this topic. Login to reply