tags:

views:

90

answers:

5

I have a table that holds monthly data of billing records. so say Customer 1234 was billed in Jan/Feb and Customer 2345 was billing Jan/Feb/Mar. How can I group these to show me a concurrent monthly billing cycle. But also need to have non-concurrent billed months, so Customer 3456 was billed Feb/Apl/Jun/Aug

SELECT custName, month, billed, count(*) as Tally
FROM db_name
WHERE
GROUP BY

Results needed:

Customer 1234 was billed for 2 months Concurrent
Customer 2345 was billed for 3 months Concurrent
Customer 3456 was billed for 4 months Non-Concurrent

Any suggestions?

A: 

how about:

SELECT custName, month, count(*) as tally
from billing
where billed = 1
group by custName, month
sean riley
This is kinda what I need but I also needed multiple WHERE conditions which I think the CASE statement will do, Thanks
Phill Pafford
+1  A: 

If the months were all in a sequence, and we are limiting our search to a particular year then Min(month) + Count(times billed) - 1 should = Max(month).

declare @billing table(Custname varchar(10), month int, billed bit)

insert into @billing values (1234, 1, 1)
insert into @billing values (1234, 2, 1)
insert into @billing values (2345, 3, 1)
insert into @billing values (2345, 4, 1)
insert into @billing values (2345, 5, 1)
insert into @billing values (3456, 1, 1)
insert into @billing values (3456, 3, 1)
insert into @billing values (3456, 9, 1)
insert into @billing values (3456, 10, 1)

Select CustName, Count(1) as MonthsBilled, 
Case 
  when Min(Month) + Count(1) - 1 = Max(Month) 
  then 1 
  else 0 
end Concurrent
From @billing 
where Billed = 1
Group by CustName

Cust   Months   Concurrent 
1234    2        1
2345    3        1
3456    4        0
cmsjr
Thanks this helped
Phill Pafford
+1  A: 

If the month is stored as a datetime field, you can use DATEDIFF to calculate the number of months between the first and the last bill. If the number of elapsed months equals the total number of bills, the bills are consecutive.

select 
 'Customer ' + custname + ' was billed for ' +
 cast(count(*) as varchar) + ' months ' + 
 case 
  when datediff(month,min(billdate),max(billdate))+1 = count(*) 
   then 'Concurrent'
  else 'Non-Concurrent'
 end
from @billing
where billed = 1
group by custname

If you store the billing month as an integer, you can just subtract instead of using DATEDIFF. Replace the WHEN row with:

when max(billdate)-min(billdate)+1 = count(*)

But in that case I wonder how you distinguish between years.

Andomar
Thanks this helped out a lot
Phill Pafford
There's only one bill per month? I missed that completely.
Larry Lustig
Assumed that based on the sample data, you could replace "from @billing" with "from (select custname, billdate from billing group by custname, billdate) sub" if the assumption was incorrect
Andomar
+1  A: 

The suggestions here work based on an assumption that you will never bill a customer twice or more in the same month. If that isn't a safe assumption, you need a different approach. Let us know if that's the case.

Steve Kass
Good point, could be solved with a group by in a subquery
Andomar
You're right, but I don't need to know if they are billed more than once in a month just that they are billed. I thought the GROUP BY would take care of this situation. The query if for some business analytic analysis over a given amount of time.
Phill Pafford
A: 

You left out some important information (like how Month is stored) and what database you're using, but here's a logical approach that you can start with:

CREATE VIEW CustomerBilledInMonth (CustName, Month, AmountBilled, ContinuousFlag) AS
    SELECT CustName, Month, SUM(AmountBilled), 'Noncontinuous'
    FROM BillingTable BT1 
    WHERE NOT EXISTS 
    (SELECT * FROM BillingTable BT2 WHERE BT2.CustName = BT1.CustName AND BT2.Month = BT1.Month - 1) 
    GROUP BY CustName, Month
    UNION
    SELECT CustName, Month, SUM(AmountBilled), 'Continuous'
    FROM BillingTable BT1 
    WHERE EXISTS 
    (SELECT * FROM BillingTable BT2 WHERE BT2.CustName = BT1.CustName AND BT2.Month = BT1.Month - 1) 
    GROUP BY CustName, Month

Assuming that Month here is a consecutive integer field incremented by one from the first possible month in the system, this gives you with each customer's billing for each month summed up, and an additional flag containing 'Continuous' for those months that followed a month in which the customer was also billed and 'Noncontinuous' for those months that followed a month in which the customer was not billed.

Then:

SELECT CustName, LISTOF(Month), SUM(AmountBilled), MAX(ContinuousFlag) 
    FROM CustomerBilledInMonth GROUP BY CustName

will give you more or less what you want (where LISTOF is some kind of COALESCE type function dependent on the exact database you're using).

Larry Lustig
This approach will always list the first month as noncontinuous.
Andomar
Yes, that is true. Among my unstated assumptions was that either the view definition or the second ultimate SELECT would limit the query to less than the full range of months in the database.
Larry Lustig
Yeah I tried the UNION ALL and SUB SELECTS before and I was getting the results I needed but I think the CASE statement is a better approach to what I would like in the end. Thanks for your input
Phill Pafford
@Phil: the CASE solution will work if you are guaranteed to only bill once per month, otherwise two bills in one month and none in another will produce a false "continuous". To avoid that, you'd have to first GROUP BY CustName and Month, then use the CASE solution on that view.
Larry Lustig