Discussion:
SQL help needed: max(trans_date) for each account
(too old to reply)
Cool
2004-04-23 19:44:20 UTC
Permalink
Using SQL, I want to select one record per account number (acct_id) having the latest latest transaction date. I need the entire source record (many columns), not just the max trans_date for each acct_id. Can anyone point me in the right direction?

Thanks a bunch,
Cool
Terjeson, Mark
2004-04-23 20:29:35 UTC
Permalink
Hi cool,


Try this:



data sample;
acct_id = 1; stuff = 'A'; trans_date = '15jan2003'd; output;
acct_id = 1; stuff = 'B'; trans_date = '25feb2003'd; output;
acct_id = 1; stuff = 'C'; trans_date = '14mar2003'd; output;
acct_id = 1; stuff = 'D'; trans_date = '16feb2003'd; output;
acct_id = 1; stuff = 'E'; trans_date = '13jan2003'd; output;
acct_id = 2; stuff = 'A'; trans_date = '22oct2003'd; output;
acct_id = 3; stuff = 'A'; trans_date = '11jan2003'd; output;
acct_id = 3; stuff = 'B'; trans_date = '13sep2003'd; output;
acct_id = 3; stuff = 'C'; trans_date = '19apr2003'd; output;
format trans_date mmddyy10.;
run;


proc sql;
create table result as
select
*
from
sample
group by
acct_id
having
trans_date eq max(trans_date)
order by
acct_id
;
quit;



Hope this is helpful,
Mark Terjeson
Reporting, Analysis, and Procurement Section
Information Services Division
Department of Social and Health Services
State of Washington
360.725.1598 voice
360.725.0615 fax
mailto:***@dshs.wa.gov














-----Original Message-----
From: Cool [mailto:***@ODDPOST.COM]
Sent: Friday, April 23, 2004 12:44 PM
To: SAS-***@LISTSERV.UGA.EDU
Subject: SQL help needed: max(trans_date) for each account

Using SQL, I want to select one record per account number (acct_id) having
the latest latest transaction date. I need the entire source record (many
columns), not just the max trans_date for each acct_id. Can anyone point me
in the right direction?

Thanks a bunch,
Cool
Richard A. DeVenezia
2004-04-24 03:24:17 UTC
Permalink
Post by Cool
Using SQL, I want to select one record per account number (acct_id)
having the latest latest transaction date. I need the entire source
record (many columns), not just the max trans_date for each acct_id.
Can anyone point me in the right direction?
Thanks a bunch,
Cool
Typical transaction systems will have multiple rows on the given latest
transaction date.
What are the criteria for selecting a record when the latest date has more
than one ?
Usually there is satellite information such as time or transaction id that
can be used. In your case, you don't state one, so we will use the
undocumented SQL function monotonic() to obtain a unique sequenced value
that can be applied in a criteria.

data foo;
retain seed 31415926;

do account = 1 to 100;
do date = today()-10 to today()-3*ranuni(seed);
do i = 1 to 10*ranuni(seed);
transid+1;
retain v1-v40 .;
output;
end;
end;
end;
stop;
format date date9.;
drop i seed;
run;

proc sql;
create table bar as
select *, monotonic() as counter
from foo
group by account
having date = max(date) /* an accounts max date might have many records
*/
and counter = max(counter) /* but only one max counter within the
account */
;
quit;
--
Richard A. DeVenezia
http://www.devenezia.com/downloads/sas/macros/
Richard A. DeVenezia
2004-04-26 21:46:17 UTC
Permalink
Post by Richard A. DeVenezia
Post by Cool
Using SQL, I want to select one record per account number (acct_id)
having the latest latest transaction date. I need the entire source
record (many columns), not just the max trans_date for each acct_id.
Can anyone point me in the right direction?
Thanks a bunch,
Cool
Typical transaction systems will have multiple rows on the given
latest transaction date.
What are the criteria for selecting a record when the latest date has
more than one ?
Usually there is satellite information such as time or transaction id
that can be used. In your case, you don't state one, so we will use
the undocumented SQL function monotonic() to obtain a unique
sequenced value that can be applied in a criteria.
...
Post by Richard A. DeVenezia
proc sql;
create table bar as
select *, monotonic() as counter
from foo
group by account
having date = max(date) /* an accounts max date might have many
records */
and counter = max(counter) /* but only one max counter within the
account */
;
quit;
This SQL is correct only when natural order is sorted by account and date.
If the data is disordered the query will return no rows when sorted by
descending date (When highest counter of an account group would correspond
to the oldest date [not the newest date as cool desires])

Here is corrected SQL where a subquery returns the sets of rows having group
account date=max(date) and then from therein is one row selected by the
criteria counter=max(counter) where counter is monotonic()

data foo;
retain seed 31415926;
do account = 1 to 100;
do date = today()-10 to today()-3*ranuni(seed);
do i = 1 to 10*ranuni(seed);
transid+1;
retain v1-v40 .;
output;
end;
end;
end;
stop;
format date date9.;
drop i seed;
run;

* disorder the data;
proc sql;
create table foo as
select * from foo
order by ranuni(1)
;
quit;

