Thread: plpgsql locate provider by zip code

plpgsql locate provider by zip code

From
Phill Kenoyer
Date:
I spent the last 15 hours writing this function.  It took me all day to
figure it out.  This is my first postgresql function.

I have a zip code database (the free one).  I wanted to locate providers
by zip code, and also have the distance to each provider.  I had a PHP
script that did this well, but I needed to move it into the database to
make it easy for the IVR system.  This is what I did:

/* My database of zips */
CREATE TABLE "zips" (
   "zip" int8 NOT NULL,
   "state" char(2) DEFAULT 'NA' NOT NULL,
   "city" varchar(50) DEFAULT 'NA' NOT NULL,
   "location" point DEFAULT '(0,0)' NOT NULL -- (longitude,latitude)
);

/* Earthdistance from Postgresql contrib */
CREATE FUNCTION geo_distance (point, point) RETURNS float8
  AS '/usr/local/pgsql/share/contrib/earthdistance.so' LANGUAGE 'c';

CREATE OPERATOR <@> (
  leftarg = point,
  rightarg = point,
  procedure = geo_distance,
  commutator = <@>
);


I figured out this query to select my providers:

/* get my location by zip */
select location from zips where zip=95661;

/* using my location get 10 providers */
select p.name, p.city, p.phone1, '(121.234,38.7346)'::point <@> location as distance
from zips z, providers p
where '(121.234,38.7346)'::point <@> location < 50
and z.zip = p.zip
order by distance
limit 10;

This worked well, now time to move it into the database:

create function locate_providers (int8,int2) returns text as '
declare
 source_zip alias for $1;
 offset alias for $2;
 source_point point;
 result record;
begin
  -- Get the source point by zip
  select into source_point location from zips where zip = source_zip;

  if not found
  then return ''Error: ZIP not found'';
  end if;

  -- Get the providers one at a time by offset
  select into result
    trim(p.name)::text as name, trim(p.address1)::text as address,
    p.phone1::text as phone, trim(p.city)::text as city,
    round(source_point <@> location)::text as distance
  from zips z, providers p
  where source_point <@> location < 50 and z.zip = p.zip
  order by distance
  limit 1, offset;

  -- Return
  if found
    then return result.name || '', '' || result.address || '', '' ||
                result.city || '', '' || result.phone || '', '' ||
                result.distance || '' Miles'';
  end if;

  -- if nothing found
  return ''Error: Provider Not Found'';
end;'
language 'plpgsql';


Now I could issue:

    select locate_providers(95661,0);
    select locate_providers(95661,1);
    select locate_providers(95661,2);
    ...
    select locate_providers(95661,10);

and get back each provider by distance.

It works well for me, probably a few things wrong with it, but I'm a
novice! 8-)  I hope this helps some of the people out there trying to
figure out this stuff.

--
 _   |       _
(_()(|('.|)('||.|()|`|(

Re: plpgsql locate provider by zip code

From
Joel Burton
Date:
On Fri, 27 Apr 2001, Phill Kenoyer wrote:

> I spent the last 15 hours writing this function.  It took me all day to
> figure it out.  This is my first postgresql function.
>
> I have a zip code database (the free one).  I wanted to locate providers
> by zip code, and also have the distance to each provider.  I had a PHP
> script that did this well, but I needed to move it into the database to
> make it easy for the IVR system.  This is what I did:
>
> /* My database of zips */
> CREATE TABLE "zips" (
>    "zip" int8 NOT NULL,
>    "state" char(2) DEFAULT 'NA' NOT NULL,
>    "city" varchar(50) DEFAULT 'NA' NOT NULL,
>    "location" point DEFAULT '(0,0)' NOT NULL -- (longitude,latitude)
> );

Phill --

This looks interesting. I haven't tried to the geographic functions yet,
but this is inspiring me to take a look.

Two thoughts:

1) please submit this (& other plpgsql functions you write!) to the
   cookbook at techdocs.postgresql.org.

2) you might want to consider using a varchar() for zip, rather than
   int. ZIP codes in the northeast US frequently start w/ '0' or '00',
   which would be dropped off the front when you show them. People in
   Mass. frequently find it annoying to get mail to zip code '315', rather
   then 00315. Using VARCHAR() also gives you some breathing room
   if you add non-US postal codes, since practically every country
   uses alphanumeric systems.

Thanks for posting this,
--
Joel Burton   <jburton@scw.org>
Director of Information Systems, Support Center of Washington


Re: Re: plpgsql locate provider by zip code

From
Shay Moreno
Date:
On Fri, 27 Apr 2001, Phill Kenoyer wrote:

> >    "zip" int8 NOT NULL,
>
>    then 00315. Using VARCHAR() also gives you some breathing room
>    if you add non-US postal codes, since practically every country
>    uses alphanumeric systems.

    I couldn't agree more.  It is very annoying not to be able to input
Brazilian zip codes in US systems.  Even if they accept letters, our system
is in the form 99.999-999 -- that's 10 characters, or 9 if you omit the dot,
but omiting the hyphen is so unusual it goes against the grain.  Even if we
use just the numbers it's still 8 numbers, and most US systems do not accept
more than six, which force us to use, not the first six numbers (that would
be a mistake and then the letter might take weeks more to arrive than usual)
but only the first five numbers - while it isn't a gross error it is still a
serious omission and will delay the correspondence for some days.




--
  _
 / \       Leandro Guimarães Faria Corcete Dutra        +55 (11) 3040 8913
 \ /       Amdocs Brasil Ltda at Tele Danmark                +45 3387 5214
  X      http://geocities.com./lgdutra/       mailto:leandrod@amdocs.com
 / \     Campanha fita ASCII                        mailto:moreno@tdk.dk