Tim Bunce wrote:
 > Looks interesting, but I'd suggest the Databases chapter rather than String
 > Processing.

Has someone created the PAUSE namespace entry needed for me to go ahead and 
upload the module?  (It will be ready soon -- before YAPC at any rate.)  Again 
I apologize the namespace form wasn't working for my original request.  I can 
try that form again if it makes your job easier...

:-)
Phil


 > On Thu, May 23, 2002 at 05:17:35PM -0400, Phil R Lawrence wrote:
 >
 >>Since the PAUSE form is not responding for me, please forgive this manual
 >>email...
 >>
 >>Thanks, Phil R Lawrence prl -AT- cpan -DOT- org
 >>
 >>Name of the module SQL::Snippet
 >>
 >>Module List Chapter
 >>11) String Processing, etc.
 >>
 >>Development Stage (Note: No implied timescales) b -- beta
 >>
 >>Support Level d -- developer
 >>
 >>Language Used p -- perl
 >>
 >>Interface Style O -- object oriented
 >>
 >>Public license p -- Standard Perl
 >>
 >>Description in Module List (44 chars limit) Enables an OO Interface to
 >>Your RDBMS
 >>
 >>Places where this module has been or will be discussed publicly clpm,
 >>YAPC::NA 2002
 >>
 >>Modules with similar functionality SQL::QueryBuilder::Simple,
 >>Relations::Query
 >>
 >>Rationale OVERVIEW
 >>
 >>SQL::Snippet enables an OO (Object Oriented) interface to your RDBMS
 >>(Relational Database Management System). It does this by generating correct
 >>SQL based on your OO requests. Actual RDBMS-specific info (table names,
 >>joins, subselects, etc.) does not appear in your invocant script.
 >>
 >>While other modules exist that will create SQL for you, they do so only
 >>insofar as the invocant script passes RDBMS-specific info. SQL::Snippet is
 >>much simpler to use. You tell it you want SQL for 'this' or 'that', and it
 >>takes care of figuring out what tables and joins are involved.
 >>
 >>
 >>APPLICATION
 >>
 >>Reports and other scripts that use SQL often look like someone's first
 >>effort at web programming with Perl; only instead of being littered with
 >>HTML they are full of SQL: SQL that is duplicated in another script, and
 >>another, and another... So if it's a good thing to get your HTML out of your
 >>Perl scripts and into its own repository, why not do the same with your SQL?
 >>
 >>SQL::Snippet will require you to create a Perl module full of, well,
 >>snippets of SQL. For example, that snippet you use to limit a selection by
 >>gender. And that other one you use to grab all the miscreants that have
 >>never ordered from you even though they're still receiving your catalog.
 >>Wait, that's two snippets: the 'catalog_recipient' population and the
 >>'never_ordered_a_single_thing' limit, which creates a subset of the
 >>'catalog_recipient' population.
 >>
 >>The point is that, like most things, you can break those SQL statements down
 >>into their parts and store them in one central module, instead of in
 >>individual scripts. Then you can use SQL::Snippet to dynamically assemble
 >>these snippets as needed. This can even be done in an ad hoc manner; you can
 >>let the user build whatever question he likes of the RDBMS, and SQL::Snippet
 >>will take care of turning it into SQL. Once you have modularized your SQL
 >>into snippets, all you're left with is some easy OO syntax.
 >>
 >>
 >>PARADIGM
 >>
 >>There are four elements to the paradigm that SQL::Snippet uses to enable
 >>easy interaction with your RDBMS.
 >>  1.  pop - Populations
 >>  2.  lim - Limits applicable to those populations
 >>  3.  parm - Parameters needed to flesh out the pops and lims
 >>  4.  methods - each of the above three items has various built in methods
 >>      associated with it. Additionally, objects and methods are
 >>      automatically generated from the snippets in your Repository as
 >>      needed. (See below.)
 >>
 >>Populations Population objects are real world things that are represented
 >>in your RDBMS. The following are example of objects you might find in
 >>your RDBMS:
 >>  - people
 >>  - suppliers
 >>  - parts
 >>  - assemblies
 >>  - SKUs (Stock Keeping Units)
 >>
 >>All of these are real world things. Information about them in your RDBMS may
 >>be contained in one and only one table, or the information may be normalized
 >>(split) between many different tables. In the latter case, the SQL necessary
 >>to query your object of interest could get rather complicated, necessarily
 >>including all of the relevant tables and joins.
 >>
 >>SQL::Snippet abstracts the details of your RDBMS table structure into
 >>objects that you can simply reference in your scripts; SQL::Snippet will
 >>fill in all the details behind the scenes. For example:
 >>
 >>  # prints the canonical SQL statement needed to query info about
 >>  # assemblies from your RDBMS
 >>  print $snippet->pop->assemblies->query;
 >>
 >>Limits Limits are types of real world attributes that can apply to the
 >>population objects. For example, gender is a type of attribute applicable to
 >>people, but not to assemblies. Weight is a type of attribute applicable to
 >>both people and assemblies. By indicating a limit for a given population,
 >>you can sharpen the definition of that population. For example:
 >>
 >>  # apply the gender limit to our 'people' population
 >>  $snippet->pop->people->lim->new( 'gender' );
 >>
 >>Note that neither we nor the user of the script has yet specified what
 >>gender to limit the population by (Unknown, Female, etc.). We have only
 >>indicated that the population should have a gender limit applied to it. To
 >>complete the job we need to look at the next element of the SQL::Snippet
 >>paradigm.
 >>
 >>Parameters Parameters are the actual attributes used by population limits.
 >>In the above example we specified a gender limit, so now we should specify
 >>the gender parameter to be used by that limit. The gender limit requires
 >>only one parameter, aptly named 'gender'.
 >>
 >>  # limit the people population to those with gender attribute of Female
 >>  # or Unknown
 >>  $snippet->pop->people->lim->gender->parm->gender( ['U','F'] );
 >>
 >>Actually, we don't need to say:
 >>  $snippet->pop->people->lim->new( 'gender' );
 >>before saying:
 >>  $snippet->pop->people->lim->gender->parm->gender( ['U','F'] );
 >>
 >>The gender limit is autoinstantiated in the latter example. In fact, it's
 >>all autoinstantiated, from pop to gender. You start your script with a
 >>simple snippet object, and the rest autoinstantiates as needed.
 >>
 >>Methods In the above examples you see that pop, lim, and parm are
 >>autoinstantiated objects furnished by SQL::Snippet. There are multiple
 >>methods associated with each one. For example, we called the 'new' method on
 >>lim. But note that most of the methods and objects we used were actually
 >>named by us, the *users* of SQL::Snippet, not by me, the guy who wrote
 >>SQL::Snippet. For example, not only did we use the autoinstantiated 'gender'
 >>object, we also called the 'gender' method on parm (see the last example
 >>above). Note that we, the users of SQL::Snippet did not have to actually
 >>code a 'gender' method somewhere -- far from it. This method was AUTOLOADed
 >>for us. All we had to do was create a repository with pop, lim, and parm
 >>snippets, and the rest was automatic. For example, here is the 'gender'
 >>snippet from SQL::Snippet::ExampleRepository, an example repository included
 >>with the distribution:
 >>
 >>  if ($parm eq 'gender') {
 >>          return (
 >>              name        =>  $parm,
 >>              default     =>  '',
 >>              msg         =>  "\nBy default, gender will bear no impact " .
 >>                              "on selection.  Press ENTER to accept " .
 >>                              "this default, or, to limit the selection " .
 >>                              "by gender, type one or more gender codes " .
 >>                              "from the following list:  M (Male), " .
 >>                              "F (Female), U (Unknown)",
 >>              delimiter   =>  ',',
 >>              prompt      =>  'Gender',
 >>              list_check  =>  [ 'M','F','U' ],
 >>          );
 >>  } elsif ($parm eq ...
 >>
 >>When you reference 'gender' in your code, either as an object or method (as
 >>in the above examples), SQL::Snippet will automatically create the needed
 >>object or method using this snippet as a source of basic information. If you
 >>can follow this easy pattern for creating snippets, you can use
 >>SQL::Snippet. SQL::Snippet itself automatically creates the back end stuff
 >>needed to allow the use of intuitive OO syntax.
 >>
 >>
 >>HOW I USE IT
 >>
 >>I use SQL::Snippet in a production system with seven basic populations that
 >>my clients like to report on. I then have 31 different limits. Multiple
 >>limits can, of course, be combined with a population. Not every limit is
 >>compatible with all base populations, but most are, leaving the client with
 >>more possible questions they can ask of the RDBMS than I have statistical
 >>chops to calculate.  :-)
 >>
 >>To make it all work I have 47 different parameters. Some limits only require
 >>one parameter (i.e. the gender limit only needs one parameter -- gender),
 >>but some require more than one. Also, many lim snippets need access to extra
 >>parms in order to correctly tie into the population. For example, if the
 >>gender information in the database is stored in table not included in the
 >>chosen pop snippet, the gender lim snippet will also need something like
 >>'base_table' and 'linking_field' parms to correctly link itself in to the
 >>base population.
 >>
 >>Most of the populations are actually pre-limited (i.e. the populations don't
 >>refer to so basic an object as 'people', but rather to different kinds of
 >>people). Thus most require more than one parameter and tend to have multiple
 >>tables included in their definition.
 >>
 >>My clients love their ability to ask anything they can think of from the
 >>RDBMS; I love the ability to maintain the SQL snippets in one and only
 >>one place.
 >>
 >>
 >>EXAMPLE SCRIPT 1
 >>
 >>  # specify the repository to be used.  The repository is
 >>  # subclassed from SQL::Snippet.
 >>  use SQL::Snippet::ExampleRepository;
 >>
 >>  # If you don't specify the parm values needed to fill out
 >>  # your pops and lims, the user will be prompted automatically.
 >>  # I use Term::Interact to enable this in interaction in a
 >>  # command line environment.
 >>  use Term::Interact;
 >>
 >>  # We'll use DBI to execute the SQL we get from SQL::Snippet.
 >>  # Also, SQL::Snippet may use DBI to validate user input (if
 >>  # you so specify in the repository) and to quote parameters.
 >>  use DBI;
 >>
 >>  my $ti = Term::Interact->new;
 >>  my $dbh = DBI->connect( ... );
 >>  my $snippet = SQL::Snippet::ExampleRepository->new(
 >>      dbh => $dbh,
 >>      ui => $ti,
 >>      syntax => 'oracle'
 >>  );
 >>
 >>
 >>  ###EXAMPLE 1###
 >>
 >>  # We need to specify what our SELECT statement will be.
 >>  # A future version will allow prompting of the user to
 >>  # build his own SELECT based on meta-data supplied by
 >>  # the snippets.
 >>  $snippet->pop->catalog_recipient->select( 'SELECT count(*)' );
 >>
 >>  # get the SQL necessary to pull a count of the catalog
 >>  # recipient population out of your RDBMS.
 >>  print $snippet->pop->catalog_recipient->query;
 >>
 >>
 >>  ###EXAMPLE 2###
 >>
 >>  # instead of grabbing the whole population of catalog
 >>  # recipients, let's limit it to those who never placed
 >>  # an order.
 >>  $snippet->pop->catalog_recipient->lim->last_order->parm->last_order(
 >>      'null'
 >>  );
 >>
 >>  # our SELECT is still set for this pop from above...
 >>  print $snippet->pop->catalog_recipient->query;
 >>
 >>
 >>  ###EXAMPLE 3###
 >>
 >>  # instead of forcing a last_order limit, let's let the user
 >>  # say whether or not he wants the limit, and if so what sort
 >>  # of limit.  Possible inputs by the user might be:
 >>  #    '> 1/1/2001'       # with orders after 1/1/2001
 >>  #       -or-
 >>  #    'null'             # with no orders ever
 >>  #       -or-
 >>  #    ''                 # let's not limit, thank you
 >>
 >>  # set the limit for this pop
 >>  $snippet->pop->catalog_recipient->lim->new( 'last_order' );
 >>
 >>  # when generating the SQL, SQL::Snippet will notice we have
 >>  # requested a 'last_order' limit, but no parm value for
 >>  # 'last_order' has been set.  The user will be prompted with
 >>  # whatever verbiage we have stored in the repository, and
 >>  # their input will be parsed by whatever logic we have in the
 >>  # repository.  (See perldoc Term::Interact for the details
 >>  # of user prompting, and the source for SQL::Snippet::
 >>  # ExampleRepository for boilerplate and example Repository
 >>  # logic.
 >>  print $snippet->pop->catalog_recipient->query;
 >>
 >>
 >>EXAMPLE SCRIPT 2
 >>
 >>  # this example script uses some more advanced functionality.
 >>  # see perldoc SQL::Snippet for full documentation.
 >>
 >>  use SQL::Snippet::ExampleRepository;
 >>  use Term::Interact;
 >>  use DBI;
 >>
 >>  my $ti = Term::Interact->new;
 >>  my $dbh = DBI->connect( ... );
 >>  my $snippet = SQL::Snippet::ExampleRepository->new(
 >>      dbh => $dbh,
 >>      ui => $ti,
 >>      syntax => 'oracle'
 >>  );
 >>
 >>  # all pops referenced from the current snippet will
 >>  # automatically have the zip limit applied.
 >>  $snippet->shared_lim->new( 'zip' );
 >>
 >>  # Since all pops will share the zip lim, let's set
 >>  # the zip parm value *once* the top level (instead
 >>  # of once for each pop at the pop->lim->parm level).
 >>  # Here, the value will be set via user interaction
 >>  # since we have passed in no value and none has
 >>  # been previously set.
 >>  $snippet->parm->zip->value;
 >>
 >>  $snippet->pop->pre_sale->select( 'SELECT SKU, count(SKU)' );
 >>  $snippet->pop->pre_sale->group_by( 'SKU' );
 >>  $snippet->pop->pre_sale->order_by( 'count(SKU)' );
 >>
 >>  $snippet->pop->sale->select( 'SELECT SKU, count(SKU)' );
 >>  $snippet->pop->sale->group_by( 'SKU' );
 >>  $snippet->pop->sale->order_by( 'count(SKU)' );
 >>
 >>  $snippet->pop->re_sale->select( 'SELECT SKU, count(SKU)' );
 >>  $snippet->pop->re_sale->group_by( 'SKU' );
 >>  $snippet->pop->re_sale->order_by( 'count(SKU)' );
 >>
 >>  my $pre_sale_hits_by_SKU = $dbh->selectall_arrayref(
 >>      $snippet->pop->pre_sale->query
 >>  );
 >>  my $sales_by_SKU = $dbh->selectall_arrayref(
 >>      $snippet->pop->sale->query
 >>  );
 >>  my $re_sales_by_SKU = $dbh->selectall_arrayref(
 >>      $snippet->pop->re_sale->query
 >>  );
 >>
 >>  print       "                  ----SKU----  --COUNT--\n";
 >>  print       "Pre-Sales Hits\n";
 >>  for (@$pre_sale_hits_by_SKU) {
 >>      print   "                  $_->[0]      $_->[1]\n";
 >>  }
 >>  print       "Sales\n";
 >>  for (@$sales_by_SKU) {
 >>      print   "                  $_->[0]      $_->[1]\n";
 >>  }
 >>  print       " Re-Sales\n";
 >>  for (@$re_sales_by_SKU) {
 >>      print   "                  $_->[0]      $_->[1]\n";
 >>  }
 >>
 >>  # print any notes the repository has associated with
 >>  # limits placed on the whole report.
 >>  print scalar($snippet->get_shared_lim_notes);

Reply via email to