I have a table of donations, all random amounts but each one is under 10.00

I need to create a cursor that aggregates these into totals, each of which
must be under £500. In manual terms, go through the list, adding up each
donation with a calculator until the next donation would take it over £500,
then put the entry into the cursor then continue through the list. The
resulting cursor might have 8 records in it, each with a total of between
490 and 500. As a cherry on the cake it would be nice if the cursor also
showed how many donations had made up each total.

Is there any way these can be done via SQL - at the moment I am disappearing
down a black hole of do while, skips and exits

Sincerely

Stephen

Re: Totals under 500 using SQL by Rush

Rush
Thu Jun 19 09:48:40 CDT 2008

Stephen Ibbs wrote:
> I have a table of donations, all random amounts but each one is under 10.00
>
> I need to create a cursor that aggregates these into totals, each of which
> must be under £500. In manual terms, go through the list, adding up each
> donation with a calculator until the next donation would take it over £500,
> then put the entry into the cursor then continue through the list. The
> resulting cursor might have 8 records in it, each with a total of between
> 490 and 500. As a cherry on the cake it would be nice if the cursor also
> showed how many donations had made up each total.
>
> Is there any way these can be done via SQL - at the moment I am disappearing
> down a black hole of do while, skips and exits
>
> Sincerely
>
> Stephen
>

Doesn't sound very tough without SQL. This isn't tested, but it looks
good . . .

*---------------------------------------------------------

USE Donations
USE LogFile IN 0

lnTarget = 500

SCAN

lnTotal = 0
lnDonationsCount = 0

SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
lnTotal = lnTotal + Donations.DonationAmount
lnDonationsCount = lnDonationsCount + 1
ENDSCAN

*-- We are now either on the first record of the next batch, or at
EOF().
* In the first case, we back off one to allow for the SCAN skip.
* In the latter case, the EOF() will properly toss us out of the
first SCAN.
IF EOF()
SKIP -1
ENDIF

*-- Only add a log record if we picked up any donations
IF lnDonationsCount > 0
INSERT INTO LogFile;
(DonCount, DonTotal);
VALUES (lnTotal, lnDonationsCount)
ENDIF

ENDSCAN

*-------------------------------------------

- Rush




Re: Totals under 500 using SQL by Stephen

Stephen
Thu Jun 19 10:04:55 CDT 2008

Thanks Rush - I went and had a lie down and started again - and realised
that it was quite simple. The code I have used is:

GO TOP

DO WHILE ! EOF()

dlnAggTotal = 0

DO WHILE dlnAggTotal <=500

IF dlnAggTotal + dlnAmount <=500

dlnAggTotal = dlnAggTotal + dlnAmount

ELSE

EXIT

ENDIF

SKIP

ENDDO

INSERT INTO AggTemp VALUES(etc etc)

ENDDO

I had avoided SCAN/ENDSCAN in this case becuase of its implicit SKIP and I
wanted to be sure of where I was.

I would still be curious to know whether this was possible - and easier -
with a SQL statement or two.

Sincerely

Stephen

"Rush Strong" <rpstrong@gmail.com> wrote in message
news:clu6k.92180$bs3.63597@trnddc07...
> Stephen Ibbs wrote:
>> I have a table of donations, all random amounts but each one is under
>> 10.00
>>
>> I need to create a cursor that aggregates these into totals, each of
>> which must be under £500. In manual terms, go through the list, adding up
>> each donation with a calculator until the next donation would take it
>> over £500, then put the entry into the cursor then continue through the
>> list. The resulting cursor might have 8 records in it, each with a total
>> of between 490 and 500. As a cherry on the cake it would be nice if the
>> cursor also showed how many donations had made up each total.
>>
>> Is there any way these can be done via SQL - at the moment I am
>> disappearing down a black hole of do while, skips and exits
>>
>> Sincerely
>>
>> Stephen
>>
>
> Doesn't sound very tough without SQL. This isn't tested, but it looks
> good . . .
>
> *---------------------------------------------------------
>
> USE Donations
> USE LogFile IN 0
>
> lnTarget = 500
>
> SCAN
>
> lnTotal = 0
> lnDonationsCount = 0
>
> SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
> lnTotal = lnTotal + Donations.DonationAmount
> lnDonationsCount = lnDonationsCount + 1
> ENDSCAN
>
> *-- We are now either on the first record of the next batch, or at
> EOF(). * In the first case, we back off one to allow for the SCAN
> skip.
> * In the latter case, the EOF() will properly toss us out of the
> first SCAN.
> IF EOF()
> SKIP -1
> ENDIF
>
> *-- Only add a log record if we picked up any donations
> IF lnDonationsCount > 0
> INSERT INTO LogFile;
> (DonCount, DonTotal);
> VALUES (lnTotal, lnDonationsCount)
> ENDIF
>
> ENDSCAN
>
> *-------------------------------------------
>
> - Rush
>
>
>



Re: Totals under 500 using SQL by Anders

Anders
Sat Jun 21 13:56:45 CDT 2008

Hi Stephen,
Well it is possible:

CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
FOR i = 1 TO 2
FOR k= 1 TO 30
INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
NEXT
NEXT
GO TOP IN Q1
BROWSE LAST NOWAIT

