5

I'm trying to do a query on this table:

Id       startdate     enddate       amount
1        2013-01-01   2013-01-31      0.00
2        2013-02-01   2013-02-28      0.00
3        2013-03-01   2013-03-31      245
4        2013-04-01   2013-04-30      529
5        2013-05-01   2013-05-31      0.00
6        2013-06-01   2013-06-30      383
7        2013-07-01   2013-07-31      0.00
8        2013-08-01   2013-08-31      0.00

I want to get the output:

2013-01-01          2013-02-28          0
2013-03-01          2013-06-30          1157
2013-07-01          2013-08-31           0

I wanted to get that result so I would know when money started to come in and when it stopped. I am also interested in the number of months before money started coming in (which explains the first row), and the number of months where money has stopped (which explains why I'm also interested in the 3rd row for July 2013 to Aug 2013).

I know I can use min and max on the dates and sum on amount but I can't figure out how to get the records divided that way.
Thanks!

6
  • 1
    thanks for the edit Mahmoud Gamal. I was trying to ask the question from my android phone. Commented Jul 30, 2013 at 12:56
  • 2
    Based on what do you want to do the grouping? Commented Jul 30, 2013 at 12:56
  • 2
    It looks like you want to group consecutive rows of zero and non-zero, but why would you not separate out the May row? Commented Jul 30, 2013 at 12:58
  • 1
    He's trying to create three slices for the data - (1) no money coming in (2) money coming in (3) no money coming in again. The two periods of drought will bookend the time when money was arriving. Commented Jul 30, 2013 at 12:59
  • based on inclusive dates where the amount is zero and non zero. Commented Jul 30, 2013 at 13:03

6 Answers 6

4
with CT as
(
    select t1.*,
           ( select max(endDate) 
             from t 
             where startDate<t1.StartDate and SIGN(amount)<>SIGN(t1.Amount)
           ) as GroupDate
    from t  as t1
) 
select min(StartDate) as StartDate,
       max(EndDate) as EndDate,
       sum(Amount) as Amount
from CT
group by GroupDate
order by StartDate

SQLFiddle demo

Sign up to request clarification or add additional context in comments.

1 Comment

pretty useful query. Although this one returns records for zeros in between.
2

Here's one idea (and a fiddle to go with it):

;WITH MoneyComingIn AS
(
    SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, 
        SUM(amount) AS amount
    FROM myTable
    WHERE amount > 0
)
SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, 
    SUM(amount) AS amount
FROM myTable
WHERE enddate < (SELECT startdate FROM MoneyComingIn)
UNION ALL
SELECT startdate, enddate, amount
FROM MoneyComingIn
UNION ALL
SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, 
    SUM(amount) AS amount
FROM myTable
WHERE startdate > (SELECT enddate FROM MoneyComingIn)

And a second, without using UNION (fiddle):

SELECT MIN(startdate), MAX(enddate), SUM(amount)
FROM
(
    SELECT startdate, enddate, amount,
    CASE 
        WHEN EXISTS(SELECT 1 
                    FROM myTable b 
                    WHERE b.id>=a.id AND b.amount > 0) THEN
            CASE WHEN EXISTS(SELECT 1 
                             FROM myTable b 
                             WHERE b.id<=a.id AND b.amount > 0) 
                 THEN 2 
                 ELSE 1 
            END
        ELSE 3
    END AS partition_no
    FROM myTable a
) x
GROUP BY partition_no

although I suppose as written it assumes Id are in order. You could substitute this with a ROW_NUMBER() OVER(ORDER BY startdate).

Comments

1

Something like that should do it :

select min(startdate), max(enddate), sum(amount) from paiements
   where enddate   < (select min(startdate) from paiements where amount >0)
union
select min(startdate), max(enddate), sum(amount) from paiements
   where startdate >= (select min(startdate) from paiements where amount >0)
     and enddate   <= (select max(enddate) from paiements where amount >0)
union
select min(startdate), max(enddate), sum(amount) from paiements
   where startdate > (select max(enddate) from paiements where amount >0)

But for this kind of reporting, It's probably more explicit using multiple queries.

Comments

1

This does what you want:

-- determine the three periods
DECLARE @StartMoneyIn INT
DECLARE @EndMoneyIn INT

SELECT @StartMoneyIn = MIN(Id)
FROM [Amounts]
WHERE amount > 0

SELECT @EndMoneyIn = MAX(Id)
FROM [Amounts]
WHERE amount > 0

-- retrieve the amounts
SELECT MIN(startdate) AS startdate, MAX(enddate) AS enddate, SUM(amount) AS amount
FROM [Amounts]
WHERE Id < @StartMoneyIn
UNION
SELECT MIN(startdate), MAX(enddate), SUM(amount)
FROM [Amounts]
WHERE Id >= @StartMoneyIn AND Id <= @EndMoneyIn
UNION
SELECT MIN(startdate), MAX(enddate), SUM(amount)
FROM [Amounts]
WHERE Id > @EndMoneyIn

Comments

0

If all you want to do is to see when money started coming in and when it stopped, this might work for you:

select 
    min(startdate),
    max(enddate),
    sum(amount)
where
    amount > 0

This would not include the periods where there was no money coming in though.

2 Comments

That's just going to give you the first startdate, last enddate and total amount: link
Yes, but as OP stated: "I wanted to get that result so I would know when money started to come in and when it stopped", which this does. Minus the empty periods before and after, that is :)
0

If you don't care about the total in the period, but only want the records where you go from 0 to something and vica versa, you could do something crazy like this:

select *
from MoneyTable mt
where exists ( select *
               from MoneyTable mtTemp
               where mtTemp.enddate = dateadd(day, -1, mt.startDate)
               and mtTemp.amount <> mt.amount
               and mtTemp.amount * mt.amount = 0)

Or if you must include the first record:

select *
from MoneyTable mt
where exists ( select *
               from MoneyTable mtTemp
               where mtTemp.enddate = dateadd(day, -1, mt.startDate)
               and mtTemp.amount <> mt.amount
               and mtTemp.amount * mt.amount = 0 )
or not exists ( select *
                from MoneyTable mtTemp
                where mtTemp.enddate = dateadd(day, -1, mt.startDate))

Sql Fiddle

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.