* The From sub-query ensures the outer having operates only on
* rows corresponding to max date of an account;
proc sql;
create table bar as
select monotonic() as counter, *
from (select * from foo group by account having date=max(date) ) /*
possibly many trans on max date */
group by account
having counter = max(counter) /* but only one max counter within the account
max trans date group */
;
quit;


proc sort data=foo;
by account descending date ;
run;

* Zero rows! Unexpected but true,
* Sorting by descending date causes max counter to occur on min(date);
proc sql;
create table bar2 as
select *, monotonic() as counter
from foo
group by account
having date = max(date) /* an accounts max date might have many records */
and counter = max(counter) /* but only one max counter within the account */
;
quit;
--
Richard A. DeVenezia
http://www.devenezia.com/downloads/sas/macros/?m=xmlib
Richard Ristow
2004-04-25 04:05:34 UTC
Permalink
Post by Cool
Using SQL, I want to select one record per account number (acct_id)
having the latest latest transaction date. I need the entire source
record (many columns), not just the max trans_date for each acct_id.
Well, the current ethos does seem to be to use SQL wherever possible.
But there's always,

PROC SORT DATA=ACCOUNT.TRANSACT OUT=BY_ID;
BY ACCT_ID DESCENDING TRANS_DATE;
RUN;
/* To select ONE record (unspecified) */
/* on the latest transaction date */
DATA LASTDATE;
SET BY_ID;
BY ACCT_ID DESCENDING TRANS_DATE;
IF FIRST.ACCT_ID THEN OUTPUT;
RUN;
/* To select ALL records on the latest */
/* transaction date */
DATA LASTDATE;
SET BY_ID;
BY ACCT_ID DESCENDING TRANS_DATE;
RETAIN TAKE_IT;
DROP TAKE_IT;
IF FIRST.ACCT_ID THEN TAKE_IT = 1.
ELSE IF FIRST.TRANS_DATE THEN TAKE_IT = 0.
IF TAKE_IT = 1 THEN OUTPUT.
RUN;
Sigurd Hermansen
2004-04-27 13:46:14 UTC
Permalink
Richard:
I believe that this is the same problem that I reported a number of months
ago on SAS-L. The undocumented MONOTONIC() function does not appear in the
documentation for a reason. If invoked in a CREATE VIEW statement or an
in-line view, it seems that the MAX() and MIN() functions do not recognize
its calculated value. If instantiated in a prior CREATE TABLE or a Data
step, the MAX() etc. functions do recognize it.

As you know, assuming ordering by row ID's has its dangers in SQL queries. I
usually attribute the need to fix a row order as a DB design failure. Even
so, it often becomes a practical necessity.
Sig

-----Original Message-----
From: Richard A. DeVenezia [mailto:***@IX.NETCOM.COM]
Sent: Monday, April 26, 2004 5:46 PM
To: SAS-***@LISTSERV.UGA.EDU
Subject: Re: SQL help needed: max(trans_date) for each account
Post by Richard A. DeVenezia
Post by Cool
Using SQL, I want to select one record per account number (acct_id)
having the latest latest transaction date. I need the entire source
record (many columns), not just the max trans_date for each acct_id.
Can anyone point me in the right direction?
Thanks a bunch,
Cool
Typical transaction systems will have multiple rows on the given
latest transaction date. What are the criteria for selecting a record
when the latest date has more than one ?
Usually there is satellite information such as time or transaction id
that can be used. In your case, you don't state one, so we will use
the undocumented SQL function monotonic() to obtain a unique
sequenced value that can be applied in a criteria.
...
Post by Richard A. DeVenezia
proc sql;
create table bar as
select *, monotonic() as counter
from foo
group by account
having date = max(date) /* an accounts max date might have many
records */
and counter = max(counter) /* but only one max counter within the
account */
;
quit;
This SQL is correct only when natural order is sorted by account and date.
If the data is disordered the query will return no rows when sorted by
descending date (When highest counter of an account group would correspond
to the oldest date [not the newest date as cool desires])

Here is corrected SQL where a subquery returns the sets of rows having group
account date=max(date) and then from therein is one row selected by the
criteria counter=max(counter) where counter is monotonic()

data foo;
retain seed 31415926;
do account = 1 to 100;
do date = today()-10 to today()-3*ranuni(seed);
do i = 1 to 10*ranuni(seed);
transid+1;
retain v1-v40 .;
output;
end;
end;
end;
stop;
format date date9.;
drop i seed;
run;

* disorder the data;
proc sql;
create table foo as
select * from foo
order by ranuni(1)
;
quit;

* The From sub-query ensures the outer having operates only on
* rows corresponding to max date of an account;
proc sql;
create table bar as
select monotonic() as counter, *
from (select * from foo group by account having date=max(date) ) /*
possibly many trans on max date */ group by account having counter =
max(counter) /* but only one max counter within the account max trans date
group */ ; quit;


proc sort data=foo;
by account descending date ;
run;

* Zero rows! Unexpected but true,
* Sorting by descending date causes max counter to occur on min(date); proc
sql; create table bar2 as select *, monotonic() as counter from foo group by
account having date = max(date) /* an accounts max date might have many
records */ and counter = max(counter) /* but only one max counter within the
account */ ; quit;


--
Richard A. DeVenezia http://www.devenezia.com/downloads/sas/macros/?m=xmlib
Continue reading on narkive:
Loading...