This example below exercises two N:1 (N-to-one) follower queries along with the main query. For more information, see Follower queries.
Data is created as follows in the same database using the same user as the first example.
create table data (id serial unique, name text); create table more_data ( id serial unique, boss_id int, name text, foreign key (boss_id) references data (id)); create table moar_data ( sk_id int, name text, foreign key (sk_id) references more_data (id)); insert into data (name) values ('Snow White'), ('Batman'), ('Cinderella'), ('Hansel'), ('Little Red Riding Hood'), ('Robin Hood'); insert into more_data (boss_id, name) values (1, 'Doc'), (1, 'Dopey'), (1, 'Sneezy'), (1, 'Happy'), (1, 'Bashful'), (1, 'Sleepy'), (1, 'Grumpy'), (2, 'Robin'), (3, 'Fairy Godmother'), (3, 'Mice'), (3, 'Pidgeons'), (4, 'Gretel'), (6, 'Little John'); insert into moar_data (sk_id, name) values (3, 'Coughy'), (3, 'Crippley'), (9, 'Prince Charming'), (9, 'Shrek'), (13, 'Will Scarlet'), (13, 'Brother Tuck');
The query that the N:1 followers in this report simulate is:
ocrpttest=> select * from data left outer join more_data on (data.id = more_data.boss_id) ocrpttest-> left outer join moar_data on (more_data.id = moar_data.sk_id) ocrpttest-> order by data.id, more_data.id; id | name | id | boss_id | name | sk_id | name ----+------------------------+----+---------+-----------------+-------+----------------- 1 | Snow White | 1 | 1 | Doc | | 1 | Snow White | 2 | 1 | Dopey | | 1 | Snow White | 3 | 1 | Sneezy | 3 | Coughy 1 | Snow White | 3 | 1 | Sneezy | 3 | Crippley 1 | Snow White | 4 | 1 | Happy | | 1 | Snow White | 5 | 1 | Bashful | | 1 | Snow White | 6 | 1 | Sleepy | | 1 | Snow White | 7 | 1 | Grumpy | | 2 | Batman | 8 | 2 | Robin | | 3 | Cinderella | 9 | 3 | Fairy Godmother | 9 | Shrek 3 | Cinderella | 9 | 3 | Fairy Godmother | 9 | Prince Charming 3 | Cinderella | 10 | 3 | Mice | | 3 | Cinderella | 11 | 3 | Pidgeons | | 4 | Hansel | 12 | 4 | Gretel | | 5 | Little Red Riding Hood | | | | | 6 | Robin Hood | 13 | 6 | Little John | 13 | Will Scarlet 6 | Robin Hood | 13 | 6 | Little John | 13 | Brother Tuck (17 rows)
The program code adds the three queries and establishes
the follower links between them. Note that the match
expressions can be anything, just like in SQL using
the LEFT OUTER JOIN ON ( ... )
clause.
#include <stdio.h> #include <opencreport.h> int main(int argc, char **argv) { opencreport *o = ocrpt_init(); struct ocrpt_input_connect_parameter conn_params[] = { { .param_name = "dbname", .param_value = "ocrpttest" }, { .param_name = "user", .param_value = "ocrpt" }, { NULL } }; ocrpt_datasource *ds = ocrpt_datasource_add(o, "pgsql", "postgresql", conn_params); ocrpt_query *q1 = ocrpt_query_add_sql(ds, "q1", "select * from data order by id;"); ocrpt_query *q2 = ocrpt_query_add_sql(ds, "q2", "select * from more_data order by id;"); ocrpt_query *q3 = ocrpt_query_add_sql(ds, "q3", "select * from moar_data order by sk_id;"); ocrpt_expr *match = ocrpt_expr_parse(o, "q1.id = q2.boss_id", NULL); ocrpt_query_add_follower_n_to_1(q1, q2, match); ocrpt_expr *match2 = ocrpt_expr_parse(o, "q2.id = q3.sk_id", NULL); ocrpt_query_add_follower_n_to_1(q2, q3, match2); if (!ocrpt_parse_xml(o, "example6.xml")) { printf("XML parse error\n"); ocrpt_free(o); return 0; } ocrpt_set_output_format(o, OCRPT_OUTPUT_PDF); ocrpt_execute(o); ocrpt_spool(o); ocrpt_free(o); return 0; }}
Here's the equivalent program code in PHP.
<?php $o = new OpenCReport(); $conn_params = [ "dbname" => "ocrpttest", "user" => "ocrpt" ]; $ds = $o->datasource_add("pgsql", "postgresql", $conn_params); $q1 = $ds->query_add("q1", "select * from data order by id;"); $q2 = $ds->query_add("q2", "select * from more_data order by id;"); $q3 = $ds->query_add("q3", "select * from moar_data order by sk_id;"); $match1 = $o->expr_parse("q1.id = q2.boss_id"); $q1->add_follower_n_to_1($q2, $match1); $match2 = $o->expr_parse("q2.id = q3.sk_id"); $q2->add_follower_n_to_1($q3, $match2); if (!$o->parse_xml("example6.xml")) { echo "XML parse error" . PHP_EOL; exit(0); } $o->execute(); $o->spool();
Here's the equivalent program code in PHP, using the RLIB compatibility functions. Note that the RLIB compatible API is more limited as it expects a single field name matching.
<?php $r = rlib_init(); rlib_add_datasource_postgres($r, "pgsql", "dbname=ocrpttest user=ocrpt"); rlib_add_query_as($r, "pgsql", "select * from data order by id;", "q1"); rlib_add_query_as($r, "pgsql", "select * from more_data order by id;", "q2"); rlib_add_query_as($r, "pgsql", "select * from moar_data order by sk_id;", "q3"); rlib_add_resultset_follower_n_to_1($r, "q1", "id", "q2", "boss_id"); rlib_add_resultset_follower_n_to_1($r, "q2", "id", "q3", "sk_id"); if (!rlib_add_report($r, "example6.xml")) { echo "XML parse error" . PHP_EOL; exit(0); } rlib_execute($r); rlib_spool($r);
The program code uses this file contents
from example6.xml
.
Note that when using multiple queries in the same
report, column names may be identical. Because
of this, using queryname.columnname
will indicate which one is needed. When using
columnname
then it will mean
the first query's column.
<?xml version="1.0"?> <!DOCTYPE report > <OpenCReport> <Report orientation="landscape"> <Detail> <FieldHeaders> <Output> <Line> <literal width="30">Boss name</literal> <literal width="1"/> <literal width="30">Sidekick name</literal> <literal width="1"/> <literal width="30">Sidekick's sidekick name</literal> </Line> </Output> </FieldHeaders> <FieldDetails> <Output> <Line> <field value="q1.name" width="30" align="left" /> <literal width="1"/> <field value="q2.name" width="30" align="left" /> <literal width="1"/> <field value="q3.name" width="30" align="left" /> </Line> </Output> </FieldDetails> </Detail> </Report> </OpenCReport>