lnGroupSum = 51
Note: in order to get the groups sum >=50, the integer division must be set
to 51.
SELECT id, date, contrib,;
(SELECT SUM(contrib) FROM Q1 AS Q2 ;
WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
FROM Q1 AS Q1 ;
INTO CURSOR Q2
BROWSE LAST NOWAIT

SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
FROM Q2 GROUP BY id,Grouping ;
INTO CURSOR Q3
BROWSE LAST NOWAIT

This can be rolled into a single query, as of VFP 9.
The part that could be slow with big datasets is the correlated subquery
that returns the accumulated sum.
As it's repeated twice the query could be rewritten to only calculate that
sum once.
I've used a date column to aggregate the sum, but a serial number or even
recno() could do as well.
-Anders


"Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
news:eHtGZ3h0IHA.1768@TK2MSFTNGP03.phx.gbl...
> Thanks Rush - I went and had a lie down and started again - and realised
> that it was quite simple. The code I have used is:
>
> GO TOP
>
> DO WHILE ! EOF()
>
> dlnAggTotal = 0
>
> DO WHILE dlnAggTotal <=500
>
> IF dlnAggTotal + dlnAmount <=500
>
> dlnAggTotal = dlnAggTotal + dlnAmount
>
> ELSE
>
> EXIT
>
> ENDIF
>
> SKIP
>
> ENDDO
>
> INSERT INTO AggTemp VALUES(etc etc)
>
> ENDDO
>
> I had avoided SCAN/ENDSCAN in this case becuase of its implicit SKIP and I
> wanted to be sure of where I was.
>
> I would still be curious to know whether this was possible - and easier -
> with a SQL statement or two.
>
> Sincerely
>
> Stephen
>
> "Rush Strong" <rpstrong@gmail.com> wrote in message
> news:clu6k.92180$bs3.63597@trnddc07...
>> Stephen Ibbs wrote:
>>> I have a table of donations, all random amounts but each one is under
>>> 10.00
>>>
>>> I need to create a cursor that aggregates these into totals, each of
>>> which must be under £500. In manual terms, go through the list, adding
>>> up each donation with a calculator until the next donation would take it
>>> over £500, then put the entry into the cursor then continue through the
>>> list. The resulting cursor might have 8 records in it, each with a total
>>> of between 490 and 500. As a cherry on the cake it would be nice if the
>>> cursor also showed how many donations had made up each total.
>>>
>>> Is there any way these can be done via SQL - at the moment I am
>>> disappearing down a black hole of do while, skips and exits
>>>
>>> Sincerely
>>>
>>> Stephen
>>>
>>
>> Doesn't sound very tough without SQL. This isn't tested, but it looks
>> good . . .
>>
>> *---------------------------------------------------------
>>
>> USE Donations
>> USE LogFile IN 0
>>
>> lnTarget = 500
>>
>> SCAN
>>
>> lnTotal = 0
>> lnDonationsCount = 0
>>
>> SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
>> lnTotal = lnTotal + Donations.DonationAmount
>> lnDonationsCount = lnDonationsCount + 1
>> ENDSCAN
>>
>> *-- We are now either on the first record of the next batch, or at
>> EOF(). * In the first case, we back off one to allow for the SCAN
>> skip.
>> * In the latter case, the EOF() will properly toss us out of the
>> first SCAN.
>> IF EOF()
>> SKIP -1
>> ENDIF
>>
>> *-- Only add a log record if we picked up any donations
>> IF lnDonationsCount > 0
>> INSERT INTO LogFile;
>> (DonCount, DonTotal);
>> VALUES (lnTotal, lnDonationsCount)
>> ENDIF
>>
>> ENDSCAN
>>
>> *-------------------------------------------
>>
>> - Rush
>>
>>
>>
>
>



Re: Totals under 500 using SQL by Anders

Anders
Sun Jun 22 03:18:45 CDT 2008

"Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
news:udoTGtf0IHA.1240@TK2MSFTNGP02.phx.gbl...
>I have a table of donations, all random amounts but each one is under 10.00
>
> I need to create a cursor that aggregates these into totals, each of which
> must be under £500. In manual terms, go through the list, adding up each
> donation with a calculator until the next donation would take it over
> £500, then put the entry into the cursor then continue through the list.
> The resulting cursor might have 8 records in it, each with a total of
> between 490 and 500. As a cherry on the cake it would be nice if the
> cursor also showed how many donations had made up each total.
>
> Is there any way these can be done via SQL - at the moment I am
> disappearing down a black hole of do while, skips and exits
>
> Sincerely
>
> Stephen
>
>
Hi Stephen,
Well it is possible:

CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
FOR i = 1 TO 2
FOR k= 1 TO 30
INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
NEXT
NEXT
GO TOP IN Q1
BROWSE LAST NOWAIT

lnGroupSum = 51
Note: in order to get the groups sum >=50, the integer division must be set
to 51.
SELECT id, date, contrib,;
(SELECT SUM(contrib) FROM Q1 AS Q2 ;
WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
FROM Q1 AS Q1 ;
INTO CURSOR Q2
BROWSE LAST NOWAIT

SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
FROM Q2 GROUP BY id,Grouping ;
INTO CURSOR Q3
BROWSE LAST NOWAIT

This can be rolled into a single query, as of VFP 9.
The part that could be slow with big datasets is the correlated subquery
that returns the accumulated sum.
As it's repeated twice the query could be rewritten to only calculate that
sum once.
I've used a date column to aggregate the sum, but a serial number or even
recno() could do as well.
-Anders



Re: Totals under 500 using SQL by Stephen

Stephen
Sun Jun 22 05:21:35 CDT 2008

awesome - thanks Anders

Stephen

"Anders Altberg" <anders.altberg> wrote in message
news:%2330lqCE1IHA.2084@TK2MSFTNGP06.phx.gbl...
> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
> news:udoTGtf0IHA.1240@TK2MSFTNGP02.phx.gbl...
>>I have a table of donations, all random amounts but each one is under
>>10.00
>>
>> I need to create a cursor that aggregates these into totals, each of
>> which must be under £500. In manual terms, go through the list, adding up
>> each donation with a calculator until the next donation would take it
>> over £500, then put the entry into the cursor then continue through the
>> list. The resulting cursor might have 8 records in it, each with a total
>> of between 490 and 500. As a cherry on the cake it would be nice if the
>> cursor also showed how many donations had made up each total.
>>
>> Is there any way these can be done via SQL - at the moment I am
>> disappearing down a black hole of do while, skips and exits
>>
>> Sincerely
>>
>> Stephen
>>
>>
> Hi Stephen,
> Well it is possible:
>
> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
> FOR i = 1 TO 2
> FOR k= 1 TO 30
> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
> NEXT
> NEXT
> GO TOP IN Q1
> BROWSE LAST NOWAIT
>
> lnGroupSum = 51
> Note: in order to get the groups sum >=50, the integer division must be
> set
> to 51.
> SELECT id, date, contrib,;
> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
> FROM Q1 AS Q1 ;
> INTO CURSOR Q2
> BROWSE LAST NOWAIT
>
> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
> FROM Q2 GROUP BY id,Grouping ;
> INTO CURSOR Q3
> BROWSE LAST NOWAIT
>
> This can be rolled into a single query, as of VFP 9.
> The part that could be slow with big datasets is the correlated subquery
> that returns the accumulated sum.
> As it's repeated twice the query could be rewritten to only calculate that
> sum once.
> I've used a date column to aggregate the sum, but a serial number or even
> recno() could do as well.
> -Anders
>
>
>




Re: Totals under 500 using SQL by Anders

Anders
Sun Jun 22 12:19:05 CDT 2008

I took a while before the solution dawned on me.
BTW, you can put a COUNT(*) into the final query to get the number of
donations in each group.

-Anders

"Stephen Ibbs" <stephen@ibbs.org.uk> wrote in message
news:PIp7k.116347$9x.28031@newsfe05.ams2...
> awesome - thanks Anders
>
> Stephen
>
> "Anders Altberg" <anders.altberg> wrote in message
> news:%2330lqCE1IHA.2084@TK2MSFTNGP06.phx.gbl...
>> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
>> news:udoTGtf0IHA.1240@TK2MSFTNGP02.phx.gbl...
>>>I have a table of donations, all random amounts but each one is under
>>>10.00
>>>
>>> I need to create a cursor that aggregates these into totals, each of
>>> which must be under £500. In manual terms, go through the list, adding
>>> up each donation with a calculator until the next donation would take it
>>> over £500, then put the entry into the cursor then continue through the
>>> list. The resulting cursor might have 8 records in it, each with a total
>>> of between 490 and 500. As a cherry on the cake it would be nice if the
>>> cursor also showed how many donations had made up each total.
>>>
>>> Is there any way these can be done via SQL - at the moment I am
>>> disappearing down a black hole of do while, skips and exits
>>>
>>> Sincerely
>>>
>>> Stephen
>>>
>>>
>> Hi Stephen,
>> Well it is possible:
>>
>> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
>> FOR i = 1 TO 2
>> FOR k= 1 TO 30
>> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
>> NEXT
>> NEXT
>> GO TOP IN Q1
>> BROWSE LAST NOWAIT
>>
>> lnGroupSum = 51
>> Note: in order to get the groups sum >=50, the integer division must be
>> set
>> to 51.
>> SELECT id, date, contrib,;
>> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
>> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
>> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
>> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
>> FROM Q1 AS Q1 ;
>> INTO CURSOR Q2
>> BROWSE LAST NOWAIT
>>
>> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
>> FROM Q2 GROUP BY id,Grouping ;
>> INTO CURSOR Q3
>> BROWSE LAST NOWAIT
>>
>> This can be rolled into a single query, as of VFP 9.
>> The part that could be slow with big datasets is the correlated subquery
>> that returns the accumulated sum.
>> As it's repeated twice the query could be rewritten to only calculate
>> that
>> sum once.
>> I've used a date column to aggregate the sum, but a serial number or
>> even
>> recno() could do as well.
>> -Anders
>>
>>
>>
>
>
>



Re: Totals under 500 using SQL by Leonid

Leonid
Mon Jun 23 02:30:41 CDT 2008

Hi Anders,

Seems that your code doesn't solve the problem. The author wrote "each of
which
must be under 500". Add RAND(1) as a first row to your code and you will see
that the second Sum_contrib will be 56 i.e. more than 50.

Leonid




"Anders Altberg" <anders.altberg> wrote in message
news:eLoI$zD1IHA.4220@TK2MSFTNGP02.phx.gbl...
> Hi Stephen,
> Well it is possible:
>
> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
> FOR i = 1 TO 2
> FOR k= 1 TO 30
> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
> NEXT
> NEXT
> GO TOP IN Q1
> BROWSE LAST NOWAIT
>
> lnGroupSum = 51
> Note: in order to get the groups sum >=50, the integer division must be
> set to 51.
> SELECT id, date, contrib,;
> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
> FROM Q1 AS Q1 ;
> INTO CURSOR Q2
> BROWSE LAST NOWAIT
>
> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
> FROM Q2 GROUP BY id,Grouping ;
> INTO CURSOR Q3
> BROWSE LAST NOWAIT
>
> This can be rolled into a single query, as of VFP 9.
> The part that could be slow with big datasets is the correlated subquery
> that returns the accumulated sum.
> As it's repeated twice the query could be rewritten to only calculate that
> sum once.
> I've used a date column to aggregate the sum, but a serial number or even
> recno() could do as well.
> -Anders
>
>
> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
> news:eHtGZ3h0IHA.1768@TK2MSFTNGP03.phx.gbl...
>> Thanks Rush - I went and had a lie down and started again - and realised
>> that it was quite simple. The code I have used is:
>>
>> GO TOP
>>
>> DO WHILE ! EOF()
>>
>> dlnAggTotal = 0
>>
>> DO WHILE dlnAggTotal <=500
>>
>> IF dlnAggTotal + dlnAmount <=500
>>
>> dlnAggTotal = dlnAggTotal + dlnAmount
>>
>> ELSE
>>
>> EXIT
>>
>> ENDIF
>>
>> SKIP
>>
>> ENDDO
>>
>> INSERT INTO AggTemp VALUES(etc etc)
>>
>> ENDDO
>>
>> I had avoided SCAN/ENDSCAN in this case becuase of its implicit SKIP and
>> I wanted to be sure of where I was.
>>
>> I would still be curious to know whether this was possible - and easier -
>> with a SQL statement or two.
>>
>> Sincerely
>>
>> Stephen
>>
>> "Rush Strong" <rpstrong@gmail.com> wrote in message
>> news:clu6k.92180$bs3.63597@trnddc07...
>>> Stephen Ibbs wrote:
>>>> I have a table of donations, all random amounts but each one is under
>>>> 10.00
>>>>
>>>> I need to create a cursor that aggregates these into totals, each of
>>>> which must be under £500. In manual terms, go through the list, adding
>>>> up each donation with a calculator until the next donation would take
>>>> it over £500, then put the entry into the cursor then continue through
>>>> the list. The resulting cursor might have 8 records in it, each with a
>>>> total of between 490 and 500. As a cherry on the cake it would be nice
>>>> if the cursor also showed how many donations had made up each total.
>>>>
>>>> Is there any way these can be done via SQL - at the moment I am
>>>> disappearing down a black hole of do while, skips and exits
>>>>
>>>> Sincerely
>>>>
>>>> Stephen
>>>>
>>>
>>> Doesn't sound very tough without SQL. This isn't tested, but it looks
>>> good . . .
>>>
>>> *---------------------------------------------------------
>>>
>>> USE Donations
>>> USE LogFile IN 0
>>>
>>> lnTarget = 500
>>>
>>> SCAN
>>>
>>> lnTotal = 0
>>> lnDonationsCount = 0
>>>
>>> SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
>>> lnTotal = lnTotal + Donations.DonationAmount
>>> lnDonationsCount = lnDonationsCount + 1
>>> ENDSCAN
>>>
>>> *-- We are now either on the first record of the next batch, or at
>>> EOF(). * In the first case, we back off one to allow for the SCAN
>>> skip.
>>> * In the latter case, the EOF() will properly toss us out of the
>>> first SCAN.
>>> IF EOF()
>>> SKIP -1
>>> ENDIF
>>>
>>> *-- Only add a log record if we picked up any donations
>>> IF lnDonationsCount > 0
>>> INSERT INTO LogFile;
>>> (DonCount, DonTotal);
>>> VALUES (lnTotal, lnDonationsCount)
>>> ENDIF
>>>
>>> ENDSCAN
>>>
>>> *-------------------------------------------
>>>
>>> - Rush
>>>
>>>
>>>
>>
>>
>
>



Re: Totals under 500 using SQL by Stephen

Stephen
Mon Jun 23 07:25:13 CDT 2008

Hi Anders,

When I run the code I sometimes get a total greater than 50. I think it is
becase:

if group1 totalled 41 and group2 totalled 42 then group3 could be e.g. 54
because the total would be 138 so the integer would still be 2

I am not sure if that makes any sense - but I know what I mean <vbg>

Sincerely

Stephen

************


then the grouping, because
"Anders Altberg" <anders.altberg> wrote in message
news:%23bVzvxI1IHA.3680@TK2MSFTNGP05.phx.gbl...
>I took a while before the solution dawned on me.
> BTW, you can put a COUNT(*) into the final query to get the number of
> donations in each group.
>
> -Anders
>
> "Stephen Ibbs" <stephen@ibbs.org.uk> wrote in message
> news:PIp7k.116347$9x.28031@newsfe05.ams2...
>> awesome - thanks Anders
>>
>> Stephen
>>
>> "Anders Altberg" <anders.altberg> wrote in message
>> news:%2330lqCE1IHA.2084@TK2MSFTNGP06.phx.gbl...
>>> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
>>> news:udoTGtf0IHA.1240@TK2MSFTNGP02.phx.gbl...
>>>>I have a table of donations, all random amounts but each one is under
>>>>10.00
>>>>
>>>> I need to create a cursor that aggregates these into totals, each of
>>>> which must be under £500. In manual terms, go through the list, adding
>>>> up each donation with a calculator until the next donation would take
>>>> it over £500, then put the entry into the cursor then continue through
>>>> the list. The resulting cursor might have 8 records in it, each with a
>>>> total of between 490 and 500. As a cherry on the cake it would be nice
>>>> if the cursor also showed how many donations had made up each total.
>>>>
>>>> Is there any way these can be done via SQL - at the moment I am
>>>> disappearing down a black hole of do while, skips and exits
>>>>
>>>> Sincerely
>>>>
>>>> Stephen
>>>>
>>>>
>>> Hi Stephen,
>>> Well it is possible:
>>>
>>> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
>>> FOR i = 1 TO 2
>>> FOR k= 1 TO 30
>>> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
>>> NEXT
>>> NEXT
>>> GO TOP IN Q1
>>> BROWSE LAST NOWAIT
>>>
>>> lnGroupSum = 51
>>> Note: in order to get the groups sum >=50, the integer division must be
>>> set
>>> to 51.
>>> SELECT id, date, contrib,;
>>> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
>>> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
>>> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
>>> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
>>> FROM Q1 AS Q1 ;
>>> INTO CURSOR Q2
>>> BROWSE LAST NOWAIT
>>>
>>> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
>>> FROM Q2 GROUP BY id,Grouping ;
>>> INTO CURSOR Q3
>>> BROWSE LAST NOWAIT
>>>
>>> This can be rolled into a single query, as of VFP 9.
>>> The part that could be slow with big datasets is the correlated subquery
>>> that returns the accumulated sum.
>>> As it's repeated twice the query could be rewritten to only calculate
>>> that
>>> sum once.
>>> I've used a date column to aggregate the sum, but a serial number or
>>> even
>>> recno() could do as well.
>>> -Anders
>>>
>>>
>>>
>>
>>
>>
>
>



Re: Totals under 500 using SQL by Anders

Anders
Tue Jun 24 08:23:32 CDT 2008

Could be because INT() rounds contrib/M.lnGroupSum down? See if using
CEILING instead of INT solves it.

-Anders

"Leonid" <ctr34 at inbox dot lv> wrote in message
news:etohlEQ1IHA.1240@TK2MSFTNGP02.phx.gbl...
> Hi Anders,
>
> Seems that your code doesn't solve the problem. The author wrote "each of
> which
> must be under 500". Add RAND(1) as a first row to your code and you will
> see that the second Sum_contrib will be 56 i.e. more than 50.
>
> Leonid
>
>
>
>
> "Anders Altberg" <anders.altberg> wrote in message
> news:eLoI$zD1IHA.4220@TK2MSFTNGP02.phx.gbl...
>> Hi Stephen,
>> Well it is possible:
>>
>> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
>> FOR i = 1 TO 2
>> FOR k= 1 TO 30
>> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
>> NEXT
>> NEXT
>> GO TOP IN Q1
>> BROWSE LAST NOWAIT
>>
>> lnGroupSum = 51
>> Note: in order to get the groups sum >=50, the integer division must be
>> set to 51.
>> SELECT id, date, contrib,;
>> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
>> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
>> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
>> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
>> FROM Q1 AS Q1 ;
>> INTO CURSOR Q2
>> BROWSE LAST NOWAIT
>>
>> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
>> FROM Q2 GROUP BY id,Grouping ;
>> INTO CURSOR Q3
>> BROWSE LAST NOWAIT
>>
>> This can be rolled into a single query, as of VFP 9.
>> The part that could be slow with big datasets is the correlated subquery
>> that returns the accumulated sum.
>> As it's repeated twice the query could be rewritten to only calculate
>> that sum once.
>> I've used a date column to aggregate the sum, but a serial number or
>> even recno() could do as well.
>> -Anders
>>
>>
>> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
>> news:eHtGZ3h0IHA.1768@TK2MSFTNGP03.phx.gbl...
>>> Thanks Rush - I went and had a lie down and started again - and realised
>>> that it was quite simple. The code I have used is:
>>>
>>> GO TOP
>>>
>>> DO WHILE ! EOF()
>>>
>>> dlnAggTotal = 0
>>>
>>> DO WHILE dlnAggTotal <=500
>>>
>>> IF dlnAggTotal + dlnAmount <=500
>>>
>>> dlnAggTotal = dlnAggTotal + dlnAmount
>>>
>>> ELSE
>>>
>>> EXIT
>>>
>>> ENDIF
>>>
>>> SKIP
>>>
>>> ENDDO
>>>
>>> INSERT INTO AggTemp VALUES(etc etc)
>>>
>>> ENDDO
>>>
>>> I had avoided SCAN/ENDSCAN in this case becuase of its implicit SKIP and
>>> I wanted to be sure of where I was.
>>>
>>> I would still be curious to know whether this was possible - and
>>> easier - with a SQL statement or two.
>>>
>>> Sincerely
>>>
>>> Stephen
>>>
>>> "Rush Strong" <rpstrong@gmail.com> wrote in message
>>> news:clu6k.92180$bs3.63597@trnddc07...
>>>> Stephen Ibbs wrote:
>>>>> I have a table of donations, all random amounts but each one is under
>>>>> 10.00
>>>>>
>>>>> I need to create a cursor that aggregates these into totals, each of
>>>>> which must be under £500. In manual terms, go through the list, adding
>>>>> up each donation with a calculator until the next donation would take
>>>>> it over £500, then put the entry into the cursor then continue through
>>>>> the list. The resulting cursor might have 8 records in it, each with a
>>>>> total of between 490 and 500. As a cherry on the cake it would be nice
>>>>> if the cursor also showed how many donations had made up each total.
>>>>>
>>>>> Is there any way these can be done via SQL - at the moment I am
>>>>> disappearing down a black hole of do while, skips and exits
>>>>>
>>>>> Sincerely
>>>>>
>>>>> Stephen
>>>>>
>>>>
>>>> Doesn't sound very tough without SQL. This isn't tested, but it looks
>>>> good . . .
>>>>
>>>> *---------------------------------------------------------
>>>>
>>>> USE Donations
>>>> USE LogFile IN 0
>>>>
>>>> lnTarget = 500
>>>>
>>>> SCAN
>>>>
>>>> lnTotal = 0
>>>> lnDonationsCount = 0
>>>>
>>>> SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
>>>> lnTotal = lnTotal + Donations.DonationAmount
>>>> lnDonationsCount = lnDonationsCount + 1
>>>> ENDSCAN
>>>>
>>>> *-- We are now either on the first record of the next batch, or at
>>>> EOF(). * In the first case, we back off one to allow for the
>>>> SCAN skip.
>>>> * In the latter case, the EOF() will properly toss us out of the
>>>> first SCAN.
>>>> IF EOF()
>>>> SKIP -1
>>>> ENDIF
>>>>
>>>> *-- Only add a log record if we picked up any donations
>>>> IF lnDonationsCount > 0
>>>> INSERT INTO LogFile;
>>>> (DonCount, DonTotal);
>>>> VALUES (lnTotal, lnDonationsCount)
>>>> ENDIF
>>>>
>>>> ENDSCAN
>>>>
>>>> *-------------------------------------------
>>>>
>>>> - Rush
>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>



Re: Totals under 500 using SQL by Leonid

Leonid
Wed Jun 25 03:42:31 CDT 2008

No, CEILING will not help. The problem is that SUM(contrib) is not good
enough to distinguish between different Grouping. Let's say we have two
sequences

9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10

and

10, 10, 10, 10, 10, 10, 10, 10, 10, 10

The last members of them have equal SUM(contrib)=100, but they must lie in
different groups: in third for the first and in second for the second. I
doubt that modifying your code this may be accomplished. Actually I doubt
that this may be done with standard ANSI SQL. May be with ANSI 2003, using
windows functions?

Leonid

"Anders Altberg" <anders.altberg> wrote in message
news:ukSUG2f1IHA.528@TK2MSFTNGP02.phx.gbl...
> Could be because INT() rounds contrib/M.lnGroupSum down? See if using
> CEILING instead of INT solves it.
>
> -Anders
>
> "Leonid" <ctr34 at inbox dot lv> wrote in message
> news:etohlEQ1IHA.1240@TK2MSFTNGP02.phx.gbl...
>> Hi Anders,
>>
>> Seems that your code doesn't solve the problem. The author wrote "each of
>> which
>> must be under 500". Add RAND(1) as a first row to your code and you will
>> see that the second Sum_contrib will be 56 i.e. more than 50.
>>
>> Leonid
>>
>>
>>
>>
>> "Anders Altberg" <anders.altberg> wrote in message
>> news:eLoI$zD1IHA.4220@TK2MSFTNGP02.phx.gbl...
>>> Hi Stephen,
>>> Well it is possible:
>>>
>>> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
>>> FOR i = 1 TO 2
>>> FOR k= 1 TO 30
>>> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
>>> NEXT
>>> NEXT
>>> GO TOP IN Q1
>>> BROWSE LAST NOWAIT
>>>
>>> lnGroupSum = 51
>>> Note: in order to get the groups sum >=50, the integer division must be
>>> set to 51.
>>> SELECT id, date, contrib,;
>>> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
>>> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
>>> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
>>> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
>>> FROM Q1 AS Q1 ;
>>> INTO CURSOR Q2
>>> BROWSE LAST NOWAIT
>>>
>>> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
>>> FROM Q2 GROUP BY id,Grouping ;
>>> INTO CURSOR Q3
>>> BROWSE LAST NOWAIT
>>>
>>> This can be rolled into a single query, as of VFP 9.
>>> The part that could be slow with big datasets is the correlated subquery
>>> that returns the accumulated sum.
>>> As it's repeated twice the query could be rewritten to only calculate
>>> that sum once.
>>> I've used a date column to aggregate the sum, but a serial number or
>>> even recno() could do as well.
>>> -Anders
>>>
>>>
>>> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
>>> news:eHtGZ3h0IHA.1768@TK2MSFTNGP03.phx.gbl...
>>>> Thanks Rush - I went and had a lie down and started again - and
>>>> realised that it was quite simple. The code I have used is:
>>>>
>>>> GO TOP
>>>>
>>>> DO WHILE ! EOF()
>>>>
>>>> dlnAggTotal = 0
>>>>
>>>> DO WHILE dlnAggTotal <=500
>>>>
>>>> IF dlnAggTotal + dlnAmount <=500
>>>>
>>>> dlnAggTotal = dlnAggTotal + dlnAmount
>>>>
>>>> ELSE
>>>>
>>>> EXIT
>>>>
>>>> ENDIF
>>>>
>>>> SKIP
>>>>
>>>> ENDDO
>>>>
>>>> INSERT INTO AggTemp VALUES(etc etc)
>>>>
>>>> ENDDO
>>>>
>>>> I had avoided SCAN/ENDSCAN in this case becuase of its implicit SKIP
>>>> and I wanted to be sure of where I was.
>>>>
>>>> I would still be curious to know whether this was possible - and
>>>> easier - with a SQL statement or two.
>>>>
>>>> Sincerely
>>>>
>>>> Stephen
>>>>
>>>> "Rush Strong" <rpstrong@gmail.com> wrote in message
>>>> news:clu6k.92180$bs3.63597@trnddc07...
>>>>> Stephen Ibbs wrote:
>>>>>> I have a table of donations, all random amounts but each one is under
>>>>>> 10.00
>>>>>>
>>>>>> I need to create a cursor that aggregates these into totals, each of
>>>>>> which must be under £500. In manual terms, go through the list,
>>>>>> adding up each donation with a calculator until the next donation
>>>>>> would take it over £500, then put the entry into the cursor then
>>>>>> continue through the list. The resulting cursor might have 8 records
>>>>>> in it, each with a total of between 490 and 500. As a cherry on the
>>>>>> cake it would be nice if the cursor also showed how many donations
>>>>>> had made up each total.
>>>>>>
>>>>>> Is there any way these can be done via SQL - at the moment I am
>>>>>> disappearing down a black hole of do while, skips and exits
>>>>>>
>>>>>> Sincerely
>>>>>>
>>>>>> Stephen
>>>>>>
>>>>>
>>>>> Doesn't sound very tough without SQL. This isn't tested, but it looks
>>>>> good . . .
>>>>>
>>>>> *---------------------------------------------------------
>>>>>
>>>>> USE Donations
>>>>> USE LogFile IN 0
>>>>>
>>>>> lnTarget = 500
>>>>>
>>>>> SCAN
>>>>>
>>>>> lnTotal = 0
>>>>> lnDonationsCount = 0
>>>>>
>>>>> SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
>>>>> lnTotal = lnTotal + Donations.DonationAmount
>>>>> lnDonationsCount = lnDonationsCount + 1
>>>>> ENDSCAN
>>>>>
>>>>> *-- We are now either on the first record of the next batch, or at
>>>>> EOF(). * In the first case, we back off one to allow for the
>>>>> SCAN skip.
>>>>> * In the latter case, the EOF() will properly toss us out of
>>>>> the
>>>>> first SCAN.
>>>>> IF EOF()
>>>>> SKIP -1
>>>>> ENDIF
>>>>>
>>>>> *-- Only add a log record if we picked up any donations
>>>>> IF lnDonationsCount > 0
>>>>> INSERT INTO LogFile;
>>>>> (DonCount, DonTotal);
>>>>> VALUES (lnTotal, lnDonationsCount)
>>>>> ENDIF
>>>>>
>>>>> ENDSCAN
>>>>>
>>>>> *-------------------------------------------
>>>>>
>>>>> - Rush
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>



Re: Totals under 500 using SQL by GaryBrueggeman

GaryBrueggeman
Wed Jun 25 10:49:02 CDT 2008


This may be too much of a kludge but you might be able to use the report
writer with the right print when conditions on a totals variable. You could
report to a text file in the form of a CSV, create a cursor and append from
the created CSV. You should be able to get the values you need with less
programming.

"Stephen Ibbs" wrote:

> Hi Anders,
>
> When I run the code I sometimes get a total greater than 50. I think it is
> becase:
>
> if group1 totalled 41 and group2 totalled 42 then group3 could be e.g. 54
> because the total would be 138 so the integer would still be 2
>
> I am not sure if that makes any sense - but I know what I mean <vbg>
>
> Sincerely
>
> Stephen
>
> ************
>
>
> then the grouping, because
> "Anders Altberg" <anders.altberg> wrote in message
> news:%23bVzvxI1IHA.3680@TK2MSFTNGP05.phx.gbl...
> >I took a while before the solution dawned on me.
> > BTW, you can put a COUNT(*) into the final query to get the number of
> > donations in each group.
> >
> > -Anders
> >
> > "Stephen Ibbs" <stephen@ibbs.org.uk> wrote in message
> > news:PIp7k.116347$9x.28031@newsfe05.ams2...
> >> awesome - thanks Anders
> >>
> >> Stephen
> >>
> >> "Anders Altberg" <anders.altberg> wrote in message
> >> news:%2330lqCE1IHA.2084@TK2MSFTNGP06.phx.gbl...
> >>> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
> >>> news:udoTGtf0IHA.1240@TK2MSFTNGP02.phx.gbl...
> >>>>I have a table of donations, all random amounts but each one is under
> >>>>10.00
> >>>>
> >>>> I need to create a cursor that aggregates these into totals, each of
> >>>> which must be under £500. In manual terms, go through the list, adding
> >>>> up each donation with a calculator until the next donation would take
> >>>> it over £500, then put the entry into the cursor then continue through
> >>>> the list. The resulting cursor might have 8 records in it, each with a
> >>>> total of between 490 and 500. As a cherry on the cake it would be nice
> >>>> if the cursor also showed how many donations had made up each total.
> >>>>
> >>>> Is there any way these can be done via SQL - at the moment I am
> >>>> disappearing down a black hole of do while, skips and exits
> >>>>
> >>>> Sincerely
> >>>>
> >>>> Stephen
> >>>>
> >>>>
> >>> Hi Stephen,
> >>> Well it is possible:
> >>>
> >>> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
> >>> FOR i = 1 TO 2
> >>> FOR k= 1 TO 30
> >>> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
> >>> NEXT
> >>> NEXT
> >>> GO TOP IN Q1
> >>> BROWSE LAST NOWAIT
> >>>
> >>> lnGroupSum = 51
> >>> Note: in order to get the groups sum >=50, the integer division must be
> >>> set
> >>> to 51.
> >>> SELECT id, date, contrib,;
> >>> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
> >>> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
> >>> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
> >>> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
> >>> FROM Q1 AS Q1 ;
> >>> INTO CURSOR Q2
> >>> BROWSE LAST NOWAIT
> >>>
> >>> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
> >>> FROM Q2 GROUP BY id,Grouping ;
> >>> INTO CURSOR Q3
> >>> BROWSE LAST NOWAIT
> >>>
> >>> This can be rolled into a single query, as of VFP 9.
> >>> The part that could be slow with big datasets is the correlated subquery
> >>> that returns the accumulated sum.
> >>> As it's repeated twice the query could be rewritten to only calculate
> >>> that
> >>> sum once.
> >>> I've used a date column to aggregate the sum, but a serial number or
> >>> even
> >>> recno() could do as well.
> >>> -Anders
> >>>
> >>>
> >>>
> >>
> >>
> >>
> >
> >
>
>
>

Re: Totals under 500 using SQL by Anders

Anders
Wed Jun 25 13:47:27 CDT 2008

CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
ZAP IN q1
FOR i = 1 TO 9
INSERT INTO q1 values (1,DATE()+i,9)
ENDFOR
FOR i = 1 TO 11
INSERT INTO q1 values (1,DATE()+9+i,10)
ENDFOR

lnGroupSum = 50
SELECT id, date, contrib,;
(SELECT SUM(contrib) FROM Q1 AS Q2 ;
WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
(SELECT CEILING(SUM(contrib)/M.lnGroupSum) FROM Q1 AS Q2;
WHERE Q2.id=Q1.id AND Q2.date<=q1.date) AS Grouping;
FROM Q1 AS Q1 ;
INTO CURSOR Q2
BROWSE LAST NOWAIT

SELECT id, MIN(date),MAX(date),COUNT(*),SUM(contrib),MAX(total) ;
FROM Q2 GROUP BY id,Grouping ;
INTO CURSOR Q3
BROWSE LAST NOWAIT

Result:
Id Min_date Max_date Cnt Sum_contrib Max_total
12008-06-26 2008-06-30 5 45.00 45.00
12008-07-01 2008-07-05 5 46.00 91.00
12008-07-06 2008-07-10 5 50.00 141.00
12008-07-11 2008-07-15 5 50.00 191.00

Looks right to me.

"Leonid" <ctr34 at inbox dot lv> wrote in message
news:%23adWB2p1IHA.4704@TK2MSFTNGP05.phx.gbl...
> No, CEILING will not help. The problem is that SUM(contrib) is not good
> enough to distinguish between different Grouping. Let's say we have two
> sequences
>
> 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10
>
> and
>
> 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
>
> The last members of them have equal SUM(contrib)=100, but they must lie in
> different groups: in third for the first and in second for the second. I
> doubt that modifying your code this may be accomplished. Actually I doubt
> that this may be done with standard ANSI SQL. May be with ANSI 2003, using
> windows functions?
>
> Leonid
>
> "Anders Altberg" <anders.altberg> wrote in message
> news:ukSUG2f1IHA.528@TK2MSFTNGP02.phx.gbl...
>> Could be because INT() rounds contrib/M.lnGroupSum down? See if using
>> CEILING instead of INT solves it.
>>
>> -Anders
>>
>> "Leonid" <ctr34 at inbox dot lv> wrote in message
>> news:etohlEQ1IHA.1240@TK2MSFTNGP02.phx.gbl...
>>> Hi Anders,
>>>
>>> Seems that your code doesn't solve the problem. The author wrote "each
>>> of which
>>> must be under 500". Add RAND(1) as a first row to your code and you will
>>> see that the second Sum_contrib will be 56 i.e. more than 50.
>>>
>>> Leonid
>>>
>>>
>>>
>>>
>>> "Anders Altberg" <anders.altberg> wrote in message
>>> news:eLoI$zD1IHA.4220@TK2MSFTNGP02.phx.gbl...
>>>> Hi Stephen,
>>>> Well it is possible:
>>>>
>>>> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
>>>> FOR i = 1 TO 2
>>>> FOR k= 1 TO 30
>>>> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
>>>> NEXT
>>>> NEXT
>>>> GO TOP IN Q1
>>>> BROWSE LAST NOWAIT
>>>>
>>>> lnGroupSum = 51
>>>> Note: in order to get the groups sum >=50, the integer division must be
>>>> set to 51.
>>>> SELECT id, date, contrib,;
>>>> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
>>>> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
>>>> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
>>>> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
>>>> FROM Q1 AS Q1 ;
>>>> INTO CURSOR Q2
>>>> BROWSE LAST NOWAIT
>>>>
>>>> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
>>>> FROM Q2 GROUP BY id,Grouping ;
>>>> INTO CURSOR Q3
>>>> BROWSE LAST NOWAIT
>>>>
>>>> This can be rolled into a single query, as of VFP 9.
>>>> The part that could be slow with big datasets is the correlated
>>>> subquery that returns the accumulated sum.
>>>> As it's repeated twice the query could be rewritten to only calculate
>>>> that sum once.
>>>> I've used a date column to aggregate the sum, but a serial number or
>>>> even recno() could do as well.
>>>> -Anders
>>>>
>>>>
>>>> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
>>>> news:eHtGZ3h0IHA.1768@TK2MSFTNGP03.phx.gbl...
>>>>> Thanks Rush - I went and had a lie down and started again - and
>>>>> realised that it was quite simple. The code I have used is:
>>>>>
>>>>> GO TOP
>>>>>
>>>>> DO WHILE ! EOF()
>>>>>
>>>>> dlnAggTotal = 0
>>>>>
>>>>> DO WHILE dlnAggTotal <=500
>>>>>
>>>>> IF dlnAggTotal + dlnAmount <=500
>>>>>
>>>>> dlnAggTotal = dlnAggTotal + dlnAmount
>>>>>
>>>>> ELSE
>>>>>
>>>>> EXIT
>>>>>
>>>>> ENDIF
>>>>>
>>>>> SKIP
>>>>>
>>>>> ENDDO
>>>>>
>>>>> INSERT INTO AggTemp VALUES(etc etc)
>>>>>
>>>>> ENDDO
>>>>>
>>>>> I had avoided SCAN/ENDSCAN in this case becuase of its implicit SKIP
>>>>> and I wanted to be sure of where I was.
>>>>>
>>>>> I would still be curious to know whether this was possible - and
>>>>> easier - with a SQL statement or two.
>>>>>
>>>>> Sincerely
>>>>>
>>>>> Stephen
>>>>>
>>>>> "Rush Strong" <rpstrong@gmail.com> wrote in message
>>>>> news:clu6k.92180$bs3.63597@trnddc07...
>>>>>> Stephen Ibbs wrote:
>>>>>>> I have a table of donations, all random amounts but each one is
>>>>>>> under 10.00
>>>>>>>
>>>>>>> I need to create a cursor that aggregates these into totals, each of
>>>>>>> which must be under £500. In manual terms, go through the list,
>>>>>>> adding up each donation with a calculator until the next donation
>>>>>>> would take it over £500, then put the entry into the cursor then
>>>>>>> continue through the list. The resulting cursor might have 8 records
>>>>>>> in it, each with a total of between 490 and 500. As a cherry on the
>>>>>>> cake it would be nice if the cursor also showed how many donations
>>>>>>> had made up each total.
>>>>>>>
>>>>>>> Is there any way these can be done via SQL - at the moment I am
>>>>>>> disappearing down a black hole of do while, skips and exits
>>>>>>>
>>>>>>> Sincerely
>>>>>>>
>>>>>>> Stephen
>>>>>>>
>>>>>>
>>>>>> Doesn't sound very tough without SQL. This isn't tested, but it
>>>>>> looks good . . .
>>>>>>
>>>>>> *---------------------------------------------------------
>>>>>>
>>>>>> USE Donations
>>>>>> USE LogFile IN 0
>>>>>>
>>>>>> lnTarget = 500
>>>>>>
>>>>>> SCAN
>>>>>>
>>>>>> lnTotal = 0
>>>>>> lnDonationsCount = 0
>>>>>>
>>>>>> SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
>>>>>> lnTotal = lnTotal + Donations.DonationAmount
>>>>>> lnDonationsCount = lnDonationsCount + 1
>>>>>> ENDSCAN
>>>>>>
>>>>>> *-- We are now either on the first record of the next batch, or at
>>>>>> EOF(). * In the first case, we back off one to allow for the
>>>>>> SCAN skip.
>>>>>> * In the latter case, the EOF() will properly toss us out of
>>>>>> the
>>>>>> first SCAN.
>>>>>> IF EOF()
>>>>>> SKIP -1
>>>>>> ENDIF
>>>>>>
>>>>>> *-- Only add a log record if we picked up any donations
>>>>>> IF lnDonationsCount > 0
>>>>>> INSERT INTO LogFile;
>>>>>> (DonCount, DonTotal);
>>>>>> VALUES (lnTotal, lnDonationsCount)
>>>>>> ENDIF
>>>>>>
>>>>>> ENDSCAN
>>>>>>
>>>>>> *-------------------------------------------
>>>>>>
>>>>>> - Rush
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>



Re: Totals under 500 using SQL by Leonid

Leonid
Wed Jun 25 22:43:19 CDT 2008

Yes, but

CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
ZAP IN q1
FOR i = 1 TO 10
INSERT INTO q1 values (1,DATE()+i,9)
ENDFOR
FOR i = 1 TO 11
INSERT INTO q1 values (1,DATE()+9+i,10)
ENDFOR

lnGroupSum = 50
SELECT id, date, contrib,;
(SELECT SUM(contrib) FROM Q1 AS Q2 ;
WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
(SELECT CEILING(SUM(contrib)/M.lnGroupSum) FROM Q1 AS Q2;
WHERE Q2.id=Q1.id AND Q2.date<=q1.date) AS Grouping;
FROM Q1 AS Q1 ;
INTO CURSOR Q2
BROWSE LAST NOWAIT

SELECT id, MIN(date),MAX(date),COUNT(*),SUM(contrib),MAX(total) ;
FROM Q2 GROUP BY id,Grouping ;
INTO CURSOR Q3
BROWSE LAST NOWAIT

Result:
Id Min_date Max_date Cnt Sum_contrib
Max_total
12008-06-26 2008-06-30 5 45.00 45.00
12008-07-01 2008-07-05 6 55.00 100.00
12008-07-06 2008-07-10 5 50.00 150.00
12008-07-11 2008-07-15 5 50.00 200.00

Looks wrong to me.


"Anders Altberg" <anders.altberg> wrote in message
news:%23Y1ajbw1IHA.5560@TK2MSFTNGP02.phx.gbl...
> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
> ZAP IN q1
> FOR i = 1 TO 9
> INSERT INTO q1 values (1,DATE()+i,9)
> ENDFOR
> FOR i = 1 TO 11
> INSERT INTO q1 values (1,DATE()+9+i,10)
> ENDFOR
>
> lnGroupSum = 50
> SELECT id, date, contrib,;
> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
> (SELECT CEILING(SUM(contrib)/M.lnGroupSum) FROM Q1 AS Q2;
> WHERE Q2.id=Q1.id AND Q2.date<=q1.date) AS Grouping;
> FROM Q1 AS Q1 ;
> INTO CURSOR Q2
> BROWSE LAST NOWAIT
>
> SELECT id, MIN(date),MAX(date),COUNT(*),SUM(contrib),MAX(total) ;
> FROM Q2 GROUP BY id,Grouping ;
> INTO CURSOR Q3
> BROWSE LAST NOWAIT
>
> Result:
> Id Min_date Max_date Cnt Sum_contrib
> Max_total
> 12008-06-26 2008-06-30 5 45.00 45.00
> 12008-07-01 2008-07-05 5 46.00 91.00
> 12008-07-06 2008-07-10 5 50.00 141.00
> 12008-07-11 2008-07-15 5 50.00 191.00
>
> Looks right to me.
>
> "Leonid" <ctr34 at inbox dot lv> wrote in message
> news:%23adWB2p1IHA.4704@TK2MSFTNGP05.phx.gbl...
>> No, CEILING will not help. The problem is that SUM(contrib) is not good
>> enough to distinguish between different Grouping. Let's say we have two
>> sequences
>>
>> 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10
>>
>> and
>>
>> 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
>>
>> The last members of them have equal SUM(contrib)=100, but they must lie
>> in different groups: in third for the first and in second for the second.
>> I doubt that modifying your code this may be accomplished. Actually I
>> doubt that this may be done with standard ANSI SQL. May be with ANSI
>> 2003, using windows functions?
>>
>> Leonid
>>
>> "Anders Altberg" <anders.altberg> wrote in message
>> news:ukSUG2f1IHA.528@TK2MSFTNGP02.phx.gbl...
>>> Could be because INT() rounds contrib/M.lnGroupSum down? See if using
>>> CEILING instead of INT solves it.
>>>
>>> -Anders
>>>
>>> "Leonid" <ctr34 at inbox dot lv> wrote in message
>>> news:etohlEQ1IHA.1240@TK2MSFTNGP02.phx.gbl...
>>>> Hi Anders,
>>>>
>>>> Seems that your code doesn't solve the problem. The author wrote "each
>>>> of which
>>>> must be under 500". Add RAND(1) as a first row to your code and you
>>>> will see that the second Sum_contrib will be 56 i.e. more than 50.
>>>>
>>>> Leonid
>>>>
>>>>
>>>>
>>>>
>>>> "Anders Altberg" <anders.altberg> wrote in message
>>>> news:eLoI$zD1IHA.4220@TK2MSFTNGP02.phx.gbl...
>>>>> Hi Stephen,
>>>>> Well it is possible:
>>>>>
>>>>> CREATE CURSOR Q1 (id Int , date Date, contrib Num(5,2))
>>>>> FOR i = 1 TO 2
>>>>> FOR k= 1 TO 30
>>>>> INSERT INTO Q1 VALUES (i, DATE()+k, INT(1+RAND()*10 ))
>>>>> NEXT
>>>>> NEXT
>>>>> GO TOP IN Q1
>>>>> BROWSE LAST NOWAIT
>>>>>
>>>>> lnGroupSum = 51
>>>>> Note: in order to get the groups sum >=50, the integer division must
>>>>> be set to 51.
>>>>> SELECT id, date, contrib,;
>>>>> (SELECT SUM(contrib) FROM Q1 AS Q2 ;
>>>>> WHERE Q2.id=q1.id AND Q2.date<=q1.date) AS total, ;
>>>>> INT((SELECT SUM(contrib)/M.lnGroupSum FROM Q1 AS Q2;
>>>>> WHERE Q2.id=Q1.id AND Q2.date<=q1.date)) AS Grouping;
>>>>> FROM Q1 AS Q1 ;
>>>>> INTO CURSOR Q2
>>>>> BROWSE LAST NOWAIT
>>>>>
>>>>> SELECT id, MIN(date),MAX(date),SUM(contrib),MAX(total) ;
>>>>> FROM Q2 GROUP BY id,Grouping ;
>>>>> INTO CURSOR Q3
>>>>> BROWSE LAST NOWAIT
>>>>>
>>>>> This can be rolled into a single query, as of VFP 9.
>>>>> The part that could be slow with big datasets is the correlated
>>>>> subquery that returns the accumulated sum.
>>>>> As it's repeated twice the query could be rewritten to only calculate
>>>>> that sum once.
>>>>> I've used a date column to aggregate the sum, but a serial number or
>>>>> even recno() could do as well.
>>>>> -Anders
>>>>>
>>>>>
>>>>> "Stephen Ibbs" <stephen@datadevelopments.co.uk> wrote in message
>>>>> news:eHtGZ3h0IHA.1768@TK2MSFTNGP03.phx.gbl...
>>>>>> Thanks Rush - I went and had a lie down and started again - and
>>>>>> realised that it was quite simple. The code I have used is:
>>>>>>
>>>>>> GO TOP
>>>>>>
>>>>>> DO WHILE ! EOF()
>>>>>>
>>>>>> dlnAggTotal = 0
>>>>>>
>>>>>> DO WHILE dlnAggTotal <=500
>>>>>>
>>>>>> IF dlnAggTotal + dlnAmount <=500
>>>>>>
>>>>>> dlnAggTotal = dlnAggTotal + dlnAmount
>>>>>>
>>>>>> ELSE
>>>>>>
>>>>>> EXIT
>>>>>>
>>>>>> ENDIF
>>>>>>
>>>>>> SKIP
>>>>>>
>>>>>> ENDDO
>>>>>>
>>>>>> INSERT INTO AggTemp VALUES(etc etc)
>>>>>>
>>>>>> ENDDO
>>>>>>
>>>>>> I had avoided SCAN/ENDSCAN in this case becuase of its implicit SKIP
>>>>>> and I wanted to be sure of where I was.
>>>>>>
>>>>>> I would still be curious to know whether this was possible - and
>>>>>> easier - with a SQL statement or two.
>>>>>>
>>>>>> Sincerely
>>>>>>
>>>>>> Stephen
>>>>>>
>>>>>> "Rush Strong" <rpstrong@gmail.com> wrote in message
>>>>>> news:clu6k.92180$bs3.63597@trnddc07...
>>>>>>> Stephen Ibbs wrote:
>>>>>>>> I have a table of donations, all random amounts but each one is
>>>>>>>> under 10.00
>>>>>>>>
>>>>>>>> I need to create a cursor that aggregates these into totals, each
>>>>>>>> of which must be under £500. In manual terms, go through the list,
>>>>>>>> adding up each donation with a calculator until the next donation
>>>>>>>> would take it over £500, then put the entry into the cursor then
>>>>>>>> continue through the list. The resulting cursor might have 8
>>>>>>>> records in it, each with a total of between 490 and 500. As a
>>>>>>>> cherry on the cake it would be nice if the cursor also showed how
>>>>>>>> many donations had made up each total.
>>>>>>>>
>>>>>>>> Is there any way these can be done via SQL - at the moment I am
>>>>>>>> disappearing down a black hole of do while, skips and exits
>>>>>>>>
>>>>>>>> Sincerely
>>>>>>>>
>>>>>>>> Stephen
>>>>>>>>
>>>>>>>
>>>>>>> Doesn't sound very tough without SQL. This isn't tested, but it
>>>>>>> looks good . . .
>>>>>>>
>>>>>>> *---------------------------------------------------------
>>>>>>>
>>>>>>> USE Donations
>>>>>>> USE LogFile IN 0
>>>>>>>
>>>>>>> lnTarget = 500
>>>>>>>
>>>>>>> SCAN
>>>>>>>
>>>>>>> lnTotal = 0
>>>>>>> lnDonationsCount = 0
>>>>>>>
>>>>>>> SCAN WHILE lnTotal + Donations.DonationAmount <= lnTarget
>>>>>>> lnTotal = lnTotal + Donations.DonationAmount
>>>>>>> lnDonationsCount = lnDonationsCount + 1
>>>>>>> ENDSCAN
>>>>>>>
>>>>>>> *-- We are now either on the first record of the next batch, or
>>>>>>> at
>>>>>>> EOF(). * In the first case, we back off one to allow for the
>>>>>>> SCAN skip.
>>>>>>> * In the latter case, the EOF() will properly toss us out of
>>>>>>> the
>>>>>>> first SCAN.
>>>>>>> IF EOF()
>>>>>>> SKIP -1
>>>>>>> ENDIF
>>>>>>>
>>>>>>> *-- Only add a log record if we picked up any donations
>>>>>>> IF lnDonationsCount > 0
>>>>>>> INSERT INTO LogFile;
>>>>>>> (DonCount, DonTotal);
>>>>>>> VALUES (lnTotal, lnDonationsCount)
>>>>>>> ENDIF
>>>>>>>
>>>>>>> ENDSCAN
>>>>>>>
>>>>>>> *-------------------------------------------
>>>>>>>
>>>>>>> - Rush
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>