TEAM LinG - Live, Informative, Non-cost and Genuine! SQR in PeopleSoft and Other Applications Second Edition TEAM LinG - Live, Informative, Non-cost and Genuine! TEAM LinG - Live, Informative, Non-cost and Genuine! SQR in PeopleSoft and Other Applications Second Edition PEOPLESOFT V.8 GALINA LANDRES VLAD LANDRES MANNING Greenwich (74° w. long.) TEAM LinG - Live, Informative, Non-cost and Genuine! For online browsing and ordering of this and other Manning books, please visit http://www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact: Special Sales Department Manning Publications Co. 209 Bruce Park Avenue Fax: (203) 661-9018 Greenwich, CT 06830 email: manning@manning.com ©2004 by Manning Publications Co. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps. PeopleSoft, PeopleTools, PeopleBooks, PeopleCode are registered trademarks of PeopleSoft, Inc. PeopleSoft is not the publisher of this book and it is not responsible for it in any way. PeopleSoft owns the copyrights to all screen reproductions from PeopleSoft software products or documentation contained in this publication. SQRiBE, SQR, SQR Workbench for Windows, SQR Server, SQR Execute, SQR Print, SQR Viewer are trademarks or registered trademarks of SQRiBE Technologies. The author and publisher have made their best efforts to prepare this book, and the content is based upon final release software whenever possible. Portions of the manuscript may be based upon pre-release versions supplied by software manufacturer(s). The author and the publisher make no representation or warranties of any kind with regard to the completeness or accuracy of the contents herein and accept no liability of any kind including but not limited to performance, merchantability, fitness for any particular purpose, or any losses or damages of any kind caused or alleged to be caused directly or indirectly from this book. Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end. Manning Publications Co. 209 Bruce Park Avenue Greenwich, CT 06830 Copyeditor: Elizabeth Martin Typesetter: Dottie Marsico Cover designer: Leslie Haimes ISBN: 1-932394-00-1 Printed in the United States of America 1 2 3 4 5 6 7 8 9 10 – VHG – 07 06 05 04 03 TEAM LinG - Live, Informative, Non-cost and Genuine! “It's never too late to be what you might have been.” —GEORGE ELIOT To our dearest children Inna and Gene: you are the world to us. TEAM LinG - Live, Informative, Non-cost and Genuine! TEAM LinG - Live, Informative, Non-cost and Genuine! brief contents 1 Introducing SQR 3 2 Structured Query Language 11 3 Getting started 32 4 SQR data elements and data manipulations 41 5 Basic program structure 65 6 Working with data from a database 79 7 Taking full advantage of SQL 95 8 Loops and decision logic in SQR 102 9 Enhancing your report 111 10 Using break logic in your SQR program 135 11 Run-time and compile-time variables 159 12 Working with arrays 175 13 Creating multiple reports 197 14 Creating SQR Portable Files 211 15 Generating letters 219 16 Using graphics 237 17 Working with flat files 256 18 Interacting with operating systems and other applications 277 vii TEAM LinG - Live, Informative, Non-cost and Genuine! 19 Internet enabling 310 20 Debugging techniques 348 21 Good programming practices 356 22 Running SQR in PeopleSoft applications 377 23 Attaching an SQR program to PeopleSoft objects 400 24 Making an SQR program API Aware 450 25 Accepting input parameters from PeopleSoft pages 465 26 Using process recurrences and job streams 492 27 Implementing security in SQR 516 28 Working with effective-dated tables 523 appendix A Sample database 549 appendix B SQR command line flags 557 appendix C Built-in functions 568 appendix D SQR command syntax 586 viii BR IEF CONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! contents preface xix preface to the first edition xxi special thanks xxiii about the authors xxv about the cover illustration xxvi about this book xxvii Part 1 SQR basics 1 1 2 Introducing SQR 3 1.1 SQR architecture 4 1.2 Database support 6 1.3 Operating system support 7 What’s new in the recent releases 7 Structured Query Language 11 2.1 Relational database model 12 2.2 Simple SELECT statements 14 ix TEAM LinG - Live, Informative, Non-cost and Genuine! 3 4 2.3 Using relational and Boolean operators in the WHERE clause 17 2.4 Aggregate functions 18 2.5 Subqueries 22 2.6 Table joins 23 2.7 Table updates 25 2.8 Referential integrity 28 2.9 Performance considerations 29 Getting started 32 3.1 Building your first SQR program 33 3.2 SQR Dialog Box 34 3.3 SQR output 37 3.4 Adding more complexity 38 SQR data elements and data manipulations 41 4.1 SQR columns, variables, and literals 42 4.2 Predefined SQR variables 44 4.3 Working with dates 47 4.4 List variables 48 4.5 Manipulating data elements 49 Arithmetic commands 49 ✦ The Move command 49 String manipulation commands 51 ✦ The Let command 54 Built-in functions in SQR 58 Numeric functions 58 ✦ File-related functions 58 Date functions 59 ✦ String functions 61 Miscellaneous functions 63 4.6 5 x Basic program structure 65 5.1 SQR page 66 5.2 How SQR processes the source program 67 5.3 Five sections of an SQR program 68 5.4 Program section 69 5.5 Setup section 69 C ONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 6 7 8 9 5.6 Heading section 71 5.7 Footing section 73 5.8 Procedure section 74 Working with data from a database 79 6.1 The Select paragraph 80 6.2 How to reference selected columns and SQL expressions 85 6.3 Explicit and implicit printing 85 6.4 Selecting data from multiple tables 88 6.5 Using the Load-Lookup and Lookup commands to improve performance 89 Taking full advantage of SQL 95 7.1 Using the SQL paragraph in the procedure section 96 7.2 DML vs. DDL statements 99 7.3 Using the SQL paragraph in the Setup Section 100 Loops and decision logic in SQR 102 8.1 Logical expressions 103 8.2 Operands in logical expressions 103 8.3 Relational, string, and numeric operators in logical expressions 104 8.4 Functions 105 8.5 Using If … [Else] operators 105 8.6 The Evaluate statement in conditional processing 106 8.7 Using the While command in loops 108 Enhancing your report 111 9.1 Using the Print command 112 9.2 Formatting your output 115 9.3 9.4 Using edit masks 116 Text format masks 116 ✦ Numeric format masks 117 Date and time format masks 118 More about edit masks 120 9.5 Using the Position command 121 CONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! xi 9.6 Controlling the vertical spacing 122 9.7 Controlling the horizontal spacing 124 9.8 Changing report’s heading or footing 125 9.9 Delaying printing of data 126 9.10 Generating PDF files 129 Part 2 Advanced features of SQR 133 10 11 12 xii Using break logic in your SQR program 135 10.1 Using the On-Break option of the Print command 137 10.2 Using Level qualifiers for multiple level breaks 140 10.3 Using procedures when breaks occur 142 10.4 Using the Save qualifier of the On-Break option 147 10.5 Using Print=Never option in the Print command 150 10.6 Controlling page breaks 153 Run-time and compile-time variables 159 11.1 SQR bind variables 160 11.2 Substitution variables 162 11.3 Dynamic query variables 166 11.4 More about dynamic query variables 169 Working with arrays 175 12.1 SQR arrays 176 12.2 How to create an array 177 12.3 Placing data into arrays 179 12.4 Initializing arrays 181 12.5 Retrieving data from arrays 181 12.6 Performing arithmetic operations on elements of an array 182 12.7 Sorting array elements 188 12.8 Searching data in arrays 191 C ONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 13 14 15 16 17 Creating multiple reports 197 13.1 Defining multiple reports 198 13.2 Handling multiple reports in one program 201 13.3 An example of a multiple report program 202 13.4 Output files in SQR programs with multiple reports 208 Creating SQR Portable Files 211 14.1 How to create an SPF file 212 14.2 Advantages of SPF files 213 14.3 Using the SQR Viewer 214 14.4 Converting SPF files to printer-specific files 216 Generating letters 219 15.1 Using the Document paragraph to create form letters 220 15.2 Document markers 224 15.3 Using variable length sections 231 Using graphics 237 16.1 Declaring a business chart 238 16.2 Creating an array 240 16.3 Printing a chart 241 16.4 Using images in reports 247 16.5 Printing bar codes 249 16.6 Drawing boxes and solid lines 251 Working with flat files 256 17.1 Files in SQR 257 17.2 Using the input/output operations in SQR 257 Opening a file 257 ✦ Closing a file 259 Reading from a file 259 ✦ Writing to a file 263 Different techniques for creating flat file output 264 Using input/output commands 265 ✦ Creating comma-separated file output 267 ✦ Using the Print command to create a flat file 269 17.3 CONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! xiii 18 17.4 Using flat files to import data into your database 270 17.5 Using operating system commands to sort files 273 Interacting with operating systems and other applications 277 18.1 18.2 Executing an SQR program from the command line 278 SQR command line flags 278 ✦ SQR command line arguments 281 ✦ Using the argument files 281 Executing a precompiled SQR program 282 18.3 Executing your SQR programs in batch mode 284 18.4 Issuing operating system commands from an SQR program 284 18.5 Calling external programs from SQR 288 Calling a Pro-Cobol program from an SQR program under UNIX 288 ✦ Calling an SQR program from another SQR program under UNIX 290 ✦ Calling a PeopleSoft Cobol program from SQR 290 Calling SQR from other programs 293 Using SQR API 293 ✦ Using PeopleCode to schedule an SQR program execution from PeopleSoft 294 Linking with a user function written in C 297 Creating a user function 297 ✦ Integrating a user function with SQR 299 ✦ Adding a function prototype 299 Adding an entry to the USERFUNCS array 300 Adding your C function to Ufunc.c 301 ✦ Re-linking SQR and testing your new function 301 ✦ Adding user-defined functions in Windows NT 301 Generating a Word document 302 18.6 18.7 18.8 19 xiv Internet enabling 310 19.1 SQR and the Internet 311 19.2 No program code changes 311 19.3 Using a table of contents 315 19.4 Bursting your HTML output 319 19.5 Defining a title and background image 322 C ONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 19.6 19.7 20 Debugging techniques 348 20.1 Using the Show and Display commands 349 20.2 Using conditional compiler statements 350 The #Debug command 350 ✦ Other conditional compiler directives 351 Using SQR command line flags to enable SQR debugging information 353 20.3 21 Adding more HTML features 324 HTML tabular procedures 326 ✦ HTML heading procedures 332 ✦ HTML hypertext link procedures 335 How an SQR program accepts parameters from the Internet 343 Good programming practices 356 21.1 Going global 357 21.2 Creating platform-independent programs 362 21.3 Database-independent programs 364 21.4 SQL or procedure calls? 367 21.5 Using the top-down approach 368 21.6 Local procedures vs. global procedures 369 21.7 Handling error conditions 370 21.8 Other useful suggestions 371 Part 3 SQR and PeopleSoft 375 22 Running SQR in PeopleSoft applications 377 22.1 SQR and PeopleSoft 378 22.2 A high-level view 379 22.3 PeopleSoft objects 380 22.4 Selecting a menu 381 22.5 Run control 383 CONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! xv 23 22.6 The Process Scheduler Request dialog 384 22.7 The Process Monitor 388 Deleting, cancelling, or putting on hold report request 391 Viewing the Process Request parameters 391 ✦ Reviewing the Message Log and Trace files 392 ✦ Viewing the report output 394 Attaching an SQR program to PeopleSoft objects 400 23.1 Behind the scenes 401 23.2 PeopleSoft Internet Architecture 402 23.3 Selecting a Run Control record 404 23.4 Selecting a Run Control page 406 23.5 Different methods of searching for a Run Control page 410 23.6 Creating a component 415 23.7 Selecting a menu for your report 419 23.8 Granting security access to a new menu item 421 23.9 Creating a Process definition for your program 432 The Process Definition page 434 ✦ Process Definition Options 436 ✦ Override Options page 440 ✦ Destination page 440 ✦ Page transfer 442 ✦ Notification page 442 23.10 Placing the program into the right directory 444 23.11 Testing your Process Definition 445 24 25 Making an SQR program API Aware 450 24.1 Using PeopleSoft-delivered SQC files 451 24.2 Incorporating SQC files into your program 452 24.3 Testing your changes 459 24.4 Communicating errors back to the Process Scheduler 461 Accepting input parameters from PeopleSoft pages 465 25.1 xvi Using application-specific SQC files to obtain input parameters 466 How the Temporary Employees program accepts its input parameters 466 ✦ Changing your SQR program to accept from and thru dates as input parameters 469 Adding unique input parameters to your SQR program 472 C ONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 25.2 25.3 25.4 25.5 26 27 28 Creating your own Run Control records and pages 473 Building a custom Run Control page 477 ✦ Modifying the component 480 ✦ Creating the Process definition 480 Granting Permissions 480 Creating your own SQC files 482 Creating an SQC file to select parameters from the Run Control record 482 ✦ Creating an SQC file to format selected input parameters 483 Changing your SQR program to accept parameters from the Run Control record 484 Testing your SQR program 487 Using process recurrences and job streams 492 26.1 Recurrence definition 493 26.2 Scheduling programs for execution on a recurring basis 496 26.3 Using Job Streams 502 Creating a Component for a Job stream 503 ✦ Creating a Menu Item for our new Job stream 505 ✦ Granting security access to the new objects 506 ✦ Creating a Job definition 509 Scheduling a Job for Execution 512 Implementing security in SQR 516 27.1 Why SQR needs security 517 27.2 Preventing an SQR from running outside the Process Scheduler 517 27.3 Using PeopleSoft Security views in SQR to implement row-level security 519 Working with effective-dated tables 523 28.1 Understanding effective-dated records 524 28.2 Multiple records with the same effective date 525 28.3 Different techniques of selecting data from effective-dated tables 526 Selecting the current data row 526 ✦ Selecting the current data row from a table which includes multiple records with the same effective dates 529 ✦ Using the Loops parameter in the Select paragraph to limit the number of selected rows 530 Selecting the top row from an effective-dated table 532 CONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! xvii 28.4 Selecting the current and prior rows 533 ✦ Using Exists in the Where clause to check for prior rows 539 Other frequently used operations with effective-dated tables 540 Identifying orphan rows 540 ✦ Calculating date intervals between events 541 ✦ Calculating time difference in years since employee’s last promotion 541 ✦ Producing an employee fifth anniversary list 545 appendix A Sample database 549 appendix B SQR command line flags 557 appendix C Built-in functions 568 appendix D SQR command syntax 586 bibliography 650 index 651 xviii C ONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! preface Almost five years have passed since the first edition of this book arrived at bookstores and on the Internet. Since then, SQR has been upgraded to version 6 and many PeopleSoft developers are busy working with version 8. The first edition had become dated and we faced the dilemma: to upgrade or not to upgrade. Almost 200 of our readers emailed and asked us to upgrade. Our publisher asked us to upgrade. Still we were hesitant. But when a friend bought the book and expressed puzzlement about a subchapter addressing the Year 2000 problem, we knew we were outvoted. The polls closed, the final count was 3 to 2: the readers, the publisher, and the friend vs. the two of us. Now, you have the result on your bookshelf (or maybe even on your desk). So, what’s new in the second edition? A lot. The SQR language chapters have been upgraded to version 6. More real-life examples were added; for instance, how to generate Word documents from SQR. HTML enabling coverage was expanded to cover SQR features added since the time of the first edition release. The PeopleSoft part was almost entirely redone to cover PeopleSoft version 8. We discuss the new PeopleSoft Internet Architecture (PIA). We added a separate chapter concerning the Process Scheduler recurrences and Job streams. Special attention was given to PeopleSoft security and how to handle profiles, roles, and permission lists. Finally, all navigation and screens that developers go through are for version 8. xix TEAM LinG - Live, Informative, Non-cost and Genuine! TEAM LinG - Live, Informative, Non-cost and Genuine! preface to the first edition After struggling to make an SQR program work one cold winter night in 1998, we searched through the vendor’s manual for help. We were disappointed. Hoping to find a good book—maybe several books—on SQR, we hopped into a car and drove to a local Borders store. Both of us love the look and smell of today’s superbookstores. We can spend endless hours browsing through the computer-book shelves in those miraculous stores, sometimes forgetting the original purpose of our visit, lost in reading and discovery. On that night, we were optimistic that we would be successful. Well, guess what? We found nothing devoted to SQR. Not a single book! Shortly after our disappointing search, we began to notice we weren’t alone. We read one frantic email after another from scores of SQR and PeopleSoft programmers, all lamenting the absence of books on SQR. As often happens in business, demand eventually gave birth to supply. After some agonizing, we decided to step up to fill the void. And after many long, hardworking nights, reams of paper and heaps of printer cartridges—as well as new, stronger glasses for Vlad—we finished the manuscript for this book. Welcome to the world of SQR. The Structured Query Report Writer may not sound exciting at first, but where else can you find a programming tool, both robust and flexible, that works with nearly all known relational databases and operating systems, combines the power of SQL and the intelligence of procedural logic, and is easy to learn? “Come on,” you may say, “there are other languages like that.” That may be true, but there is something special about SQR: it is used as the main relational data- xxi TEAM LinG - Live, Informative, Non-cost and Genuine! base reporting engine by PeopleSoft. Since PeopleSoft brought SQR to the front lines of today’s ERP world, a number of other software package vendors have taken advantage of this language’s unique capabilities. Recently, we read that another ERP software giant, SAP, is considering making SQR its report writing tool. Yes, this product definitely has a future! We would not have done this language justice if we had limited our coverage to SQR’s usage within PeopleSoft. So we decided to widen our presentation to include a discussion of all main features and capabilities of SQR outside of any specific connection to PeopleSoft. We wrote this book for a broad audience. Readers who know nothing about SQR or PeopleSoft will find that it gives them a jump start. PeopleSoft developers will expand their knowledge of the SQR language. SQR programmers with little or no experience with PeopleSoft will be able to increase their scope. And readers who already have experience in both these areas will find many practical suggestions in over a hundred almostreal-life examples: this book is not just for beginners. If you find this book interesting and useful, all the hard work and pain will have paid off for us. We hope that it will help SQR acquire new followers; that it will help our readers gain new and vital information; and that we will win new friends among them. Ultimately, these are our goals. xxii PR EFACE TO THE F IRS T ED ITIO N TEAM LinG - Live, Informative, Non-cost and Genuine! special thanks It really took a village to shape our terribly imperfect manuscript into a real book. We would like to thank the entire Manning Publications team for helping us to transform our pile of paper into this nice, neat volume. Our special thanks to Marjan Bace, Ted Kennedy, Mary Piergies, Adrianne Harun, Dottie Marsico, Leslie Haimes, and Sharon Mullins. Our deepest appreciation goes to the following people who participated in the technical review of this book: Dia Barman, Bill McAllister, Steven Stein, George Jansen, Bob Talda, Peter Choi, Frank Monteleone, Vishwa Gaddamanugu, Early Stephens, and Tony DeLia. Not only did our reviewers help us to detect numerous glitches and inconsistencies in the original manuscript, but, even more importantly, they had the vision to recognize the value of our work, thereby encouraging us to continue. Tony Delia, Isidor Rivera, Prakash Sankaran, and Bill McAllister deserve a special recognition for their continuous assistance and lots of invaluable suggestions. To the entire team at Seagram—many thanks for your support and encouragement. We also thank Ahmet Ackman, Ellen Tyvrovsky, and Henry Marx for helping us with this project. And finally, this book would not have been possible without understanding and support from our children, Inna and Gene, our parents, relatives, and friends. xxiii TEAM LinG - Live, Informative, Non-cost and Genuine! Our special thanks to those who helped us with the second edition The second edition of this book obviously would not have happened without the first one. Therefore, our thanks to those who helped us with the first edition remain effective for the second edition. For this edition, first and foremost, we would like to thank our readers who posted their reviews on Amazon, Barnes and Noble, Borders, and other booksellers’ web sites and to those who sent us their feedback directly. Your comments were of tremendous importance to us. The Manning team put a lot of effort on the second edition. Marjan Bace, Mary Piergies, Dottie Marsico, and Elizabeth Martin: we truly appreciate your assistance on every step. Katherine Treiber from PeopleSoft Training: thank you for all your help. Ian Robinson from Brio Technology helped us with the software, Stan Quick from Brio Technology answered all of our questions about SQR version 6, and Wilbert Ngo spent so much time and effort reviewing our manuscript. Without this help, the second edition of this book would still be in our desk drawer. We also would like to thank PeopleSoft Inc. and Brio Technology for endorsing our book and encouraging us to continue working on the second edition. xxiv SPECIAL THA NK S TEAM LinG - Live, Informative, Non-cost and Genuine! about the authors Galina Landres has 20 years of professional experience as a full-time employee and consultant. She has developed multi-platform applications for a number of companies, including Waldenbooks, Siemens, Coopers & Lyband, and Seagram. Vlad Landres has been involved in systems design and programming for 25 years. He has worked on both mainframe and client/server applications for the banking and insurance industries. Galina and Vlad Landres are the founders of their consulting company SQRLand specializing in SQR and PeopleSoft applications. For more information about SQRLand, check its web page at www.sqrland.com. xxv TEAM LinG - Live, Informative, Non-cost and Genuine! about the cover illustration The cover illustration of this book is from the 1805 edition of Sylvain Maréchal’s four-volume compendium of regional dress customs. This book was first published in Paris in 1788, one year before the French Revolution. Its title alone required no fewer than 30 words. Costumes Civils actuels de tous les peuples connus dessinés d’après nature gravés et coloriés, accompagnés d’une notice historique sur leurs coutumes, moeurs, religions, etc., etc., redigés par M. Sylvain Maréchal The four volumes include an annotation on the illustrations: “gravé à la manière noire par Mixelle d’après Desrais et colorié.” Clearly, the engraver and illustrator deserved no more than to be listed by their last names—after all they were mere technicians. The workers who colored each illustration by hand remain nameless. The colorful variety of this collection reminds us vividly of how culturally apart the world’s towns and regions were just 200 years ago. Dress codes have changed everywhere and the diversity by region, so rich at the time, has faded away. It is now hard to tell the inhabitant of one continent from another. Perhaps we have traded cultural diversity for a more varied personal life—certainly a more varied and exciting technological environment. At a time when it is hard to tell one computer book from another, Manning celebrates the inventiveness and initiative of the computer business with book covers based on the rich diversity of regional life of two centuries ago, brought back to life by Maréchal's pictures. Just think, Maréchal’s was a world so different from ours people would take the time to read a book title 30 words long. xxvi TEAM LinG - Live, Informative, Non-cost and Genuine! about this book Why SQR? Structured Query Report Writer (SQR) is a programming language that combines the power of Structured Query Language (SQL) queries, the sophistication of procedural logic, and the freedom of multiple-platform development. Increasingly popular since PeopleSoft selected this language as its main SQL processing and reporting tool, SQR’s unique combination liberates developers from the constraints of SQL and allows them to concentrate on the application aspects of their programs. SQR is not just a language. It also includes an industrial-strength engine for extracting, transforming, and distributing data throughout the enterprise. It works equally well on both client and server, crosses almost seamlessly between different platforms, and covers nearly all relational databases. Unlike most report writing tools, SQR is extremely flexible. With SQR you can extract data from and load data into the database, process complex file structures, print sophisticated reports with dynamic breaks at multiple levels, create interfaces between different systems, generate form letters with business charts, graphs and images, and perform many other tasks. At the same time, for those who used to work with drag-anddrop Graphical User Interface (GUI) development tools, SQR may look a little old-fashioned and Cobol-like (although an optionally purchased SQR Workbench eliminates xxvii TEAM LinG - Live, Informative, Non-cost and Genuine! this problem). The absence of a slick development environment is fully compensated by the robustness, scalability, and solid performance of this product: this is a serious tool for serious people. Why this book? We are presenting the only book about SQR available today. It provides a comprehensive guide to the SQR language covering all its elements and features, showing readers the best ways of utilizing SQR’s capabilities, and demonstrating good programming habits. But our book does not stop at this. It also covers all aspects of interaction between SQR programs and PeopleSoft. Many activities and tasks involved in PeopleSoft application development and maintenance are discussed in considerable depth, making the book a working manual for both SQR programmers and PeopleSoft developers. The book is written as a “let’s do it together” tutorial, starting with the basics and gradually progressing to fairly complex subjects. It guides a reader through its topics step by step, providing close-to-real-life examples along the way with the aim of ensuring the complete understanding of each subject. The discussion is not restricted to demonstrating a single technical solution to each problem, but challenges readers to explore various approaches while showing both the benefits and potential pitfalls of each option. Who can use this book? This book can be used by: • programmers responsible for the systems analysis, development, and support of various SQR-based applications • PeopleSoft developers and consultants who write new SQR programs or modify the existing ones, create interfaces between PeopleSoft and other systems, and convert legacy system data to PeopleSoft • PeopleSoft support programmers responsible for maintaining PeopleSoft-delivered SQR programs and upgrading them to the next versions • PeopleSoft functional users who would like to expand their technical background • PeopleSoft database administrators • database developers and administrators • project leaders and managers responsible for SQR or PeopleSoft-related projects • computer specialists who would like to learn about SQR and PeopleSoft by self-study Our book requires no more than a basic understanding of programming and relational databases. At the same time, this book is not just for beginners. It presents SQR in considerable depth and will be useful for seasoned SQR developers. Most examples in xxviii ABOU T TH IS BOOK TEAM LinG - Live, Informative, Non-cost and Genuine! this book are not light. They are close to real-life programs used in PeopleSoft-related SQR applications as well as other SQR-based applications. As we mentioned, the book consists of three major parts. The first two describe the SQR language, both basic and advanced, while the third covers the integration of SQR programs with PeopleSoft. This should allow many readers who are familiar with only one of these two subjects to expand their knowledge of the other subject and to achieve proficiency in all areas: • PeopleSoft developers who will read this book will benefit from learning all intricate details of the SQR language and will be able to write serious SQR programs using all capabilities of this language. • SQR programmers with little or no knowledge of PeopleSoft will be able to learn PeopleSoft basics and how to make their programs run under PeopleSoft, thus expanding their professional horizons (and marketability). What’s in this book? The book consists of three major parts. Part 1 introduces the basics of the SQR language; part 2 advances the reader’s understanding of SQR by exploring its capabilities in greater depth; finally part 3 covers many aspects of PeopleSoft development involving integration of SQR programs with PeopleSoft. Within each major part of this book, chapters are organized in such a way that every chapter introduces a group of related technical concepts and features like data elements, commands, methods, and techniques. Each chapter starts with a brief list of main topics covered in the chapter, follows with extensive discussions of these subjects, and concludes with a list of the chapter’s key points. Chapter 1 presents an overall picture of SQR and its main components. It includes a list of the databases and operating systems supported by SQR. This chapter also briefly describes the changes made to SQR in different versions. Chapter 2 refreshes your knowledge about the basic concepts of the relational database model and SQL and also introduces a set of sample tables that will be used in examples throughout the book. Readers with solid SQL knowledge can skip this chapter; however, we suggest that every reader become familiar with the sample tables presented here. Chapter 3 drives you through the process of building your first SQR program. In it we describe the SQR Dialog Box and show how to run SQR programs with the help of this tool. In addition, we introduce SQR program sections, procedures, and files, as well as an example SQR program with multiple procedures. Chapter 4 introduces the building blocks of the SQR language: its data elements. The chapter covers all types of SQR data elements including: strings, dates, numerics, xxix TEAM LinG - Live, Informative, Non-cost and Genuine! and SQR predefined variables, as well as different commands and built-in functions that are used to manipulate the data elements. Chapter 5 discusses three fundamental topics: the SQR page, the structure of an SQR program, and SQR local and global procedures. Here we lay the foundation for a basic understanding of any SQR program no matter how complex. By dissecting the SQR page into its three main sections, we show you how these sections are used to produce a complete report page. We also discuss all five SQR program sections, and the difference between SQR local and global procedures. Chapter 6 concerns the essence of SQR: the Select paragraph. Here is where you will see the source of SQR power in the combination of SQL and procedural logic that makes this product so different from many other reporting tools. In addition, we introduce the two SQR commands, Lookup and Load-Lookup, that let you pre-load frequently used tables into the computer memory, possibly saving precious processing time by reducing the number of database table lookups. Chapter 7 sheds light on another SQR feature: the SQL paragraph. The SQL paragraph is the only place where you can use native SQL statements in SQR. You do not need to use native SQL to select data from the database, but if you want to update the content of certain tables or execute SQL Data Definition Language (DDL) commands to create, alter, or drop tables, the SQL paragraph is at your service. In chapter 8 you learn how to use SQR logical expressions and operators to manage the program control flow. We show you how SQR handles elements common to any programming language: logical expressions, decisions, loops, etc. Chapter 9 addresses the process of enhancing SQR-generated reports. While SQR is capable of performing many important tasks, printing reports is still its main job. We describe different methods of controlling the report appearance, including using edit format masks. Chapter 10 covers one of the most difficult and confusing aspects of SQR—working with breaks. We cover this subject in-depth by presenting different techniques of controlling breaks. We use a number of examples to ensure a complete understanding of this important topic. Chapter 11 deals with the issue of making your program flexible, using run-time variables and compile-time variables. We explain the difference between the usage of substitution variables and bind variables. We also discuss the difference between the Ask and Input commands as well as each command’s appropriate usage. Another interesting subject covered in this chapter is building dynamic SQL statements and using the dynamic query variables. Chapter 12 introduces SQR arrays. Arrays can be used to simplify your program and to improve its performance. They also serve as data sources for business charts and graphs. We show you how to create an array, populate an array with data, and to retrieve xxx ABOU T TH IS BOOK TEAM LinG - Live, Informative, Non-cost and Genuine! data from an array. We also demonstrate different techniques for sorting data elements in arrays and searching data in arrays. Chapter 13 concentrates on multiple-report programs. We show you how to declare individual reports, define custom printer types, and direct output to specific reports. We also look at the naming conventions SQR uses to assign output file names to individual reports in a multiple-report program. Chapter 14 explores SQR Portable Files (SPF). We discuss the advantages of using SPF files and different techniques that can be used to create, view, or print these files, including the SQR Viewer and the SQR Print tools. Chapter 15 covers the Document paragraph, which is used to generate letters in SQR. We introduce document markers and show you how to use them to control the format and appearance of your letters. We talk about variable length sections in the Document paragraph and how to control pages in multiple-page letters. The main inconvenience of the Document paragraph is that it cannot cross over pages, and here we also will show you how to solve this problem. In Chapter 16 you hopefully will have some fun by learning how to program graphics in SQR. The list of the graphical objects includes business charts and graphs, images, bar codes, lines, boxes, etc. Chapter 17 addresses flat-file processing in SQR. We will discuss operations that are familiar to anyone who works with flat files in any programming language: opening and closing files, reading from and writing to files. SQR uses the Print and NewPage commands as a special way of outputting data to flat files, and we illustrate this simple technique. Chapter 18 shows you how SQR interacts with other programs, products, and operating systems. The chapter covers submitting SQR programs from the operating system command line, running SQR in batch mode, invoking operating system commands and non-SQR programs from SQR, calling user functions from SQR, as well as calling SQR from various environments. Chapter 19 extends the power of SQR to cyberspace. You learn how to convert the existing reports to HTML format without touching a single line in your programs, as well as how to write SQR programs specifically designed to generate Internet reports or webpages. We also show you how to place hypertext links into your reports and how an SQR program can accept input arguments from the Internet. Chapter 20 talks about debugging SQR programs. Debugging in SQR is done in the old-fashioned way of tracing the program control flow and displaying the values of program variables with the help of the Show and Display commands. In addition, you can use SQR conditional compile directives to encapsulate the debugging logic in your program and keep this logic dormant until the need to re-activate debugging comes. xxxi TEAM LinG - Live, Informative, Non-cost and Genuine! Chapter 21 concludes the SQR language part of our book. Instead of introducing new commands, concepts or features, we demonstrate how the already-explored methods can make your programs easier to build, test, maintain, and support. Do not expect ultimate solutions and carved-in-stone rules: they do not exist. Consider this chapter a set of ideas, suggestions, and recommendations that may be helpful for some developers. Chapter 22 is the first PeopleSoft-related chapter. In it we show you how to run PeopleSoft-delivered SQR programs under the PeopleSoft Process Scheduler. We introduce basic PeopleSoft objects such as records, panels, panel groups, and menus. We also demonstrate how to schedule an SQR program execution under PeopleSoft and monitor this program’s status via the Process Monitor. In Chapter 23, we invite you for a brief tour that shows you what happens behind the scenes when users run their reports under PeopleSoft. Based on this knowledge, you will be able to figure out how to reuse the existing PeopleTools objects to run your own SQR programs under the PeopleSoft Process Scheduler. Chapter 24 elaborates on the interaction between SQR programs and PeopleSoft by detailing the program changes that are necessary to make your programs Application Programming Interface (API) Aware. We discuss PeopleSoft-delivered SQC files, how to use the PeopleSoft API variables to communicate between your program and the Process Scheduler, and how to make the Process Scheduler aware of an error situation in your program. Chapter 25 explores how an SQR program can accept input from PeopleSoft online panels. This chapter also includes writing your own SQC files, creating your own Run Control records and online panels, and granting your users access to the newly created or modified objects. Chapter 26 describes the process of creating the Recurrence Definitions in PeopleSoft. The Recurrence Definitions are used to schedule programs for execution on a recurring basis. Starting from version 8, Recurrence Definitions are created under the Process Scheduler Manager. Chapter 26 also shows you how to create job streams that contain multiple processes. Chapter 27 addresses implementing security in PeopleSoft. The chapter’s main focus is how to use PeopleSoft security views in SQR to implement row-level security. In addition, this chapter teaches you how to prevent an SQR program from running outside the Process Scheduler. Chapter 28 focuses on the concept of effective-dated data. Working with effectivedated tables presents certain challenges to SQR developers, and here we suggest a few techniques for solving problems common to many PeopleSoft developers. The appendices of this book describe the sample database tables used in the examples throughout this book (appendix A); SQR command line flags (appendix B); SQR built-in functions (appendix C); and the SQR command syntax (appendix D). xxxii ABOU T TH IS BOOK TEAM LinG - Live, Informative, Non-cost and Genuine! What about the examples? All the materials presented in the book are supported by over a hundred examples most of which are very close to real business situations and can be successfully used by application developers. All sample programs discussed in this book are freely available from http://www.manning.com/landres. Most examples in our book are centered around the sample database presented in chapter 2 and appendix A. This database contains a few tables that are functionally similar to the corresponding PeopleSoft Human Resource Management Systems (HRMS) tables, but include only those columns which are essential to run our examples. Because of this similarity, many PeopleSoft developers will find our examples reflecting the situations that commonly occur in their business environments. In most cases, the examples in this book were tested using Windows 98 or Windows NT as a client and HP UNIX as a server. We used the Oracle database under UNIX or SQLBase under Windows 95. While we did our best to make our examples as platform- and database-independent as possible, there is still a chance that a few examples may produce somewhat different results when run on different databases or under different operating systems. xxxiii TEAM LinG - Live, Informative, Non-cost and Genuine! TEAM LinG - Live, Informative, Non-cost and Genuine! P A R T 1 SQR basics TEAM LinG - Live, Informative, Non-cost and Genuine! TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 Introducing SQR IN THIS CHAPTER • SQR architecture (the main components of SQR and their interrelationships) • The databases supported by SQR • Operating systems and platforms that SQR can run under • SQR versions and releases 3 TEAM LinG - Live, Informative, Non-cost and Genuine! 1.1 SQR architecture Since Brio Technology became the SQR vendor, the names of some components have change. To ensure consistency with the previous edition of this book, we will keep SQR in the names of components that used to have this word in their names, but will name all newly added components the same as Brio calls them. SQR components include SQR Server, Personal SQR, SQR Report Brio Viewer, SQR Print, SQR Execute, SQR Insight Report Builder (formerly SQR Workbench [SWRW]), Brio Report Activator, Personal SQR SQR SQR Print Viewer Brio Insight, DDO Kernel, and a set of DDO drivers (figure 1.1). SQR The Windows version of the SQR Report SQR SQR Builder Server allows you to run SQR programs Execute Server on your client machine under MS-DOS, Windows or Windows NT. SQR proFigure 1.1 SQR main components grams can be submitted from the operating system command line or via the SQRW Dialog Box. The program output can be directed to a printer, to a printer-specific file (or LIS file), to a printer independent file (SPF file), which later can be viewed via the SQR Viewer, to an Adobe PDF (Portable Document File) file, to a CSV (comma separate values) file, or to an HTML file. The non-Windows versions of SQR Server are used to submit SQR programs on the server from the server operating system command line. Similarly to the Windows version, the program output can be directed to a printer, to a printer-specific file (LIS file), to a printer independent file (SPF file), to a PDF file, to a CSV file, or to an HTML file. The printerindependent files generated on server can be viewed via the SQR Report Viewer on Windows only. Personal SQR is a single-user Windows-based emulation of SQR Server used for local reporting, technical training, or testing SQR programs prior to their deployment. SQR Report Viewer (also known as Brio Report Viewer) is a Windows product that is used to view reports online. The reports must be generated in an SPF format. SPF reports can be generated on either client or server but can be viewed only on the client under Windows or Windows NT. SQR Print is used to convert reports generated in SPF format to printer-dependent files (LIS files) that can be directed to a printer. SQR Execute is a run-time version of SQR. It allows you to execute previously compiled SQR programs (sometimes, called SQT files) thereby eliminating the need to combine both compile and run stages in one step. Brio Report Activator 4 DDO Kernel & Drivers CH APTER 1 INTR ODUC ING SQR TEAM LinG - Live, Informative, Non-cost and Genuine! SQR Report Builder (also known as Brio Report Builder, formerly SQR Workbench or VisualSQRIBE) for Windows provides application developers, database administrators, and power users with a graphical development environment for rapidly building SQR applications using simple drag-and-drop facilities. Brio Report Activator (formerly InSQRIBE for SQR) is a set of three ActiveX components that allow programmers to embed SQR functionality (print, view, or execute) into customer applications. Brio Insight is a web browser plug-in that provides optional Brio interactive analysis capabilities for data delivered within SQR HTML output. DDO Kernel and DDO drivers support Brio DDO (Direct Data Object) technology that may be optionally installed with SQR Server. DDO objects are used by SQR Server to access data from various relational and non-relational data sources. DDO drivers are tuned to read specific relational databases, ODBC, JDBC, as well as CSV files, XML data, multi-dimensional OLAP sources, OLEDB (Microsoft ADO OLEDB Client), and SAP BAPIs. Figure 1.2 illustrates the interaction among SQR components. SQR Execute SQT Files LIS Files SPF Files PDF Files HTML Files Printer SQR Print SPF Files LIS Files PDF Files HTML Files Printer SQR Viewer SQR Server LIS Files PDF Files HTML Files Operating System Commands/ Utilities Adobe Acrobat Printer Printer Printer HTML Browser Printer Figure 1.2 Interaction among different SQR components SQR A RCHITECTU RE TEAM LinG - Live, Informative, Non-cost and Genuine! 5 1.2 Database support SQR products are available for use with all major databases. The list of the databases includes Oracle, Sybase, Microsoft SQL Server, IBM DB2 (on mainframe and Windows/Windows NT), Centura SQLBase, Informix, Ingres, Red Brick, Rdb, and AllBase (figure 1.3). NonRelational Data Sources JDBC Rdb AllBase Red Brick Ingres SQR DB2 Informix SQL Server SQL Base Oracle ODBC Sybase Figure 1.3 Databases supported by SQR SQR native drivers ensure maximum performance for data extraction. SQR Server is optimized for more than 100 database/operating system combinations, with features such as direct array access and manipulation, as well as dynamic SQL support for both columns and tables. In addition, SQR supports connections with Open Database Connectivity (ODBC) and JDBC, thus providing a potential for a further expansion of the available RDBMS and operating system combination list. Native SQR language constructions that support database access are universal for all databases, which makes SQR programs database-independent. This does not mean that SQR program developers are restricted from using database-specific add-ons that can be instrumental in simplifying the program logic and improving data access performance. In such cases, a number of techniques are available to reduce or completely eliminate database dependency while preserving the optimal program efficiency. New feature in SQR 5 and 6 is its support of Brio DDO. DDO drivers are used by SQR Server to access data from various relational and non-relational data sources such as CSV files, XML data, multi-dimensional OLAP sources (e.g., Essbase), OLEDB, and SAP R/3 BAPIs. 6 CH APTER 1 INTR ODUC ING SQR TEAM LinG - Live, Informative, Non-cost and Genuine! 1.3 Operating system support SQR Server runs on a wide variety of both desktop and enterprise operating systems, including MS-DOS, OS/2, Windows, Windows NT, UNIX, AS/400, VAX/VMS, MVS and, most recently, Red Hat Linux (figure 1.4). Windows 95/98 Windows NT OS/2 MS-DOS UNIX VAX VMS AS/400 MVS Figure 1.4 Operating systems supported by SQR SQR programs are not distributed in the form of platform-dependent executables. They can be easily moved between platforms either at source level or as pre-compiled pseudo-code modules. All SQR commands, directives, and operators are platformtransparent and require no changes when the programs are moved across platforms. At the same time, programmers are free to invoke any operating system’s specific commands or utilities if they feel the benefits of platform independence are outweighed by other considerations such as performance, ease of maintenance, or the need to integrate their programs into certain specific environments. 1.3.1 What’s new in the recent releases SQR products are rapidly changing. Release after release, the SQR vendor (presently, Brio Technology) keeps adding new features and eliminating bugs and inconsistencies. Version 3 introduced SQR to the world of graphics by bringing in the DeclareChart, Print-Chart, Declare-Image, Print-Image, and Print-Bar-Code commands. Another dramatic change was to allow multiple report programs. This change was implemented with another series of newly introduced commands: DeclareReport and Use-Report, as well as by adding new parameters to already existed commands such as the For-Reports parameter in the Begin-Heading, Begin-Footing, Declare-Printer, Declare-Procedure, and Use-Procedure commands. Version 3 also added a number of useful built-in functions, including a newly introduced class of file manipulation functions (delete(), exists(), and rename()), the array() function, and a very useful getenv() function. OPERATING SY STEM SUPPOR T TEAM LinG - Live, Informative, Non-cost and Genuine! 7 Other changes included allowing multiple report layouts (the Declare-Layout command), dynamic printer type change at run time (the Alter-Printer command), and a number of new printing options like the Column command; checking the status of input/output commands; invoking stored procedures (Ingres, Sybase, and Microsoft SQL Server), and so on. Version 4 was equally revolutionary. First, it catapulted SQR into the cyberspace by Internet-enabling this product. The program developers were given a choice between an automatic conversion of SQR-generated reports without changing the source programs (by using the PRINTER:HT command line flag) or using a number of SQR HTML procedures to write programs specifically designed for the Internet, thereby taking advantage of the advanced HTML features. Second, this version broke the language barriers by introducing SQR locales or sets of national preferences for language, currency, and presentation of dates and numbers. The Alter-Locale command introduced in Version 4 can be used to dynamically switch from one locale to another or to override certain characteristics of the current locale. Third, the version introduced explicit declaration of SQR variables by using the Declare-Variable command. This allowed for the addition of date variables, a completely new variable type, and for the splitting the numeric variables into decimal, float, and integer variables. In conjunction with this change, version 4 brought in Money, Date, and Number default edit format masks. Along with the date variables, came date built-in functions that support a wide range of string-to-date, date-to-string, and date and time manipulations. These functions included: dateadd(), datediff(), datenow(), datetostr(), and strtodate(). Prior to Version 4, programmers had to use database-specific SQL functions to handle date manipulations. Other improvements in version 4 included adding GIF and JPEG image files to the list of available image file types. Starting from Version 4.2, SQR can work with double-byte characters. The ENCODING environmental variable can be set to ASCII, SJIS, or JEUC. The two new doublebyte string manipulation SQR commands, Mbtosbs and Sbtombs, carry out doublebyte to single-byte and single-byte to double-byte conversions respectively. A number of built-in functions were added to facilitate double-byte string processing including instrb(), lengthb(), substrb(), to_multi_byte(), and to_single_byte(). Two other unrelated to multiple-byte manipulation functions were added: roman()—converts its argument to lower-case roman numerals, and wrapdepth()— returns the number of print lines required to wrap a string. The HTML-related improvements in version 4.2 included the new Declare-Toc and Toc-Entry commands. These commands allow developers of Internet-enabled reports to customize the table of contents portions of their reports. Without these 8 CH APTER 1 INTR ODUC ING SQR TEAM LinG - Live, Informative, Non-cost and Genuine! commands, SQR generates standard tables of contents with basic page navigation for any report. In conjunction with this change, a number of SQR commands such as Begin-Heading, Begin-Footing, and Declare-Report were modified to include references to the related tables of contents. The selection of SQR output formats was improved under version 4.3 by adding the CSV and Adobe PDF files. Version 5 added support for Unicode (UCS-2 and UTF-8). All character set encodings are supported by one set of executables. The HTML support was improved with more options of controlling the enhanced HTML output, including the support of HTML 4. Starting from version 5, SQR supports Brio DDO. DDO objects are used by SQR Server to access data from various relational and non-relational data sources such as CSV files, XML data, multi-dimensional OLAP sources, OLEDB, and SAP BAPIs. This made SQR even a more robust tool in enterprise reporting. The same SQR operators can now be used to select and process data from a much wider variety of sources. SQR’s strength of combining data row selection with procedural logic allows SQR programmers to use existing reports to query new data sources with minimal changes to the report’s logic. As a part of the DDO support, a new type of variables, list variables, can be used to create arrays in memory. These arrays can be passed as parameters when making selections from various DDO sources. Another important feature of version 5 is the ability to generate the BQD (Brio Query Data) output. Programmers can place a special BQD icon in the navigation bar of their HTML output. Clicking on this icon invokes the Brio Query client that should be installed on your machine. Version 6 brought expanded DDO functionality, different multi-byte encodings for the database, input files, output files, and report files and color printing support. The expanded DDO functionality includes more database/platform combinations for both relational and non-relational datasources, datasource-specific aggregation functions for DDO-JDBC sources and multiple discrete connections to a given datasource. SQR 6 supports random access to specific row elements in SQR list variables. You can now modify a specific row element of any list item. You can define default colors globally and redefine colors using the RGB (red, green, blue) coding schema. You can also create your own color palette and use it when printing graphical charts. Starting with this version, you can use a new SQR command, Alter-Report, to change report’s heading or footing while report is running. OPERATING SY STEM SUPPOR T TEAM LinG - Live, Informative, Non-cost and Genuine! 9 Another important improvement is a new keyword Delay of the Print command. This new keyword allows you to go back and change the value of a printed variable in the output buffer. A new SQR function, replace(), will help you to replace all substrings within a string with a new value. KEY POINTS 10 1 SQR components include SQR Server, SQR Viewer, SQR Print, SQR Execute, and SQR Workbench for Windows. 2 SQR products are available for use with all major databases including Oracle, Sybase, Microsoft SQL Server, DB2, SQLBase, Informix, Ingres, Red Brick, Rdb, and AllBase. 3 SQR server runs on a wide variety of both desktop and enterprise operating systems. 4 SQR programs are distributed at source level or as pre-compiled pseudocode modules. 5 All SQR commands and functions are platform-transparent and require no changes when SQR programs are moved across platforms. CH APTER 1 INTR ODUC ING SQR TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 2 Structured Query Language IN THIS CHAPTER • The concepts of the relational database model and the Structured Query Language or SQL • A set of sample tables to be used in examples throughout this book • SQL queries, subqueries, and table joins • Relational and Boolean operators and aggregate functions • Different techniques to improve the SQL query performance • Referential integrity when table updates are done 11 TEAM LinG - Live, Informative, Non-cost and Genuine! SQR works with relational databases. It is therefore important to discuss the concepts of the relational database model and how information is retrieved from relational tables using the Structured Query Language (SQL). The usage of SQL statements in SQR programs is common practice, and SQR programmers with a solid understanding of SQL have a definite advantage when it comes to writing effective programs and reports. This chapter is a brief description of SQL and is being presented to help readers with their understanding of SQR. Readers with solid SQL knowledge do not have to read this chapter. However, we suggest that every reader be familiar with the sample tables presented here since these tables are used in many examples throughout the book. Please keep in mind that any kind of comprehensive coverage of SQL and relational databases is beyond the scope of this book. There are many books on this subject presently available on the market. Another important point is that, when discussing access to relational databases, we will not refer to any specific system like DB2, Oracle, or Sybase, but will rather try to present the subject in terms of the ANSI SQL, the subset of the SQL language defined by the American National Standards Institute. In some cases, however, we will be referring to certain product-specific features of SQL when it is necessary to use SQL expressions that are not supported by the ANSI SQL. 2.1 Relational database model Relational database model The relational database model concept was first proposed by Dr. E. Codd in his well known paper “A Relational Model of Data for Large Shared Data Banks” published in the Association for Computing Machinery magazine “Communications of the ACM” in 1970. However, until the mid-1980s, no commercial RDBMS existed on the market. The first commercial RDBMS products were offered by IBM under the name of DB2 and by Relational Software (now Oracle) under the name of Oracle. Later, a number of other companies joined the club of RDBMS vendors. Presently, the most significant players are IBM, Oracle, Sybase, Microsoft, and Informix. The relational database model represents data as a set of tables. Tables are logical structures made up of columns and rows. Information in different tables may be interdependent. In other words, certain tables may have logical relationships to each other by having the same values in the data elements corresponding to similar columns. Any RDBMS product includes an engine to create, maintain, update, and query the table elements. Let us consider an example of a few related tables. Since we will be using this set of tables in the examples throughout this book, it is important for our readers to become familiar with these tables. We modeled our tables after a few core tables that belong to the PeopleSoft HRMS system. When creating the tables, we included only those columns that were essential to run our examples. Therefore, while readers may find a resemblance between our sample tables and their PeopleSoft counterparts, the two sets 12 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! of tables are different. There is a more detailed description of our sample tables in appendix A. Take a look at figure 2.1. Every table here has an identifying column or a group of columns called primary key. In addition, a table may have columns whose values are also present in one or more columns of another table. These columns are called foreign keys. Primary and foreign keys help to maintain logical relationships between different tables. For example, the Personal_Data table has a primary key column Emplid. The SAMPLE DATABASE GENL_DEDUCTN PERSONAL_DATA EMPLID NAME ADDRESS1 CITY STATE POSTAL COUNTRY PHONE SEX MAR_STATUS BIRTHDATE SMOKER FT_STUDENT EMPLOYMENT EMPLID (FK) EMPL_RCD HIRE_DT REHIRE_DT TERMINATION_DT BUSINESS_TITLE SUPERVISOR_ID JOB EMPLID (FK) EMPL_RCD (FK) DEDCD (FK) EFFDT DED_CALC DEDUCTION_END_DT GOAL_AMT EMPLID (FK) EMPL_RCD (FK) EFFDT EFFSEQ DEPTID (FK) JOBCODE EMPL_STATUS ACTION COMPANY (FK) PAYGROUP ANNUAL_RT DEDUCTION_TBL PLAN_TYPE DEDCD EFFDT DESCR DEPT_TBL COMPANY_TBL DEPTID EFFDT EFF_STATUS DESCR MANAGER_ID LOCATION (FK) COMPANY (FK) DEPENDENT_BENEF EMPLID (FK) DEPENDENT_BENEF NAME SAME_ADDRESS_EMPL ADDRESS1 CITY STATE POSTAL COUNTRY RELATIONSHIP DEP_BENEF_TYPE SEX BIRTHDATE STUDENT Figure 2.1 COMPANY EFFDT EFF_STATUS DESCR ADDRESS1 CITY STATE POSTAL COUNTRY FEDERAL_EIN LOCATION_TBL LOCATION EFFDT EFF_STATUS DESCR DESCRSHORT LOCALITY BEN_PLAN_TBL HEALTH_BENEFITS EMPLID (FK) EMPL_RCD (FK) PLAN_TYPE (FK) EFFDT DEDUCTION_END_DT COVERAGE_BEGIN_DT COVERAGE_END_DT COVERAGE_ELECT COVERAGE_ELECT_DT BENEFIT_PLAN (FK) COVERAGE_CD PLAN_TYPE BENEFIT_PLAN EFFDT DESCR DESCRSHORT GROUP_NBR HEALTH_DEPENDNT EMPLID (FK) EMPL_RCD (FK) PLAN_TYPE (FK) EFFDT (FK) DEPENDENT_BENEF HLTH_PROVIDER_ID Sample database tables RE L ATIONAL DATA BASE MODEL TEAM LinG - Live, Informative, Non-cost and Genuine! 13 Employment table has a primary key that is made up of two columns: Emplid and Empl_Rcd. At the same time, the Emplid column is a foreign key to this table. This column connects the Employment table to the Personal_Data table. The columns that participate in a logical relationship do not have to have the same name, but this is usually a convenient method of naming such columns. 2.2 Simple SELECT statements Simple Select statements SQL is a set of programming operators that support access to the elements of a relational database. The simplest and most common operator is the SELECT operator. We will use this operator to build a query against the Personal_Data table (figure 2.2). Figure 2.2 Simple SELECT statement and its output Figure 2.2 shows a portion of the output of our first SELECT statement. This simple query selects all the columns and all the rows from the Personal_Data table. SELECT statements of this kind are called unqualified SELECT statements. Obviously, in most cases, unqualified SELECT statements are not very convenient because a) they may produce too much output (some tables may contain millions of rows), and b) they may not be able to address any specific business needs. Let us be a little more specific in our query by selecting employees who live in Montreal. Now, the SELECT statement will look like that in figure 2.3. Here, the query selects all the columns and all the rows with the column City value equal to ‘Montreal’. Our output has now become more manageable! 14 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 2.3 A qualified SELECT statement In most real projects, tables have many columns and selecting all table columns in one query may produce too much output. Another disadvantage of selecting all columns is that when a particular table structure is changed due to a column addition or deletion (yes, SQL allows you to do this!), the output of the same SELECT statement will also change. The SELECT statement in figure 2.4 retrieves only columns Name, Birthdate, Address1, and City from the Personal_Data table. It also sorts the output rows by Name in ascending alphabetical order. By the way, when you list columns in the SELECT statement, you can list them in any order, not just in the order they appear in the table structure. Please note that in the WHERE clause, you can use columns that are not listed among the selected columns in the SELECT statement. In some cases, the SELECT statement may produce duplicate values in the output rows. For example, let us select column Supervisor_Id values from the Employment table (figure 2.5). Figure 2.4 Selecting only certain columns SIMPL E SELECT STATEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 15 Figure 2.5 Duplicate rows in SELECT output Our SELECT statement now produces duplicate rows because the Employment table has multiple rows with the same Supervisor_Id (some supervisors have more than one subordinate). If we want only unique records in the output (i.e., if we want to produce a list of unique Supervisor_id values), we can use the SELECT statement shown in figure 2.6. Figure 2.6 16 Using SELECT DISTINCT to eliminate duplicate rows CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! 2.3 Using relational and Boolean operators in the WHERE clause Relational & Boolean operators Selection conditions in the WHERE clause must, in many cases, meet a number of complex criteria. Relational and Boolean operators can be used to refine your SELECT statements and make them rather sophisticated. The following relational operators can be used in the WHERE clause: = (equal to), > (greater than), < (less than). You can also use combinations of two relational operators. For example, you can use >= (greater than or equal) or <> (not equal). In addition to the relational operators, you can also use the following Boolean operators: AND, OR, NOT. Let us consider the selection in figure 2.7. Figure 2.7 Relational and Boolean operators in the SELECT statement Here we select employees who were born between 1944 and 1954, reside in the state of California, and live outside of Los Angeles. In this query, we use the date format of dd-mon-yyyy. Different RDBMS ven- Note dors use vendor-specific date formats, for example, 12/31/1970 can be coded ‘31-DEC-70’ or ‘31-DEC-1970’ for Oracle and ‘1970-12-31’ for DB2. You can also use parentheses in the WHERE clause expressions as in figure 2.8. Another flavor of the WHERE clause is the LIKE operator which supports wildcard selections (figure 2.9). REL ATIONAL & BOOL EAN OPERATOR S TEAM LinG - Live, Informative, Non-cost and Genuine! 17 Figure 2.8 Using parentheses in the WHERE clause As you can see, the LIKE operator allows you to select rows based on only partial knowledge of a column’s content. In our case, by placing the percent sign in front of a search argument, we tell SQL to look for all values of column Name that are a combination of an unlimited number of any characters followed by the string “Frank.” The query in figure 2.9 helps us find all employees with first name Frank although, strictly speaking, Figure 2.9 Using the LIKE operator in “Frank” could be a part of the last name. The perthe WHERE clause cent sign could have been put anywhere in the search argument, not just at the beginning. The drawback of this kind of wildcard search is a poor search performance because the system does not know the exact starting position of the string to be searched. If you know for sure how many positions can be ignored during the search and where exactly these positions are, you can simply replace these positions with the underscores. 2.4 Aggregate functions Aggregate functions Aggregate functions apply to the entire SELECT statement output (also called the “result set”), not to specific rows. Each aggregate function summarizes information selected from a number of rows into one value. Here is a list of these functions: 18 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! SUM(column name) calculates the arithmetic sum of all selected values of a col- umn. This function can work only with columns of the numeric format. AVG(column name) calculates the average value of all selected values of a column. As with the SUM function, AVG can work only with columns of the numeric format. COUNT counts the number of selected rows. There are different ways to use the COUNT function in SELECT statements and you will see some examples of this function in this chapter. COUNT can work with columns of any format. MAX(column name) produces the largest of all selected values of a column. This function works with columns of any format. MIN(column name) produces the smallest of all selected values of a column. This function works with columns of any format. When functions MAX and MIN are applied to a non-numeric column, they produce the largest or the smallest binary value of the column. In order to better illustrate the use of aggregate functions, let us discuss the GROUP BY clause. It allows you to define subsets of the SELECT statement output based on the values of a certain column. The query shown in figure 2.10 counts employees in each state. (In this query, we will process the Canadian provinces in the same way as the U.S. states.) While it is possible to have multiple rows for some of the states, the query returns only one row per each state value. Please also note that we are ordering the output by State even though State is not included in the SELECT statement list. Sometimes, you need to impose certain restrictions on the selection results obtained with the help of an aggregate function. This can be done by using the HAVING clause Figure 2.10 Using the aggregate function COUNT AGGR EGATE FU NCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 19 which allows you to specify a logical expression that includes only certain aggregate rows in the query result set. Similarly to the logical expression in the WHERE clause, the expression in HAVING may include relational and Boolean operators. What is the difference between the WHERE and HAVING clauses? The WHERE clause limits the row selection prior to applying the aggregate functions. The HAVING clause applies to the results of the already-made selection thereby further refining the result set. Unlike the WHERE clause, the logical expression in the HAVING clause cannot include columns or aggregate functions that are not included in the SELECT statement. In figure 2.11, we make some modifications to the previous query. We include the State column into the selection and use the HAVING clause to filter out states with only one employee. As you can see, the result set does not include one row with the COUNT(*) = 1. Figure 2.11 Using the HAVING clause If we would like to count the number of U.S. states with at least one employee, our query will look like that in figure 2.12. Now, let us use the MAX aggregate function. The Job table contains multiple records per employee, and each record represents the employee status at a particular moment (not necessarily for a particular date because it is possible to have multiple records within the same date). If we need to know the last date of termination for an employee with Emplid = 8146, the query in figure 2.13 will do the job. 20 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 2.12 A combination of COUNT and DISTINCT helped us to eliminate duplicate rows in the output and produce only one record. Figure 2.13 Using the aggregate function MAX We can use the same table to demonstrate how to use the AVG aggregate function (see figure 2.14). Figure 2.14 Using the aggregate function AVG AGGR EGATE FU NCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 21 The query produces a list of average annual rates for each pay group for the period between 1990 and 1995. We have to limit our selection to active employees only. Please note that the logical operator BETWEEN is used instead of two comparison operators to limit the value of the Effdt column. 2.5 Subqueries subqueries The SELECT statements can be nested within one query. Usually, one query is used to perform the data selection while the result of another query execution determines the selection criteria for the first query. More than one table can be used in nested queries. If we need to produce a list of all employees who were hired in 1996, the nested query shown in figure 2.15 will be of help. Figure 2.15 Nested SELECT statements Let us analyze this query. The inner SELECT statement generates a list of employee Ids with the hire date within the year of 1996. This generated list is used to limit the output of the outer SELECT statement. Later in this chapter, we will see that the same result could have been achieved by executing just one SELECT statement against both Personal_Data and Employment, something called “table join.” Please note that we use operator IN instead of an equal operator in the WHERE clause of the outer SELECT statement. This is because the inner SELECT might produce multiple values of column Emplid. 22 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! 2.6 Table joins Table joins One of the most important and exciting features of the relational databases is that you can join tables in your query based on the values of certain columns in these tables. SQL allows you to use multiple tables in one SELECT statement, taking advantage of logical relationships between the tables. Consider the SELECT statement shown in figure 2.16. As you can see, the output of this query looks rather strange. This is called a Cartesian product. It contains all possible combinations of the rows from each table. In order to make sense of our join we need to refine our query. Figure 2.16 A Cartesian product of two tables In figure 2.17, the query output looks more intelligent. We just join our two tables by the key column Emplid in such a way that only rows with the same values of column Emplid in both tables are selected. Note that key columns used in the table join do not have to have the same name. In our example, table Personal_Data and table Employment have a column with the same name, Emplid. Please note how we managed to differentiate between these columns from different tables by prefixing the column name with the table name. Unfortunately, this made our WHERE clause too lengthy. SQL allows us to simplify the SELECT statements by using table name aliases. With table name aliases, our query will look like that in figure 2.18. TABLE JO INS TEAM LinG - Live, Informative, Non-cost and Genuine! 23 Figure 2.17 A table join Figure 2.18 Using table name aliases The aliases are especially useful when joining the same table. (Yes, a table can be joined to itself!) As with any other table join, when a table is joined to itself, each row of this table combines with itself and with every other row in the table. Then, assuming we use the proper WHERE clause, only certain rows are selected to the output of the query. The query in figure 2.19 joins table Employment to itself in order to produce a list of employees and their respective supervisors. 24 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 2.19 Joining a table to itself 2.7 Table updates Table updates SQL includes operators to support table field value updates: INSERT, UPDATE, and DELETE. These three operators are called Data Manipulation Language (DML) commands. Although SQR is used, for the most part, to generate reports, it supports the usage of DML commands. The INSERT operator inserts rows into a table from the program’s working storage or from another table. Figure 2.20’s INSERT statement adds a row to table Personal_Data. Figure 2.20 Inserting a row into the Personal_Data table The only output operator INSERT generates is the number of inserted rows. We recommend checking this number to make sure the operation was completed as planned. To successfully perform an insertion operation, SQL expects that all inserted column value types match the types that have been previously defined during the table TABLE U P DATES TEAM LinG - Live, Informative, Non-cost and Genuine! 25 creation. And, of course, the order of fields in the VALUES clause must be the same as the column order in the table. Figure 2.21 illustrates how to insert rows from one table into another table. In this example, the Employment table from our sample database is populated with data from the PS_Employment table from the PeopleSoft database. Figure 2.21 Inserting multiple rows from another table Take a closer look at this example. As you can see, the SELECT statement generates the input for INSERT from another table. You can limit the selection only to the values you need. This example also demonstrates that you do not have to insert all columns from one table into another one. You can specify only the columns you need. But wait a second! We know that the target table Employment has more columns than the ones listed in the INSERT statement. What values will be assigned to these columns? The columns not listed in the INSERT statement will be assigned their default values. The default values for each column are defined during the table creation stage. Some columns may have no default values and they are assigned special values called NULL values. (Please note that when a table is created, certain columns may be defined as NOT NULL columns. This means that these columns cannot be assigned NULL values. When an insert operation is performed, all such columns must be listed in the INSERT statement.) If we select the newly inserted rows from the Employment table now, we will see that column Business_title was assigned spaces even if this column was missing from the INSERT column list (figure 2.22). If there is an insertion operator, is there also a deletion operator? Yes, there is, and it is simply called DELETE. It will help us to undo the previous INSERT operation (figure 2.23). Take a close look at figure 2.23. What is the difference between the formats of INSERT and DELETE operators? The INSERT statement names the inserted columns while DELETE does not. This is logical. We are deleting the entire rows no matter how many columns these rows have and what the column values are. 26 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 2.22 Column Business_title was assigned a default value (spaces) Figure 2.23 Deleting rows from a table DELETE is a powerful and dangerous operation. If your WHERE clause is not pre- Note cise enough, you can delete rows you never intended to delete. In fact, you can easily empty out an entire table! It is often recommended that a SELECT statement be executed with the same WHERE clause that will be used in the DELETE statement and that the selection output be reviewed prior to deletion just to make sure that you are deleting only the rows you want to delete. We just learned how to insert and delete rows from a table but what if we want to change some column values in certain rows? SQL has the answer and—you guessed it— it is called UPDATE. This operator tells the RDBMS what rows are to be updated and what the new values should be. In figure 2.24, with just one SQL statement, we will promote all four data entry clerks to administrative assistants! TABLE U P DATES TEAM LinG - Live, Informative, Non-cost and Genuine! 27 Figure 2.24 Updating multiple rows 2.8 Referential integrity Referential integrity We already noticed that the INSERT, DELETE, and UPDATE operators can create a mess if not used carefully. This is especially true when certain tables are logically related. Column value updates in one table may have an unexpected impact on other tables. For example, tables Personal_Data, Job, and Employment have the same column Emplid. Table Personal_Data is a parent to Employment. The Employment table is, in turn, a parent to Job. This type of relationship is called logical parent-child relationship. This means that rows with the same Emplid value in tables Job and Employment are dependent on rows in Personal_Data. Deleting a row from Personal_Data makes all rows with the same Emplid in the other two tables “orphans.” What sense does it make to talk about an annual rate, business title, or job code of a non-existing employee? At the same time, we can, theoretically, delete one record in table Job and still keep the table relationship intact. When a field in a table refers to a field in another table or even in the same table, it is called a foreign key; the field to which it refers is called its primary key. In our case, Emplid in the Employment table is a foreign key to the Personal_Data table, and Emplid in table Personal_Data is a primary key. The Emplid and Empl_Rcd columns in the Job table make up a foreign key to the Employment table whose primary key includes the two columns with the same names. The names of primary and foreign keys do not have to be the same; this is just a convenient way to emphasize their relationship. Tables in a database are considered to be in a state of referential integrity when all rows with the same foreign key value in a child table refer to one and only one row in the parent table. A row in the Employment table can refer only to one row in the Personal_Data table. This means that column Emplid in Personal_Data must have unique values. At the same time, one row in the Personal_Data table may be referred to by more than one number of rows in the Employment table since one employee can have multiple jobs. What happens to referential integrity when primary and foreign key values are changed or deleted? In that case, referential integrity can be broken. Therefore, it is 28 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! necessary to impose certain restrictions on these values. For primary keys, the restrictions are relatively simple. They must be unique and contain no NULL values. Enforcing referential integrity for foreign keys means that every foreign key may contain only values that are present in the primary key or NULL values. When an insert or update operation is performed to a foreign key column, any value assigned to this column must be already present in its primary key. The only exception is the NULL value. You can create a child record with its foreign key set to NULL and reassign its value sometime later. (Whether or not it makes business sense is a subject of a separate discussion.) There is no restriction on delete operations. You can delete any rows with foreign keys without affecting their primary key. For the primary key value changes, the answer is a little less straightforward. According to the ANSI standards, primary key changes are restricted. No primary key value that is referenced by one or more foreign key values can be deleted or changed. Actual RDBMS implementations, however, are more liberal. You are given the following choices when changing or deleting primary key values: you can • restrict primary key changes (ANSI standard) • cascade primary key changes to foreign keys, in other words, changing primary key values will cause exactly the same changes in the corresponding foreign keys, deleting parent rows will cause automatic deletion of all their children rows • set all corresponding foreign keys to their NULL values when the primary key is changed or deleted • set all corresponding foreign keys to their default values when the primary key is changed or deleted No restrictions are imposed on inserting new parent rows. You can insert as many new parent records as you want and you do not have to create their children right away. Specific business requirements, however, may call for immediate creation of foreign key rows once their primary keys are created. Actual methods of maintaining referential integrity differ for different databases. For DB2, these methods can be a part of table declarations, while Oracle and Sybase use special program exits called triggers to handle primary key changes. 2.9 Performance considerations Performance considerations Tables in a database can be very large, and selecting records from one or more tables may require substantial computer resources. An SQL query execution performance depends on table size, database design, and the way the SQL query is built. As far as an SQR PERFO RMANCE CONSIDERATIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 29 programmer is concerned, nothing can be done to table sizes; occasionally, certain things can be done to the database design; and a lot can be done to improve SQL queries. When an SQL operator is executed, the internal RDBMS engine selects the necessary rows by performing data search in tables involved in the query. There are two modes of this search: table scan and index scan. When table scan is done, the entire content of the tables is searched and selected rows are returned back to the user application. For index search, the RDBMS engine uses previously built indexes to select only necessary rows from the tables. In most cases, the second method proves more efficient. Who creates table indexes? Usually, the database administrator does. It makes sense to create indexes on table columns that are frequently used in the WHERE clause. Indexes are always created on all primary and foreign keys. If you perform frequent SELECT statements and experience performance problems, you might want to discuss this with your database administrator to see if additional indexes can be of help. In many cases, however, SQL statements can be improved to make the database engine work with indexes instead of doing table scans and to minimize repeatable table reads in correlated queries. Teaching the most effective SQL writing methods is certainly beyond the scope of our book, therefore, we just would like to include a few general suggestions: • use the indexed columns whenever possible in the WHERE clause • try to avoid ORDER BY, GROUP BY, and DISTINCT because these statements invoke internal sorts • avoid Boolean NOT, use <> instead • when joining tables, avoid using non-indexed columns in the WHERE clause • when using sub-SELECT statements, use smaller tables in inner SELECT statements • when possible, use the underscore instead of the percent sign in the WHERE clause for partial searches • avoid using columns of different type and length in logical operators • avoid arithmetic expressions in SELECT, WHERE, and HAVING • minimize the usage of the HAVING clauses in SELECT statements. HAVING filters selected rows after all rows have been retrieved. Use WHERE instead of HAVING whenever possible • use NOT EXISTS instead of NOT IN in subqueries • avoid calculations on indexed columns 30 CHAPTER 2 STRU CTUR ED QU ER Y L ANGUAGE TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS 1 The relational database model represents data as a set of tables. 2 Tables are logical structures made up of columns and rows. 3 SQL is used to access the elements of a relational database. 4 The SELECT statement is used to retrieve entire rows or specific columns from one or more tables. 5 The WHERE clause in the SELECT statement is used to limit the retrieval based on certain criteria. 6 Relational and Boolean operators can be used to define the selection criteria in the WHERE clause. 7 Primary keys and foreign keys help to maintain logical relationships between different tables. PERFO RMANCE CONSIDERATIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 31 C H A P T E R 3 Getting started IN THIS CHAPTER • The process of building your first SQR program • The Program Section • The SQR Dialog Box and how to run SQR programs with the help of this tool • SQR output files • SQR procedures • An example of an SQR program with multiple procedures 32 TEAM LinG - Live, Informative, Non-cost and Genuine! 3.1 Building your first SQR program Are you ready for a ride? Buckle up and let’s get moving. For some, Structured Query Report Writer sounds like a report generator, but you will soon learn that this fourth generation language can help you create rather complex programs. What will you need to start? Not much! You will need the SQR product itself and any ASCII text editor of your choice to type in your program. Let’s begin with creating a very simple SQR program. In this book, we will be using Microsoft Notepad as our text editor. Please keep in mind that, for large SQR programs, you may need some other editor, for example Microsoft WordPad. In fact, a more sophisticated editor that displays line numbers and supports convenient search/replace logic will save you time when it comes to working with complex programs. Make sure you save your program as a standard ASCII text format file. Your editor will not know that you are writing an SQR program and, therefore, Note will not highlight syntax errors as you type. You will need to compile or run your program to have SQR inform you of errors (if any). Our first program name will be Test3A.sqr. Extension .sqr is a default extension, and, with a few exceptions, we will be using this extension for SQR source program files throughout our entire book. (Of course, SQR executes program file with any extension, but we are just trying to adhere to a development standard.) An SQR program consists of sections. Each section starts with the Begin statement, follows with SQR commands that make up the section body, and ends with the End statement: Begin-<section_name> < Section body> End-<section_name > Typically, an SQR program may include a number of different sections, but the only required one is the Program section. Based on the above generic section format, the Program section should look like this: Begin-Program < Section body> End-Program BUIL DING YOUR FIR S T SQR PR OGR AM TEAM LinG - Live, Informative, Non-cost and Genuine! 33 In earlier SQR versions the Program section was called the Report section. To Note maintain program compatibility, SQR still supports this section name, but in future releases the Report sections may become obsolete. Now, let’s just enter the text of our first program as shown in figure 3.1. Figure 3.1 Your first SQR program Test3A.sqr Looks simple? Sure! But this is a legitimate SQR program that can be executed using the SQR Dialog Box. Of course there are other ways of executing SQR programs besides the SQR Dialog Box, and we will discuss them later in this book. 3.2 SQR Dialog Box The SQR Dialog Box provides the perfect means of running SQR programs under Windows. It also connects you to the database. The database you are using must be active and accessible. The SQR Dialog Box is very simple to use. The very first screen you receive will look like the one in figure 3.2 (it may look a little different to you depending on your setup parameters). 34 CH APTER 3 G E TTI NG STAR TED TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 3.2 The SQR Dialog Box screen Let us discuss the meaning of each screen component: Report name Enter the name of your SQR program. The default file type is .sqr. Please note that you have to enter the fully qualified path before the program name (see figure 3.3). If you omit the program name or enter ? in this field, SQR will prompt you for the program name. If you are not sure about the file name or location, click on the Files button and SQR will display a list of available drives, directories and files. Database The name of the database which will be used in your SQR program. Username Your user access ID. Please make sure you are granted access to all tables and views that you plan to use in your programs. Password Your password to access the database. Report arguments These are the SQR command line flags and arguments. Each report flag is preceded by a dash and, for some flags, may be followed by the flag arguments. We will discuss the report flags and arguments in detail later in this book. You can also find the list of the report flags in Appendix B. For the purpose of running our first program, we will need only two flags: -F, and -O. Flag -F defines the directory where SQR will place the report output file (in our case, the directory is C:\sqrbook\output). Flag -O defines the name of the SQR log file. As you can see, SQR Dialog Box uses certain default values depending on your initial setup. In addition to the flags, there are report arguments that are used to supply an SQR program with informa- Note The report flag arguments must be tion that was not originally hard-coded in the program. entered without spaces These are usually arguments used by the Ask and between the flag and the Input commands or files containing report parameargument. ters. We will discuss these arguments and parameters later in the book. S QR DIAL OG BO X TEAM LinG - Live, Informative, Non-cost and Genuine! 35 Figure 3.3 shows the SQR Dialog Box screen after you have entered all the information necessary to run your program. (We changed the default SQR.log name to TEST3A.log). Figure 3.3 The SQR Dialog Box screen with user information After you press the OK button, SQR will execute your report (figure 3.4). The program will run to the end and display the expected program log. (This is not the program output report file.) Figure 3.4 36 An SQR program log. (This is not the report file) CH APTER 3 G E TTI NG STAR TED TEAM LinG - Live, Informative, Non-cost and Genuine! 3.3 SQR output SQR generates several output files. The most important output file is the actual report file. All Print commands direct their output to the report output file. By default, SQR creates an output file with the name of your program and the extension .lis. In our case, it will be Test3A.lis. Unless otherwise specified (as we did), SQR places the output file in the same directory with your SQR program. You can use the report flags to redirect output to any directory of your choice. As you can see from figure 3.3, the report output name will be c:\temp\Test3A.lis. The default SQR report files (files with extension .lis) are basically designed for printing. If you try to view them on screen, you may see some special printer control characters. To make SQR report files more convenient for viewing, generate the SPF files in addition to or instead of regular report files. (SPF files will be covered in detail in chapter 14.) To have SQR generate an SPF output, you need to add one of the following flags to the list of the report flags: -keep, -nolis, or -ziv (Please see appendix B for a complete list of the report flags.) Let us open and examine our program report file Test3A.lis as shown in figure 3.5. Figure 3.5 An SQR report file When an SQR program runs under Windows, you can see the results of all your Display and Show statement executions on your screen. SQR will also display all compile errors on the same screen. All this screen information is actually written to your .log file. In our example, the name of the SQR log file is c:\temp\Test3A.log. You already saw our log file in figure 3.4. do not specify different log file names for each SQR program run, the Note Ifnextyouprogram execution will overlay the previously created log file. S QR O UTP UT TEAM LinG - Live, Informative, Non-cost and Genuine! 37 3.4 Adding more complexity Now that we know how to run an SQR program, let us enhance it a bit to make it look more like a real program by introducing the Procedure section. An SQR program may include one or more SQR procedures. Procedures allow you to split your program into several logical pieces which makes the program easy to understand and maintain. Each SQR procedure is placed into a separate Procedure section. Let’s add several procedures to our program: procedures Main and Print_Totals will be called from the main program section, whereas procedure Select_Employees will be called from procedure Main. After adding the Procedure sections, the program becomes a little more serious: !TEST3B.SQR ! A program with multiple procedures !****************** Begin-Program !****************** !This indicates a comment in SQR ! … Do Main Two procedures are invoked in the Program section: Main, and Print_Totals Do Print_Totals End-Program !***************************** Begin-Procedure Main !***************************** Show 'Main started.' Print 'Employee List' (1,4) Do Select_Employees End-Procedure Main The Select_Employees procedure is invoked in the Main procedure !******************************************** Begin-Procedure Select_Employees !******************************************** Show 'Select Employees started.' Print 'Mary A. Jones Secretary ' (+1,1) Print 'Alex S. Clarke VP Marketing ' (+1,1) ! +1 in the first parameter of the Print command ! indicates next page line End-Procedure Select_Employees !************************************ Begin-Procedure Print_Totals !************************************ Print 'Total number of employees: 2' (+1,1) End-Procedure Print_Totals 38 CH APTER 3 G E TTI NG STAR TED TEAM LinG - Live, Informative, Non-cost and Genuine! Let us run the program and examine its output files, that is, Test3B.lis (figure 3.6) and Test3B.log (figure 3.7). Please note that file Test3B.lis includes output from all three procedures: Main, Print_Totals, and Select_Employees. This is because all these procedures used the Print command. File Test3B.log, however, includes only output from Main and Select_ Employees since only these two procedures had the Show statement coded. Figure 3.6 Program Test3B.sqr report file Figure 3.7 Program Test3B.sqr log file uses an exclaNote SQR mation sign to indicate the start of a comment line in the program. ADDING MOR E COMPL E XITY TEAM LinG - Live, Informative, Non-cost and Genuine! 39 KEY POINTS 40 1 You can use any ASCII text editor to type in your program. 2 The SQR Dialog Box can be used to submit your program for execution under Windows. 3 The following are commonly used extensions for different SQR files: .sqr for SQR programs, .lis for SQR report files, and .log for SQR log files. 4 All Print commands direct their output to the report file. 5 All Show and Display commands direct their output to the log file. 6 If you do not specify different log file names for each SQR program run, the next program execution will overlay the previously created log file. 7 An SQR program may include a number of different sections, but the only required one is the Program section. 8 An SQR Program section may call one or more procedures using the Do command. Procedures may, in turn, call other procedures. 9 SQR uses an exclamation mark to indicate the start of a comment line in the program. CH APTER 3 G E TTI NG STAR TED TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 4 SQR data elements and data manipulations IN THIS CHAPTER • Main types of SQR data elements • SQR predefined variables • Date variables • Data elements manipulation • The Let command • Built-in functions 41 TEAM LinG - Live, Informative, Non-cost and Genuine! Any programming language deals with data elements. SQR has three main categories of data elements: SQR columns, variables, and literals. We will show you how SQR figures out each element’s category and type by its name, and also what types can be used for the SQR data elements. SQR has a number of reserved (or predefined) variables that are useful in monitoring your database selection status, checking the program execution platform, detecting the end-of-file condition, and so on. We will list all these variables in this chapter and will show how to use them in this and subsequent chapters. Date variables are relatively new to SQR, and we will also talk about dates and date manipulation techniques in SQR. Finally we will discuss different SQR data element manipulation techniques, including the arithmetic operations, string manipulations, the Move and Let commands as well as the usage of built-in functions. 4.1 SQR columns, variables, and literals Just as a finite set of chemical elements can be combined into thousands of chemical compounds, the main SQR data elements—columns, literals, and variables—provide the basis of all SQR functionality. SQR data elements must be identified with a special character that specifies both the category and format of the data element. This special character must be placed at the beginning of each data element’s name. SQR columns are used to reference columns defined in the database. In order to reference a table column, SQR uses the symbol & as a prefix to the table column name. For example, &Emplid or &Hire_Date. SQR assigns types (character, number, or date) to columns based on their definition in the database. You do not have to declare SQR columns in your program. SQR will recognize them as long as they are valid SQL elements. (In chapter 6 we will show you how to assign a name to an SQL expression.) SQR variables can be string variables, numeric variables, date variables (date variables are available starting from version 4), or list variables (available starting from version5). SQR uses the special character $ for both string and date variables, the special character # for numeric variables, and the special character % for list variables. Examples: $Name, #Count, #Number_Of_Pages, $Date_Of_Hire, %My_List. SQR numeric variables can be floating point numbers (default), decimal numbers, or integers (decimal numbers and integers are available starting from version 4). Floating point variables may hold up to fifteen digits of precision. Decimal numbers are the most accurate, providing up to 38 digits of precision, but are slower to process. String variables can be of any length within the memory limitations of your computer. In addition to string, numeric, and date variables, SQR uses special variables in the Begin-Document paragraph to define field positions for printing. These variables are 42 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! called document markers. The symbol @ is used to identify document marker variables. Document markers are used only in the Document paragraph, and we will discuss them in chapter 15. SQR literals are string, numeric, or date constants. String literals must be enclosed in single quotes, for example, 'This is a string literal'. Numeric literals may include digits with optional decimal point and leading sign. You can also use the scientific notation for numeric literals. These are examples of numeric literals of different formats: 2043.44, –434.55, 0, 1234567, 5.6E5. Like string literals, date literals must also be enclosed in single quotes, but the content of any date literal must be in one of the SQR date formats which will be discussed later in this chapter. SQR variables do not have to be explicitly declared in the program. When a variable appears for the first time, SQR assigns this variable its type (based on the first character of the variable’s name) and initial value. All string variables are initialized to the NULL value. All numeric variables are initialized to zeroes. All string variables have variable length. When a new value is assigned to a string variable, its length is automatically adjusted. Prior to version 4, numeric variables were only floating point numbers. As of version 4, numeric variables can be floating point numbers (default), decimal numbers, or integers. In addition, version 4 introduced date variables. The first character of a variable name ($ or #) is no longer sufficient to define the variable. For example, floating point variables, decimals, and integers all have the same prefix: #. Similarly, both string and date variables have the same prefix: $. The problem can be solved by using the Declare-Variable command which allows you to explicitly declare variables (please see the command syntax in appendix D). In most cases, however, you can still declare variables (other than dates) implicitly by using the proper name prefixes. By the way, when date variables are declared, they are initialized to NULL values similarly to string variables. Variable names are not case-sensitive; for example, $Name is the same as $NAME or $name. In our book we will be using, for the most part, word-cap variable names with the first letters of every word of the variable name typed in the upper case and the rest of the word typed in the lower case. We will use an underscore to connect words in multiple-word names of variables and procedure names. Variable names can be of any length, for example, $Employee_Date_Of_Hire is a valid name. SQR variables can be global or local. We will give more detailed information about global and local variables when we discuss local and global procedures in chapter 5. For now, let’s just keep in mind that global variables can be referenced throughout the entire program, whereas local variables are effective only within a local procedure. Please note that local variables are initialized only once when their respective procedures are invoked for the first time. Figure 4.1 shows the SQR data element’s hierarchy. S QR C O LUMN S , VA RIABL E S, AN D LITE R ALS TEAM LinG - Live, Informative, Non-cost and Genuine! 43 SQR DATA ELEMENTS SQR COLUMNS (Data types are defined by the database) SQR VARIABLES LIST VARIABLES DATE VARIABLES STRING VARIABLES SQR COLUMNS DATE STRING Figure 4.1 SQR LITERALS NUMERIC VARIABLES NUMERIC DATE DECIMAL DOCUMENT MARKERS STRING FLOAT NUMERIC INTEGER SQR data elements 4.2 Predefined SQR variables It is important to know that SQR reserves a number of variables with special names. These variables are not declared in the program, but they can be used in the program logic. Predefined SQR variables are extremely helpful and are used in programs extensively. We will be using different predefined variables in many examples throughout this book. Predefined variables are not write-protected, so you can change their values just as you would any other variable values. In some cases, you might want to change some predefined variables, such as #current-line or #page-count and use these changes in your program logic. In most cases, however, changing the values of SQR predefined variables won’t make much sense. Why would you want to change the value of the #sql-status or $sqr-platform variables? You would rather check their values, wouldn’t you? Here is a list of SQR predefined variables: • #current-line denotes the current line number within the current page. It is not, however, the current line in the report body. As we will discuss later, SQR page consists of a heading, a body, and a footing. Variable #current-line gives the number of the current line throughout the entire page. • #current-column the current column number on the page. • #end-file this numeric variable is set to 1 when the Read statement results in the end-of-file condition. Otherwise, #end-file is always 0. 44 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! • #page-count the current page number. • $current-date the current date and time on the local machine when your program started running. • #return-status this value will be returned by SQR to the calling program (in most cases, to the operating system). By default, this variable is initialized to the “success” return value for the operating system. It is, therefore, your responsibility to assign this variable the proper value in your SQR program. • #sql-count the number of rows affected by any SQL DML statement (Insert, Update, or Delete). It is always a good idea to check the value of #sql-count in your program logic after each table update to ensure that the update has been actually performed as expected. Note that #sql-count cannot be used to check the number of selected rows. It is used only to verify updates. • $sql-error this string variable contains an error explanation message from the database. Whenever a database error condition occurs, it is recommended to display variable $sql-error in your SQR program. • #sql-status a status value returned from the database after each SQL statement is executed. Specific returned values differ for each database. You can find these values in your database manual. • $sqr-database your SQR installation is set up to work with some database and this variable can be checked in an SQR program to determine what database you are working with. It is helpful if you want to make your program database-independent. Valid values are 'ALLBASE', 'DB2', 'INFORMIX', 'INGRES', 'ORACLE', 'RDB', 'SQLBASE', 'SYBASE', 'REDBRICK', 'ODBC'. • #sqr-pid the process Id of the current SQR run. This value is unique for each run of an SQR program and can be used to create unique composite file names. • $sqr-platform this variable tells your SQR program which operating system the program is run under. Valid values are 'DOS', 'WINDOWS', 'WINDOWS-NT', 'UNIX', 'VM', 'VMS', 'MVS'. • $sqr-locale the name of the current locale. The SQR locale name is defined in the SQR.ini file. This variable controls the way SQR handles variables that may be displayed differently in different countries, for example, currencies, dates, time, names of days in the week, names of months, etc. • $sqr-dbcs specifies whether SQR recognizes double-character strings (available starting from version 4.2). Valid values are 'Yes' or 'No'. PREDEFINED SQR VARIA BLE S TEAM LinG - Live, Informative, Non-cost and Genuine! 45 • $sqr-encoding the value of the SQR environmental variable ENCODING (available starting from version 4.2). Valid values are: 'ASCII', 'JEUC', 'SJIS','EBCDIC', 'EBCDIK290', 'EBCDIK1027', 'UTF-8', or 'UCS-2'. • $sqr-encoding-console the name of encoding for character data written to the log file or console (available starting from version 5). This value is defined by the SQR environmental variable ENCODING-CONSOLE. Can be used as a substitution variable. • $sqr-encoding-database the name of encoding for character data retrieved from and inserted into the database (available starting from version 5). This value is defined by the SQR environmental variable ENCODING-DATABASE. Can be used as a substitution variable. • $sqr-encoding-file-input the name of encoding for character data read from files used with the Open command (available starting from version 5). This value is defined by the SQR environmental variable ENCODING-FILE-INPUT. Can be used as a substitution variable. • $sqr-encoding-file-output the name of encoding for character data written to files used with the Open command (available starting from version 5). This value is defined by the SQR environmental variable ENCODING-FILE-OUTPUT. Can be used as a substitution variable. • $sqr-encoding-report-output the name of encoding for character data written to the report generated by SQR such as an LIS file (available starting from version 5). This value is defined by the SQR environmental variable ENCODINGREPORT-OUTPUT. Can be used as a substitution variable. • $sqr-encoding-source the name of encoding for character data for SQR Source files and Include files (available starting from version 5). This value is defined by the SQR environmental variable ENCODING-SOURCE. Can be used as a substitution variable. • $sqr-program the name of the SQR program file. • $sqr-report the name of the report output file. This is the actual name specified in the -F flag parameter or in the New-Report command. • $sqr-ver a string with your SQR version ID. • $username the user name specified on the command line. • $sqr-hostname the name of the computer on which your SQR program is executed (available starting from version 5). Can be used as a substitution variable. • #sqr-max-lines the maximum number of lines for the current report (available starting from version 5). 46 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! • #sqr-max-columns the maximum number of columns for the current report (available starting from version 5). • #sqr-toc-level the current TOC level (available starting from version 4.3.3). • #sqr-toc-page sion 4.3.3) the current TOC page number (available starting from ver- • $sqr-toc-text the current TOC text (available starting from version 4.3.3) 4.3 Working with dates Date variables and date handling logic in SQR deserve a special discussion. In SQR, dates can be stored as character strings in string format variables or as dates in special date format variables (available starting from version 4). Like string variables, date variable names are prefixed with a dollar sign ($) but, unlike string variables, date variables must be explicitly declared as such with the help of the Declare-Variable command (available starting from version 4): Begin-Setup Declare-Variable Date $Mydate End-Declare End-Setup In many cases, string variables are sufficient to hold dates. Generally, a variable gets populated with a date value by • selecting this value from a database • accepting user input via the Input command • reading a date value from a flat file • referencing the reserved SQR variable $current-date • performing date manipulations using special SQR date functions. When printing a date stored in a string variable, SQR column, or literal, the date must be in one of the following formats (unless a special date edit mask is specified): • the format controlled by the SQR environmental variable SQR_DB_DATE_FORMAT or the corresponding setting in the SQR.ini file • your database-specific format • the date literal format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]' (the square brackets denote optionality). Table 4.1 lists the default date formats for different databases. WOR KIN G WITH D ATE S TEAM LinG - Live, Informative, Non-cost and Genuine! 47 Table 4.1 Default date format masks for different databases in SQR Database Default Database Format DB2 YYYY-MM-DD-HH:MI:SS.NNNNN YYYY-MM-DD Informix YYYY-MM-DD HH:MI:SS.NNN MM/DD/YYYY MM-DD-YYYY MM.DD.YYYY Ingres DD-MON-YYYY HH:MI:SS MM/DD/YYYY HH:MI:SS MM-DD-YYYY HH:MI:SS ODBC MM-DD-YY Oracle DD-MON-YY DD-MON-YYYY Sybase MON DD YYYY HH:MIPM MON DD YYYY [HH:MI[:SS[:NNN]][PM] MON DD YYYY [HH:MI[:SS[.NNN]][PM] YYYYMMDD [HH:MI[:SS[:NNN]][PM] YYYYMMDD [HH:MI[:SS[.NNN]][PM] SQLBase YYYY-MM-DD-HH.MI.SS.NNNNNN YYY-MM-DD HH.MI.SS.NNNNNN 4.4 List variables List variables (available starting from version 5) contain ordered collections of SQR variables. These variables are not nested: you cannot include one list variable within another. List variables cannot be passed as parameters to local functions. List variables are denoted with the special character %. Unlike other types of variables, list variables cannot be declared via the Declare-Variable command. They are created and manipulated using the Let command (see more about the Let command in 4.5.4). A list variable may include one or more SQR columns, variables or literals of any valid type except another list variable. You can use list variables to hold one set of variables or multiple rows of information of similar structure. List variables are used in SQR for DDO in conjunction with the Begin-Execute command that uses list variables as parameters to pass them to external sources of information. SQR arrays cannot be used for this purpose. 48 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 4.5 Manipulating data elements Every language has a set of data element manipulation commands that help to move information from one element to another, perform arithmetic, string, and logical operations, convert data elements from one data type to another, and perform other related operations. The following commands support data element manipulations in SQR: Add, Subtract, Multiply, Divide, Move, and Let. In addition, SQR provides a set of special string manipulation commands. 4.5.1 Arithmetic commands The four arithmetic commands—Add, Subtract, Multiply, and Divide—are simple and straightforward. They work with two numeric fields, (a source field and a target field), perform the proper arithmetic operation, and store the result in the target field. A source field can be a variable, a column, or a literal; a target field can be only a variable; literals and SQR column variables cannot be modified. Let’s take a look at some examples of the arithmetic commands: Add #Employee_Salary To #Total_By_Department Subtract 1 From #Employee_Count Multiply &Rate_Increase Times #Total Round 2 Divide #Number_Of_Employees Into #Average_Salary Round 2 On-Error=Zero The Round parameter rounds the result to the specified number of digits to the right of the decimal point. For float variables, this value can be between zero and fifteen. For decimal variables, this value cannot exceed the precision of the target variable. If the target variable is an integer, the Round parameter should not be used. (Please be aware that when dealing with money-related values, it is recommended to use decimals rather than float variables to avoid small inaccuracies in calculations.) The Divide command has one special parameter, On-Error. It tells SQR how to handle a zero-division situation. In case of zero-division, you can set the result to a high value, to zero, or you can ask SQR to halt processing (the default option). 4.5.2 The Move command The Move command is more versatile than the arithmetic commands. In addition to moving data from one field to another, it can also perform data conversions and data editing using special edit masks. (We will discuss different types of edit masks and their formats in chapter 9 when presenting the Print command because the Print command uses exactly the same types of masks as the Move command.) MANIPULA TING DATA ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 49 As with the arithmetic commands, the Move command utilizes the source field— which can be an SQR column, variable, or literal as well as the target field, which can be only a variable. Unlike the arithmetic commands, the Move command can handle data of any format. The source and target fields for the Move command can have different formats. For example, the source can be a numeric variable, and the target can be a string variable. In this case, the command not only moves data, but also performs the numeric-to-string conversion. Can the Move command move a string to a numeric field? Yes, but under one condition: the string must contain a valid number. Otherwise, the move will be performed incorrectly. Please be aware that SQR does not treat commas as valid separators in numbers. For example the following Move command Move '15,995.00' To #Total ! Results in incorrect move will move 15.00000 to #Total. The command scans the source string up to the first non-numeric character (in this case, it is the comma) and converts only the scanned portion of the string. But the dot as a decimal part separator is processed correctly: Move '15.99500' To #Total ! Results in correct move There is only one incompatible combination in the Move command: date and numeric fields cannot be converted into each other. Let’s consider a few examples of the Move command: 1. Move &Phone To $Disp_Phone (xxx)bxxx-xxxx 2. Move #Salary to $Disp_Salary $9,999,999.99 3. Move 'Month DD, YYYY' To $Date_Mask Move &Effdt To $Effective_Dt :$Date_Mask 4. Move &Counter To #Number_Of_Employees Number 5. Move &Annual_Rate To #Annual_Salary Money 6. Move $Hire_Date To $Start_Date Date In the first line of the code, the value stored in the SQR column variable &Phone is moved to the string variable $Disp_Phone and is edited according to the specified edit mask. If the source field value is 2031234567, the target field after the move will be (203) 123-4567. The 'b' character in the edit mask denotes a space. In the second line, the value in the numeric variable #Salary is converted to the string format according to the edit mask specified and moved to the string variable $Disp_Salary. In these first two examples, edit masks were coded as literals. The third example demonstrates how you can build a mask dynamically, store the mask value in a string variable, and use this variable instead of a hard-coded literal. Please note that when you use a dynamic edit mask, you have to prefix the name of the corresponding string 50 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! variable with a colon. The Move command in the third example uses a date format mask when moving the value of the source field &Effdt to the target field $Effective_Dt. The target field can be a string field or a date field; SQR will perform the move anyway. If the source field contains an invalid date, SQR will generate an error. The fourth, fifth, and sixth examples demonstrate the Move commands’ use of special SQR-reserved keywords in place of edit masks. These keywords, Number, Money, and Date, are used as the default masks for numeric, money, and date fields. The exact format of the default masks is controlled by the settings in the SQR.ini file. These settings can be changed dynamically with the help of the Alter-Locale command. 4.5.3 String manipulation commands SQR has a few special commands to perform string manipulations. These commands are Find, Concat, Extract, Encode, String, Unstring, Uppercase, and Lowercase. SQR also has a number of string functions that offer a similar capability. These functions will be discussed later in this chapter. The string manipulation commands are helpful in finding portions of text within strings, extracting parts of a string into another string, concatenating multiple strings in one string, parsing a string into portions, and so on. You can find all the command descriptions and syntax in appendix D. It is worth mentioning that string manipulation commands work not only with strings but also with dates. In this case, date fields are converted internally to strings. The Uppercase and Lowercase commands are pretty straightforward, therefore we will not discuss these two commands in this chapter. The Find command helps you to locate the starting position of a sub-string within a string or date field. It returns the position of the first byte of the sub-string in a specially designated numeric variable. For example, Find 'John' In $Full_Name 0 #Position will look for the first occurrence of the character string 'John' in the $Full_Name starting from the leftmost byte of $Full_Name and, if found, will place the position of the found string into the numeric variable #Position. If the string you are looking for is not found, SQR moves –1 instead of the string position. What if the original string has more than one occurrence of the sub-string (as in 'John Johnston')? In this case, the Find command finds only the first occurrence of the sub-string unless you adjust the starting position in the Find command and repeat the search. By the way, the starting position can be coded as a literal or as a variable. What happens if the sub-string or the target string are date fields? In this case, the Find command converts the date into a string before starting the search. The format of the string must be the format specified by the environmental variable SQR_DB_DATE_FORMAT, MANIPULA TING DATA ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 51 or, if no format is specified, your database-specific format (please see table 4.1), or the default date literal command. The Extract command extracts a sub-string of specified length from a source string or date field and moves the sub-string to the specified target string or date variable. In the next example, the Extract command extracts a 3 byte-long area code from a string containing a telephone number and places the area code into the $Area_Code variable. The extraction starts from the second position of the source string (after the left parenthesis): Extract $Area_Code From $Phone_Number 1 3 When the starting position of the sub-string is not known, the Find and Extract commands can work together: the Find command determines the location of a substring, then the Extract command uses the location as the starting point of the extraction. In the following example, the string $Full_Name contains last name (of variable length), comma, first name, and space. If we need to extract the first name from $Full_ Name, our program will have to find the position of the comma in this string, then locate the ending space and calculate the length of the first name. Finally, the program will use the Extract command to extract the first name starting from the next position after the comma and using the calculated length of the first name: Find ',' In $Full_Name 1 #Location Move #Location To #Start_Location Add 1 To #Start_Location Find ' ' In $Full_Name #Start_Location #End_Location Move #End_Location To #Length Subtract #Start_Location From #Length Extract $First_Name From $Full_Name #Start_Location #Length When the source field in the Extract command is a date column or variable, the command converts this field to a string before extraction. The conversion is performed according to the format specified by the environmental variable SQR_DB_DATE_FORMAT, or, if not specified, according to your database-specific format (see table 4.1), or to the default date literal format. The Encode command allows you to encode and place a sequence of special characters onto a string variable. The special characters can be non-display characters or escape sequence. For example, this command moves the code sequence that turns bold on to the $Bold string variable: Encode '<27>L11233' Into $Bold 52 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! The Concat command appends one string to another string with optional editing if an edit mask is specified. The concatenated string can be a variable, a column, or a literal. The target string must be a variable. Here is an example of the Concat command: Move 'bxxxxx-xxxx' To $Zip_Mask Concat $Full_Zip With $Address :$Zip_Mask In the example, the edit mask is built dynamically and stored in the string variable $Zip_Mask. When an edit mask is stored in a variable, the variable name must be prefixed with a colon. In the Concat command of this example, the $Full_Zip variable is edited using the mask specified and then is appended to the $Full_Address variable. If you try to concatenate a number to a string, the Concat command will automatically convert the number to a string using the edit mask if specified. If the concatenated field is a date variable or column and no edit mask is specified, the Concat command will convert the date into a string according to the format specified by the environmental variable SQR_DB_DATE_FORMAT, or, if not specified, according to your database-specific format, or to the default date literal format. The String command is used to build a string from a list of sub-strings. The substrings in the newly-built string will remain separated from each other by one or more characters specified in the String command. This command is very convenient in creating comma-separated files that can be used in non-SQR applications, such as Microsoft Excel. In the following example, we build a comma-separated employee information record made up of different fields: String $Emplid $Empl_Name $Birth_Dt $Address By ',' Into $Empl_Record What if you do not want to separate sub-strings when building your string? The syntax of the String command requires you to specify some sub-string separator. The problem can be solved by specifying a null delimiter (''). The Unstring command is an opposite to the String command. It breaks a source string into a number of substrings using one or more specified characters as the substring separators. The command is very helpful in parsing composite fields into components. It can also be helpful in working with files created by non-SQR applications. Here is an example of the Unstring command: Unstring $Empl_Name By '-' Into $First_Name $Mid_Initial $Last_Name If you specify more substrings than can be found in the source strings, the extra substrings are set to empty strings. Conversely, if the source string has more substrings than specified in the Unstring command, the extra strings will be ignored. If one of the fields specified in either String or Unstring command is a date variable or MANIPULA TING DATA ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 53 column, it will be converted to a string according to the format specified by the environmental variable SQR_DB_DATE_FORMAT, or, if not specified, according to your databasespecific format, or to the default date literal format. 4.5.4 The Let command The Let command is much more complex and versatile than other data element manipulation commands. A single Let command may be capable of replacing a number of data manipulation and logic commands. The syntax of the Let command Let target_variable = expression is very simple, but the expression in this command can be rather sophisticated. Expressions in the Let command can be combinations of operands, operators, and functions. The command can perform variable assignments, string concatenations, numerical calculations, logical expression evaluations, function calls, and various combinations of these operations. Operands in the Let command expressions can be variables, SQR columns, literals, or array fields (SQR array fields are covered in chapter 12). In certain cases, for example, in variable assignments, operands can be of different formats. SQR then performs the proper conversions. In other cases, such as calculations, operands must be only numeric. In the following example, the $Julian_Day string variable was extracted from the current date in the Julian format. The following sequence of two Let commands converts the $Julian_Day string variable into the numeric variable #Number_Of_Days and calculates the year-to-date daily expense payment average by dividing the year-to-date expense total amount by the number of days: Let #Number_Of_Days = $Julian_Day Let #Ytd_Expense_Average = #Ytd_Expense_Total / #Number_Of_Days The first Let command is an assignment command and uses operands of two different formats: string and numeric. The second command is a calculation command and can use only numeric operands. If you were to try using $Julian_Day in the calculation without a prior conversion to numeric, an error would occur: Let #Ytd_Expense_Average = #Ytd_Expense_Total / $Julian_Day ! Error If an expression contains numeric operands of different precisions, SQR converts operands with lower precision to match operands with higher precision. This way, SQR always preserves the number of significant digits when calculating an expression. For example, if an expression contains float, integer, and decimal operands, the integer operands are considered the lowest in precision; the float operands are placed next in 54 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! the precision hierarchy; and the decimal operands are considered the highest. Precision may be lost when the result of the expression is moved to the target variable and the precision of the target variable is lower than the precision of the calculated expression. Let’s consider the following two examples: 1. Let #Salary_Increase = #Salary * #Percent_Increase 2. Let #Dept_Rate_Increase = #Total_Salary_Increase / #Total_Salary In the first line, a decimal variable #Salary is multiplied by a float variable #Percent_Increase. Prior to performing multiplication, the Let command converts #Percent_Increase from float to decimal to match the #Salary precision. The result of the calculation is moved to the decimal variable #Salary_Increase without any precision loss. In the second line, the Let command performs a division on two decimal operands. The result of the division is of the decimal format and this result is converted to the float format and moved to the float variable #Dept_Rate_Increase. This variable has a lower precision than the intermediate result precision, and therefore, the Let command results in some precision loss. Why did we use the Let command in the previous examples while the same operations could have been carried out by already familiar Move, Multiply, and Divide commands? One reason is that the Let command preserves the precision in calculations. Another reason is that the Let command allows the use of complex formulae, not just simple arithmetic operations. Operators in the Let command can be arithmetic, string, or relational. The result of the Let command execution depends not only on operand values and operations performed on the operands, but also on the order of these operations. SQR assigns a precedence order to each operator. All relational operators have lower precedence than arithmetic and string operators. Operators of the same precedence are processed in the sequence they appear in the expression, from left to right. Tables 4.2 and 4.3 list separately relational SQR operators and arithmetic and string operators. The tables present operators in the descending precedence order with the lowest precedence number set to zero. Table 4.2 Relational operators Operator Meaning Precedence > Greater than 3 < Less than 3 >= Greater than or equal to 3 <= Less than or equal to 3 <> Not equal 3 MANIPULA TING DATA ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 55 Table 4.2 Relational operators (continued) Operator Meaning Precedence != Not Equal 3 = Equal 3 Not Logical NOT 2 And Logical AND 1 Or Logical OR 0 Xor Logical XOR (Exclusive OR) 0 Table 4.3 Table 4.3 String and Arithmetic Operators (have higher precedence than relational operators) Operator Meaning Precedence || Concatenate 2 strings 4 + Positive sign prefix 3 - Negative sign prefix 3 ^ Exponent 2 * Multiplication 1 / Division 1 % Remainder 1 + Plus 0 - Minus 0 While the order of operations in the Let command is determined by the precedence rules, these rules can be overridden by using parentheses. Even if you do not need to override operator precedence, parentheses are recommended in complex expressions. Parentheses make your expressions more readable and easier to debug. Let’s take a look at a few examples of expressions in the Let command: Let #A = (#K + #L / #M) * (2/#P - #Q) Let #Vac_Balance = #Vac_Accrued + #Vac_Carryover - #Vac_Taken Let #Total = #Total + (#Price * (100 - #Discount_Percent)/100) * #Quantity Let $Full_Name = $First_Name || ':' || $Mid_Init || ':' || $Last_Name Let #Flag = (#Total <= &Salary_Limit) Or (#Total <= #Special_Limit) The last line of code deserves special discussion. The expression in the Let command in this example is a logical one. It returns one if the result of this expression is 56 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! True or zero if the result is False. Therefore the #Flag variable will be assigned one or zero, depending on the results of the comparisons in the logical expression (more about logical expressions in chapter 8). The Let command can also be used to create and manipulate list variables (available starting from version 5). List variables contain ordered collections of SQR variables, literals and column variables. You can use list variables to hold one set of variables or multiple rows of information of similar structure. These variables are not nested: you cannot include one list variable within another. A list variable may include SQR columns, variables, or literals of any valid type except another list variable. List variables are used in conjunction with the Begin-Execute command that uses list variables as parameters to pass them to external sources of information. SQR arrays cannot be used for this purpose. Here is how you can create a list variable using the Let command: Let %Customer = List(#Cust_Num, $Cust_Nm, $Cust_Birth_Dt) The command creates a single-row list variable. To create a multiple-row list variable, use a slightly different format of the same command: Let %Customer[100] = List(NUMBER'.Cust_Num', TEXT'.Cust_Nm', DATE'.Cust_Birth_Dt') The number between the square brackets indicates the number of rows in the list. The Let command is also used to manipulate a list’s values. The following two Let commands demonstrate how to assign values to components of a list: Let %Customer = List(#User_Id, $User_Nm, $User_Dob) Let %Customer[#Row_Num] = List(#User_Id, $User_Nm, $User_Dob) The first command assigns values to components of a single-row list. The second command assigns values to components of a row which number is stored in the #Row_ Num variable. When assigning, the format and content of the source variables must be compatible with the types given in the definition. SQR will perform the necessary conversions between different data types. Assignments between date and numeric fields are prohibited. To retrieve values stored in components of a list variable, use the following Let command: Let $My_Cust_Name = %Customer[#Row_Num].Cust_Nm MANIPULA TING DATA ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 57 As you can see, each Let command can replace a number of elementary arithmetic or string commands, but the real strength of the Let command is in its ability to use built-in functions. 4.6 Built-in functions in SQR In addition to operands, the expressions in the Let command can include built-in functions. SQR provides a rich variety of numeric, string, date, file-related, and miscellaneous functions. You can use a function or an expression as an argument of another function as long as the inside function or expression returns a value of the proper type. SQR Version 6 offers more than 50 different built-in functions. In addition, you can write your own functions in C using the supplied source file ufunc.c. All SQR built-in functions are listed in appendix C of this book. In this chapter, we will cover only the most frequently used functions. You will also have a chance to see a number of other SQR functions in examples throughout this book. 4.6.1 Numeric functions The numeric built-in SQR functions are not used very frequently, but they may be helpful when you need to perform complex calculations in your program, for example, in financial forecasting. (A complete list of numeric functions can be found in appendix C.) We will present only one numeric function here, abs(), which returns the absolute value of the function’s numeric argument. The argument can be an integer, float, or decimal literal, column, variable, or expression. Here is an example of the abs() function: Let #Percent_Difference = (abs(#Average_Salary - &Salary) / #Average_Salary) * 100 The abs() function returns a value of the same type as its argument either integer, float, or decimal. 4.6.2 File-related functions The SQR file-related functions are helpful in performing file maintenance, and can sometimes replace calls to the operating system. The following three functions are available: delete(), exists(), and rename(). All these functions return zero if the operation was successful; otherwise, they return the system error code (platform-specific). The following example uses all three of the SQR file-related functions: !TEST4A.SQR !Using file manipulation functions Begin-Program Move 'c:\temp\TESTA.LIS' To $File_Name 58 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! Move 'c:\temp\TESTB.LIS' To $New_File_Name Let #Status = exists($New_File_Name) If #Status = 0 Show 'File ' $New_File_Name ' exists. Will be deleted. ' Let #Status = delete($New_File_Name) End-If Let #Status=rename($File_Name, $New_File_Name) If #Status = 0 Show 'File ' $File_Name ' successfully renamed to ' $New_File_Name Else Show 'Error renaming ' $File_Name ' to ' $New_File_Name ', Status= ' #Status End-If End-Program In the Test4A.sqr program, we use the exists() function to check if the TestB.lis file presently exists. We check the function return value stored in the #Status variable. If it is zero, the file exists, and we use the delete() function to delete this file. Then, we use the rename() function to rename TestA.lis to TestB.lis. 4.6.3 Date functions Beginning with version 4, SQR provides a number of date handling functions including strtodate(), datetostr(), dateadd(), datediff(), and datenow(). Most string functions can also work with dates stored in string variables, but string functions handle dates as any other character sequences without recognizing their datespecific features. The date functions, however, offer additional features, for example, you can calculate the difference between two dates in any specified units from years to seconds. Let’s start with the simplest date function, datenow(). It returns the current date and time. This is how you use this function: Let $Date_Time = datenow() Looks simple, doesn’t it? It is very simple, indeed. In fact, we could have also used the SQR predefined variable $current-date instead with one important difference: the $current_date variable contains the date and time when your program started while the datenow() function returns the current date and time. Let us take a look at the function strtodate(). It helps to convert a text string to the date format. The conversion can be done with or without using a date edit mask. You can also use a specially reserved keyword Date in place of a mask to specify the default date mask. SQR processes edit masks differently in the strtodate() function than in the Move command. In the Move command, the mask tells SQR the look of the target field after the conversion. In the strtodate() function, the mask tells SQR what to expect BUIL T-IN F UNCTIONS IN SQR TEAM LinG - Live, Informative, Non-cost and Genuine! 59 from the source field when doing the conversion. If no mask is specified, the source must be in the format specified by the SQR_DB_DATE_FORMAT environmental variable, in one of the database-specific formats (see table 4.1), or in the database-independent date literal format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'. The following example demonstrates both ways of using the strtodate() function (with and without an edit mask): !TEST4B.SQR !Using the strtodate() function Begin-Setup Declare-Variable Date $Mydate1 Date $Mydate2 End-Declare End-Setup Begin-Program Let $Mytext1 = '19980315' ! Here, we use independent date ! literal format Let $Mytext2 = '98/03/15' ! Here, we use mask-dependent format Let $Mydate1 = strtodate($Mytext1)! No mask, use independent date ! literal format Let $Mydate2 = strtodate($Mytext2, 'YY/MM/DD') ! Source content matches the mask End-Program The datetostr() function is an exact opposite of the strtodate(). It converts a date to a string. An edit mask specifying the look of the target string may be used in this function. The keyword Date in place of mask is used to specify the default date mask. If no mask is specified, the format specified by the SQR_DB_DATE_FORMAT environmental variable will be used. If this variable is not set, SQR will use one of the database-specific formats (see table 4.1), or the database-independent date literal format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'. This is an example of the datetostr() function: Let $Date_String = datetostr($Mydate, 'MonbDD,bYY') Another date function, datediff(), is convenient for calculating the difference between two dates. Before this function was made available, SQR programmers had to write rather sophisticated procedures to perform the task of converting years to months, months to days, accounting for the leap years, and so on. Now you need only the datediff() function to calculate the difference in required time units: years, quarters, months, weeks, days, hours, minutes, or even seconds! The function returns a float number that can be negative if the first date is earlier than the second date. Here is how you can use the datediff() function to calculate the difference between two dates in months: 60 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! Let #Diff_Months = datediff(&Effdt, $Calc_Date, 'MONTH') Sometimes, after obtaining the difference between two dates as a number of specified time units, you need to convert this number back into years, months, days, etc. The current version of SQR does not have such a function. You will need to write a procedure or a user function to address this problem. The dateadd() function adds the specified number of time units to a date. It returns another date as the result of the calculation. You can also perform a subtraction with the help of this command by specifying a negative number of the time units. Let $Fifth_Anniversary_Dte = dateadd(&Hire_Dte,'YEAR',5) 4.6.4 String functions String functions are extremely helpful and are widely used in SQR programs. There are sixteen string functions in SQR version 6: instr(), isblank(), isnull(), length(), lower(), upper(), lpad(), rpad(), ltrim(), replace(), rtrim(), substr(), to_char(), to_number(), translate(), and edit(). All these functions are described in appendix C. In this chapter we will discuss only a few frequently used ones. The length() function is helpful in determining the number of characters in a string or date: Let #Input_Length = length($Input) The ltrim() and rtrim() functions use the specified set of characters (usually just one character) to trim the source string or date characters one by one from the left or right until the functions find a character that does not belong to the specified character set. At that moment, the trimming stops. The following example uses a space as a one-character set: Let $Source_String = ' Smith , John, Jr. ' Let $New_String = ltrim(rtrim($Source_String, ' '), ' ') ! The result will be: 'Smith , John, Jr.' The source phrase in the example includes a number of spaces in the beginning, between the words, and in the end. The combination of the ltrim() and rtrim() functions eliminates all spaces in the beginning and end of the source string, but leaves the spaces between the words. The to_char() function converts the source numeric literal, column, variable, or expression to the character format and preserves the precision of the source. This is an example of the to_char() function: BUIL T-IN F UNCTIONS IN SQR TEAM LinG - Live, Informative, Non-cost and Genuine! 61 Let $My_String = to_char(#My_Number) The to_number() function, an antipode of the to_char() function, converts the source string or expression to the numeric format. The function returns a float value, which is then converted to match the format of the target field (integer, float, or decimal). The source string or expression must contain a valid number: Let #My_Number = to_number($My_String) As with the Move command, the to_number() function scans the source string up to the first non-numeric character (commas are considered non-numeric) and converts only the scanned portion of the string, but the dot as a decimal part separator is processed correctly. The lower() and upper() functions convert the contents of their argument to the lower or upper case, respectively (numbers and special characters are not converted), for example: Let $Upper_String = upper($Mixed_String) The edit() function is similar to the Move command, formatting the source field according to the specified edit mask. Here are a few examples of the edit() function: Let $Soc_Sec_Number = edit(#SSN, 'xxx-xx-xxxx') Let $Disp_Salary = edit((&Annual_Rte + #Bonus), '$$$,$$$,$$$.99') Let $Disp_Date = edit(datenow(), 'DD/MM/YY') You may be asking yourself why we need another function to do something that Note can be done by a simple Move command. The answer is that you cannot use Move or any other command in SQR expressions. SQR has a number of functions that do the same jobs as SQR commands, but these functions give you the advantage of being usable in expressions. You can use a combination of string functions and commands. The following example will strip a carriage return character and a line feed character by using the ENCODE command and the TRANSLATE function: encode '<13>' into $carriage_return let $new_val1 = translate($old_val, $carriage_return, ") encode '<10>' into $line_feed let $new_val 2= translate($new_val1, $line_feed, ") 62 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 4.6.5 Miscellaneous functions SQR has a number of functions that cannot be assigned to any distinct category: array(), ascii(), asciic(), chr(), cond(), getenv(), nvl(), range(). All these functions are described in detail in appendix C. The range() function deserves a mention here. Use the range() function to check if the value of a literal, column, variable, or expression is between two specified boundary values. The boundary values can be specified as literals, columns, variables, or expressions. The function works with numbers, strings, or dates. If the checked argument is numeric, the boundary arguments must be numeric too. If the checked argument is a string, the boundary arguments can be strings or dates. The function returns one if the checked value is within the range and zero if it is not. Please note that the second operand in the function must specify the lower boundary, whereas the third operand must specify the upper boundary of the range. Otherwise, the function will always return zero. The following three Let commands give you three different flavors of the range() function: numeric, string, and date. Let #Flag = range(#Hourly_Rte, #Min_Rte, #Max_Rte) Let #Flag = range(substr($Last_Name,1,1) 'A', 'M') ! Check if the first character ! is in range Let #Flag = range($Date1, &Eff_Dte, datenow()) In the last line of the example, the $Date1 variable is checked for being between the effective date and current date. KEY POINTS 1 The three main SQR data elements are columns, variables, and literals. 2 SQR columns are fields defined in the database. SQR uses the symbol & as the first character of a table column name. 3 SQR variables can be either string variables or numeric variables. SQR uses the special character $ for string variables and the special character # for numeric variables. 4 Starting from version 4, SQR introduced another type of variable: date variables. The numeric variables now can be integers, floats, or decimals. BUIL T-IN F UNCTIONS IN SQR TEAM LinG - Live, Informative, Non-cost and Genuine! 63 KEY POINTS (CONTINUED) 5 SQR list variables (introduced in version 5) are ordered collections of SQR variables, literals or column variables. 6 SQR literals are string, date, or numeric constants. 7 SQR variables (except for date variables) do not have to be explicitly declared in the program; when a variable appears for the first time, SQR assigns this variable its initial value. 8 SQR variables can be global or local. Global variables can be referenced throughout the entire program while local variables are effective only within a local procedure. 9 SQR provides you with a number of data element manipulation commands, including arithmetic commands and string manipulation commands. 10 The Move command helps you to move data elements from one field to another with optional editing using special edit format masks. 11 The Let command has the most power allowing you the use of sophisticated expressions. As a result, one Let command can replace a number of elementary arithmetic or string commands. 12 SQR provides you with a rich variety of built-in functions 64 CHAPTER 4 D ATA EL EMENTS AND MANIPULA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 5 Basic program structure IN THIS CHAPTER • SQR page • SQR program sections • SQR procedures 65 TEAM LinG - Live, Informative, Non-cost and Genuine! Mastering SQR pages is crucial when you design reports. Real business reports may sometimes be tricky. In the beginning of this chapter, we will discuss the concept of the SQR page without going into many details, but later we will add more information, and by the end of the chapter you will have become rather friendly with the SQR pages. An SQR program consists of sections. We will discuss all five SQR program sections: the Program Section, the Setup Section, the Heading Section, the Footing Section, and the Procedure Section. Finally, we will talk about local and global procedures and about the difference between these two procedure types. You will encounter a number of unfamiliar words and expressions. Don’t worry! Little by little, all these terms will be explained in this chapter or in the subsequent chapters. All topics covered in this chapter will lay a foundation for a basic understanding of any SQR program no matter how complex the program might be. 5.1 SQR page One of the most important SQR concepts is the way SQR builds its output pages. SQR does not print into its output file every time you use the Print command. Instead, SQR accumulates the entire page in the memory. The heading and footing sections are generated after the body of the page has been filled. Once the entire page is complete, it is written to the output file and is erased from the memory just in time to be replaced with the next page. Figure 5.1 will help you to understand how SQR views a page. Heading lines Heading Actual lines on physical page Body lines Body Footing lines Footing Figure 5.1 66 Main components of SQR page C HAP TE R 5 BAS IC P R OGRAM S TRU C TURE TEAM LinG - Live, Informative, Non-cost and Genuine! 5.2 How SQR processes the source program SQR goes over the source program in two stages. The first stage is the compile stage, during which • All external source files are inserted into the program source file. • All compiler directives are evaluated. Compiler directives start with # and are processed during the compile stage, not the execution stage. These directives help to automatically change the program logic based on the values of certain variables (called substitution variables) without changing the source code of the program. It is achieved by making the SQR compiler ignore certain parts of the source code, depending on the values of certain substitution variables. This makes SQR programs more flexible and easier to maintain. (We will discuss compiler directives in chapters 11 and 20.) • All substitution variables are resolved. These variables play the same role for parts of the source program as compiler directives do for the program logic. With just one key stroke, text substitution variables allow you to change variable names, literals, or even entire pieces of code, thus eliminating the need for a time-consuming search-and-replace. • SQR executes all Ask commands that prompt the user for the values of some substitution variables not coded in the program (more about the Ask command in chapter 11). • Program memory and work buffers are allocated. All memory arrays are created. • SQR checks the syntax of the source program. • SQR determines how to optimize the SQL data access. The second stage comprises the actual program execution and takes place only if SQR finds no compile errors in your program. During the execution stage, SQR • starts processing at Begin-Program and stops at End-Program • calculates the size of the report working page • processes the report body • processes the report heading • processes the report footing • writes the entire page to the output file and gets ready for the next page. Usually you won’t separate the two stages when running the source program: SQR runs through the compile stage and, if no errors are found, immediately switches into the execution stage. HOW S Q R PR O C E S SE S T H E S O UR C E P RO G RAM TEAM LinG - Live, Informative, Non-cost and Genuine! 67 In some cases, though, you may not want to proceed directly to the execution stage. SQR does allow you to perform the compile step once, then save the pre-compiled version of the program, to rerun your report at a later date. Keep in mind, however, that if you use a pre-compiled version of a program, SQR will not re-execute all the above mentioned compile steps, including the Ask commands. SQR will use all external source files inserted during the compilation stage as well as all substitution variable values assigned. SQR does not compile your program into machine language: it will still run under SQR. The result of the compilation is portable between different hardware platforms. (Refer to chapter 18 for more details about running precompiled SQR programs.) 5.3 Five sections of an SQR program An SQR program consists of the following five sections (also depicted in figure 5.2): • Program section • Setup section • Heading section • Footing section • Procedure section SQR program Setup section HEADING SECTION HEADING SECTION Heading section SQL SQL PARAGRAPH SQL PARAGRAPH paragraph Program section FOOTING SECTION FOOTING SECTION Footing section SQL SELECT SELECT paragraph PARAGRAPH SQL SQL paragraph PARAGRAPH DOCUMENT Document PARAGRAPS paragraph SQL SELECT paragraph Figure 5.2 68 PROCEDURE SECTION PROCEDURE SECTION Procedure section PARAGRAPH SQL paragraph DOCUMENT Document PARAGRAPS paragraph Different elements of an SQR program C HAP TE R 5 BAS IC P R OGRAM S TRU C TURE TEAM LinG - Live, Informative, Non-cost and Genuine! Each section starts with the Begin command and ends with the End command. The Begin and End commands consist of the Begin or End verb followed by the hyphen and the section name, for example, Begin-Setup. The only required section in SQR program is the Program section. All other sections are optional. 5.4 Program section The Program section is the most important section in any SQR program. It defines the beginning and the end of the program. After SQR detects the Begin-Program statement in your source, it executes all other commands one by one, stopping only when the End-Program statement is read. This is how it looks: Begin-Program ! Program section commands End-Program Place the Program section near the top of your In SQR versions prior program for better readability (if you have the Setup Note to version 4, the section and/or the Heading and Footing sections in Program section was also your program, the Program section is usually located called the Report section. below these three sections). You can have only one Program section in a program and, for simple reports, it may be the only section you need. In complex programs, it may be a good idea to use the Program section to control the general flow of processing by calling different procedures (discussed later in this chapter). This approach is called modular programming, and it makes your programs easier to debug and maintain. 5.5 Setup section The Setup section contains commands that determine the overall characteristics of the program. This section is optional but, if included, must be placed at the beginning of the program. The Setup section is processed during the program compilation stage before actual program execution. The following commands can be used in the Setup section (commands marked with an asterisk are used only in the Setup section) • Ask * • Begin-SQL • Create-Array S ET U P S E C T I ON TEAM LinG - Live, Informative, Non-cost and Genuine! 69 • Declare-Chart * • Declare-Color-Map * • Declare-Connection • Declare-Image * • Declare-Layout * • Declare-Printer * • Declare-Procedure * • Declare-Report * • Declare-TOC * • Declare-Variable • Load-Lookup • Use (Sybase and Microsoft SQL Server only) * For a complete command syntax and usage, please see appendix D. The following example illustrates how the Setup section is incorporated in an SQR program: !TEST5A.SQR !A Setup section !**************************** Begin-Setup !**************************** Declare-Layout Default Paper-Size=(8.5,11) In this Setup section, some default Left-Margin=1.5 page layout parameters are overridRight-Margin=1.5 den with parameters specified in the Top-Margin=1 Declare-Layout command. Bottom-Margin=1 End-Declare End-Setup !**************************** Begin-Program ! ... End-Program In Test5A.sqr, we use the Setup section to control the print parameters via the Declare-Layout command. In addition to paper size and margins, we could also have specified the maximum number of lines and columns as well as page orientation (the default orientation is portrait). Take another look at our program. Note we use the Default as the layout name. SQR will take all the default layout parameters and substitute only the ones specified in 70 C HAP TE R 5 BAS IC P R OGRAM S TRU C TURE TEAM LinG - Live, Informative, Non-cost and Genuine! the program. The final page layout parameter list will be a combination of default parameters and parameters specified in the program (paper size and margins are in inches). Paper-Size = (8.5, 11) Left-Margin = 1.5 Right-Margin = 1.5 Top-Margin = 1 Bottom-Margin = 1 Orientation = Portrait ! (default value) Columns = 65 ! (default value) Lines = 60 ! (default value). 5.6 Heading section Commands in the Heading section are executed every time SQR generates a page. This section is used only when your report has a heading which is almost always the case for any business report. When an SQR program generates multiple reports, you can have more than one Heading section; however, there can be only one heading for each report. This does not mean that you have to code a Heading section for every single report. If some reports have identical headings, one Heading section can be shared by these reports. Typically, report headings contain report title, date, page number, and similar information. The Heading section is processed just before the report page is written to the output file, after the page body is completed or a special command, New-Page, is issued. The overall structure of the Heading section is rather simple: Begin-Heading #lines ! Heading section commands End-Heading Parameter #lines in the Begin-Heading statement defines the number of lines allocated for the heading. You must specify the number of heading lines to help SQR reserve enough room for heading at the page top, including the blank lines. SQR will calculate the number of lines available for the page body and footing by subtracting the #lines value from the Max-Lines parameter in the Declare-Layout command (if specified) or from the default value of the Max-Lines parameter. Now, let us add the Heading section to our program: !TEST5B.SQR !A Heading section !*********************** Begin-Setup !*********************** ! Setup section commands HE ADING SECTION TEAM LinG - Live, Informative, Non-cost and Genuine! 71 End-Setup !*********************** This parameter specifies the number of lines reserved for Begin-Heading 3 the report heading. Let $Date = datenow() Print $Date (1,1) Edit 'DD/MM/YYYY' Print 'Report TEST5B.SQR' (2,1) Page-Number (3,1) 'Page' End-Heading !************************ Begin-Program !*********************** ! Program section commands End-Program !*********************** Our program allocates three lines on each page for heading, and includes the date and time, report title, and page number in the heading area. Take another look at figure 5.1. Line numbers within the heading, the body, and footing sections of the report are numbered separately, therefore, each section begins with its own line one. What would happen if the number of lines for the heading section was coded equal to two, but the Print commands in the Heading section use lines number three and four? In that case SQR would consider the first two lines as heading lines and the next two lines as report body lines, possibly causing the last two lines of the report page body to disappear from the output. It is, therefore, a good practice to always reserve enough lines for your headings. Because SQR generates the heading and footing after the body of the page has been filled, by the time the heading and footing are processed, all variable values in the Program section have been assigned or calculated. You can take advantage of this fact and print in the Heading section any information selected from the database, or calculated, or derived some other way in the Program section as shown in the next example. (For a complete command syntax and usage, please see appendix D.) !******************* Begin-Heading Print 'Report TEST5B.SQR' (1,1) Print 'List of all active employees for company ' (2,10) Print $Company (2,+1) The value of $Company is assigned in the End-Heading Program section but can be printed in the Heading section. !****************** Begin-Program !****************** Move 'ABCD Corporation' To $Company End-Program The value of $Company is assigned in the Program section and is available in the Heading and Footing sections. !**************** 72 C HAP TE R 5 BAS IC P R OGRAM S TRU C TURE TEAM LinG - Live, Informative, Non-cost and Genuine! 5.7 Footing section The general structure of the Footing section is as simple as that for the Heading section (the complete command syntax can be found in appendix D). All commands in this section are executed before the current page is written to the output file. Lines allocated for the footing, as well as lines allocated for the heading, reduce the number of lines available for the report body. Multiple-report programs may have multiple Footing sections. Begin-Footing #lines ! Footing section commands End-Footing Parameter #lines also defines the number of lines allocated for the footing. You must specify the number of footing lines to be reserved at the bottom of each page, including the blank lines. SQR will calculate the number of lines available for the page body and heading by subtracting the #lines value from the Max-Lines parameter in the Declare-Layout command (if specified) or from the default value of the MaxLines parameter. !TEST5C.SQR !A Footing section !*********************** Begin-Setup !*********************** ! Setup section commands End-Setup 3 lines reserved for !*********************** the report heading Begin-Heading 3 Print 'Report TEST5C.SQR' (1,1) End-Heading 2 lines reserved for !************************ the report footing Begin-Footing 2 Page-Number (1,35) 'Page' Last-Page () ' of ' '.' End-Heading !************************ Begin-Program !*********************** ! Program section commands End-Program !*********************** In Test5C.sqr, we decided to print the current page number and the total of all pages at the bottom of the page. How does SQR know the total number of pages while generating a page in the beginning or in the middle of the report? The Last-Page command used in the Footing section allows SQR to accumulate the entire report without printing it until after the last page is processed. FOO TI NG S EC T I O N TEAM LinG - Live, Informative, Non-cost and Genuine! 73 You cannot use Print commands in the Program section to print in lines that Note belong to the Heading or Footing section. Only commands executed from the Heading or Footing section may place data in the corresponding areas of the page. 5.8 Procedure section While the Procedure section is optional, it can be used to break your program into manageable and easy-to-maintain pieces. Consequently, the Procedure section is one of the most important parts of an SQR program. Multiple Procedure sections can exist in your program, but must have unique names and be invoked with the help of the Do command. Every procedure starts with the Begin-Procedure command (with an optional procedure name) and ends with the End-Procedure command. Procedures may contain commands and one or more of the following paragraphs: Select, SQL, or Document. (We will cover all these paragraphs in later chapters.) Program Test5D.sqr is an example of the usage of SQR procedures: !TEST5D.SQR !Using the Procedure Sections !**************************** Begin-Program !**************************** Do Main The Main procedure is invoked End-Program with the help of the Do command. !**************************** Begin-Procedure Main !**************************** Do Select_Employee End-Procedure The Select_Employee procedure is invoked in the Main procedure. !****************************************** Begin-Procedure Select_Employee !****************************************** Begin-Select Emplid Name From Personal_Data Where Company = 'ABC' End-Select End-Procedure !********************************** Procedures can be global or local. By default, all procedures are global; all variables and selected columns from a global procedure can be referenced in other procedures. To 74 C HAP TE R 5 BAS IC P R OGRAM S TRU C TURE TEAM LinG - Live, Informative, Non-cost and Genuine! make a procedure local, you have to explicitly declare it local using the Local keyword (as shown in the next example), or to use arguments in the procedure. All procedures with arguments are considered local. All variables and selected columns created within a local procedure can be referenced only in this procedure and will not be recognized outside the procedure. So far, all procedures in our previous examples have been global. The following example demonstrates how to explicitly declare a local procedure using the Local keyword: Begin-Procedure List_Employees Local ! … End-Procedure The second example of declaring a local procedure using the procedure arguments is Begin-Procedure List_Employees($Company) ! … End-Procedure The following rules apply to variables in local procedures: • When a query is defined in a local procedure, all selected column names are considered local and cannot be referenced in other procedures. • If a local procedure is called recursively, SQR will maintain only one copy of its local variables. • To reference a global variable in a local procedure, you have to add an underscore to the variable name after its special character $, #, or &. Examples: #_Count, $_Street, &_Emplid. • All SQR reserved variables are global. Therefore, when referencing SQR-reserved variables in a local procedure, you have to add an underscore to their names, for example, $_sql-error or #_sql-status. creating a library procedure, that is a procedure to be used later in other Note When programs, it is always a good idea to declare it local. This will help you to avoid variable name conflicts that might arise when a calling program’s variables have the same name as the library procedure variables. In that case SQR will not be able to differentiate between the variables. Declaring a variable local allows SQR to process local and global variables with the same names as different variables. As you may have already noticed, global procedures are simpler to use than local procedures. You can use variables defined in other places of the program without special P RO C E D UR E S E C T I O N TEAM LinG - Live, Informative, Non-cost and Genuine! 75 effort. At the same time, this flexibility may backfire when it comes to debugging a large program. It is easy to incorrectly reuse a variable which has already been defined somewhere else in the program. In a local procedure, this will not happen; different local procedures may use local variables with the same names without interfering with each other. One of the local procedure’s specialties is passing and accepting arguments. To better understand local procedures and their arguments, let us consider the following example: !TEST5E.SQR !Passing parameters between global and local procedures !******************* Begin-Program !******************* Do Main End-Program !******************* Begin-Procedure Main !******************* Do Select_Employee By default, the Main procedure is considered global. End-Procedure !****************************** By default, the Select_Employee Begin-Procedure Select_Employee procedure is considered global. !****************************** Begin-Select Distinct A.Company A.Emplid Show 'Selected Emplid = ' &A.Emplid Let $Found = 'N' Do Select_Empl_Name(&A.Emplid, $Name) If $Found = 'Y' Do Print_Report_Line(&A.Company, &A.Emplid, $Name) End-If From Job A Where A.Effdt <= Sysdate End-Select End-Procedure !*************************************************************** Begin-Procedure Select_Empl_Name($Empl,:$Empl_Name) !*************************************************************** Begin-Select The Select_Empl_Name B.Name procedure is local: it uses Show 'Found ' $Empl ' ' $Empl_Name input and output argument. Move 'Y' To $_Found Move &B.Name To $Empl_Name From Personal_Data B Where B.Emplid = $Empl 76 C HAP TE R 5 BAS IC P R OGRAM S TRU C TURE TEAM LinG - Live, Informative, Non-cost and Genuine! End-Select End-Procedure !********************************************************** Begin-Procedure Print_Report_Line($Company, $Empl, $Name) !********************************************************** Print $Company (+1,1) The Print_Report_Line procePrint $Empl (,+2) dure is local: it uses input arguments. Print $Name (,+2) End-Procedure !********************* In the example, the Main procedure is a global List variables may not procedure that uses the Do command to invoke Note be passed as parameanother global procedure, Select_Employee. Every ters to local procedures. database column value selected in Select_Employee is available throughout the entire program because this procedure is global. After the value of Emplid is found, it is passed to procedure Select_Empl_Name to obtain a name of the selected employee. Procedure Select_Empl_Name is local because it uses arguments. In our case, there are two arguments: variable $Emplid is passed to Select_Empl_Name as an input argument, whereas variable $Empl_Name is returned back to the calling procedure as an output argument. All output procedure argument names must be prefixed with a colon in the Begin-Procedure statement. Please note that when referencing global variable $Found in local procedure Select_Empl_Name, we added an underscore to the variable name after its special character $. KEY POINTS 1 SQR page consists of a heading, a body, and a footing. 2 SQR accumulates all the report page lines in the memory and writes the entire page into the output file. 3 In most cases, SQR processes programs in two steps: the compile step and the execution step. 4 An SQR program may include the Program section, the Setup section, the Heading section, the Footing section, and the Procedure section. The only required section is the Program section. P RO C E D UR E S E C T I O N TEAM LinG - Live, Informative, Non-cost and Genuine! 77 KEY POINTS (CONTINUED) 5 The Setup section determines the overall characteristics of the program. This section is optional. The Setup section is processed during the program compilation stage before actual program execution. 6 Commands in the Heading and Footing sections are executed every time SQR generates a page. 7 The Procedure section is used to break your program into manageable and easy to maintain pieces. You can have multiple Procedure sections in your program. Each procedure is invoked with the help of the Do command. 8 Procedures can be global or local. By default, all procedures are global. To make a procedure local you have to explicitly declare it local or define procedure arguments. 9 All procedures that have arguments are local. 10 To reference a global variable within a local procedure, you have to add an underscore to the variable name after its type character $, #, or &. 11 All SQR reserved variables are global, therefore, when referencing SQR reserved variables in a local procedure, you have to add an underscore to their names. 78 C HAP TE R 5 BAS IC P R OGRAM S TRU C TURE TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 6 Working with data from a database IN THIS CHAPTER • The SQR Select paragraph • SQR commands in the Select paragraph • The print position parameters in the Print command • Referencing SQL table columns and SQL expressions • The two ways of printing selected columns in the Select paragraph: explicit printing and implicit printing • The Lookup and Load-Lookup commands 79 TEAM LinG - Live, Informative, Non-cost and Genuine! 6.1 The Select paragraph The Select paragraph The Select paragraph is the essence of an SQR program. It is crucial that you understand how it works. The beauty of the Select paragraph is that it is actually a combination of SQL SELECT statements and SQR processing; it allows you to apply SQR commands to the values of the selected rows. The Select paragraph starts with the Begin-Select statement, followed by a list of all columns to be selected from the database as well as the table names and the selection criteria. It is very similar to the SQL SELECT statement. The End-Select command is coded at the end of the Select paragraph. All selected column names are placed one per program line at the beginning of each line with no comma. Alternatively, you can place multiple columns on one program line but, if this format is used, you have to separate the column names with commas. What makes the Begin-Select statement different from a plain SQL SELECT statement is that you can insert SQR commands in this statement. Here is an example: Begin-Select Emplid Name City State Do Print_Row From Personal_Data Where Name like 'A%' End-Select You can insert SQR commands in the Select paragraph. In the example, all rows from the All selected columns must Personal_Data table with the column Name Note be listed in the Select values starting with A are selected from the dataparagraph; you cannot use an base. Please note that all column names are coded asterisk in the Select paragraph. at position one. All command lines, however, must be indented. This is how SQR distinguishes between column names and SQR commands. In our example, command Do Print_Row is indented relative to all column names. Let us see how this all works. What would happen if we did not have the Do command in our program? SQR would simply select the proper rows one by one and do nothing. Every selected row would overlay the previous row. Fortunately, SQR allows you to execute any number of SQR commands for each selected row. In our simple example, the Do command invokes the Print_Row procedure for each selected row. Later, in the following chapters, you will see that you can apply sophisticated logic when processing the selected rows. The Select statement actually creates a program loop and 80 CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! invokes the specified SQR commands in every loop iteration. Figure 6.1 explains the concept of the Select loop. The loop will end when there are no more rows left to satisfy the Where clause criteria. Figure 6.1 The Select paragraph with SQR commands Since SQR commands are executed after all requested column values from a table row are selected, these commands can be placed anywhere in the Select paragraph (between Begin-Select and From) as long as they are indented. For example Begin-Select Emplid Move &Emplid to $Emplid Show 'Emplid = ' &Emplid Name Show 'Name = ' &Name City Do Get_Hire_Date Do Print_Row From Personal_Data Where State = 'NY' End-Select As you can see, two procedures—Get_Hire_Date and Print_Row—are called from the Select paragraph with the help of the Do command. You can have more than one Select paragraph in your program and the invoked procedures may also contain one or more Select paragraphs: THE SELECT PAR AGR APH TEAM LinG - Live, Informative, Non-cost and Genuine! 81 !TEST6A.SQR !Using Nested Selects !*********************************** Begin-Procedure Get_Employees !*********************************** Begin-Select Emplid Move &Emplid to $Emplid Show 'Emplid = ' &Emplid Name Show 'Name = ' &Name City State Zip Do Get_Hire_Date Do Print_Row From Personal_Data Where State = 'NY' End-Select End-Procedure !*********************************** Begin-Procedure Get_Hire_Date !*********************************** Begin-Select Invoked procedures may include Hire_Dt one or more Select paragraphs. From Employment Where Emplid = $Emplid End-Select End-Procedure !********************************** Begin-Procedure Print_Row !********************************** Print $Emplid (+1,1,11) Print &Name (,+2,30) The numbers in parentheses in Print &City (,+2,20) the Print command indicate page line, page column, and outPrint &State (,+2,15) put field length. Print &Zip (,+2,10) Print &Hire_Dt (,+2,10) End-Procedure !*********************************** The numbers in parentheses in the Print command indicate the following: the first number defines the absolute or relative page line number; the second number defines the absolute or relative print column number; and the third number indicates the output field length (if omitted, the selected column length is used as the default). (We will discuss the Print command in detail in chapter 9 and subsequent chapters.) 82 CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! Take a closer look at the example. In the Get_Employees procedure, for every row selected from Personal_Data, two procedures are called: one to obtain Hire_Dt and another to print all information selected for the employee. This is an example of the nested query technique. The bind variable $Emplid is used to join the inner query with the outer query. (See chapter 11 for a discussion of bind variables.) What would happen if the inner query did not return any rows from table Employment? When a row is not returned from a query, none of the SQR commands in the Select paragraph are executed. In our case, variable &Hire_Dt for this particular employee would be set to the NULL value. All other variables, however, would be assigned their respective values, and the program would still generate an output line. To avoid the problem, our program needs some refinement. Let us initialize string variable $Hire_Date with spaces and print the hire date value from string variable $Hire_ Date rather than from SQR column &Hire_Dt. Just a reminder: SQR column names start with & whereas string variable names start with $. This is how procedures Get_ Hire_Date and Print_Row will look after the change: !TEST6B.SQR !Avoiding printing the NULL values !**************************************** Begin-Procedure Get_Hire_Date !**************************************** To $Hire_Date Move ' ' Begin-Select Hire_Dt Move &Hire_Dt To $Hire_Date From Employment Where Emplid = $Emplid End-Select End-Procedure !**************************************** Begin-Procedure Print_Row !**************************************** Print $Emplid (+1,1,11) Print &Name (,+2,30) Print &City (,+2,20) Print &State (,+2,15) Print &Zip (,+2,10) Please note we print Print $Hire_Date(,+2,10) $Hire_Date, not &Hire_Dt. End-Procedure !**************************************** The first SQR command in procedure Get_Hire_Date just initializes variable $Hire_Date. If the SQL query returns a row, SQR will move the returned value to $Hire_Date; if no rows are returned, the Move operator (the only SQR command in the Select paragraph) will not be executed, and $Hire_Date will remain filled with spaces. THE SELECT PAR AGR APH TEAM LinG - Live, Informative, Non-cost and Genuine! 83 Another way to deal with empty SQL result sets is to use a switch variable. Let us change the Get_Hire_Date procedure !TEST6C.SQR !Using flags to check if any rows were selected !**************************************** Begin-Procedure Get_Hire_Date !**************************************** Move 'N' to $Found_Flag Begin-Select Hire_Dt Move 'Y' to $Found_Flag From Employment Where Emplid = $Emplid End-Select End-Procedure !**************************************** Begin-Procedure Print_Row !**************************************** Print $Emplid (+1,1,11) Print &Name (,+2,30) Print &City (,+2,20) Print &State (,+2,15) Print &Zip (,+2,10) The value of $Found_Flag is checked to If $Found_Flag = 'Y' make sure the row has been selected. Print &Hire_Dt (,+2,10) End-If End-Procedure !**************************************** After procedure Get_Hire_Date is executed, the Print_Row procedure checks the value of the $Found_Flag variable to make sure the row has been selected. It is always a good practice to check if your Select paragraph returns the expected rows. When a Select paragraph contains a local procedure call, make sure that any variable, which may be used as a record count, is not defined in the local procedure. Otherwise, this record count will not be available to the calling procedure. If you need to update the record count in the local procedure, use a global variable defined outside of the local procedure and do not forget to use an underscore in the variable name. For example, if the name of a record count variable is #Rec_Count, it should be referred to as #_Rec_Count in the local procedure. 84 CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! 6.2 How to reference selected columns and SQL expressions Referencing selected columns & SQL expressions You already know that when a column value is selected Column variables from a database, SQR automatically creates a column Note are read-only varivariable for this column. The name of the column vari- ables. You cannot change able is the column name prefixed with an ampersand (&). column variable values or You can use this name to reference the column variable assign them new values. later in your program. In our previous examples, all the selected table columns were automatically assigned their respective column variable names, for example, &Emplid, &Name, &Zip. If you use a table name alias in your Select paragraph, SQR will include the alias in the column variable name, for instance, &Personal_Data.Emplid or &A.Name. You can also have SQR assign a different name for a column variable by placing this new name right after the selected column in the Begin-Select statement. Why would someone want to rename a default column name? One reason is to avoid duplicate names. For example, table Personal_Data and table Employment both have the same column Emplid. You can use a different name for one of the column variables. Another possible use of name reassignment is to assign a name to an SQL expression or to an SQL aggregate function. Just one example: Begin-Select Count(*) &EE_Count Print 'Employee count = ' (1,1) Print &EE_Count (,+1) The $EE_Count column variable will From Employment_Data hold the value of SQL function Count. Where Hire_Dt < Sysdate And Empl_Rcd# = 0 ! We use this because !there may be multiple employee records End-Select 6.3 Explicit and implicit printing Explicit and implicit printing In all previous examples we used the Print command to print the selected column values. This is called explicit printing. You may have SQR print a value of a selected column without issuing the Print command by placing a position parameter immediately after the column name. The printing will be performed at the moment of selection. This method of printing is called implicit printing. Let us change the Get_Employees procedure to print the employee information right after selection: EXPL ICI T AND IMPLICIT PR INTING TEAM LinG - Live, Informative, Non-cost and Genuine! 85 !TEST6D.SQR !Implicit printing in the Select paragraph !***************************** Begin-Program !***************************** Do Get_Employees End-Program !***************************** Begin-Procedure Get_Employees !***************************** Begin-Select Emplid (+1,1) !Implicit Printing Move &Emplid to $Emplid Name (,+2) City (,+2) State (,+2) Do Get_Hire_Date From Personal_Data Where State = 'NY' End-Select End-Procedure The print position parameters next to a table column cause the column value to be printed without using the Print command. !**************************************** Begin-Procedure Get_Hire_Date !**************************************** Begin-Select Hire_Dt (,+2) From Employment Where Emplid = $Emplid End-Select End-Procedure !**************************************** Notice the difference? We eliminate the entire Print_Row procedure by placing the print position parameters in the Select paragraph. SQR automatically executes the Print command for all selected columns with print position parameters specified. Figure 6.2 shows the program output using implicit printing. Figure 6.2 86 An output of a program that uses implicit printing CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! What would be the order of print commands execution in a Select paragraph with mixed implicit and explicit printing? To answer this question, let’s run the following example and consider the results in figure 6.3. !TEST6E.SQR !Combining implicit and explicit printing in one Select paragraph !***************************** Begin-Program !***************************** Do Get_Employees End-Program !***************************************** Begin-Procedure Get_Employees !***************************************** Begin-Select Emplid (+1,1) !Implicit Printing Move &Emplid to $Emplid Print 'After Move Command. This is an example of explicit printing' (+1,6) Name (+1,1) City (,+2) State (,+2) Print 'About to call Get_Hire_Date procedure' (+1,6) Do Get_Hire_Date From Personal_Data Where State = 'NY' End-Select End-Procedure !**************************************** Begin-Procedure Get_Hire_Date !**************************************** Begin-Select Hire_Dt (+1,1) From Employment Where Emplid = $Emplid End-Select End-Procedure !**************************************** Let’s analyze figure 6.3. As you can see, the program printed the employee Id first. This was the result of the first implicit printing specified for the Emplid column. Next, the program printed the output of the Print command that was coded immediately after the Emplid column, followed by the results of an implicit printing for the Name, City, and State columns. As you can see, the output of the last explicit Print command concluded the sequence. Therefore, the order of both implicit and explicit print EXPL ICI T AND IMPLICIT PR INTING TEAM LinG - Live, Informative, Non-cost and Genuine! 87 Figure 6.3 The order of both implicit and explicit print commands output is determined by the order in which these commands are coded in the Select paragraph. commands output is determined by the order in which these commands are coded in the Select paragraph. In addition to print position parameters, you can also include print edit parameters in your Select paragraph. These parameters define the output edit masks for each column, for example: Begin-Select A.Zip (,+2) Edit 99999-9999 A.Phone (,+2) Edit xxx/xxx-xxxxx From Personal_Data End-Select See chapter 9 for a complete description of all Print command parameters. 6.4 Selecting data from multiple tables Selecting data from multiple tables You can select data from multiple tables by joining these tables in the main query. Let us rewrite our program and join table Personal_Data and table Employment in the main query: !TEST6F.SQR !Using table joins in the Select paragraph !**************************************** Begin-Procedure Get_Employees !**************************************** Begin-Select A.Emplid (+1, 1) A.Name (,+2) 88 CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! A.City (,+2) A.State (,+2) A.Postal (,+2) We do not need to call the B.Hire_Dt (,+2) Get_Hire_Dt procedure. ! Do Get_Hire_Dt From Personal_Data A, Employment B where A.Emplid=B.Emplid And B.Empl_Rcd=0 End-Select End-Procedure !**************************************** Our program becomes simpler and smaller. Needless to say, simpler programs, although easier to maintain, are not always better. If records for a particular employee exist in one table, but not in the other, our simple join query would return no rows, and nothing would be printed for this particular Emplid. If, however, you need to print the information for every employee from the Personal_Data table (even if this employee has not been entered into the Employment table), you may use the nested query from our previous example instead of a table join, or use outer join. 6.5 Using the Load-Lookup and Lookup commands to improve performance Load-Lookup and Lookup commands Another very useful technique of retrieving information from multiple tables employs the Lookup and Load-Lookup commands. Not only does this method significantly improve the program performance, but it may also simplify table joins. The Load-Lookup command creates an internal memory array and populates this array Note Please note that the Return_Value paramewith keys and the corresponding column values ter in the Load-Lookup comfrom a specified table. This command can be used mand is coded with an in either the Setup section or any procedure, but underscore! This is rather unusual it is important to remember that, if the Loadfor SQR. In most cases, SQR uses Lookup command is used in the Setup section, it a hyphen in the names of its comis processed only once. When used in a procedure, mands and parameters. the command is processed every time the procedure gets executed. Load-Lookup retrieves two fields from the database: the Key field and the Return_Value field. The Key field may be of the string or numeric format, and it must refer to a column with unique values in the table. No NULL values are allowed in the field. The Return_Value field may refer to just one table column or a combination of several table columns. In case of a column combination, the columns must be concatenated. You can have the Load-Lookup command populate its array with only certain records based on some specified selection criteria (see the example following). LOAD- LOO KUP AND L OOKU P COMMANDS TEAM LinG - Live, Informative, Non-cost and Genuine! 89 The Lookup command is used to search through internal memory arrays created and populated by the Load-Lookup command. It returns the Return_Value field value for each specified Key field value. SQR uses the binary search algorithm to search through the array. The following example shows how to use these two commands. !TEST6G.SQR !Using the Lookup and Load-Lookup commands !************** Begin-Setup The Load-Lookup command cre!************** ates the Company_Names array Load-Lookup Name =Company_Names and populates the array with data Rows=200 from the Company table. Table=Company_Tbl Key=Company Return_Value=Descr Where=Country='USA' End-Setup !******************************************* Begin-Program !******************************************* Do Select_Employees End-Program !******************************************* Begin-Procedure Select_Employees !******************************************* Begin-Select Distinct Emplid (+1,1) Company Lookup Company_Names &Company $Comp_Name Print $Comp_Name (,+1) Where From Job A A.Effdt = (Select max(effdt) from Job where Emplid = A.Emplid and Empl_Rcd = A.Empl_Rcd) and A.Effseq = (Select max(effseq) from Job where Emplid = A.Emplid and Empl_Rcd = A.Empl_Rcd and Effdt = A.Effdt) End-Select End-Procedure !******************************* The Lookup command retrieves the $Comp_Name value based on the key stored in &Company. In this example, the Load-Lookup command creates an array named Company_ Names and populates the array with the company names from the Descr column of the Company_Tbl. When populating the array, Load-Lookup uses the SQL WHERE clause to select only table rows with the Country column value equal to 'USA'. 90 CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! The Rows parameter specifies the initial size of our lookup array as 200 rows. This parameter is optional and, if omitted, a default value of 100 will be used. The Rows parameter value is not critical, it just helps SQR to manage memory more efficiently. When an array becomes full, another optional parameter, Extent, will be used to increase this array. If no Extent is specified, 25% of the initial size will be used as a default value. Note that the only limit to the size of a lookup table is the amount of memory available in your computer. (The Load-Lookup command has a number of other useful parameters. For a complete list of the command parameters, please refer to the command description in appendix D.) The Lookup command in the Select_Employees procedure is executed for each selected table row and retrieves from the Company_Names array the value of $Comp_ Name based on the key value stored in &Company. Let’s consider a more complex example of the Lookup and Load-Lookup commands. Suppose you need to write field edit logic in a data extraction program for a payroll application. One of the input fields is the earning code for general deductions. This field verification can be done by performing the Deduction_Table lookup for each input record. For obvious reasons, this may not be the most efficient solution when the program input is large. This is a classic example of an instance in which the Lookup command may help to speed up processing. By loading the Deduction_Table into memory only once, and performing a binary search in the program memory (rather than reading the table to find the earning code), the processing time may be decreased ten-fold! To make the program work even faster, you can limit the size of the lookup array by selecting only a certain type of deductions with Plan_Type = '00' (General Deductions) and loading only the most current rows into the lookup array. This is how our code will look: !TEST6H.SQR !Using a complex Where clause with a subselect in the Load-Lookup !******************* Begin-Setup !******************* Load-Lookup name=Ded_Codes Rows=500 Table='Deduction_Tbl A' Key=Dedcd Return_Value=DED_PRIORITY Where='A.Plan_Type=''00'' and A.Effdt= (Select max(Effdt) from Deduction_Tbl Where Plan_Type=A.Plan_Type and Dedcd=A.Dedcd)' End-Setup !******************* Begin-Program LOAD- LOO KUP AND L OOKU P COMMANDS TEAM LinG - Live, Informative, Non-cost and Genuine! 91 !******************* !… Let $Dedcd='NETPAY' !… Do Lookup_Dedcd End-Program !***************************** Begin-Procedure Lookup_Dedcd !***************************** Lookup Ded_Codes $Dedcd $DED_PRIORITY If Not Isnull($DED_PRIORITY) Show '$DED_PRIORITY=' $DED_PRIORITY Else Let $Err = 'Dedcd '||$Dedcd ||' Not Found' Show $Err End-If End-Procedure !… As you can see, the Load-Lookup command allows for employing fairly complex Where clauses. You can also build the Where clause in the Load-Lookup command dynamically by placing it into a string variable: !TEST6I.SQR !Using a dynamically built Where clause in the Load-Lookup command !******************* Begin-Program !******************* Let $Where = 'A.Plan_Type=''00'' and' ||' A.Effdt=(Select max (Effdt) from Deduction_Tbl' ||' Where Plan_Type=A.Plan_Type and Dedcd=A.Dedcd)' Load-Lookup name=Ded_Codes Rows=5 Table='Deduction_Tbl A' Key=Dedcd Return_Value='DESCR||'',''||DED_PRIORITY' Where=$Where Let $Dedcd='NETPAY' Do Lookup_Dedcd End-Program !***************************** Begin-Procedure Lookup_Dedcd !***************************** Lookup Ded_Codes $Dedcd $Return_Val If Not Isnull($Return_Val) Unstring $Return_Val by ',' into $Descr $DED_PRIORITY Show '$Decscr=' $Descr 92 CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! Show '$DED_PRIORITY=' $DED_PRIORITY Else Let $Err = 'Dedcd '||$Dedcd ||' Not Found' Show $Err End-If End-Procedure Did you notice that the Load-Lookup command in this example was moved from the Setup section to the Program section? The Let command used to build the $Where string is not allowed in the Setup section. Please note another special feature in this example: the value in the Return_Value parameter includes not just one column name, but a comma-separated list of column names. We use a concatenation to store more than one column in the Return_Val string. In the Lookup_Dedcd procedure, we use the SQR Unstring command to parse the $Return_Val string to separate the column values. Figure 6.4. shows the Test6I.sqr execution log: Figure 6.4 The Test6I.sqr program execution log In Test6I.sqr, we used a hard-coded plan type value in the Where clause. If you want to use a string variable $Plan_Type instead of '00', you cannot simply replace '00' with the $Plan_Type. SQR will not be able to interpret this string variable correctly in a quoted expression. Here is the correct way to do this: Let $Plan_Type='00' Let $Where = 'A.Plan_Type=' ||''''||$Plan_Type||'''' ||' and A.Effdt=(Select max (Effdt) from Deduction_Tbl' ||' where A.Plan_Type=A.Plan_Type and Dedcd=A.Dedcd)' In this example, the $Plan_Type string variable is surrounded with four quotes. If we did not enclose $Plan_Type in the quotes, this part of the Where clause would look LOAD- LOO KUP AND L OOKU P COMMANDS TEAM LinG - Live, Informative, Non-cost and Genuine! 93 like A.Plan_Type = value. The correct SQL syntax must be A.Plan_Type = 'value'. Therefore, we need to concatenate $Plan_Type with a quote on each side of this variable. Because a quote is considered a special character by the SQR compiler, we need to type it twice and enclose the double quote in single quotes. Now that you know how to use the Load-Lookup and Lookup commands, let’s discuss the benefits of their usage. In particular, let’s consider whether table joins or lookups are more efficient. Generally, if your report is not too large and the number of table rows to be joined is also small, you would probably be better off using regular table joins because the Load-Lookup command loads selected table rows into the memory every time the report runs. However, for long program runs, Load-Lookup has proven to greatly improve performance. You may also consider using Load-Lookup when joining more than three large tables. KEY POINTS 1 The Select paragraph starts with Begin-Select and ends with EndSelect. 2 SQR commands can be placed in the Select paragraph anywhere between the column names and the From clause. 3 SQR commands in the Select paragraph must be indented. 4 Select * is not allowed in SQR. 5 SQR commands in the Select paragraph are executed for each selected row. 6 SQR column variables are read-only. 7 You can use an implicit Print command in the Select paragraph by placing the position qualifiers next to the selected columns. 8 There are many ways to select data from multiple tables. You can use SQL table joins, nested or hierarchical queries, or the Load-Lookup and Lookup commands. 9 Using the Lookup technique for long reports may significantly improve performance. 10 The Load-Lookup command can be used in the Setup section or in any procedure. It is used in conjunction with one or more Lookup commands. 94 CH APTER 6 WOR KING W ITH DATA FR OM A DATA BASE TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 7 Taking full advantage of SQL IN THIS CHAPTER • Recommendations on the usage of native SQL statements in SQR • The SQL paragraph • How to execute more than one SQL statement in a single SQL paragraph • The importance of handling error conditions in the OnError procedure • The difference between the update DML statements and the DDL statements and their usage in SQR • Temporary tables and the usage of the SQL paragraph in the Setup section 95 TEAM LinG - Live, Informative, Non-cost and Genuine! 7.1 Using the SQL paragraph in the procedure section SQL paragraph in the procedure section In chapter 6, we showed you how to use the SQR Select paragraph to retrieve data from your database. We learned that SQR Select is similar to SQL SELECT, but not exactly the same. You have to use strict SQR syntax to select data and to process each selected row, and cannot use native SQL SELECT statements in SQR. What about other native SQL statements? Native SQL statements other than SQL SELECT are handled in the SQL Paragraph. For example, you may use this paragraph to load data into the database or to update certain records in the database. The SQL Paragraph can be used in one of the following three sections: the Procedure section, the Program section, or the Setup section. You can reference any SQR column or variable in the SQL Paragraph. The following example shows how to use the SQL Paragraph in the Procedure section: !************************** Begin-Procedure Update-Zip !************************** Begin-SQL Update Personal_Data Set Postal = $New_Zip Where Postal = $Old_Zip End-SQL End-Procedure !************************* As you see, the SQL Paragraph starts with the Begin-SQL command, ends with the End-SQL command, and has exactly the same syntax as that for native SQL. (Please be aware that native SQL syntax may be different for different databases. Use the one that corresponds to your environment.) If you have more than one SQL statement in the same SQL Paragraph, the SQL statements must be separated by a semicolon: !**************************************** Begin-Procedure Update_Delete !**************************************** Begin-SQL On-Error=Db_Error Update Personal_Data Set Postal = $New_Zip Multiple SQL statements in one SQL Where Postal = $Old_Zip; paragraph are separated by a semicolon. Delete from Temp_Empl End-SQL End-Procedure !**************************** Begin-Procedure Db_Error 96 CHAPTER 7 TAKING F UL L ADV ANTAGE OF SQL TEAM LinG - Live, Informative, Non-cost and Genuine! !**************************** Show 'SQL Error occured in Update_Delete proc' End-Procedure !************************* The first SQL statement is terminated with a semicolon, whereas the last SQL statement does not have a semicolon at the end. In our example, we used the On-Error clause in the Begin-SQL statement. The On-Error clause specifies the name of the procedure to be executed if an error occurs during the SQL execution. We will talk more about the importance of error-handling routines and the usage of the On-Error clause in chapter 21 of this book. A professionally-written program should always have error conditions handling, and, therefore, it is extremely useful to include the On-Error routines in your program. Many SQR commands and operators have optional On-Error clauses, an option not meant to be ignored. Take another look at the previous example. It includes two unrelated table updates. The Personal_Data table is updated by changing zip codes for all records that satisfy the criterion in the Where clause. Separately, we delete all records from the Temp_Empl table. We wrote the program this way just to demonstrate how to place more than one SQL statement in one SQL paragraph. In real life, you would probably prefer to have two separate SQL Paragraphs for this type of processing to make debugging of your program much easier. In this example, all table updates are placed into separate SQL paragraphs: !************************************************ Begin-Procedure Update_Delete_Insert !************************************************ Let $SQL_Error_Text='Updating Personal Data for ZIP = ' || $Old_Zip Begin-SQL On-Error=Db_Error Table Personal_Data is updated Update Personal_Data in a separate SQL paragraph. Set Postal = $Old_Zip Where Postal = $New_Zip End-SQL Show 'Rows Updated = ' #sql-count Let $SQL_Error_Text='Deleting Temp_Empl' Begin-SQL On-Error=Db_Error The temporary table Temp_Empl is Delete from Temp_Empl emptied in a separate SQL paragraph. End-SQL show 'Rows Deleted = ' #sql-count Let $SQL_Error_Text='Inserting into Temp_Personal for Emplid=' || $Emplid Begin-SQL ON-Error=Db_Error The temporary table Personal_Data is Insert Into Temp_Personal populated in a separate SQL paragraph. (Emplid, Name , SQL PAR AGR APH IN THE PRO CEDU RE SECTION TEAM LinG - Live, Informative, Non-cost and Genuine! 97 Phone) Select Emplid, Name, Phone From Personal_Data Where Emplid = $Emplid And Country='USA' End-SQL If #sql-count=0 Show 'No rows inserted into Temp_Personal' Else Show 'Rows Inserted = '#sql-count End-If End-Procedure !********************************** Begin-Procedure Db_Error !********************************** Show 'SQL Error occurred in the Update_Delete_Insert procedure' Show $SQL_Error_Text If #sql-status = -9! If duplicate (this error code is for Oracle. ! It is database specific) Show 'Insert Error: Duplicate row is not allowed for emplid=' $Emplid Else Show 'Error number: ' #sql-status Show 'SQL-Error = ' $sql-error Stop Quiet The Quiet parameter in the Stop command causes the End-if program to stop instead of aborting with an error message. End-Procedure !***************************** Didn’t we say that SQL SELECT statements cannot be used in SQR? What about the one in our example? This is not an independent SQL SELECT statement, but rather a part of a DML statement Insert and, as such, can be used in an SQL Paragraph. In this example, we specify the same error handling procedure—the Db_Error procedure—in different SQL paragraphs. In order to have SQR print different error messages in different situations, we move the proper message text into the $Sql_Error_Text variable before executing an SQL statement. This useful technique helps to display error-specific messages while using the same routine. We also use another reserved SQR variable, $sql-error, that contains the error message returned from the database and is useful in analyzing the cause of the error. If we did not specify the On-Error argument, SQR would, by default, display an error message and abort the program. By adding the Db_Error routine, we not only log the specifics about the error, but also let SQR continue with the program execution, unless a serious error occurs in which case the Stop command is executed. If the Stop command is not invoked, execution resumes at the statement following the SQL paragraph. 98 CHAPTER 7 TAKING F UL L ADV ANTAGE OF SQL TEAM LinG - Live, Informative, Non-cost and Genuine! Note that after each SQL paragraph execution, we display the value of the SQR predefined variable #sql-count. It is a very good idea to always check this variable in order to make sure that the SQL statement has been executed correctly. Please keep in mind that the #sql-count variable is only affected by DML statements (Insert, Update, or Delete). You cannot use it to find out how many rows were selected in a query. In order to do this in the Select paragraph, use your own counter and increment its value by 1 in the body of the Select paragraph. When using the Insert, Update, or Delete statements, be aware that the RDBMS engines lock the changes made by these statements until the entire transaction is completed with the help of either the Commit operator or the Rollback operator. You can use either the SQR Commit command which should be used outside of the SQL paragraph, or your database-specific SQL COMMIT commands which can be used only within the SQL paragraph. If you use database-specific commands, keep in mind the following: • for Sybase and Microsoft SQL Server, use BEGIN TRANSACTION and COMMIT TRANSACTION • for Informix, use BEGIN WORK and END WORK • for Oracle, it is recommended to use the SQR Commit. Please keep in mind that the Commit command in some databases (for example, Oracle) automatically closes all opened cursors. Therefore, this operation should not be performed while there are any active Select operations. When your program finishes without errors, Commit is performed by SQR automatically. 7.2 DML vs. DDL statements DML vs. DDL statements In previous examples, we demonstrated the use of SQL Data Manipulation Language (DML) in the SQL paragraph. DML operators include SQL table update statements such as Insert, Update, and Delete. You can also use SQL Data Definition Language (DDL) statements such as Create, Drop, Alter, Grant, and Revoke in the SQL paragraph. Beware that SQR processes DDL statements differently from DML statements. DML statements are verified before the actual program execution. DDL statements, however, are not preprocessed prior to execution. If you happen to have a syntax error in a DDL statement, the error will not appear among the compilation errors. Oracle users have to keep in mind that DDL statements cannot be executed while your Select queries are still active. DDL statement execution triggers the Commit DML VS. DD L STATEMEN TS TEAM LinG - Live, Informative, Non-cost and Genuine! 99 operation which, in turn, closes all opened cursors. This could result in the loss of the next row content. Therefore, it is recommended to use DDL statements in the beginning or at the end of the program. Sybase users should know that Select … Into … is considered a part of a DDL statement. It is, therefore, permitted in the SQL paragraph. 7.3 Using the SQL paragraph in the Setup Section SQL paragraph in the Setup Section It may be a good idea to code your DDL statements in the Setup section of your program. In fact, both DDL and DML statements could be used in the SQL paragraph of the Setup section. We recommend, however, to help you differentiate between DML and DDL coding errors, that you keep the DML and DDL statements separate since only DML statements are preprocessed by SQR in the compile stage. In SQR programs, DDL statements are often used with temporary tables. Temporary tables are handy to hold the intermediate results. If you want to create a temporary table, you may code the SQL paragraph in the Setup section as follows: !*************** Begin-Setup !*************** The Warn value in the On-Error clause Begin-Sql On-Error=Warn tells SQR to display a warning message. Drop table Temp_Test_Tbl Create table Temp_Test_Tbl (Emplid char(11), Name char(30)) End-SQL End-Setup !*************************** We use the Warn parameter in the On-Error clause because, in the Setup section, the On-Error clause has a different syntax. In any other section, you have to specify an error-handling procedure name in the On-Error clause. In the Setup section, you can use the On-Error clause with one of the following parameters: • Stop is used when you want to halt the program if an error occurs. Note that the rest of the program will be scanned for errors but will not be processed. • Warn is used when you want a warning message to be displayed when an error occurs. • Skip is used when you want to ignore all errors and to continue to run the program. The default On-Error parameter in the Setup section is Stop. You may use more than one SQL paragraph in the Setup section, each with different error-handling routine. 100 CHAPTER 7 TAKING F UL L ADV ANTAGE OF SQL TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS 1 You can use native SQL statements in SQR with the exception of the SQL SELECT statement. 2 It is a good idea to use the On-Error clause when updating tables. 3 SQR verifies SQL DML statements at the compile stage, but leaves SQL DDL statements unchecked. 4 The best place to create temporary tables is the Setup section. S QL P A R A GR A P H I N T H E S E T UP S E C T I ON TEAM LinG - Live, Informative, Non-cost and Genuine! 101 C H A P T E R 8 Loops and decision logic in SQR IN THIS CHAPTER • The three major elements in the SQR logical expressions – operands – relational, string, and numeric operators – built-in functions • The order in which SQR evaluates different parts of a logical expression • Different types of the If … Else operators • The Evaluate operator • The While loop in SQR 102 TEAM LinG - Live, Informative, Non-cost and Genuine! 8.1 Logical expressions Logical expressions As with most programming languages, SQR includes basic program flow control logical operators. Each SQR operator (If … Else, Evaluate, While) usually contains one or more logical expressions to be evaluated. After evaluation, logical expressions return a numeric value: zero if FALSE or non-zero if TRUE. Here are a few examples of logical expressions in SQR: (#Salary >= #Max_Salary) #Count Not #sql-status = 0 Strtodate(&Effdte, 'MM-DD-YYYY') < $Calc_Date And &Paygroup = 'BW1' An SQR logical expression can be a combination of three major elements: operands, operators, and functions. Do not confuse SQR logical expressions with SQR expressions used in the Let command. Both types of expressions look alike and use the same named elements, but expressions in the Let command have different rules than those for logical expressions. For example, you can mix operands of different types in expressions used in the Let command, but you cannot do this in logical expressions: If #Num = $Char Let #Num = $Char ! Error: you cannot mix numeric and ! string operands in a comparison ! Correct As you can see, the expressions in the two statements are identical, but the first statement results in an error because it includes a string-to-number comparison, while the second statement, in which it converts a string to a number is perfectly legal. Another major difference between logical expressions and expressions in the Let command occurs when SQR processes logical expressions. SQR does not change the values of operands in these expressions, whereas the execution of an expression in the Let command may result in a change in the target operand value as in: If #Num1 = #Num2 Let #Num1 = #Num2 ! The values of #Num1 and #Num2 are not changed ! The value of #Num1 is changed 8.2 Operands in logical expressions Operands logical expressions Logical inexpressions can be simple elementary (atomic) expressions, such as comparisons of two variables, or combinations of atomic expressions. Operands in atomic SQR logical expressions must be of the same type, but you can mix different elements as long as they belong to different atomic expressions. For example, logical expression OPERANDS IN L OGICAL EX PRES S IONS TEAM LinG - Live, Informative, Non-cost and Genuine! 103 $Char > #Num ! Error is incorrect because it mixes string and numeric operands in one atomic expression, but $Char1 > $Char2 And #Num1 > #Num2 ! Valid is a valid logical expression. If a logical expression contains two numeric operands of different precision, SQR converts an operand with lower precision to match the operand of higher precision. Integer variables are considered the lowest in precision, followed by float variables, and then decimal variables as the highest in precision. 8.3 Relational, string, and numeric operators in logical expressions Relational, and numeric operators A logicalstring, expression may include multiple operations on different operands. The result of the expression evaluation may depend not only on operand values, but also on the order of these operations, making it important to know the order in which SQR will execute operations in the expression. Normally, this sequence is defined by SQR precedence rules, but it can be overridden by using parentheses. Operators of the same precedence are processed in sequence, from left to right, within the logical expression. Two types of logical operators exist: • relational operators • arithmetic and string operators All relational operators have lower precedence to arithmetic and string operators. Refer to tables 4.2 and 4.3 in chapter 4. These tables list separately relational SQR operators and arithmetic and string SQR operators, respectively. The tables present operators in the descending precedence order with the lowest precedence number set to zero. Expressions with relational operators look like this: #Rate >= &Average_Rte And &B.Paygroup <> 'M01' $Empl_Name = &A.Name $Date1 <= &Effdt OR $Record_Num <= &B.Empl_Rcd# (#Total = &Salary_Total) Or (#Total = #Calc_Total) Here are examples of expressions with string and numeric operators: &Company || &Paygroup <> $Security_Code (#Ytd_Salary + #Ytd_Bonus) * #Soc_Sec_Rte > #Annual_Limit -#Company_Match datetostr($New_Date,'YYYY')>'1999' 104 CHA PTER 8 LO OPS AND DECISION LO GIC IN SQR TEAM LinG - Live, Informative, Non-cost and Genuine! The use of parentheses in complex expressions is recommended even if there is Note no need to override operator precedence. Parentheses make your expressions more readable and easier to debug. 8.4 Functions Functions You can use any number of built-in functions in a logical expression. You can also use a function within another function. (Note, though, that when a function uses another function as its argument, the inner function must return a value whose type is compatible with that of the outer function.) SQR provides a rich variety of numeric, string, date, file-related, and miscellaneous functions. Please refer to the Let command discussion in chapter 4 and to appendix C where all SQR functions are listed. The following are just a few examples of SQR logical expressions with built-in functions: (Isnull(&Amount)) Or Not #sql-status = 0 To_char((#A + #B)*#C) Length(&A.Name) >= 25 Upper(Rtrim(&A.Last_Name,' ')) || ' ' || &First_Name = $Input_Name When calculating amounts, always use the Round function to maintain consistent decimal places. 8.5 Using If … [Else] operators Using operators The IF If... [Else] operator is a backbone decision-making construct in any programming lan- guage. SQR is not an exception. In its simplest form of If <Logical expression> <SQR commands> End-If the If operator allows your program to examine the logical expression and, based on the test result, designate courses of action. For example: If (#Salary > #Salary_Limit) Do Print_Error End-If Sometimes, this form of the If operator is called a single-alternative If operator. If, however, we want to have more than one alternative courses of action, depending on the value of the tested logical expression, we will need another form of the If statement, a dual-alternative If operator: US ING IF ... [EL SE] OPERA TORS TEAM LinG - Live, Informative, Non-cost and Genuine! 105 If <Logical expression> <SQR commands> Else <SQR commands> End-If SQR commands that follow the If and Else parts of the operator must start on a new line as in If (#Salary > #Salary_Limit) Do Print_Error Else Do Calculate_Bonus End-If The result of evaluation of the logical expression in the If statement is either FALSE (zero) or TRUE (non-zero). Remember that TRUE is not necessarily one. It can be any non-zero value. Therefore, instead of logical expression (#Count<>0) you can use #Count in the following example: If #Count Show 'Number of rows processed = ' #Count Else Show 'There were no rows found' End-If Each If statement must also have a matching End-If statement to help SQR correctly parse nested If operators (if any): If (&A.Empl_Status = 'T') Do Process_Terminated_Employees Else If (&A.Empl_Status = 'L') Do Process_Leave_Of_Absence Do Print_LOA Else If (&A.Empl_Status = 'A') Do Process_Active_EE End-If End-If End-If 8.6 The Evaluate statement in conditional processing The Evaluate statement Multiple-level nested If … [Else] operators may look confusing and difficult to debug. The Evaluate statement offers a special form of multiple-alternative decision making 106 CHA PTER 8 LO OPS AND DECISION LO GIC IN SQR TEAM LinG - Live, Informative, Non-cost and Genuine! that is useful when you have a large decision tree and all decisions depend on a value of the same variable. We can rewrite the previous example of nested If … [Else] operators to use the Evaluate statement: Evaluate &A.Empl_Status When = 'T' Do Process_Terminated_Employees Break When='L' Do Process_Leave_Of_Absence Do Print_LOA Break When='A' Do Process_Active_EE Break When-Other Show 'Unknown Empl, Status ' &A.Empl_Status Break End-Evaluate The argument in the Evaluate statement is compared with the value in the first When statement. If the result of the comparison is TRUE, SQR executes the commands below the When. If the expression is FALSE, the next When is evaluated. The optional When-Other statement is a “catch-all” clause that specifies the default actions when all previous When statements yield a FALSE result. The Break command is used to specify an immediate exit off the Evaluate statement. If Break is not coded, the comparison in the next When statement is executed, which takes additional processing time. It is a good habit to use Break after the processing commands to specify exit when needed. In some cases, the Break command not only saves processing time, but can also spare you from a programming error. Let’s consider the following code: !… Evaluate #Number When = 5 Do Proc_1 When = 6 Do Proc_2 When = 7 Do Proc_3 When-Other Do Proc_Other End-Evaluate !… Begin-Procedure Proc_1 Let #Number = #Number + 1 End-Procedure THE EVAL UA TE STA TEMENT TEAM LinG - Live, Informative, Non-cost and Genuine! 107 This is a typical SQR problem with the Evaluate statement, since it is easy to come to the conclusion that this logical construction works the same way as similar commands in C and some other languages. The difference is that, in every When statement, SQR evaluates not the original value of #Number, but the current one. In the example previous, if the Proc_1 procedure changes the value in #Number from 5 to 6, the result of the logical expression evaluation in the next When statement will be True, and the Proc_2 procedure will be also executed. Therefore, placing the Break command before the next When statement eliminates any possible problems. There may be more than one When statement—sometimes, one after another—in which you need to specify multiple alternative values for comparison: Evaluate &A.Empl_Status When = 'T' Do Process_Terminated_Employees Break When='L' When='A' Do Process_Active_EE Break When-Other Show 'Unknown Empl, Status ' &A.Empl_Status Break End-Evaluate Procedure Process_Active_EE will be executed if &A.Empl_Status='L' or &A.Empl_Status='A', i.e., if any of these conditions is TRUE. Use the When-Other statement to specify the default process when all other When statements return FALSE. It must be the last argument. 8.7 Using the While command in loops Using the are Whilepowerful command incomputer loops Loops language constructs that enable computers to perform many repetitive tasks. Without loops, computers might not exist. The While … EndWhile statement is a classical loop construct used to process the body of the loop a number of times while the specified condition is TRUE. Once the condition becomes FALSE, the repetition terminates. For example: While #Max_Records < 100 Do Load_Table1 ! This is the body of the Add 1 to #Max_Records ! While loop statement End-While 108 CHA PTER 8 LO OPS AND DECISION LO GIC IN SQR TEAM LinG - Live, Informative, Non-cost and Genuine! A logical expression in the While loop is tested for TRUE or FALSE. Logical expression (#Max_Records<100) is evaluated at the end of each loop iteration. As soon as this expression turns out FALSE (when the value of #Max_Records reaches the limit of 100), the End-While statement is reached and the loop ends. Logical expressions in the While statement may include multiple conditions: While ($Reprint <> 'Y') And ($Reprint <> 'N') Input $Reprint 'Is this a Reprint [Y/N] ?' Let $Reprint = Upper(Rtrim($Reprint, ' ')) End-While To exit from the While loop before the While test condition returns FALSE, use the Break command: While #I < #Total_Records_Read If &A.Empl_Stat = 'T' Break End-If If &A.Empl_Stat = 'A' Do Process_Active End-If Add 1 To #I End-While Note that the Break command causes an exit from the entire While loop, not from the If statement. The Break command cannot be used to exit from If statements. You can have nested While statements each with its own End-While statement: Begin-Program Let $Input = 'Y' While $Input <> '' Input $Input 'Please Enter Numeric code or Press Enter to Exit' Let #loc = 0 While #loc < Length($Input) Extract $char from $Input #loc 1 Evaluate $char when < '0' when > '9' Let $loc = edit((#loc+1),'999') Show 'Not Numeric Value found, char = ' $char ' loc = ' $loc break End-Evaluate Add 1 to #loc End-While End-While End-Program US ING THE WH ILE COMMAND IN L OOPS TEAM LinG - Live, Informative, Non-cost and Genuine! 109 Notice the two While loops. The outer loop checks for the end of user entry executing commands until the user enters an empty string. The inner loop examines every single character of the input string, checking for numbers and stopping after the last character in the input is checked. Have a look at the log file of the sample run in figure 8.1. Figure 8.1 The Test8A.log file KEY POINTS 1 An SQR logical expression can be a combination of three major elements: operands, operators, and functions. 2 There are two types of logical operators: relational operators, and arithmetic and string operators. 3 Parentheses in logical expressions help you to override operator precedence. 4 Parentheses in complex logical expressions make your expressions more readable and easier to debug. 5 You can use any number of built-in functions in a logical expression. 6 The result of a logical expression evaluation in the If statement is either FALSE (zero) or TRUE (non-zero). 7 The Evaluate statement is useful when you have a large decision tree, and all the decisions depend on a value of the same variable. 8 The Break command is used to exit from the While or Evaluate loops. 110 CHA PTER 8 LO OPS AND DECISION LO GIC IN SQR TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 9 Enhancing your report IN THIS CHAPTER • Output formatting • Using editing masks – text format masks – numeric format masks – date format masks • Using the Position command • Using the Next-Listing command to create detail groups in a report • Vertical and horizontal spacing 111 TEAM LinG - Live, Informative, Non-cost and Genuine! In chapter 6, we introduce explicit and implicit printing of selected table columns. Since SQR’s main job is to generate reports, one of the most important things SQR needs to know is where and how you want your data to appear on the page. The Print command places data on the page, indicating the row, the column, and the length. A second method—the Document paragraph—is useful for laying out the page and mixing it with data of different formats, for example, creating form letters. The Document paragraph will be covered in chapter 15. 9.1 Using the Print command Because the Print command places data on the page grid, you must specify the position and length of each output field with the help of three parameters (X, Y, Z). X signifies the line position of the page; Y, the column number; and Z, the number of positions allocated for the field. It is important to remember that the line position is always specified relative to the current page section, not to the entire report page. If you use the Print command in the Heading section, you have to specify the line number within the heading portion of the page. If the Print command is used within any section other than the Heading or Footing section, the line number will be considered within the body of the report. The Print command used in the Footing section should refer to the line number in the footing portion of the report. For example Begin-Heading 1 Print $Report_Name (1,1,20) End-Heading The Print command in the example places the value of the text variable $Report_Name in the first line of the heading portion of your report, starting from position one and allocating twenty positions in the page for this variable. X,Y,Z can be numeric literals or variables. The position can be either absolute or relative. To indicate that a relative position is used, place the plus or minus sign before the position parameter X or Y (if you use a variable in place of X or Y, you can use only the plus sign). All relative positions are coded relative to the current position on the page. You can also use variables containing negative numbers to indicate relative negative positions, but you have to place the plus sign before the variable names. If you omit the position value or code a zero value instead, SQR will use the current position as default. If you specify zero for the length parameter Z or omit this parameter, the variable actual length will be used. The following illustrates the parameter rules: !Different methods of coding the print position qualifiers !in the Print command 112 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! Print $Name (+1, 1, 20) ! Print $Name on the next line, ! position 1,width=20 Print $Name (+2, +3) ! Print $Name 2 lines below the current line, ! starting from the current column + 3 ! and use width = actual length of $Name. Let #Line=-2 Let #Column=5 Let #Length=10 Print $Name (+#Line,#Column,#Length)! Print $Name in the line that ! is 2 lines above the current ! line, from column 5, and ! allocate only 10 positions for ! field $Name. Print $Name (,+3) ! Print at the current line position, ! current column+3, use the actual length of ! $Name for space allocation. Print $Name () ! Print in the current line, current column, ! use actual field $Name length Print $Name(0,0,0)! Same as above Let #i = -1 Let #j = -2 Print 'Error' (#i, #j)! This is incorrect, you must place a plus ! before variables containing negative numbers Let #i = -1 Let #j = -2 Print 'Error' (-#i, -#j) ! This is incorrect, you must place a plus ! before variables containing negative ! numbers Let #i = -1 Let #j = -2 Print 'Correct' (+#i, +#j) ! Now, this is correct. All these rules are also valid for implicit printing (in the Select paragraph only). You can implicitly print a table column by specifying the print position coordinates (X,Y) and the space allocation Z to the right of the column name, as shown in this example. !TEST9A.SQR !Specifying print positions while using implicit printing Begin-Program Do Select_EE End-Program Begin-Procedure Select_EE Begin-Select Emplid (+1, 1,11) Name (, +2, 20) US ING TH E PRINT COMMAND TEAM LinG - Live, Informative, Non-cost and Genuine! 113 City (, +2, 15) State (, +2) From Personal_Data Where State = 'CA' End-Select End-Procedure Figure 9.1 shows the output of our example. 6601 6603 7702 7705 8001 8101 8102 8105 8121 8201 8202 8203 8223 8225 Jones,Gladys Pitman,Earl Atchley,Tamara Holt,Susan Schumacher,Simon Penrose,Steven Sullivan,Theresa DeHaven,Joanne Gregory,Jan Rifkin,Cheri Hadley,Charles Webb,Floren Buchanan,Cheryl Sterling,Sharon Los Alamos Los Angeles San Diego La Mirada Moraga Lafayette Moraga Danville Alamo Walnut Creek Lafayette Concord Danville Danville CA CA CA CA CA CA CA CA CA CA CA CA CA CA Figure 9.1 Using print positions in implicit printing You can also use substitution variables defined in the beginning of your program to code all printing parameters. The benefit of using substitution variables is that you can use the same variable in the Print commands throughout your program. Substitution variables (discussed in detail in chapter 11) can be used explicitly in the Print command or implicitly by using substitution variables in place of print parameters next to table column names. The following example shows how to combine the usage of substitution variables and implicit printing: !Using substitution variables to specify print positions Begin-Procedure Init #Define col_emplid 1 #Define col_name 15 #Define col_city 30 #Define col_state 40 End-Procedure ! … Begin-Select Emplid (+1, {col_emplid}) Name (, {col_name}) 114 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! City (, {col_city}) State (, {col_state}) From Personal_Data Where State = 'NJ' End-Select 9.2 Formatting your output You can use a number of print format options to enhance the appearance of your report (see the complete list of options of the Print command in appendix D). In this chapter, we will cover the most commonly used format options. You can use the Bold format qualifier to print a string in bold type: Print $Name (+1, 1) Bold Keep in mind, however, that for some printers, the Bold format may not work without some special effort. For example, for HP LaserJet printers, the appropriate boldface font must be loaded into the printer, and for some fonts, the bold option may not be available. The commands Declare-Printer and Alter-Printer are used to control Note font selection. The Print command does not have this capability. We will discuss Declare-Printer and Alter-Printer in chapter 13. To underline the printed string, use the Underline format qualifier: Print $Header (2, 5) Underline When printing the heading information, the Center format qualifier is really handy. You can use it for any line you want to print in the center of your report. When the Center option is used, the column positions are ignored. To determine column positions, SQR uses the Page-Size parameter in the Setup section. If you do not code the Page-Size parameter, SQR will use the default value. Print 'Test Report' () Center Often, it is necessary to fill a whole line or a part of a line with the same characters. You can save on keystrokes by using the Fill format qualifier for this purpose: Print '*' (1,1,25) Fill ! Fills line with 25 asterisks If you need to wrap a line, you may use the Wrap format qualifier. FOR MATTING YOU R OU TPUT TEAM LinG - Live, Informative, Non-cost and Genuine! 115 Print $Comments (1,1) Wrap 15, 3 This command prints the value of string variable $Comments in several lines, fifteen characters per line for a maximum of three lines. When using the Wrap qualifier, the wrapdepth() function introduced in version 4.2 can be helpful in controlling string wrapping dynamically. This function returns the number of print lines required to wrap the specified string, thus allowing you to avoid hard-coding this parameter. Match is another interesting print format qualifier. It allows you to replace the orig- inal value with another value from a specified keyed value list thereby eliminating the need to write a conversion subroutine. For example, if we have an employee status code in the $Empl_Status variable but need to print the employee status instead of the code, the Match option will spare us the writing of a routine that will convert status code to status description: Print $Empl_Status (0, 16) Match A 0 25 'Active' T 0 25 'Terminated' L 0 25 'On Leave' When you use the Match format qualifier, if SQR finds a match, it will print the substituted value in the line and position specified in the Match parameters. Otherwise, SQR will print the original value in the line and position specified in the Print command. In our case, if a match is found, the corresponding substituted value ('Active', 'Terminated' or 'On Leave') will be printed in position 25 of the current line. If no match is found, an original employee status code will be printed in position 16 of the current line. 9.3 Using edit masks The Edit argument is the most powerful among all Print command options. This option uses special format masks. When this argument is coded, the field is edited before it is printed. There are three types of edits: Text Edit, Numeric Edit, and Date Edit. Each of these three edit types has a different set of rules for mask coding. Let’s take a look at mask types: 9.3.1 Text format masks These special characters are used for text format masks: • X place a character in the output: Move '123451234' To $Zip 116 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! Print $Zip (1,1) edit xxxxx-xxxx ! the output will be 12345-1234 • b insert a blank in the output: Move '2031231234' To $Phone Print $Phone (1,1) edit (xxx)bxxx-xxxx ! the output will be (203) 123-1234 If you enclose the edit masks into quotes, you can use spaces to indicate blank characters. • ~ (tilde) skip a character in the output: Print 'ABCDEFGH' (1,1) edit ! the output will be ABCDGH • R xxxx~~xx reverse the sequence of characters in the string for languages such as Hebrew • any character can be used as a text constant in the output: Move '12A' To $Apt Print $Apt (1,1) edit Apt.bxxxxx ! the output will be Apt. 12A The characters 8, 9, 0, V, and $ are used as special indicators for numeric format Note masks, and therefore, cannot be used as parts of text format masks. If you need to use any special character as a part of a mask text, place a backslash (“\”) before this character. This is true not only for special numeric characters, but for any other special character, for example, “b”, “x”, “~”, etc. 9.3.2 Numeric format masks The numeric format masks use a number of characters that create special editing effects to the output: • 8 digit, zero fill to the right of the decimal point, trim leading blanks (left justify the number) • 9 digit, zero fill to the right of the decimal point, space fill to the left Let #Unit_Price = 10.459 Let #Quantity = 10 Let #Total = 104.59 Let #Credit_Balance = -50.00 Print #Unit_Price (1,1) edit 9999.99 Print #Quantity (1,+3) edit 9999.99 Print #Total (1,+3) edit 9999.99 Print #Credit_Balance (1,+3) edit 9999.99 ! The output will be: 10.46 10.00 104.59 -50.00 ! Please note that SQR performed decimal rounding for #Unit_Price US ING EDIT MASKS TEAM LinG - Live, Informative, Non-cost and Genuine! 117 • 0 digit, zero fill to the left • B treated as a “9” but if a value is zero the output is printed blank • $ dollar sign, optionally floats to the right Let #Unit_Price = 10.459 Let #Quantity = 10 Let #Total = 104.59 Let #Credit_Balance = -50.00 Print #Unit_Price (1,1) edit $$$$.99 Print #Quantity (1,+3) edit 9999 Print #Total (1,+3) edit $$$$.99 Print#Credit_Balance (1,+3) edit $$$$.99 ! The output will be: $10.46 10 $104.59 $-50.00 • V implied decimal point Let #Total = 12104.59 Print #Total (1,1) edit ! The output will be: $,$$$,$$$V99 $12,10459 • MI if placed at the end of the mask, causes a minus to be displayed at the right of a negative number • PR if placed at the end of the mask, causes angle brackets (< >) to be displayed around a negative number • PS if placed at the end of the mask, causes parentheses to be displayed around a negative number • NA if placed at the end of the mask, causes “N/A” to be displayed if the numeric column variable is NULL • NU if placed at the end of the mask, causes blanks to be displayed if the numeric column variable is NULL • E denotes scientific format. The number of 9s after the decimal point determines the number of significant digits displayed • .(dot) denotes decimal point • ,(comma) denotes comma. 9.3.3 Date and time format masks Date format masks are used not only for printing, but also in the date manipulation built-in functions. As with the text or numeric masks, the date format masks use different characters to control the display of date format fields: • YYYY 4-digit year; YYY, 3-digit year (the current millennium is assumed); YY, 2-digit year (the current century is assumed); Y, 1-digit year (the current century and decade are assumed) 118 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! • RR 2-digit year, the century is calculated as follows: – if RR is between 00 and 49 and the 2-digit current year is between 00 and 49, the current century is assumed – if RR is between 50 and 99 and the 2-digit current year is between 50 and 99, the current century is assumed – if RR is between 00 and 49 and the 2-digit current year is between 50 and 99, the next century is assumed – if RR is between 50 and 99 and the 2-digit current year is between 00 and 49, the century before the current one is assumed • Q quarter of year (1–4) • WW week of year (1–53). W week of month (1–5) • DDD day of year (1–366). DD day of month (1–31). D day of week (1–7) starting from Sunday. DAY name of day. DY abbreviated name of day • MONTH full name of month. MON abbreviated name of month. MM month (1–12) 2-digit • RM Roman numeral month (I–XII) • CC century • BC or AD BC / AD indicator • HH, MI, SS hour (based on 24 hour clock), minute (0–59), second (0–59). • NNNNNN fractions of second (the precision depends on the computer, operating system, and database used) • AM or PM • | meridian indicator used to concatenate different masks Any other characters in a date mask are treated as parts of a constant and will be included in the output field as text. If you would like to include in this text some characters that are special date mask characters from the previous list, you have to precede the entire text with a backslash (\). The masks DAY, MONTH, MON, AM, PM, BC, and AD are case-sensitive and follow Note the case of the mask entered. For example, if the month is January, the mask MON results in JAN, the mask Mon results in Jan. Let us summarize what we just learned about the print format masks in the following table (here, we use the literal date format for date source fields): US ING EDIT MASKS TEAM LinG - Live, Informative, Non-cost and Genuine! 119 Table 9.1 Using print format masks Edit Mask Type Source Field Result SSN:bbxxx-xx-xxxx Text 123456789 SSN: 123-45-6789 Accountb#b~~~x~xxxxx Text 0001001234 Account # 1-01234 999,999.99 Numeric 123.4567 123.46 999V99 Numeric 123.4567 12346 9999 Numeric 123 123 9999 Numeric -123 -123 9999 Numeric -1234 **** (overflow) 9999 Numeric 12345 **** (overflow) $$$,$$$,$$$.$$MI Numeric -500,000 $500,000.00- B999,999.99 Numeric 0 (blank) 999,999.99 Numeric 0 0.00 999,990.99 Numeric 0 0.00 $$$,$$$,$$$.$$MINA Numeric NULL N/A $$$,$$$,$$$.$$MINU Numeric NULL (blank) 9.999E Numeric 123456 1.235E+005 MM/DD/YY Date 19980115 01/15/98 MM:DD:YY Date 19980115 01:15:98 MM/DD/YYYY Date 19980115 01/15/1998 CC Date 19980115 20 Q Date 19980115 1 WW Date 19980315 12 W Date 19980115 3 D Date 19980115 3 YYYYDDD Date 19980115 1999015 Day,bMONTHbDD,YYYY Date 19980115 Wednesday, JANUARY 15,1998 DAY,bDD-Mon-YY Date 19980115 WEDNESDAY, 15-Jan-98 DD-RM-YYYY Date 19981115 15-XI-1998 MM/DD/YY:HH:MI:SS Date 19980115131545 01/15/98:13:15:45 HH:MI:SS Date 131545 13:15:45 9.4 More about edit masks In addition to the Print command, edit masks can be used in other SQR commands such as Move, Concat, Display, Show, as well as in some built-in functions. Edit masks can be built and changed dynamically. You can assign a mask value to a text variable, and then reference this variable by its name, prefixed by a colon, for example: 120 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! Move '$999.99' to $My_Mask Print #Test () edit :$My_Mask SQR supports multiple language conversions for the following date format masks: MON, MONTH, DAY, DY, AM, PM, BC, and AD (see chapter 21). Beginning with version 4, SQR provides three special keywords—Money, Date, and Number—that can be used in place of edit format masks. Money is a special mask recommended for monetary fields. When this mask is used, SQR formats the output according to the current value of the environmental variable Money-Edit-Mask in the current locale. This is a good way to make your program country-independent. This mask cannot be used with date literals, variables or columns. Number can be used as the default mask when printing or displaying numeric fields. When this mask is used, SQR formats the output according to the current value of the environmental variable Number-Edit-Mask in the current locale, making sure that the proper separator for thousands and the proper decimal place indicator are used, depending on the current locale (see chapter 21 for details and examples). This mask cannot be used with date literals, variables, or columns. Date is a special keyword that can be used in place of date masks. When this keyword is used, SQR will format the output using the Date-Edit-Mask environmental variable in the current locale. If Date-Edit-Mask is not defined, the first databasedependent date format will be used. The source field must contain a valid date. As with the Money and Number keywords, the Date keyword is very helpful when writing programs designed for multiple language support: formatting all dates according to the national standards and using the names of months and days of week translated into the proper language, according to the current locale (see chapter 21). Besides being used in the Print command, edit masks can also be used to move data from one field to another, thus supporting the conversion functionality: Move #Count to $Row_Count 99999 Let $Amount = Edit(#Price * #Rate, '$99999.99') 9.5 Using the Position command You can control the look of your printouts by coding a specific position on a page. The Position command will allow you to set the current position: Position (10,2) !Set the current position to line 10, column 2 Print #Amount() ! Print amount in the current position Position (+3, 1)! Set the current position 3 lines down Print #Total () ! Print Total in current position US ING TH E POSITION COMMAND TEAM LinG - Live, Informative, Non-cost and Genuine! 121 As you may have noticed in the example, you can use either absolute or relative positions. You can also use negative position numbers to move back from the current position on the page. When you indicate print positions using plus or minus signs, make sure you do not specify positions outside the page range. The example next demonstrates the common practice of using the Position command in the Select paragraph: Begin-Select Emplid (, 1, 11) Name (,+2, 20) Position (+1) !Move the print position to the next line From Personal_Data End-Select 9.6 Controlling the vertical spacing You can use the Next-Listing command to create detail groups in your report and to control the vertical spacing between detail groups on a page as in the following example. (The output of our example appears in figure 9.2.) !Using the Next-Listing command Begin-Program Do Select_EE End-Program Begin-Procedure Select_EE Begin-Select Name (1, 1, 40) Address1 (2, 1, 30) City (3, 1, 15) State ( , +2, 2) Postal ( , +2,10) Next-Listing Skiplines=1 Need = 3 From Personal_Data Where State = 'CA' End-Select End-Procedure Here columns Name, Address, City, State, and Postal make up a detail group that is printed on three lines every time a row is selected. The Next-Listing command moves the current position to one line below the current line in the page body, skipping one line and starting the next detail group on the next line. The implicit print positions on the columns are, in fact, positions relative to the 122 C H AP TE R 9 The Next-Listing Note command does not change the value of the SQRreserved variable #currentline which holds the actual line number within the page body. EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 9.2 Using the Next-Listing command to create detail groups in a report beginning of the new detail group. The Need parameter controls the end of the page printing, directing SQR to begin a new page if there are less than three lines on the current page left, thus eliminating a potential situation in which a detail group can be broken between two different pages. You can also use the Next-Listing command when you need to create variable length detail groups by using the Wrap qualifier: !Test9c.SQR !Next-Listing command with variable length text paragraphs Begin-Program Do Select_EE End-Program Begin-Procedure Select_EE Begin-Select A.Emplid (1,1,11) A.Name (2, 1, 20) Wrap 15 2 B.Name (2, +2, 20) Wrap 15 2 Next-Listing Skiplines=1 Need = 3 From Personal_Data A, Dependent_Benef B Where A.Emplid=B.Emplid End-Select End-Procedure After a wrapped string is printed, the current position on the page shifts one character to the right of the paragraph’s bottom line. To avoid this, we specified the same starting line number (2) for both column A.Name and column B.Name in the example. For CONTROL LING TH E VERTICA L SPACING TEAM LinG - Live, Informative, Non-cost and Genuine! 123 each detail group, both A.Name and B.Name will start on line 2 and may wrap into line 3 (figure 9.3). Figure 9.3 Using the Wrap command to create variable length detail groups 9.7 Controlling the horizontal spacing The Columns, Next-Column and Use-Column commands are used to control the horizontal spacing in your report. They define and navigate logical columns on the page. The Columns command defines one or more logical columns in the report. It also makes the first column of the group the current column. Columns 10 20 30 !Define three columns in your report This command defined three logical columns and made column 10 current. Now, if you use a Print command, it will shift the current column position from the left edge of the page and add ten (10) to the position qualifier in any Print command: Columns 10 20 30 Print $Name (3, 2, 30) $Name will be printed in the second position of the first logical column. What will be the actual SQR page position? The answer is: eleven. In order to advance from one column to another, you use the Next-Column command. 124 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! Columns 10 20 30 Print $Name (3, 2, 30) Next-Column Print #Amount () The Next-Column command will move the current column position to the second declared logical column, that is the column located at absolute column position 20. Variable #Amount will be printed in the first position of the second logical column. The Use-Column command sets a specific logical column as current or turns off column printing. To stop printing within logical columns simply specify 0 (zero) as a Use-Column parameter. Columns 20 50 Use-Column 2 !The Columns command must be issued !prior to Use-Column Print $Last_Name () !Prints $Last_Name in the second column Use-Column 0 !Stops printing within columns. Even though your printing mode is reset to normal after issuing the Use-Column 0 command, the logical columns you have previously defined are available, and you are able to go back to logical column printing by using the Next-Column or UseColumn commands. 9.8 Changing report’s heading or footing Starting with version 6, you can use a new SQR command, Alter-Report, to change a report’s heading or footing sections while the report is running. You can change the size of heading or footing or switch from one heading or footing to another dynamically. The Alter-Report command (please see the command reference in appendix D) has two pairs of operands, one pair for heading (Heading and Heading-Size) and another one for footing (Footing and Footing-Size). Let’s see how you can change your report’s heading dynamically. The Heading operand specifies the name of the heading section to be used in the report. This name must be coded in the Name operand of the Begin-Heading command (this operand is also new in version 6). You can also set the value of the Heading operand to 'NONE' to disable the heading section for this report. The Heading-Size operand allows you to change the depth of your report’s heading dynamically. Here is an example: Begin-Setup Begin-Heading 3 Name = Domestic Print ' North American ' (1,1,0) Center CHANGING REPORT’ S HEADING OR FO OTING TEAM LinG - Live, Informative, Non-cost and Genuine! 125 Print $current-date (1,50) Edit MM/DD/YYYY End-Heading Begin-Heading 3 Name = International Print ' International ' (1,1,0) Center Print $current-date (1,50) Edit MM/DD/YYYY End-Heading ! . . . End-Setup Begin-Program ! . . . Alter-Report Heading='Domestic' Heading-Size=5 ! . . . Alter-Report Heading='International' Heading-Size=4 End-Program The Footing and Footing-Size operands work similarly to that of Heading and Heading-Size. If the Heading or Footing value is set to 'Default', SQR will revert to whatever was in effect when the report was initiated. It is important to understand that the Alter-Report command affects the current report only. If anything has been placed on the page prior to issuing this command, the command will take effect for the next page; otherwise it takes effect for the current page. Please note that the Alter-Report command does not switch to another report: it only changes the current report’s heading or footing characteristics. To switch to another report in a multiple report program, use the Use-Report command (discussed in chapter 13). 9.9 Delaying printing of data Starting with version 6, you can use a new keyword Delay of the Print command to delay the actual printing of data until the Set-Delay-Print command is issued. The Set-Delay-Print command allows you to go back and change the value of the printed variable in the output buffer based on the value of the With argument of this command. The Delay keyword is applied only to the data printed by the Print command the keyword is used with. All data printed by other Print commands without the Delay keyword are printed without a delay. The Print Delay and Set-DelayPrint commands work in pairs: 126 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! Print $Var1 (1,1,10) Delay ! . . . Set-Delay-Print $Var1 With $Var2 It is important to understand that the Delay keyword impacts only the timing of your program’s output, not the order in which print lines will appear on the page. How does SQR do this? The Delay keyword tells SQR to accumulate the entire report in a file without printing until the Set-Delay-Print command is issued. Then, SQR goes back and updates the value of the printed variable in the file. In order to achieve this, SQR in addition to its regular LIS output, creates a so-called SPF file (read more about SPF files in chapter 14). When can this technique be useful? One example is when you need to print a report summary in the beginning of the report. Without the Delay option, you will have to write a multiple report program (see chapter 13) or access your database twice: first to print the summary data, second to print the detail report. With the Delay option, you can print your report’s summary with empty totals using the Print Delay command, print the detail report and then, use the Set-Delay-Print command to update the report’s summary with accumulated totals. Here is an example of such a program. Let’s say we need to print a list of employees and place certain employee summary information on the top of the report: !**************************** !TEST9D.SQR ! Using the Print Delay and Set-Delay-Print Commands !**************************** Begin-Program !**************************** Do Print_Summary Do List_Employees Do Release_Delay End-Program !**************************** Begin-Heading 2 Name = Summary Print 'Employee Status Summary' (1,1,30) Center End-Heading !**************************** !**************************** Begin-Heading 5 Name = Detail Print 'Active Employee List' (1,1,30) Center Print '=' (+1,1,50) Fill Print 'Company' (+1,1 ) Print 'Emplid' (,+2 ) Print 'Name ' (,+4 ) Print 'Annual Salary' (,+15) DELAYING PRINTING OF DATA TEAM LinG - Live, Informative, Non-cost and Genuine! 127 Print '=' End-Heading !**************************** (+1,1,50) Fill !***************************************** Begin-Procedure Print_Summary !***************************************** Alter-Report Use Delay argument to defer printing the totals Heading = 'Summary' Print 'Number of Active Employees : ' (+1, 1) Print #Active (,+1,10) Edit 9,999,999 Delay Print 'Number of Terminated Employees : ' (+1, 1) Print #Terminated (,+1,10) Edit 9,999,999 Delay Print 'Number of Retired Employees : ' (+1, 1) Print #Retired (,+1,10) Edit 9,999,999 Delay New-Page When using Delay, always code End-Procedure the field length explicitly !***************************************** Begin-Procedure List_Employees !***************************************** Alter-Report Changing heading from Heading = 'Detail' Summary to Detail Begin-Select B.Company A.Emplid A.Name B.Empl_Status B.Annual_Rt Evaluate &B.Empl_Status When='A' Add 1 to #Active_Total Print &B.Company (,1,7) Print &A.Emplid (,+2,8) Print &A.Name (,+2,20) Print &B.Annual_Rt (,+2,12) Edit $,$$$,$$$.00 Position (+1) Break When='T' Add 1 To #Terminated_Total Break When='R' Add 1 To #Retired_Total Break End-Evaluate From Ps_Personal_Data A, PS_Job B Where A.Emplid=B.Emplid And B.Effdt = (Select Max(D.Effdt) From PS_Job D Where D.Effdt<=Sysdate 128 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) From Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order By B.Company, A.Emplid End-Select End-Procedure !****************************************** Begin-Procedure Release_Delay Use Set-Delay-Print to go back and !****************************************** populate totals Set-Delay-Print #Active with #Active_Total Set-Delay-Print #Terminated with #Terminated_Total Set-Delay-Print #Retired with #Retired_Total End-Procedure In Test9D.sqr, we want to print a list of only active employees and place the total number of active, terminated and retired employees on the top of the report so that the company management will be able to see the totals first. Of course, there are many ways to print this report, but if we want to make it in one database pass, using the Print Delay command may be a good solution. First, we define two headings for the report: one for the total part, another one for the detail part. We use the Alter-Report command to switch headings. The Print_ Summary procedure prints the summary portion of the report, but uses the Print Delay command to defer printing the actual totals until the employee selection is complete. The List_Employees procedure prints the list of active employees and accumulates the totals of active, terminated, and retired employees. The Release_Delay procedure uses the Set-Delay-Print command to go back and populate the total fields in the report’s summary. Please note that SQR will update these fields according to the positioning and formatting specified by the previous Print Delay commands. The Print Delay and Set-Delay-Print commands always work in pairs: for Note each Print Delay command you have to code the corresponding Set-DelayPrint command. 9.10 Generating PDF files Starting with version 4.3.2.2, SQR can generate Adobe PDF files. You don’t have to change your program, all you have to do is to use the –PRINTER:PD SQR command GENERATING PDF FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! 129 line flag. The SQR.INI file has a new section called [PDF Fonts] which provides the mapping between SQR fonts and Adobe fonts. The name of the PDF file will be same as your report name with the .PDF extension. Another SQR command line flag, -EH_PDF creates a PDF icon in the navigation bar when you generate HTML output. KEY POINTS 1 The Print command controls the position and the length of each output field with the help of three parameters: the two coordinates on the page grid (line and column) and the number of positions allocated for the field. 2 Line positions are always specified relative to the current page section (header, body, or footer), not to the entire report page. 3 The position and length parameters in the Print command can be numeric literals or variables. 4 In the Select paragraph, you can implicitly print a table column by specifying the print position coordinates (X,Y) and the space allocation Z to the right of the column name. 5 You can use substitution variables to define all printing parameters at the beginning of your report. 6 The Print command format options help to enhance the appearance of your report. 7 There are three types of edit format masks: Text Edit, Numeric Edit, and Date Edit. Each of these three edit types uses its own set of special characters for mask coding. 8 Edit masks can be built and changed dynamically by assigning a mask value to a text variable, and then referencing this variable by its name prefixed by a colon. 9 The Position command will allow you to set the current position in the report page. 10 The Next-Listing command is handy when your report has groups of detail records. 130 C H AP TE R 9 EN H AN C IN G Y OU R R E P ORT TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 11 The Columns, Next-Column, and Use-Column commands are used to control the horizontal spacing. 12 The SQR reserved variable #current-line always holds the actual line number within the page body. 13 The Print Delay command allows you to go back and change the value of an output variable before printing. 14 You can create PDF output without changing your program. 15 You can change report’s heading or footing dynamically with the help of the Alter-Report command. GENERATING PDF FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! 131 TEAM LinG - Live, Informative, Non-cost and Genuine! P A R T 2 Advanced features of SQR TEAM LinG - Live, Informative, Non-cost and Genuine! TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 0 Using break logic in your SQR program IN THIS CHAPTER • Different print actions of the On-Break option • Multiple-level breaks • The Before and After qualifiers of the On-Break option • The Save qualifier of the On-Break option • The Print=Never option • Controlling page breaks 135 TEAM LinG - Live, Informative, Non-cost and Genuine! In business reporting, when the value of a column is changed (that is, when a break occurs), it is common to do some special processing. You may need to skip lines, print a value only if changed, execute a special procedure before or after the break, print subtotals, etc. SQR break handling commands allow you to do all of the above and more. Let us look at the following program, which, for every selected employee, prints the employee’s name, his company, and the employee’s salary. This example does not use any break logic. Figure 10.1 displays the program’s output. !**************************** !TEST10A.SQR !An employee list program that uses no break logic !**************************** Begin-Program !**************************** Do List_Employees End-Program !**************************** Begin-Heading 2 !**************************** Print 'Company' (1,1) Print 'Paygroup' (,+2) Print 'Emplid' (,+2) Print 'Name ' (,+4) Print 'Annual Salary' (,+15) Print ' ' (2,1) End-Heading !***************************************** Begin-Procedure List_Employees !***************************************** Begin-Select B.Company (,1,7) B.Paygroup (,+2,8) A.Emplid (,+2,8) A.Name (,+2,20) B.Annual_Rt (,+2,12) Edit $,$$$,$$$.00 Position (+1) From Personal_Data A, Job B Where A.Emplid=B.Emplid And B.Effdt=(Select Max(D.Effdt) From Job D Where D.Effdt<=Sysdate And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) from Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order By B.Company, B.Paygroup, A.Emplid 136 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! End-Select End-Procedure !**************************** Company Paygroup Explid Name CBL CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB CCB MN BW0 BW0 BW0 BW0 BW0 BW0 BW0 BW0 BW0 BW1 BW1 BW1 BW1 LT1 LT1 LT1 LT1 LT1 B002 E2301 E2302 E2303 E2304 E2305 G500 G501 G502 G503 G701 G702 G703 G704 8530 LT001 LT002 LT003 LT005 Maertens,Marianne Jacobs,Carol Gardner,John Andrews,Frank Jeffries,Anne Masterson,Marie Miller,Robin Caldwell,John Downs,Megan Castro,Mary Cortez,Isabella Jonas,Wendy Lotta,Emmanuel Dempsey,Elaine Vierra,Gina Santos,Charles A. Hiromoto,Seiko Souza,Isobel Seca,Wenda A. Figure 10.1 Annual Salary $769,392.00 $46,800.00 $64,800.00 $70,800.00 $31,200.00 $8,195.20 $26,000.00 $38,400.00 $19,448.00 $36,000.00 $4,825.60 $4,825.60 $4,825.60 $4,825.60 $19,344.00 $36,400.00 $46,904.00 $72,800.00 $34,645.00 List of employees by company and paygroup without using on-break logic 10.1 Using the On-Break option of the Print command On-break option of the Print command As you can see in figure 10.1, the report is just a list of employees sorted by company, paygroup, and employee Id. The look is not very appealing, but can be improved by using the On-Break option of the Print command. The On-Break option causes the specified action to take place when the value of a certain output field is changed. It can be used for both explicit and implicit printing. You can specify a number of qualifiers of the On-Break option. These qualifiers define specific actions to be taken when the break occurs. The most popular qualifier is Print (not to be confused with the Print command). The Print qualifier is the default qualifier of the On-Break option: you do not have to specify this qualifier when coding the On-Break option. We will discuss other qualifiers later in this chapter. ON-BR E AK OPTION O F TH E PRINT COMMAND TEAM LinG - Live, Informative, Non-cost and Genuine! 137 When using the Print qualifier of the On-Break option, you can specify when the break field should (or should not) be printed: • Always The break field will be printed for each detail group. • Change The break field will be printed only when its value is changed. This is the default option. • Change/Top-Page The break field will be printed when its value is changed plus at the top of each new page. • Never The break field will not be printed. The default action prints the break field only when its value changes. Let us use the On-Break option in our program and see the difference: !**************************** !TEST10B.SQR !Using break logic in the employee list program !**************************** Begin-Program !**************************** Do List_Employees End-Program !**************************** Begin-Heading 2 !**************************** Print 'Company' (1,1) Print 'Paygroup' (,+2) Print 'Emplid' (,+2) Print 'Name ' (,+4) Print 'Annual Salary' (,+15) Print ' ' (2,1) End-Heading !***************************************** Begin-Procedure List_Employees !***************************************** Begin-Select ! We use the On-Break option below: B.Company (,1,7) On-Break Print=Change/Top-Page Skiplines=1 B.Paygroup (,+2,8) Company is defined as A.Emplid (,+2,8) a break field. A.Name (,+2,20) B.Annual_Rt (,+2,12) edit $,$$$,$$$.00 Position (+1) From Personal_Data A, Job B Where A.Emplid=B.Emplid And B.Effdt=(Select Max(D.Effdt) From Job D Where D.Effdt<=Sysdate And D.Emplid=B.Emplid 138 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) from Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order By B.Company, B.Paygroup, A.Emplid End-Select End-Procedure !**************************** In Test10B.sqr, we defined Company as a break field and used the On-Break option for this field. In the Print qualifier, we chose to print the company ID 1) when its value is changed and 2) at the top of each new page even if the value is not changed. In addition to the Print qualifier, we also used another qualifier: Skiplines. This qualifier specifies how many lines to skip when the break occurs. Now, the output will look like that in figure 10.2. Company Paygroup Explid Name CBL MN B002 Maertens,Marianne $769,392.00 CCB BW0 BW0 BW0 BW0 BW0 BW0 BW0 BW0 BW0 BW1 BW1 BW1 BW1 LT1 LT1 LT1 LT1 LT1 E2301 E2302 E2303 E2304 E2305 G500 G501 G502 G503 G701 G702 G703 G704 8530 LT001 LT002 LT003 LT005 Jacobs,Carol Gardner,John Andrews,Frank Jeffries,Anne Masterson,Marie Miller,Robin Caldwell,John Downs,Megan Castro,Mary Cortez,Isabella Jonas,Wendy Lotta,Emmanuel Dempsey,Elaine Vierra,Gina Santos,Charles A. Hiromoto,Seiko Souza,Isobel Seca,Wenda A. $46,800.00 $64,800.00 $70,800.00 $31,200.00 $8,195.20 $26,000.00 $38,400.00 $19,448.00 $36,000.00 $4,825.60 $4,825.60 $4,825.60 $4,825.60 $19,344.00 $36,400.00 $46,904.00 $72,800.00 $34,645.00 Figure 10.2 Annual Salary Using the On-Break option ON-BR E AK OPTION O F TH E PRINT COMMAND TEAM LinG - Live, Informative, Non-cost and Genuine! 139 10.2 Using Level qualifiers for multiple level breaks Level qualifiers—multiple level breaks As you can see from our previous example, employees may belong to multiple paygroups within the same company. We can add another break by paygroup to our report by adding the On-Break option to column Paygroups. In this case, we will have nested breaks. When dealing with nested breaks, use the Level qualifier to ensure that the breaks are properly nested and to control the order in which your break procedures are called (if any). The parameter in the Level qualifier specifies the level of the break for reports containing multiple breaks. When coding the value of this parameter, you should number your breaks in the same order as that in the Order By clause. In our example, Company is the first level break, Paygroup is the second level break. After coding multiple breaks, our program will look as follows: !**************************** !TEST10C.SQR !Using the Level qualifier to define multiple level breaks !**************************** Begin-Program !**************************** Do List_Employees End-Program !**************************** Begin-Heading 2 !**************************** Print 'Company' (1,1) Print 'Paygroup' (,+2) Print 'Emplid' (,+2) Print 'Name ' (,+4) Print 'Annual Salary' (,+15) Print ' ' (2,1) End-Heading !***************************************** Begin-Procedure List_Employees !***************************************** Begin-Select ! We use the On-Break option with the Level qualifiers below: B.Company (,1,7) On-Break Skiplines=1 Level=1 Company is defined as B.Paygroup (,+2,8) On-Break Level=2 a Level 1 break field. Paygroup A.Emplid (,+2,8 ) is defined as a Level 2 A.Name (,+2,20) break field. B.Annual_Rt(,+2,12) edit $,$$$,$$$.00 Position (+1) From Personal_Data A, Job B Where A.Emplid=B.Emplid 140 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! And B.Effdt=(Select Max(D.Effdt) From Job D Where D.Effdt<=Sysdate And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) from Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order By B.Company, B.Paygroup, A.Emplid End-Select End-Procedure !**************************** The output of the program with nested breaks is shown in figure 10.3. Company Paygroup Explid Name CBL MN B002 Maertens,Marianne $769,392.00 CCB BW0 E2301 E2302 E2303 E2304 E2305 G500 G501 G502 G503 G701 G702 G703 G704 8530 LT001 LT002 LT003 LT005 Jacobs,Carol Gardner,John Andrews,Frank Jeffries,Anne Masterson,Marie Miller,Robin Caldwell,John Downs,Megan Castro,Mary Cortez,Isabella Jonas,Wendy Lotta,Emmanuel Dempsey,Elaine Vierra,Gina Santos,Charles A. Hiromoto,Seiko Souza,Isobel Seca,Wenda A. $46,800.00 $64,800.00 $70,800.00 $31,200.00 $8,195.20 $26,000.00 $38,400.00 $19,448.00 $36,000.00 $4,825.60 $4,825.60 $4,825.60 $4,825.60 $19,344.00 $36,400.00 $46,904.00 $72,800.00 $34,645.00 BW1 LT1 Figure 10.3 Annual Salary Using Level qualifiers in reports with multiple breaks On-Break can only be used on string and date columns and variables. You cannot explicitly use the On-Break option directly on a numeric column or variable. To fool the system, move the numeric column value to a string variable, then code the OnBreak option in the Print command for this new string variable: !Using breaks for numeric columns Begin-Select LEVEL QUAL IFIER S —MUL TIPLE L EVEL BR EA KS TEAM LinG - Live, Informative, Non-cost and Genuine! 141 Emplid (+1,1) On-Break Level=1 Annual_Rt Move &Annual_Rt To $Annual_Rate $,$$$,$$$.00 Print $Annual_Rate On-Break Level=2 From Job Order By Emplid, Annual_Rt End-Select Controlling multiple breaks may appear straightforward, but the internal SQR logic may get very tricky. When a break occurs at one level, it triggers breaks on variables with higher or equal Level qualifiers. For example, a break on Company triggers a break on Paygroup even if the value of this field did not change. At the same time, if both Company and Paygroup changed, SQR makes sure that a break on Paygroup does not happen twice. As we will learn later, a break on a variable (that is, a change in the value of this variable) may also result in a number of events such as the printing of the variable value, the skipping of a line, the invocation of a procedure, etc. Therefore, it is important for programmers to clearly understand the mechanics of break processing in SQR. 10.3 Using procedures when breaks occur Procedures when breaks occur Many reports have fields or columns requiring special on-break processing, which is more complex than just a single action. SQR offers multiple ways of handling these situations. Using on-break procedures in your report allows you to customize breakprocessing logic, for example, to avoid printing redundant data, enhance the appearance of your report, perform conditional processing, print subtotals and totals when necessary, and so on. When you use the On-Break option of the Print The Before and command, you can have SQR automatically call proce- Note After qualifiers can dures before and after each break occurs. In order to be used only within Select accomplish this, use the Before and After qualifiers paragraphs. of the On-Break option of the Print command. The operands of the Before and After qualifiers specify the names of the procedures to be called. The Before qualifier will automatically call the specified procedure right before the column value is changed, including the processing of the first selected row. Similarly, the After qualifier will automatically call the specified procedure after the column value is changed including the last change, that is, when Select is completed. If no rows are selected, neither procedure will be called. Besides the procedure name operands in the Before and After qualifiers, there is a qualifier named Procedure. Do not confuse this qualifier with the procedure name operands. The parameter in this qualifier specifies the name of the procedure to be 142 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! invoked when the break occurs. The Procedure qualifier is an old qualifier used in previous releases and cannot be used in combination with either Before or After procedures. Before using On-Break procedures in the Print command, please read carefully the following sequence concerning events that occur when a query contains Print commands with On-Break qualifiers. 1 In the beginning of the query, all procedures specified in the Before qualifiers are invoked in the ascending Level order when the first row of the query is fetched. All break column values are printed in their respective positions unless Print=Never is specified 2 During the query execution, when a break occurs • all After procedures (if any) are invoked in descending Level order from the highest level down to the current break level of the break column. Please note that the previous values of the break columns are not available to the After procedures since they have been replaced with the new values. Use the Save qualifiers (covered in section 10.4) to keep the previous values after breaks occur. • all Before procedures (if declared) are processed in ascending Level order from the current break level to the highest break level. • if the Procedure qualifier was coded, the named procedure is invoked. • if Skiplines is specified, the current line position is advanced by the number of lines specified. • all break column values with the same or higher level are printed unless Print=Never is specified. 3 In the end of the query (at End-Select), all After procedures are processed once more in descending Level order. To make sure that all these rules do not sound too confusing to you, let us enhance our previous program and include the Before and After break procedures in our SQR code. The Show operators located at certain strategic points in the program will help us trace the program control flow. We will use one Before break procedure named Company_Name to retrieve the company name from table Company_Tbl and to print a sub-header with the company name on each Company value break. We will also use two After break procedures named Company_Totals and Paygroup_Totals to calculate the number of employees and the total salary for each company and each paygroup within a company. Figure 10.4 shows a portion of the program’s output. !TEST10D.SQR !Using the Before and After qualifiers of the On-Break option !**************************** Begin-Program PROCEDUR ES WH EN B REAKS OCCU R TEAM LinG - Live, Informative, Non-cost and Genuine! 143 !**************************** Show 'Program Started' Do List_Employees End-Program !**************************** Begin-Heading 2 !**************************** Print 'Company' (1,1) Print 'Paygroup' (,+2) Print 'Emplid' (,+2) Print 'Name ' (,+4) Print 'Annual Salary' (,+15) Print ' ' (2,1) End-Heading !***************************************** * Begin-Procedure List_Employees !****************************************** Begin-Select Add 1 to #Row_Num Show 'Selected row# ' #Row_Num ' Comp=' &B.Company ' Paygrp='&B.Paygroup B.Company (,1,7) On-Break Level = 1 Before=Company_Name After=Company_Totals Skiplines = 1 B.Paygroup (,10,8) On-Break Level = 2 After=Paygroup_Totals A.Emplid (,+2,8) A.Name (,+2,20 ) B.Annual_Rt (,+2,12) edit $,$$$,$$$.00 Position (+1) Company is defined as a Level 1 break field with Before and After procedures. Paygroup is defined as a Level 2 break field with an After procedure. Let #EE_Paygroup_Total= #EE_Paygroup_Total + 1 Let #Sal_Paygroup_Total= #Sal_Paygroup_Total + &B.Annual_Rt From Personal_Data A, Job B Where A.Emplid=B.Emplid And B.Effdt=(Select Max(D.Effdt) From Job D Where D.Effdt<=Sysdate And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) From Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order By B.Company, B.Paygroup, A.Emplid End-Select End-Procedure 144 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! !*************************************** Company_Name is defined as a Before procedure on Begin-Procedure Company_Name Company break. !*************************************** Show 'Before Procedure Company_Name is invoked' Begin-Select C1.Descr Print 'Company Name: ' (+1,1) Print &C1.Descr (,+2) Position (+1) From Company_Tbl C1 Where C1.Company = &B.Company And C1.Effdt = (Select Max(Effdt) From Company_TBL Where Company = C1.Company) End-Select End-Procedure Company_Totals is defined !*************************************** as an After procedure on Begin-Procedure Company_Totals Company break. !*************************************** Show 'After Procedure Company_Totals is invoked' Print 'Number of Employees in Company = ' (+1,5) Print #EE_Company_Total () edit 999999 Print 'Total Annual Salary Paid for Company = ' (+1,5) Print #Sal_Company_Total () edit $$$,$$$,$$$.00 Let #EE_Company_Total = 0 Let #Sal_Company_Total = 0 End-Procedure Paygroup_Totals is defined !************************************** as an After procedure on Begin-Procedure Paygroup_Totals Paygroup break. !*************************************** Show 'After Procedure Paygroup_Totals is invoked' Print 'Number of Employees in Paygroup = ' (+1,5) Print #EE_Paygroup_Total () edit 999999 Print 'Total Annual Salary Paid for Paygroup = ' (+1,5) Print #Sal_Paygroup_Total () edit $$$,$$$,$$$.00 Position (+1) Let Let #EE_Company_Total = #EE_Company_Total + #EE_Paygroup_Total #Sal_Company_Total = #Sal_Company_Total + #Sal_Paygroup_Total Let #EE_Paygroup_Total = 0 Let #Sal_Paygroup_Total = 0 End-Procedure !******************************** Figure 10.4 shows a portion of the program output. The program run log in figure 10.5 will help us to better understand the sequence of different events during the program execution. PROCEDUR ES WH EN B REAKS OCCU R TEAM LinG - Live, Informative, Non-cost and Genuine! 145 Company Name: CBL MN B002 Continental Commerce - Belgium Maertens,Marianne $769,392.00 Number of Employees in Paygroup = 1 Total Annual Salary Paid for Paygroup = $769,392.00 Number of Employees in Company = 1 Total Annual Salary Paid for Company = $769,392.00 Company Name: Continental Commerce&Business CCB BW0 E2301 E2302 E2303 E2304 E2305 G500 G501 G502 G503 Jacobs,Carol Gardner,John Andrews,Frank Jeffries,Anne Masterson,Marie Miller,Robin Caldwell,John Downs,Megan Castro,Mary $46,800.00 $64,800.00 $70,800.00 $31,200.00 $8,195.20 $26,000.00 $38,400.00 $19,448.00 $36,000.00 Number of Employees in Paygroup = 9 Total Annual Salary Paid for Paygroup = BW1 G701 Cortez,Isabella $4,825.60 G702 Jonas,Wendy $4,825.60 G703 Lotta,Emmanuel $4,825.60 G704 Dempsey,Elaine $4,825.60 Number of Employees in Paygroup = Figure 10.4 $341,643.20 4 Using the Before and After procedures in reports with breaks Let us examine figure 10.5 and retrace the sequence of events that took place during the program run: 1 After the program initiation, when the very first row is fetched, the Company_Name procedure specified in the Before qualifier for the Company column is invoked. The procedure retrieves the company name from table Company_Tbl using the Company column value from the first row as a key and returns control back to procedure List_Employees. No Before qualifier is specified for the Paygroup column and, therefore, no procedure is invoked. 2 As the subsequent rows in the List_Employees Select paragraph are being fetched, SQR keeps its tabs on the values of Company and Paygroup. 3 If Paygroup is changed, SQR identifies this event as a Break Level 2. This, in turn, triggers the invocation of procedure Paygroup_Totals specified in the After qualifier for the Paygroup column. Paygroup_Totals prints the number of 146 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! employees and the salary total for the paygroup. If the Before qualifier were specified for Paygroup, the proper procedure would have been invoked following the Paygroup_Totals procedure. 4 If Company is changed, SQR identifies the event as a Break Level 1. All procedures specified in the After qualifiers from the highest break level to Break Level 1 are invoked. In our case, procedure Paygroup_Totals (Break Level 2) is invoked first, procedure Company_Totals (Break Level 1) is invoked second. Please note that in our case, both Figure 10.5 The execution log of Test10D.sqr with Company and Paygroup the Before and After procedures changed at the same time. SQR triggers a break on Paygroup every time a break on Company occurs. To avoid double-breaking, the break on Paygroup is cleared. Then, all procedures specified in the Before qualifiers from the highest break level to Break Level 1 are invoked. In our case, there is only one procedure: Company_Name. 5 After the query execution is finished, all After procedures from the highest break level to Break Level 1 are invoked. The Paygroup_Totals procedure is called first, the Company_Totals procedure is called second. 10.4 Using the Save qualifier of the On-Break option Save qualifier of the On-break option Sometimes it is necessary to use or print the previous break value of a print variable in the After procedure. Since After procedures are executed after the values of the OnBreak columns have been changed, the columns will already hold their new values. The problem can be solved by using the Save qualifier of the On-Break option. This method allows you to store the previous column value in a specially designated variable when a break on this column value occurs. S AVE QU ALIF IER OF THE ON-BR EA K OPTION TEAM LinG - Live, Informative, Non-cost and Genuine! 147 Let us change our test program to demonstrate the Save qualifier. Suppose we want to print Paygroup and Company along with totals in both After procedures (Company_ Totals and Paygroup_Totals). This will involve some changes to our program: !TEST10E.SQR !Using the Save qualifier to print Paygroup and Company !along with the totals !**************************** Begin-Program !**************************** Show 'Program Started' Do List_Employees End-Program !**************************** Begin-Heading 2 !**************************** Print 'Company' (1,1) Print 'Paygroup' (,+2) Print 'Emplid' (,+2) Print 'Name ' (,+4) Print 'Annual Salary' (,+15) Print ' ' (2,1) End-Heading !***************************************** Begin-Procedure List_Employees !***************************************** Show 'Procedure List_Employees Started' Begin-Select Add 1 to #Row_Num Show 'Selected row# ' #Row_Num ' Comp=' &B.Company ' Paygrp=' &B.Paygroup ! We use the Save qualifiers below: B.Company (,1,7) On-Break Level = 1 After=Company_Totals Skiplines = 1 Save=$Prev_Comp B.Paygroup (,10,8) ON-BREAK Level = 2 The Save qualifiers are used to save pre-break values of After=Paygroup_Totals Save=$Prev_Paygrp Company and Paygroup. A.Emplid (,+2,8) A.Name (,+2,20 ) B.Annual_Rt (,+2,12) edit $,$$$,$$$.00 Position (+1) Let #EE_Paygroup_Total= #EE_Paygroup_Total + 1 Let #Sal_Paygroup_Total= #Sal_Paygroup_Total + &B.Annual_Rt From Personal_Data A, Job B where A.Emplid=B.Emplid And B.Effdt=(Select Max(D.Effdt) from Job D Where D.Effdt<=Sysdate 148 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) From Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order By B.Company, B.Paygroup, A.Emplid End-Select End-Procedure !****************************************** Begin-Procedure Company_Totals !****************************************** Show 'After Procedure Company_Totals is invoked' Print 'Number of Employees in Company ' (+1,5) ! We print the saved Company value below: Saved pre-break Company value is printed in the Print $Prev_Comp (,+1) After procedure. Print #EE_Company_Total (,+1) edit 999999 Print 'Total Annual Salary Paid for Company = ' (+1,5) Print #Sal_Company_Total () edit $$$,$$$,$$$.00 Position (+1) Let Let #EE_Company_Total = 0 #Sal_Company_Total = 0 End-Procedure !****************************************** Begin-Procedure Paygroup_Totals !****************************************** Show 'After Procedure Paygroup_Totals is invoked' Print 'Number of Employees in Paygroup ' (+1,5) Saved pre-break Paygroup ! We print the saved Paygroup value below: value is printed in the After Print $Prev_Paygrp (,+1) procedure. Print #EE_Paygroup_Total (,+1) edit 999999 Print 'Total Annual Salary Paid by Paygroup = ' (+1,5) Print #Sal_Paygroup_Total () edit $$$,$$$,$$$.00 Position (+1) Let Let #EE_Company_Total = #EE_Company_Total + #EE_Paygroup_Total #Sal_Company_Total = #Sal_Company_Total + #Sal_Paygroup_Total Let #EE_Paygroup_Total = 0 Let #Sal_Paygroup_Total = 0 End-Procedure !*********************** As you can see from figure 10.6, which illustrates the output of Test10E.sqr, using the Save qualifier of the On-Break option helps us retain the previous values of the company code and paygroup when breaks on these column values occurred. The S AVE QU ALIF IER OF THE ON-BR EA K OPTION TEAM LinG - Live, Informative, Non-cost and Genuine! 149 Company Paygroup Emplid Name CBL MN B002 Maertens,Marianne CCB $769,392.00 Number of Employees in Paygroup MN Total Annual Salary Paid by Paygroup = 1 $769,392.00 Number of Employees in Company CBL Total Annual Salary Paid for Company = 1 BW0 E2301 E2302 E2303 E2304 E2305 G500 G501 G502 G503 $769,392.00 Jacobs,Carol Gardner,John Andrews,Frank Jeffries,Anne Masterson,Marie Miller,Robin Caldwell,John Downs,Megan Castro,Mary $46,800.00 $64,800.00 $70,800.00 $31,200.00 $8,195.20 $26,000.00 $38,400.00 $19,448.00 $36,000.00 Number of Employees in Paygroup BW0 Total Annual Salary Paid by Paygroup = BW1 G701 Cortez,Isabella G702 Jonas,Wendy G703 Lotta,Emmanuel G704 Dempsey,Elaine 9 $341,643.20 $4,825.60 $4,825.60 $4,825.60 $4,825.60 Number of Employees in Paygroup 4 Total Annual Salary Paid by Paygroup = Figure 10.6 Annual Salary BW1 $19,302.40 The output of Test10E.sqr which used the Save qualifier saved values were used in the After procedures for each break to print the totals by paygroup or company along with the paygroup or the company code. 10.5 Using Print=Never option in the Print command print=never in the Print command By default, break column values are printed automatically. In certain cases, you may choose not to print a break column value when a break occurs. You might prefer, for instance, to print the company name instead of printing the company Id when a company break occurs. In our previous Company break examples, SQR prints the value of the Company column, which is a three-character company Id, not a full company name. The following example shows how to use the Print=Never option of the Print command to print company names on Company break (figure 10.7 shows the output of our example): 150 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! !TEST10F.SQR !Using the Print=Never to suppress on-break column values !**************************** Begin-Program !**************************** Show 'Program Started' Do List_Employees End-Program !******************************** Begin-Heading 2 !******************************** Print 'Emplid' (1,21) Print 'Name ' (,+4) Print 'Annual Salary' (,+15) Print ' ' (2,1) End-Heading !***************************************** Begin-Procedure List_Employees !***************************************** Show 'Procedure List_Employees Started' Begin-Select Add 1 to #Row_Num Show 'Selecting Row#=' #Row_Num 'Comp=' $B.Company 'Paygroup=' &B.Paygroup ! We use the Print=Never option below: B.Company () On-Break Level = 1 Before=Company_Name Print=Never B.Paygroup () On-Break Level = 2 Before=Paygroup_Code Print=Never A.Emplid (,21,8) Using Print=Never to suppress A.Name (,+2,20) on-break column prints B.Annual_Rt (,+2,12) edit $,$$$,$$$.00 Position (+1) From Personal_Data A, Job B Where A.Emplid=B.Emplid And B.Effdt=(Select Max(D.Effdt) from Job D Where D.Effdt<=Sysdate And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) From Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order by B.Company, B.Paygroup, A.Emplid End-Select End-Procedure !********************************************* Begin-Procedure Company_Name !********************************************* Show 'Procedure Company_Name is invoked' PRINT=NEVER IN THE PR INT COMMAND TEAM LinG - Live, Informative, Non-cost and Genuine! 151 Begin-Select C1.Descr Company name is printed in Print 'Company Name: ' (+1,1) the Before procedure. Print &C1.Descr (,+2) Position (+1) From Company_Tbl C1 Where C1.Company = &B.Company And C1.Effdt = (Select Max(Effdt) From Company_Tbl Where Company = C1.Company) End-Select End-Procedure !****************************************** Begin-Procedure Paygroup_Code !****************************************** Print 'Paygroup Code: ' (+1,1) Paygroup is printed in Print &B.Paygroup (,+2) the Before procedure. Position (+1) End-Procedure !****************** Emplid Company Name: Continental Commerce - Belgium Paygroup Code: MN B002 Maertens,Marianne Annual Salary $769,392.00 Company Name: Continental Commerce&Business Paygroup Code: BW0 E2301 E2302 E2303 E2304 E2305 G500 G501 G502 G503 Jacobs,Carol Gardner,John Andrews,Frank Jeffries,Anne Masterson,Marie Miller,Robin Caldwell,John Downs,Megan Castro,Mary $46,800.00 $64,800.00 $70,800.00 $31,200.00 $8,195.20 $26,000.00 $38,400.00 $19,448.00 $36,000.00 BW1 G701 G702 G703 Cortez,Isabella Jonas,Wendy Lotta,Emmanuel $4,825.60 $4,825.60 $4,825.60 Paygroup Code: Figure 10.7 152 Name Using the Print=Never option for Company and Paygroup C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! In Test10F.sqr, we use the Print=Never option for the columns Company and Paygroup to suppress automatic printing of these column values when breaks occur. Instead, we call the Company_Name procedure to retrieve the company name, to print this name whenever a Company break occurs, and to call the Paygroup_Code procedure to print the text literal 'Paygroup Code' together with the Paygroup value whenever a Paygroup break occurs. 10.6 Controlling page breaks controlling page breaks Now that you understand how to deal with multiple break columns, it will take you only a minute to incorporate page breaks into your report, won’t it? Well, it may take a little bit more than a minute ... Let us try to incorporate page breaks into our previous example. Suppose we want to start a new page every time Paygroup or Company is changed. In addition, we would like to print the company code and name along with the paygroup code in the header. The task may sound trivial, but your program has to take care of a multitude of details, and avoiding page breaking within one detail record is only one of them. For example, inserting a page break (with the help of the New-Page command) in the After procedure for Company and the After procedure for Paygroup will cause a problem when Company is changed: you will have two new pages instead of one. We recommend that, in order to avoid incorrect new page processing, you • carefully plan all your On-Break events • place all On-Break columns in the Select statement ahead of other columns • place all On-Break columns in the Select statement in the ascending level order • avoid using Wrap and On-Break on the same column. The following example will show you one of the ways of incorporating page breaks into your report correctly: !TEST10H.SQR !Controlling page breaks in your report !**************************** Begin-Program !**************************** Let $First='Y' Show 'Program Started' Do List_Employees End-Program !**************************** Begin-Heading 7 !**************************** Do Company_Name CONTROL LING PAG E BREAKS TEAM LinG - Live, Informative, Non-cost and Genuine! 153 Print 'LIST OF EMPLOYEES ' (1,1) Center Print 'Page' (,90) Print #page-count (,+1) edit 999 Print 'Company' (+2,1) Print $Company (,+1) Print $Company_Name (,+1) Print 'Paygroup' (+1,1) Print $Paygroup (,+1) Print 'Emplid' (+2,1,8) Print 'Name ' (,+2,20) Print 'Annual Salary' (,+2,13) Print '=' (+1,1,45) fill End-Heading !****************************************** Begin-Procedure List_Employees !****************************************** Begin-Select Add 1 to #Row_Num B.Company () On-Break Print=Never Level = 1 After=Company_Totals Save=$Prev_Comp B.Paygroup () On-Break Print=Never Level = 2 After=Paygroup_Totals Before=Before_Paygrp Save=$Prev_Paygrp A.Emplid (+1,1,8) Use the Before procedure to A.Name (,+2,20) handle page breaks. B.Annual_Rt (,+2,12) edit $,$$$,$$$.00 Let $Company=&B.Company Let $Paygroup=&B.Paygroup Let #EE_Paygroup_Total= #EE_Paygroup_Total + 1 Let #Sal_Paygroup_Total= #Sal_Paygroup_Total + &B.Annual_Rt From Personal_Data A, Job B Where A.Emplid=B.Emplid And B.Effdt=(Select Max(D.Effdt) From Job D Where D.Effdt<=Sysdate And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) From Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) Order By B.Company, B.Paygroup, A.Emplid End-Select End-Procedure !****************************************** Begin-Procedure Company_Name !****************************************** Begin-Select C1.Descr Move &C1.Descr to $Company_Name show '$Company_Name=' $Company_Name From Company_Tbl C1 154 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Where C1.Company=$Company And C1.effdt=(Select Max(Effdt) From Company_Tbl Where Company = C1.Company) End-Select End-Procedure !****************************************** Begin-Procedure Company_Totals !****************************************** Move $Prev_Comp to $Company ! Will be used in the Header Print 'Number of Employees in Company = ' (+1,5) Print $Prev_Comp (,+1) Print #EE_Company_Total () edit 999999 Print 'Total Annual Salary Paid for Company = ' (+1,5) Print #Sal_Company_Total () edit $$$,$$$,$$$.00 Let #EE_Company_Total = 0 Let #Sal_Company_Total = 0 End-Procedure !****************************************** Begin-Procedure Paygroup_Totals !****************************************** Move $Prev_Paygrp to $Paygroup ! Will be used in the Header Print 'Number of Employees in Paygroup = ' (+1,5) Print $Prev_Paygrp (,+1) Print #EE_Paygroup_Total () edit 999999 Print 'Total Annual Salary Paid for Paygroup = ' (+1,5) Print #Sal_Paygroup_Total () edit $$$,$$$,$$$.00 Position (+1) Let Let #EE_Company_Total = #EE_Company_Total + #EE_Paygroup_Total #Sal_Company_Total = #Sal_Company_Total + #Sal_Paygroup_Total Let #EE_Paygroup_Total = 0 Let #Sal_Paygroup_Total = 0 End-Procedure !**************************************** Begin-Procedure Before_Paygrp !**************************************** If $First='Y' Let $First='N' Else Print the current page and New-Page start a new page. End-If End-procedure !******************************** CONTROL LING PAG E BREAKS TEAM LinG - Live, Informative, Non-cost and Genuine! 155 In our enhanced version of the program, Test10H.sqr, we added a new Before procedure named Before_Paygrp to create a page break by using the New-Page command when necessary. Why do we place the New-Page command into this procedure? Here, the sequence of events that you’ve learned in this chapter comes into its own. If you remember, the Before procedure is first invoked in the very beginning of the query. We use the $First variable as a switch to determine whether or not to issue the New-Page command. In the beginning, we do not need to switch a page, therefore, in our procedure, we check the $First switch, bypass the New-Page command this time, and set $First to 'N'. The procedure is invoked again when a new value for Paygroup or Company is selected, after all totals in the After procedures are printed—a perfect place to open a new page. Remember, when the New-Page command is called, the current page is being written to the output file, and the new page has been started. In Test10H.sqr we also improve the appearance of our report by printing a header with the report name, company code and name, and paygroup code. The header is printed automatically every time the current page is printed. We want to make sure that when a new row is selected on Paygroup or Company break, the header from the current page still contains the previous value, not the newly selected one, so we move the saved Company and Paygroup values in the After procedures to the variables used in the Heading section. And now, as you can see in figure 10.8 our report looks much more professional: LIST OF EMPLOYEES Page 1 Company M04 Midwest Manufacturing Paygroup MMN Emplid Name Annual Salary ============================================= M001 Bradford,James $155,520.00 M002 Fernandez,Michael Ra $69,060.00 M003 Evans,Lawrence $43,080.00 M009 Payne,James Rocco $35,100.00 Number of Employees in Paygroup = MMN Total Annual Salary Paid for Paygroup = Figure 10.8 156 4 $302,760.00 A multiple page output in a program with breaks (continued on next page) C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! LIST OF EMPLOYEES Page 2 Company M04 Midwest Manufacturing Paygroup MWK Emplid Name Annual Salary ============================================= M004 Firestone-Marcus,Han $26,416.00 M005 Hamasaki,Lewis Georg $26,000.00 M006 Bottswell,Brian Jack $29,120.00 M007 Carlos,Tomas Massimo $22,048.00 M008 Van der Camp,Petros $28,392.00 M010 Cardella,Sharon Anne $17,940.00 Number of Employees in Paygroup = MWK Total Annual Salary Paid for Paygroup = Number of Employees in Company = M04 Total Annual Salary Paid for Company = LIST OF EMPLOYEES Page 6 $149,916.00 10 $452,676.00 3 Company MDB Multi Dimensional Business Paygroup MO2 Emplid Name Annual Salary ============================================= MD0001 Koontz,Stephan $278,604.00 MD0002 Limburg,James $130,320.00 MD0003 Kean,Betsy $100,752.00 MD0004 Miller,Jacqueline $54,528.00 MD0007 Bershas,James $81,456.00 MD0008 Grissom,Jane Frances $63,936.00 MD0009 McGregor,Julie $36,920.00 MD0010 Habeggar,Cary $120,000.00 Number of Employees in Paygroup = MO2 Total Annual Salary Paid for Paygroup = 8 $866,516.00 Number of Employees in Company = MDB Total Annual Salary Paid for Company = 8 $866,516.00 Figure 10.8 A multiple page output in a program with breaks (continued) CONTROL LING PAG E BREAKS TEAM LinG - Live, Informative, Non-cost and Genuine! 157 KEY POINTS 1 You can greatly enhance your report by using the On-Break option of the Print command. 2 You can write special procedures to be invoked before and after column breaks. The Before and After qualifiers tell SQR when to call these procedures. 3 The Level qualifier of the On-Break option is used to arrange multiple breaks in hierarchy, and to specify the sequence of events. 4 Use the Skiplines qualifier to insert the necessary number of lines between detail groups. 5 When a break occurs, the previous column value is overridden with the new one. Use the Save qualifier to save a previously selected value to be used in the After procedures. 6 On-Break can only be used on string and date columns and variables. 7 In the beginning of a query, all Before procedures are processed in ascending Level order when the first row of the query is fetched. 8 All After procedures are processed in descending Level order from the highest level to the current break level of the field where the break occurred. 9 All Before procedures are processed in ascending Level order from the current break level to the highest break level. 10 The Procedure qualifier can not be used in a combination with either Before or After qualifiers. 11 After the query execution (at End-Select), all After procedures are processed once more in descending Level order. 12 If a query returns no rows, neither Before nor After procedures is invoked. 158 C HAP TE R 1 0 BRE AK L OGIC IN Y OUR SQ R P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 1 Run-time and compile-time variables IN THIS CHAPTER • Run-time variables and compile-time variables • The difference in the usage of the substitution variables and the bind variables • Technical aspects of working with substitution variables • Ask and Input commands • Dynamic query variables and how to construct dynamic SQL statements using these variables 159 TEAM LinG - Live, Informative, Non-cost and Genuine! SQR allows one to create very flexible and truly dynamic reports. This may include not only accepting report parameters as a part of user dialogs, but also dynamically changing your SQL statements based on user input. The following three types of special variables are used to make SQR reports flexible: • SQR bind variables • Substitution variables • Dynamic query variables 11.1 SQR bind variables SQR bind variables are used when a query includes parameters defined outside of this query. These parameters may come, for example, from user input, another query, or procedure. Using these variables is a very common technique in SQR; in fact, we have already employed SQR bind variables in examples throughout the previous chapters. SQR bind variables are run-time variables: their values are assigned and changed during the program execution stage. When you use a bind variable in an SQL statement, SQR “binds” the variable before the SQL is executed. The only thing that changes between SQL executions is the value of this variable. The following code gives an example of the usage of SQR bind variables: !TEST11A.SQR !Using SQR Bind variables !**************************** Begin-Procedure Main !**************************** While #Stat <> 3 !Status=3 indicates the Null String !(not available prior to 4.0) Input $Emplid 'Please Enter Employee ID ' The value of Type=Number Status=#Stat $Emplid is obtained Do Get_Empl_Info from user input. End-While End-Procedure !****************************** Begin-Procedure Get_Empl_Info !****************************** Let $Found='N' Begin-Select Name (+1,1,20) Birthdate (,+2,10) Let $Found ='Y' From Personal_Data $Emplid is used as a bind variable. Where Emplid=$Emplid 160 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! End-Select End-Procedure !**************************** In our main procedure, we prompted the user for an employee Id, placed the response into the $Emplid variable, and then called the Get_Empl_Info procedure. In Get_Empl_Info, we simply selected and printed the selected employee information. We actually used $Emplid in our Select statement as a bind variable. Every time Select statement is executed, the current value of the variable $Emplid will be used, and therefore, the requested employee information will be printed. In the previous example, we used bind variables to link a procedure to a query in another procedure. Bind variables can also be used to link one query to another. One typical situation occurs when you use hierarchical or nested queries. Another instance takes place when a main query selects certain rows, (obtains values for, let’s say, columns A and B) and uses those values as parameters (bind variables) in the subordinate query. In the example below, we select and print the names of employees terminated from their job in 1998, and had at least one promotion prior to termination. !TEST11B.SQR !*************************** Begin-Program !*************************** Do Main End-Program !*************************** Begin-Procedure Main !*************************** Begin-Select A.Emplid A.Empl_Rcd A.Effdt A.Company B.Name Do Check_Prior_Rows If $Found='Y' Do Print_Selected_Row End-If From Job A, Personal_Data B Where A.Empl_Status='T' And To_Char(A.Effdt,'YYYY') = '1998' ! To_Char is an ! Oracle-specific function And A.Emplid=B.Emplid End-Select End-Procedure S QR BI ND V ARIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! 161 !******************************** Begin-Procedure Check_Prior_Rows !******************************** Let $Found='N' Begin-Select C.Emplid C.Effdt Let $Found='Y' From Job C Where C.Emplid=&A.Emplid And C.Empl_Rcd=&A.Empl_Rcd And C.Empl_Status = 'PRO' And C.Effdt < &A.Effdt End-Select End-Procedure !******************************** The bind variables &A.Emplid, &A.Empl_Rcd#, and &A.Effdt link two SQL queries. Two queries exist in our example. In the main query, we select all employees terminated in 1998 by joining the tables Job and Personal_Data. The next logical step is to check if every employee did, in fact, have a promotion some day prior to termination. We use a subordinate query in the Check_Prior_Rows procedure to select all rows containing effective dates prior to the employee termination date as well as Empl_Status = 'PRO' for each employee selected in the main query. (Please note that we used an Oracle built-in SQL function To_Char to extract the year from a date format column Effdt. Similar functions are available from other RDBMS vendors.) We know that the rows selected in the subordinate query belong to the employee selected in the main query because the &A.Emplid bind variable gets populated in the main query. Two other bind variables, &A.Empl_Rcd# and &A.Effdt help to refine the subordinate query result set. For every employee selected in the main query, our Check_Prior_Rows procedure selects table Job rows that satisfy the criteria in the Where clause of the subordinate query. For our particular business purpose, the selection of multiple rows is fine since the goal is to find at least one. We accomplish this selection by setting the global variable $Found to 'Y'. There may be situations in which you have to make sure that your query returns only one row, for example the last one row, or the first one, or the one prior to the last row. There are many different techniques used to accomplish this. Since PeopleSoft is heavily involved in working with effective-dated or historical information, we will discuss different techniques for those situations in our PeopleSoft-related chapters. 11.2 Substitution variables Substitution variables are used to specify SQR elements in several program locations at compile time. Substitution variables are compile-time variables. The values of substitution 162 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! variables are set during the compilation stage and checked for syntax errors before the execution begins. They cannot be changed during the program execution. The #Define statement and the Ask command present two ways to define substitution variables and to assign values to these variables. Substitution variables make your SQR program easier to maintain. Let us take a look at the following example: !TEST11C.SQR !Using substitution variables #Define col_emplid 12 !Employee ID #Define col_empl_name 30 !Name #Define col_company 7 !Company #Define col_hire_dt 10 !Hire Date #Define col_sep 2 !Column Separator The widths of variables are defined as substitution variables. !******************** Begin-Heading 5 !******************** Print ' ' Print 'Emplid' Print 'Name ' Print 'Hire Date ' Print 'Company' (+2,1,{col_emplid}) (0,1,{col_emplid}) (0,+{col_sep},{col_empl_name}) (0,+{col_sep},{col_hire_dt}) (0,+{col_sep},{col_company}) Using substitution variables in Print statements. !*************** Begin-Program !*************** Do Get_Employee_Info Do Print_Emplyee_Info … End-Program !********************************************* Begin-Procedure Print_Employee_Info !********************************************* Print 'Emplid' (+1,1,{col_emplid}) Print 'Name ' (0,+{col_sep},{col_empl_name}) Print 'Hire Date '(0,+{col_sep},{col_hire_dt}) Print 'Company'(0,+{col_sep},{col_company}) … End-Procedure !****************************** At the beginning of our program, we use substitution variables to define the width of all the variables we want to print. Please note that we define these variables only once, but use them in more than one place in the program. After defining the column widths at the beginning of our program, we use them in the Heading section and the Print_ SUBSTITUTIO N VARIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! 163 Employee_Info procedure. When referenced in the program, substitution variable names must be enclosed in braces “{}”. Is there an advantage in using the substitution variables? Well, let us assume that you want to change the appearance of your report by printing four spaces, instead of two, between columns. If you use substitution variables as we do in Test11C.sqr, all you have to do is to change the statement #Define col_sep 2 to #Define col_ sep 4. Without the #Define statement, you would have to go over your program and change every single Print command. It is common practice to define substitution variables in an external source file. Usually you put together some commonly-used variables in a separate file (called include file), then add this file to your SQR program with the help of the #Include statement. This method allows you to share one include file between multiple programs: you can change only this file without changing all other programs. SQR supports nested #Include statements: one include file may have a #Include statement referencing another include file, allowing up to four levels of nesting. Another way to utilize substitution variables is to use the Ask command. The Ask command, always coded in the Setup section, prompts the user for the value which is placed into a substitution variable. The important fact to remember is that the value is input and substituted at compile time, before actual execution of the program begins. This substitution variable in the Ask command can later be used to replace any command, SQL statement, or argument. Because scanning of substitution variables takes place before the #Include statement is processed, your program logic may change the included file’s name depending on some substitution variable value taken, for example, from the user’s response to an Ask command preceding the #Include statement. This allows you to take another step towards increasing your program’s flexibility. Remember, this is all done at compile time as in the following example: !**************************** Begin-Setup !**************************** Ask Printerid 'Please Enter Printer ID ' #Include 'printer{Printerid}.dat' End-Setup !**************************** A substitution variable obtained from user input defines which printer control file to use. The Ask command should not be confused with the Input command, another user dialog command. The Input command is processed at execution time, whereas the Ask command is processed at compile time. Remember our first bind variable example, Test11A.sqr? Let us use this example again, but this time, instead of the Input 164 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! command, we will use the Ask command. Since there are some limitations on where and how the Ask command can be used, we will change our program accordingly: !TEST11D.SQR !Using substitution variables in the Select paragraph !*************** Begin-Setup !*************** Ask Emplid 'Please Enter Employee ID' End-Setup !**************************** Begin-Program !**************************** Do Get_Empl_Info End-Program !*************************************** Begin-Procedure Get_Empl_Info !*************************************** Let $Found='N' Begin-Select Name Birthdate Let $Found='Y' The Select criteria is From Personal_Data obtained from user Where Emplid='{Emplid}' input with the help of a End-Select substitution variable. End-Procedure !**************************** Notice the difference? First, we use the Ask command in the Setup section, the only section where you can use this command. Since Ask is executed at compile time, no program loops can be used, therefore, we prompt the user only once in our example. Although there are cases (explored later in this chapter) when Ask is more powerful than Input, in this particular example we just want to demonstrate the difference between the Ask and Input commands. As with any substitution variable, when referencing a substitution variable used in the Ask command, the variable name must be placed in braces, for example, {Emplid}. Ask commands have to be issued prior to any use of their substitution variables. If the Ask command references a variable that has already been defined by the #Define statement, the user will not be prompted for the value, and Ask will use the defined value. Occasionally, as in the following example, the use of substitution variables is necessary. Let us assume that, depending on user input, you need to modify the appearance of the report. In addition, you want to prompt for the array characteristics: SUBSTITUTIO N VARIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! 165 !TEST11E.SQR !**************** Begin-Setup !**************** Ask Header_Lines 'Enter Number of lines for Heading ' Ask Footer_Lines 'Enter Number of lines for Footing ' Ask Array_Name 'Enter Array Name ' Ask Array_Size 'Enter Array Size ' Create-Array Name={Array_Name} Size={Array_Size} Field = Emplid:char Substitution variables help to modify Field = Name:char array name and size. These parameField = Zip:char ters cannot be modified dynamically End-Setup from the Input command. Begin-Heading {Header_Lines} Print 'The Use of Substitution Variables' () center End-Heading Begin-Footing {Footer_Lines} page-number (1,1) 'Page ' End-Footing Substitution variables help to change the Heading and Footing parameters. These parameters cannot be modified dynamically via the Input command. Begin-Report Print 'Test' (1,1) End-Report !***************** As you can see, substitution variables help us to modify command lines which are normally quite restrictive. The Input command can’t do this. This is an example when the Ask command has more power than the Input command. Another benefit of using substitution variables is that, since substitutions take place at compile time, any possible errors can be checked before execution starts. In many cases, however, Input is more convenient than Ask. With Input, for instance, you can validate user input and reprompt if it is not valid, something you cannot do with the Ask command. 11.3 Dynamic query variables Dynamic query variables are used to build dynamic SQL code. Sometimes called dynamic SQL variables, or simply, dynamic variables, dynamic query variables are text variables whose values are used as parts of SQL statements. When referenced in SQL statements, their names must be enclosed in square brackets, for instance, [$By], or back slashes in MVS, AS/400, for instance, \$By\. You can use dynamic query variables to substitute columns and other parts of SQL statements or to dynamically change the Where clause or an entire SQL statement. The important thing to remember is that when you use dynamic variables in your SQL statement, SQR cannot check your syntax at compile 166 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! time as it usually does. Therefore, each SQL statement will be constructed and checked at run time. Run-time errors will occur if a dynamic variable or the Where clause syntax are incorrect. Good error-handling routines in your programs are always important, but when SQL statements are modified at run time, such routines are a must. If we need to select all active employees who live in states specified in user input, dynamic query variables will be of great help: !TEST11F.SQR !Using Dynamic Where clauses !*************************** Begin-Program !*************************** Do Main End-Program !*************************** Begin-Procedure Main !*************************** Let $State_Error = 'Y' While $State_Error='Y' Input $State_List 'Enter a list of comma separated state codes (ex. NY,CT,MI)' Let $State_List=Rtrim($State_List,' ') If $State_List = '' Show 'Incorrect Input' Else Let $State_Error = 'N' Do Build_Where If $State_Error = 'N' Do Get_Employees Else Show 'Invalid State, please re-enter' End-If End-If End-While End-Procedure !************************************* Begin-Procedure Build_Where !************************************* A portion of the SQL WHERE clause is constructed based !Construct the Where statement on user input. Let $Where= 'B.State In (' ! Init Where Let #Length=Length($State_List)! Get the Length Let #Start=1 ! Start position for Substring Let $Quote = '''' Let $Comma = '' ! Initialize to null string While #Length > 0 Let $Sti = Substr($State_List,#Start,2) D YN A M I C Q UE R Y V ARIA B L E S TEAM LinG - Live, Informative, Non-cost and Genuine! 167 Do State_Lookup If $State_Error = 'Y' Break !Break from the while loop End-If Let $Where=$Where ||$Comma||$Quote|| $Sti || $Quote Let $Comma = ',' Let #Start = #Start + 3 Let #Length = #Length - 3 End-While Let $Where= $Where || ')' Show '$Where=' $Where End-Procedure !***************************************** Begin-Procedure Get_Employees !***************************************** Begin-Select On-Error=Invalid_Select A.Emplid A.Hire_Dt B.Name B.State Show &A.Emplid ' In ' &B.State From Employment A, Personal_Data B Where A.Emplid=B.Emplid And A.Termination_Dt is Null Previously created portion of the SQL WHERE clause is used in a query with the And [$Where] help of a dynamic query variable. End-Select End-Procedure !***************************************** Begin-Procedure Invalid_Select !***************************************** Show 'Error in Select Statement. Check your Where clause = ' $Where End-Procedure !***************************************** Begin-Procedure State_Lookup !***************************************** Let $State_Error = 'Y' Begin-Select State Let $State_Error = 'N' From STATE_NAMES_TBL Where State=$Sti End-Select End-Procedure !****************************** In Test11F.sqr, we prompt the user for a list of two-character state codes. The input is used to build a portion of the Where clause in the following format: “B.State 168 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! ('XX','YY','ZZ')”. In the Get_Employees procedure, we use the dynamic query variable [$Where] from the main procedure whose value is changed depending on user input. As the result, a portion of the Where clause of the Select statement in in the program will be changed during program execution depending on user input. 11.4 More about dynamic query variables Dynamic query variables can be instrumental in building rather sophisticated generic utilities to select information from the database, or insert new records into the database. For example, depending on user input, you can dynamically construct an entire SQL statement. The process may seem straightforward: just use the method employed in generating the Where clause when building the remaining parts of the entire SQL statement. There is one problem: SQR must know the types of all columns used in the Select paragraph at compile stage. This necessitates the usage of a somewhat tricky logic when working with columns of different types. We will start with an example that demonstrates how to build SQL statements with columns of the character type only: !TEST11G.SQR !Using Dynamic variables to build SQL !*************************** Begin-Program !*************************** Input $Table_Name 'Enter table name for Select Statement (eg. Job)' Input $Column_Names 'Enter char type colmns for Select stmnt comma sep.(eg. Emplid,Company)' Input $Where 'Enter the Where clause (eg. Company=''XYZ'')' Input $Order_By 'Enter Order By Statement' Unstring $Column_Names By ',' Into $Column_Name1 $Column_Name2 $Column_Name3 $Column_Name4 If Rtrim($Where,' ')<>'' Let $Where='Where '||$Where End-If If Rtrim($Order_By,' ')='' Let $Order_By= 'Order by '|| $Order_By End-If Show $Table_Name ' Do Execute_Select End-Program ' $Column_names ' ' $Where ' ' $Order_By !***************************************** Begin-Procedure Execute_Select MORE ABOUT DY NAMIC QU ERY VAR IABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! 169 !***************************************** Begin-Select On-Error=Select_Error [$Column_Name1] &colmn1=Char (+1,1) This query uses dynamic table and column names and hard[$Column_Name2] &colmn2=Char (,+1) coded column types. [$Column_Name3] &colmn3=Char (,+1) [$Column_Name4] &colmn4=Char (,+1) Show &Colmn1 ' ' &Colmn2 ' ' &Colmn3 ' ' &Colmn4 From [$Table_Name] [$Where] [$Order_By] End-Select End-Procedure !************************************* Begin-Procedure Select_Error !************************************* Show 'SQL ERROR: in Select ' $Column_Names ' From ' $Table_Names $Where ' ' $Order_By Show 'SQL-status=' #sql-status End-Procedure ' ' The resulting program may look a little artificial since its only purpose is to demonstrate the usage of dynamic variables in building all parts of the SQL paragraph. At the beginning, we prompt user to enter the input parameters. In a production environment, it may be a good idea to verify every part of user input against your database. The table name, for example, can be verified against the system catalog (the names of the catalog table vary for different databases) that contains all table names. Similarly, you can verify the entered column names. As you can see, the above example uses a hard-coded column type, and therefore, will work only with columns of the character type. If you need to build a query that uses different types of columns, you can use a number of techniques. One method is to continue using the hard-coded character type for all the columns in the Select paragraph, but to also employ the database-specific built-in conversion functions to convert each column value to the character format: !TEST11L.SQR !Building Dynamic SQL with various column data types !*************************** Begin-Program !*************************** Do Get_Input If Not IsNull($Input_Table_Name) Do Build_Dynamic_Vars Do Execute_Select Else show 'No Entries found. Program Ended' 170 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! End-If End-Program !******************************** Begin-Procedure Get_Input !******************************** Let $End='Y' While Upper($End) = 'Y' Let $End='N' Input $Input_Table_Name 'Enter table name for Select Statement (e.g. Job)' Do Verify_Table_Name If $Table_Exists = 'N' Input $End 'Table is not found. Retry? (Y/N) ' type=char End-If End-While If Not IsNull($Input_Table_Name) Input $Column_names 'Enter valid colmns, comma sep.,max=3 (e.g. Emplid,Company)' Input $Where 'Enter the Where statement criteria (e.g. Company=''XYZ'')' Input $Order_By 'Enter the Sort Order (e.g. 1,2 or Emplid,Company)' End-If End-Procedure !******************************** Begin-Procedure Verify_Table_Name !******************************** Let $Table_Exists = 'N' Let $Input_Table_Name = Upper($Input_Table_Name) Begin-Select Table_Name Let $Table_Exists = 'Y' Verifying table name in the systems catalog. From All_Tables where Table_Name=$Input_Table_Name End-Select End-Procedure !*********************************** Begin-Procedure Build_Dynamic_Vars !*********************************** Do Build_Dynamic_Colmns if rtrim($Where,' ')<>'' Let $Where='Where '||$Where End-If if rtrim($Order_By,' ')='' Let $Order_By='1' End-If End-Procedure !*********************************** Begin-Procedure Build_Dynamic_Colmns !*********************************** MORE ABOUT DY NAMIC QU ERY VAR IABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! 171 Let $q='''' unstring $Column_names by ',' into $Column_Name1 $Column_Name2 $Column_Name3 Do Convert_To_Char($Column_Name1,$Dyn_Column_Name1) show $Column_Name1 $Dyn_Column_Name1 Do Convert_To_Char($Column_Name2,$Dyn_Column_Name2) show $Column_Name2 $Dyn_Column_Name2 Do Convert_To_Char($Column_Name3,$Dyn_Column_Name3) show $Column_Name3 $Dyn_Column_Name3 End-Procedure !*************************************************************** Begin-Procedure Convert_To_Char($Column_Name,:$Dyn_Column_Name) !*************************************************************** Depending on the data type, building the data Let $Input_Column_Name = Rtrim($Column_Name,' ') conversion string If $Input_Column_Name <> '' Let $Input_Column_Name = Upper($Input_Column_Name) Do Get_Data_Type($Input_Column_Name,$Data_Type) If $Data_Type <> '' Evaluate $Data_Type When = 'DATE' Let $Dyn_Column_Name = 'To_Char('||$Column_Name||',' ||$_q ||'YYYYMMDD' ||$_q||')' Break When = 'NUMBER' Let $Dyn_Column_Name = 'To_Char('||$Column_Name||')' Break When-Other Let $Dyn_Column_Name = '' || $Column_Name End-Evaluate End-If show '$Dyn_Column_Name=' $Dyn_Column_Name Else Let $Dyn_Column_Name ='' End-If End-Procedure !************************************************************ Begin-Procedure Get_Data_Type($Input_Column_Name,:$Data_Type) !************************************************************ Let $Data_Type='' Begin-Select Column_Name Data_Type Obtaining data type Let $Data_Type=&Data_Type from systems catalog From All_Tab_Columns where Table_Name=$_Input_Table_Name and Column_Name=$Input_Column_Name End-Select End-Procedure 172 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! !******************************** Begin-Procedure Execute_Select !********************************* Begin-Select On-Error=Select_Error [$Dyn_Column_Name1] &colmn1=char (+1,1) [$Dyn_Column_Name2] &colmn2=char (+1,1) [$Dyn_Column_Name3] &colmn3=char (+1,1) show &colmn1 ' ' &colmn2 ' ' &colmn3 From [$Input_Table_Name] [$Where] Order by [$Order_By] End-Select End-Procedure The actual Select statement with Dynamic variables !************************** Begin-Procedure Select_Error !************************** Show 'SQL ERROR:' 'Select ' $Column_Names ' From ' $Input_Table_Name ' ' $Where show '$Dyn_Column_Names=' $Dyn_Column_Name1 ' ' $Dyn_Column_Name2 ' ' $Dyn_Column_Name3 Show 'SQL-status=' #sql-status End-Procedure In Test11L.sqr, the program uses the Oracle system catalog tables All_Tables (which contains all table names) and All_Tab_Columns (which contains all column names and types for each table) to verify the entered table name and to determine the type of each column entered by the user. Depending on the column type, the program then uses the proper format of the Oracle-specific function to_char() to convert the column value to the character type. This way, the column types for all columns in the Select paragraph can be coded as character. Later, all converted-to-character type column values may be reconverted back if required by the application logic. If you work with a database other than Oracle, you can use your database-specific system catalog tables and type conversion functions. Beware: using dynamically built SQL gives you lots of power. Use it with caution. The syntax of dynamically generated SQL statements is not checked at compile time. While using simple hard-coded SQL statements may seem less fancy (and sometimes increases the size of your program), in many cases, it is more reliable and efficient than generating dynamic SQL. MORE ABOUT DY NAMIC QU ERY VAR IABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! 173 . KEY POINTS 1 The three special types of variables that increase flexibility of SQR programs are: bind variables, substitution variables, and dynamic query variables. 2 Bind variables and dynamic query variables are run-time variables. 3 Substitution variables are compile time variables: their values cannot be changed at run time. 4 Bind variables are used in SQL statements in the SQL or Select paragraphs. 5 Bind variables can be used in correlated Select statements to link one query to another. 6 Substitution variables can be used to alter any part of SQR programs. 7 Substitution variable values are set at compile time by either the #Define compile directive or the Ask command. 8 The Ask command can be used only in the Setup section. 9 When referenced in a program, substitution variable names must be enclosed into braces; e.g., {Subst_Var}. 10 A change to just one substitution variable may cause multiple changes in the program. 11 Dynamic query variables are variables whose values are used as parts of SQL statements. 12 When referencing dynamic query variables you should enclose their names in square brackets; e.g., [dynamic_var]. 13 Dynamic query variables can be used to substitute any part of an SQL statement. 14 When dynamic query variables are used to generate SQL statements, SQR can not check the SQL syntax at compile time. 15 When using dynamic query variables to generate SQL statements, it is very important to code error-handling routines. 174 CH APTER 11 R UN- & COMPILE- TIME VA RIABL E S TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 2 Working with arrays IN THIS CHAPTER • Creating an array • Populating an array with data • Retrieving data from an array • Referencing individual array elements • Multiple-occurrence fields in SQR arrays • Using the generic SQR Let command and SQR array-specific commands to access array elements. • Sorting arrays • Searching data in arrays 175 TEAM LinG - Live, Informative, Non-cost and Genuine! 12.1 SQR arrays SQR arrays Arrays can be found in many programming languages. They are instrumental to success when a program needs to work with a number of identical groups of fields in the program memory. Instead of referring to each field by its unique name, arrays allow you to take advantage of the fact that all these groups have an identical or similar layout. A typical array is a collection of similar groups of fields in the program memory wherein each group can be referred to by its relative position number in the array (figure 12.1). Figure 12.1 A typical SQR array An SQR array is a memory structure that consists of rows and fields. When no arrays are used, in most cases, an SQR program processes its input records one by one. After a record is processed, it is overlaid with the next record, making the first record’s contents inaccessible. Because it is sometimes necessary to process all or some records at once, SQR arrays can serve as buffers where the data can be temporarily stored. Oftentimes, it is not only a convenient method of temporarily storing information, but also a good way of developing an efficient program. In addition to storing data in the program memory, arrays are also used by the Print-Chart command to generate graphical charts. (See chapter 16.) Do not confuse arrays with relational tables: arrays are temporary structures created in the program memory, whereas tables reside on discs. SQR has a specific set of commands used to create and populate arrays and to access information stored in arrays. The following commands work with arrays: • Create-Array • Clear-Array • Get • Put • Array-Add • Array-Subtract • Array-Multiply • Array-Divide 176 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! In addition to the above array-specific commands, you can use the already familiar SQR command Let to manipulate array elements. 12.2 How to create an array How to create an array In SQR, arrays are created during the compile stage, before the program is executed. The Create-Array command is used to create an array in any section of your program. For example Create-Array Name=Dependents Size=25 Field=Dependent_Id:Char Field=Dependent_Name:Char Field=Dependent_Birthday:Date Field=Relationship:Char Table 12.1 shows the elements of our array. Table 12.1 Array elements Array element Dependent_Id Dependent_Name Dependent_Birthday Relationship Dependents(0) 01 Smith,Maria 01-Jan-1960 Dependents(1) 02 Smith,Albert 02-Feb-1982 S Dependents(2) 03 Smith,Marsha 03-Mar-1984 D Dependents(3) 04 Smith,Lisa 04-Apr-1940 M SP In the above example, we created an array named Dependents with a size of twenty-five rows. Each row holds a record representing one dependent. There are four fields in every row of our array: Dependent_Id, Dependent_Name, Dependent_ Birthday, and Relationship. Please note that no special characters are used to denote array fields formats: formats in the Declare-Array command are declared explicitly. The Size in the Create-Array command specifies the maximum number of rows in the array. If you do not know the expected number of rows in an array, it is better to over-allocate rather than under-allocate the memory for your array. In our example, most employees will probably have two or three dependents, but we allocated room for twenty-five dependent rows just in case an unusual situation exists. If the array subscript value exceeds the maximum array size, SQR aborts the program execution and displays the following message: (SQR 1500) Array element out of range (25) for array 'Dependents' on line 28. SQRW: Program Aborting. HOW TO CR EATE AN AR RAY TEAM LinG - Live, Informative, Non-cost and Genuine! 177 In this case, our program attempted to address row 25 which is beyond the maximum number of rows (row numbers start with row zero). Each field in an array must be assigned a type: • Char for character strings • Number for default numeric type fields • Decimal for decimal numbers with an optional precision qualifier • Float for double precision floating point numbers • Integer for integer numbers • Date for dates declared as date variables. Please note that in SQR version 3.0 (and earlier versions) you can only use either Number or Char types, with Char used to store both strings and dates. In many instances, a row in an array may contain a field, consisting of a number of repeated portions of the same type, for example, three different employee phone numbers: a work phone number, a home phone number, and a beeper number. These fields are called multiple-occurrence fields. Instead of coding each portion of a multipleoccurrence field as a separate field, you can define only one field and specify the number of occurrences for this field: Create-Array Clients Size=100 Field=Name:Char Field=Phone:Char:3 In the above example, we defined an array of one hundred rows; each row includes the Name field and the three-occurrence field Phone. Figure 12.2 is a graphical illustration of an array where Field2 is a twooccurrence field. Once an array is dynamically created, any array field can be referenced by specifying the field name, the field row number starting from number zero, and, for multipleoccurrence fields, the field occurrence number starting from occurrence number zero. In the Clients array, the rows are numbered from zero to 99, and the field Phone occurrences are numbered from zero to 2. Figure 12.2 An array with multiple-occurrence 178 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! All SQR arrays are created before the program is executed. While the CreateArray command can be used in any section of your program, it is a good practice to place it into the Setup section. Also, we recommend the Size parameter be defined as a substitution variable, and placed at the top of your program. This way, if you need to change the size of an array, you will not have to go over your program to find the location of the proper parameter. Let’s use a substitution variable in our Create-Array statement: #Define Max_Dependents 25 Begin-Setup Create-Array Name=Dependents Size = {Max_Dependents} Field=Dependent_Id:Char Field=Dependent_Name:Char Field=Dependent_Birthday:Date Field=Relationship:Char End-Setup The Create-Array command just allocates and initializes memory for an array. It is still a programmer’s job to populate the array with information by reading data from the database tables or some other input, and moving the selected data into the array. When an array is created, its fields are automatically initiated to their default values based on the following rules: • all numeric fields (with types equal to Number, Decimal, Float, or Integer) are set to zero • all string fields (with type equal to Char) are set to NULL • all date fields (with type equal to Date) are set to NULL. You can specify an initial value for an array’s field in the Create-Array command, as in the following example where all three occurrences of the Phone field are set to an initial value of ‘None’. Create-Array Name=Clients Size=100 Field=Name:Char Field=Phone:Char:3='None' 12.3 Placing data into arrays placing data into arrays There are two methods of placing data into an array. You can reference the array elements in the Let command or use the array-specific command Put. Here are several examples using the Let command: PLACING DATA INTO AR RAYS TEAM LinG - Live, Informative, Non-cost and Genuine! 179 1 2 3 Let Dependents.Dependent_Id(10) = '01' Let Dependents.Dependent_Id(#Row_Num) = &A.Dependent_Benef Let Clients.Phone(#i,#j) = &A.Phone In the example’s line 1, the string literal '01' is moved into field Dependent_Id of the 11th row (counting from row number zero) of the Dependents array. The row number does not have to be coded as a constant; numeric variables can be used to store and dynamically change row numbers. In line 2, the row number is stored in the When using variables numeric variable #Row_Num, which can be changed Note to refer to row numby the program. bers or occurrence numbers, In line 3, the field Phone in the Clients array be sure to keep the variable is a multiple-occurrence field. The numeric variables values within the boundaries #i and #j store the row number and the field Phone defined in the Createoccurrence number, respectively. The Let command Array command. moves the value of &A.Phone to the field Phone of the Clients array with the row number stored in #i and the occurrence number stored in #j. The Put command was designed specifically to work with arrays, and offers more flexibility in placing data into arrays. You can move values to all of the array fields at once using just one Put command, or you can move data to one or more specified fields. For example 1 2 3 4 5 Put '01' Into Dependents(5) Put '01' 'Smith,Maria' Into Dependents(#i) Put &A.Dependnt_Benef &Birthday Into Dependents(#Row_Sub) Dependent_Id Dependent_Birthday Put &Name $Phone into Clients(#Row_Num) Name Phone(#Sub_Num) Put $Phone1 $Phone2 $Phone3 into Clients(0) Phone(0) Phone(1) Phone(2) In line 1 of the above examples, a string literal '01' is moved into the first field of the 6th row of the Dependents array (row numbers are counted from row zero). We do not specify the name of the target field, and, therefore, SQR automatically places the data into the first field of the row. In line 2, two literals—'01' and 'Smith,Maria'—are moved into a row in the Dependents array. The row number is stored in the numeric variable #i. As in the first example, no particular target fields are specified, and SQR moves both literals into the first two consecutive array fields in the order they were defined in the Create-Array statement. In this case, '01' is moved into Dependent_Id field, and 'Smith,Maria' is moved into Dependent_Name. 180 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! In line 3, the values of SQR columns &A.Dependnt_Benef and &Birthday are moved into the two specified target fields, Dependent_Id, and Dependent_Birthday, in a row with the number stored in #Row_Sub. If we did not specify the target field names, &A.Dependnt_Benef would be moved into the first field in the row; Dependent_Id, and &Birthday would be moved into the next one, Dependent_Name. We recommend you use the form of the Put statement used in the third line of the example code even if you do not have to specify the target fields: it is preferable from the program maintenance point of view. If the array row layout is changed, the Put statement in the third example will not have to be modified, as long as fields Dependent_Id and Dependent_Birthday remain parts of the array row. In line 4, the value of SQR column &Name is moved into field Name of the Client’s row with the number controlled by #Row_Num. String variable $Phone is moved into field Phone of the same row of the array. Phone is a multiple-occurrence field and the numeric variable #Sub_Num stores the occurrence number in the target field Phone. Finally, in line 5, the string variables $Phone1, $Phone2, and $Phone3 are moved into the zero, first, and second occurrences of the Phone field in the first row of the Clients array. 12.4 Initializing arrays initializing arrays As we have already mentioned, when an array is created all its field values are automatically initialized. Sometimes you need to re-initialize an array after some or all of its fields have been populated. It is a good practice to always reinitialize your array after its use. The Clear-Array command resets each field of an array to its initial value. Field initial values are specified in the Create-Array command. If no initial value was specified for a field, the field is set to its default value. Here is an example of the Clear-Array command: Clear-Array Name=Dependents 12.5 Retrieving data from arrays retrieving data from arrays Two methods of retrieving data from an array are very similar to the two previously described methods of placing data into an array. You can use the Let command or the array-specific command Get. The examples next illustrate the use of the Let command when retrieving data from an array: Let $Dep = Dependents.Dependent_Id(#i) Let $Phone = Clients.Phone(#i,#j) RE TRIEVING DATA F ROM ARRAYS TEAM LinG - Live, Informative, Non-cost and Genuine! 181 The Get command is similar to the Put command and gives you more flexibility in retrieving data from arrays than the Let command. You can retrieve all the array’s fields at once using one Get command. You can also move data from one or more specified fields or occurrences of a field: 1 2 3 4 5 Get $Dep_Id From Dependents(10) Get $Dep_Id $Dep_Name From Dependents(#i) Get $Dep_Id $Dep_Bday From Dependents(#Row_Sub) Dependent_Id Dependent_Birthday Get $Client_Name $Client_Phone From Clients(10) Name Phone(#j) Get $Phone1 $Phone2 $Phone3 From Clients(#i) Phone(0) Phone(1) Phone(2) In line 1 of our example, $Dept_Id is populated from the first field of the Dependents array’s row number 10 (counting from row number zero). Since we did not code a specific field name of the row, SQR automatically retrieves the content of the first field in the row. In line 2, the values of $Dept_Id and $Dept_Name are retrieved from the first two fields of the Dependents array element with the row number stored in #i. In line 3, the values of the Dependent_Id and Dependent_Birthday fields from the row with the number stored in #Row_Sub are moved to $Dep_Id and $Dep_Bday respectively. As we already mentioned, this method of explicit coding of the row field names is preferable from the program maintenance point of view. In line 4, field Name from the 11th row is moved into the string variable $Client_ Name. In addition, the Phone field, which occurs in row j, is moved into the specified variable $Client_Phone. In the last line of this example, the zero, the first, and the second occurrences of the Phone field in the row with the number stored in #i are moved into string variables $Phone1, $Phone2, and $Phone3. 12.6 Performing arithmetic operations on elements of an array arithmetic operations on array elements You can perform addition, subtraction, division, and multiplication operations on elements of an array by using the Let command or one of the following four array-specific commands. • Array-Add • Array-Divide 182 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! • Array-Multiply • Array-Subtract In order to use these commands, the array must be previously created by the Create-Array command. All fields used in these operations must be declared as Number, Decimal, Float, or Integer. The following examples will show you how to use these commands: 1 2 3 4 5 Let Employees.Salary(10) = Employees.Salary(10) + #Bonus Array-Add #Count To Clients(#i) Count Array-Subtract 1 From Books(500) Books_Received Array-Multiply #Incr1 #Incr2 #Incr3 Times Empl(1) Rate(0) Rate(1) Rate(2) Array-Divide 5 Into Empl(#M) In line 1, the Let command is used to add the value in #Bonus to the Salary field in the 11th row (counting from row number zero) of the Employees array. In line 2, the value of numeric variable #Count is added to the value of Count in the Clients array’s row with the number stored in #i. In line 3, we subtract one from the value in the Books_Received field in the 501st row (counting from row number zero) of the array Books. In line 4, the occurrences 0, 1, and 2 of the field Rate of the second row of array Empl are multiplied by the values stored in #Incr1, #Incr2, and #Incr3, respectively. The result is stored in the same occurrences of the field Rate in the same row of the array. In line 5, the very first field of the row with the number stored in #M is divided by five. Note that if a zero-division occurs, SQR is smart enough to intercept it. A warning message will be displayed, and the program execution will continue without changing the result field. Now let’s create a program using some of the commands we have learned in this chapter. Suppose, for example, we need to produce an employee listing by department and print the following departmental statistical information: the number of active employees, the number of women, the number of men, and an average salary for the department. Of course, this task can be accomplished without using an array. We would just need to select employees sorted by department, accumulate separate female and male count totals as well as the department salary total, and, on department break, print the tabs accumulated during the select process. But the real business environment sometimes brings additional challenges. We may be asked to print a list of employees by the department, and, at the same time, departmental statistics for each department at the bottom of the report. We can develop two separate SQR programs for these two ARITHMETIC OPERATIO NS ON ARR AY ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 183 different tasks, but wouldn’t it be more efficient to avoid accessing the database twice? Keeping this in mind, let’s write our program: !TEST12A.SQR !Using arrays in SQR #Define Stat_Array_Size 100 !*********************** Begin-Setup !*********************** Create-Array Name=Statistics Size ={Stat_Array_Size} Field=Deptid:Char='999999' The array Statistics Field=Active_EE:number is created. Field=Women:Number Field=Men:Number Field=Total_Salary:number End-Setup !******************** Begin-Heading 3 !******************** Print 'Employee Statistics by the Department' (1,10) Print 'Department Id' (2,1,) Print 'Employee Id' (,+2) Print 'Status' (,+2) Print 'Sex' (,+2) Print 'Annual Rate' (,+2) Print ' ' (3,1) End-Heading !******************** Begin-Program !******************** Let #i=0 Do Select_Employees Do Print_Summary End-Program !*********************************** Begin-Procedure Select_Employees !*********************************** Begin-Select A.Deptid (+1,1,13) On-Break A.Emplid (,+2,11) A.Empl_Status (,+2,6) B.Sex (,+2,3) A.Annual_Rt (,+2,12) Edit $,$$$,$$$.00 Do Update_Array From Job A, Personal_Data B Where A.Emplid=B.Emplid And A.Empl_Status='A' 184 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! And A.Deptid in ('10200','21700') And A.Effdt=(Select Max(Effdt) From Job Where Emplid=A.Emplid And Empl_Rcd=A.Empl_Rcd And Effdt<=Sysdate) And A.Effseq=(Select Max(Effseq) From Job Where Emplid=A.Emplid And Empl_Rcd=A.Empl_Rcd And Effdt = A.Effdt) Order By A.Deptid,A.Emplid End-Select End-Procedure !******************************* Begin-Procedure Print_Summary !******************************* Print 'Summary by Department' (+2,10) Let #i=0 While #i <= {Stat_Array_Size} Get $Deptid #Active #Women #Men #Total_Salary From Statistics(#i) The Get command is used If $Deptid='999999' to retrieve the values of Break the array’s fields. Else If #Active <> 0 Let #Average_Salary = #Total_Salary / #Active End-If Print 'Department ' (+1,1) Print $Deptid (,+1) Print 'Number of Active Employees = ' (+1,1,33) Print #Active (,+1) edit 9,999,999 Print 'Number of Women = ' (+1,1,33) Print #Women (,+1) edit 9,999,999 Print 'Number of Men = ' (+1,1,33) Print #Men (,+1) edit 9,999,999 Print 'Average Salary = ' (+1,1,33) Print #Average_Salary (,+1) Edit $$$,$$$,$$$.99 Next-Listing Need=4 Skiplines=1 End-If Add 1 to #i End-While End-Procedure !******************************* Begin-Procedure Update_Array !******************************* !Find an element in the array with Deptid = Selected Deptid !If it's not there, create a new element in the array ARITHMETIC OPERATIO NS ON ARR AY ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 185 Let $Found='N' Let #j=0 While #j < {Stat_Array_Size} Get $Deptid From Statistics(#j) Deptid If $Deptid='999999' Let #i=#j Put &A.Deptid Into Statistics(#i) Deptid Let $Found='Y' Break Else If &A.Deptid = $Deptid Let #i=#j Let $Found = 'Y' Break End-If End-If Let #j=#j+1 End-While The Put command is used to place &A.Deptid into the array. If $Found='N' Display 'ERROR: There are more than {Stat_Array_Size} departments in the array' Stop End-If The Array-Add Array-Add 1 To Statistics(#i) Active_EE command is used to Array-Add &A.Annual_Rt To Statistics(#i) Total_Salary update the array’s field values. If &B.Sex='M' Array-Add 1 to Statistics(#i) Men Else Array-Add 1 to Statistics(#i) Women End-If End-Procedure !***************************** Figure 12.3. shows the output report of our program. In the Setup section of Test12A.sqr, we use the Create-Array command to create an array of a hundred rows. Arrays are preallocated in memory, therefore, we do not want to overestimate array sizes. That is, we assume that there will not be more than a hundred departments. Since we define the upper boundary, we have to make sure that it is not exceeded during the program run. The Select paragraph in the main procedure, Select_Employees, joins the Job table and the Personal_Data table to select department ID, employee ID, employee status, employee sex, and employee salary. The selected rows are ordered by the department ID and the employee ID. For every record selected, the procedure Update_Array is invoked. The Update_Array procedure looks for an element in the array Statistics with Deptid equal to the &Deptid selected for each employee selected in the main 186 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 12.3 List of employees by the department with departmental statistics at the bottom query. If not found, a new row is added to the array. The value of the Deptid field in the row is set to the selected department Id value with the help of the Put command. When the array was created, all the Deptid values were set to '999999'. This helps to identify empty array rows. Another method of checking for empty rows is to save the subscript to the last loaded element and to check for this value when performing the search. In this case, the initial value for Deptid, will be set to NULL. After exiting from the search loop, we make sure that the array row number does not exceed its upper boundary. If so, the program displays an error message on the screen, logs the error into the log file, and stops the program execution. Alerting those responsible for program execution about this and other serious error possibilities would be a wise precaution and good programming practice. When the row is found in the array, the Array-Add command is used to update the following array fields: Active, Women, Men, and Total_Salary. When all employees are selected, our array will be filled with all the information necessary to print the departmental totals. In the Print_Totals procedure, the program uses the Get command to retrieve every row of the array and prints the statistical information for each department. Note that we use the Let command to calculate the average salary for the department. ARITHMETIC OPERATIO NS ON ARR AY ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 187 As you can see from the example, SQR arrays offer a lot of ways to create effective programs. You can eliminate the need to sort the data, and combine several reports into one by using multiple arrays with only one pass on data. If your database contains hundreds of thousands of records, arrays may save hours of computing time. 12.7 Sorting array elements Sorting array elements When working with an array, there may be a need to sort array elements by one or more fields within the array. There are many reasons why you might want to sort arrays. Often, sorting may help to rearrange an array to make generating your reports easier. In other cases, you may need to use certain search algorithms to speed up array processing. Most of these algorithms can work only with pre-sorted arrays. There are many different sort algorithms that can be used to sort arrays. Let’s apply the classical “bubble sort” algorithm to the Dependents array which was discussed in the beginning of this chapter. We will sort our array in the ascending order by Dependent_Id. The approach used in the algorithm is called “bubble sort” because rows with the smaller values of Dependent_Id will bubble up through the array. Test12B.sqr uses the “bubble sort” to order the Dependents array by the Dependent Ids: !Test12B.SQR Bubble Sort Example !Using the “bubble sort” algorithm to sort an array !******************************** #Define Max_Dependents 25 !******************************* Begin-Setup !******************************* Create-Array Name=Dependents Size = {Max_Dependents} Field=Dependent_Id:Char Field=Dependent_Name:Char Field=Dependent_Birthdate:Date Field=Relationship:Char End-Setup !******************************* Begin-Program !******************************* Let #I=0 Do Main End-Program !******************************* Begin-Procedure Main !******************************* Begin-Select A.Emplid Let #Ind = 0 Do Load_Dep_Array 188 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! Do Sort_Dep_Array Do Print_Array From Personal_Data A Where A.Emplid In ('8001','8360') !Limit selection for !testing purpose Order By A.Emplid End-Select End-Procedure !******************************* Begin-Procedure Load_Dep_Array !******************************* Begin-Select B.Emplid B.Dependent_Benef B.Name B.Birthdate B.Relationship Show &B.Emplid Show &B.Dependent_Benef Populating the Put &B.Dependent_Benef Dependents array &B.Name &B.Birthdate &B.Relationship Into Dependents (#ind) Dependent_Id Dependent_Name Dependent_Birthdate Relationship Add 1 to #Ind If #Ind >= {Max_Dependents} Show 'Array size is exceeded the Max of ' ' {Max_Dependents}' ' for Emplid = ' &B.Emplid Exit-Select End-If From Dependent_Benef B Where B.Emplid = &A.Emplid Order By B.Dependent_Benef DESC End-Select Let #Count = #Ind Show '#Count =' #Count End-Procedure !******************************* Begin-Procedure Sort_Dep_Array !******************************* Let #Ind_Out = 0 While #Ind_Out < #Count Let #Ind_In = #Ind_Out + 1 Sorting the Dependents array using the “bubble sort” algorithm While #Ind_In < #count Let $Dep_Out = Dependents.Dependent_Id(#Ind_out) Let $Dep_In = Dependents.Dependent_Id(#Ind_in) If $Dep_Out > $Dep_In Do Swap_In_Out End-If Add 1 to #Ind_In SORTING A RR AY ELEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 189 End-While Add 1 to #Ind_Out End-While End-Procedure !******************************* Swapping two elements Begin-Procedure Swap_In_Out of the array !******************************* Get $Dependent_Id_Temp $Name_Tmp $Birthdate_Tmp $Relationship_Tmp From Dependents (#Ind_In) Dependent_Id Dependent_Name Dependent_Birthdate Relationship Let Dependents.Dependent_Id(#Ind_In) = Dependents.Dependent_Id(#Ind_Out) Let Dependents.Dependent_Name(#Ind_In) = Dependents.Dependent_Name(#Ind_Out) Let Dependents.Dependent_Birthdate(#Ind_In) = Dependents.Dependent_Birthdate(#Ind_Out) Let Dependents.Relationship(#Ind_On) = Dependents.Relationship(#Ind_Out) Put $Dependent_Id_Temp $Name_Tmp $Birthdate_Tmp $Relationship_Tmp Into Dependents (#Ind_Out) Dependent_Id Dependent_Name Dependent_Birthdate Relationship End-Procedure !******************************* Begin-Procedure Print_Array !******************************* Let #Ind = 0 While #Ind < #Count Let $Dependent_Id = Dependents.Dependent_Id(#Ind) Let $Name = Dependents.Dependent_Name(#Ind) Let $Birthdate = Dependents.Dependent_Birthdate(#Ind) Let $Relationship = Dependents.Relationship(#Ind) Show &A.Emplid Show $Dependent_Id Show $Name Show $Birthdate Show $Relationship Add 1 To #Ind End-While End-Procedure !******************************* The program starts with selecting data from the Personal_Data table, and, for each selected row, calls the Load_Dep_Array procedure to populate the Dependents array with data from the Dependent_Benef table. Once the array is populated, the Sort_Dep_Array procedure is called to sort the array by the Dependent_Id field, and the Print_Array procedure is called to print information about the dependents of each selected employee. 190 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! Let’s take a closer look at the Sort_Dep_Array procedure. The processing starts with the first row of the array. The goal is to place the element with the smallest Dependent_Id into this row. The procedure goes over all remaining elements of the array, starting from the second element, and, if it finds an element which has a smaller Dependent_Id than the element in the first row, the logic swaps the two elements by calling the Swap_In_Out procedure. Once the element with the smallest Dependent_Id is put into the first row, the procedure will try to put the element with the next smallest Dependent_Id into the second row by going over all remaining elements starting from the third element and swapping any element that has a smaller Dependent_Id with the element currently in the second row. The process will continue until the entire array is sorted. The procedure has a nested two-level loop. The outer loop determines which array element is used as the basis of comparison. The inner loop goes over the list of array elements, starting from the one located after the element currently used as the basis, and compares the value of Dependent_Id in each element to the value in the basis. While the “bubble sort” algorithm is straightforward and easy to program, it is not the most efficient one when dealing with very large arrays. Depending on the array size and the importance of your program performance, you may want to try other popular sorting algorithms. Another classical sorting algorithm, called QuickSort, is demonstrated in Brio’s SQR User’s Guide (version 4). An interesting comparison of three different sorting algorithms can be found on the website http://www.sqrtools.com, created by Tony DeLia. In his website, Tony discusses the following three algorithms: Bubble sort, Insertion sort, and QuickSort, and compares their performance by sorting the same array using each of these algorithms. 12.8 Searching data in arrays Searching data in arrays Searching data in an array is even more important then sorting. SQR does not have special commands to carry out array searches; you have to program this logic yourself. There are many popular algorithms that can be used to search in arrays. Some of them are very simple to implement, while others may require some effort. As with sorting, you need to select an algorithm depending on your array size and the required program performance. Here we will discuss two algorithms: sequential search and binary search. The sequential search algorithm is very straightforward: when looking for an element with a particular value in a certain field, your program has to go over every single array element until it hits the right one. Not a very efficient method of searching, but very simple to program. In most non-time critical applications this method is quite sufficient. The sequential search was already used in Test12A.sqr, therefore we will not S EA RC HI NG D A T A I N A R RAY S TEAM LinG - Live, Informative, Non-cost and Genuine! 191 bother you with another example of an SQR code that will implement this algorithm. Please note that this method does not require a presorted array. The binary search algorithm is more efficient than the sequential search, but it has one very important limitation: it works only with presorted arrays. Depending on the order of sorting—ascending or descending—you may need to slightly modify this algorithm. The main idea of the binary search algorithm is very simple: it compares the target value to the value of the search field of the middle element of the array’s current search area. The search starts when the current search area is equal to the entire array. The search fields in the array are ordered in a predefined sequence, therefore, if the target value is less than the value in middle element, the search focuses on the lower half of the array, otherwise the search concentrates on the upper half. The remaining half of the array becomes the current search area. The next step is to compare the target value to the middle element of the current search area. This process of dividing the current search area in two continues until the element is found or the current search area is empty (element not found). As you can see, the binary search does not involve searching through the entire array. For large arrays, this algorithm may result in substantial savings. If you use the sequential search algorithm, on average, your program will have to compare the search key with half of the elements of the array. On the other hand, using the binary search algorithm for an array of one billion elements will require only a maximum of 30 comparisons to find the key! Here is an SQR program that uses the binary array search: !Test12C.SQR !Binary Search Example !******************************** #Define Max_Companies 2000 !******************************* Begin-Setup !******************************* Create-Array Name=Companies Size = {Max_Companies} Field=Company_Cd:Char Field=Company_Name:Char Field=Count:Number End-Setup !******************************* Begin-Program !******************************* Do Load_Comp_Array Do Main End-Program !******************************* 192 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! Begin-Procedure Main !******************************* Do Update_Comp_Array Do Print_Comp_Totals End-Procedure !******************************* Begin-Procedure Load_Comp_Array !******************************* Let #Idx = 0 Begin-Select Company Populating the Descrshort Companies array Let $Company = Rtrim(&Company,' ') Put $Company &Descrshort into Companies (#Idx) Company_Cd Company_Name Let #Idx = #Idx + 1 If #Idx > {Max_Companies} Show 'Array size is exceeded the Max of ' '{Max_Companies}' Move #Idx to $Idx 9999 Show 'Number of Companies loaded = ' $Idx Exit-Select End-If Show 'idx = ' #Idx From Company_Tbl Order By Company End-Select Let #Max_Loaded = #Idx - 1 Show 'Loaded:' #Max_Loaded End-Procedure !******************************** Begin-Procedure Update_Comp_Array !******************************** Begin-Select A.Emplid A.Company Let $Found = 'N' Calling the search Let $Ee_Company = Rtrim(&A.Company,' ') subroutine Do Search_Comp_Array If $Found = 'Y' Let Companies.Count(#Idx) = Companies.Count(#Idx) + 1 Else Show 'Company not found in array for Emplid/Company ---> ' &A.Emplid '/' &Company End-If From Job A Where A.Empl_Status = 'A' And A.Effdt=(Select Max(Effdt) From Job Where Emplid=A.Emplid And Empl_Rcd=A.Empl_Rcd S EA RC HI NG D A T A I N A R RAY S TEAM LinG - Live, Informative, Non-cost and Genuine! 193 And Effdt<=Sysdate) And A.Effseq=(Select Max(Effseq) From Job Where Emplid=A.Emplid And Empl_Rcd=A.Empl_Rcd And Effdt = A.Effdt) Order By A.Emplid End-Select End-Procedure !******************************** Using the binary Begin-Procedure Search_Comp_Array search algorithm !******************************** Let #Idx = 0 Let #Start = 0 Let #End = #Max_Loaded While (#Start <= #End) and $Found = 'N' Dividing the current Let #Mid = Trunc((#Start+#End)/2,0) search area in half Let $Comp = Companies.Company_Cd(#Mid) If $Ee_Company < $Comp Let #End = #Mid - 1 Else If &A.Company > $Comp Let #Start = #Mid + 1 Else Let $Found = 'Y' End-If End-If End-While Let #Idx = #Mid End-Procedure !********************************* Begin-Procedure Print_Comp_Totals !********************************* Let #Idx = 0 Print 'Number Of Employees per Company' (1,1) Center While #Idx <= #Max_Loaded Get $Company $Name #Count From Companies (#Idx) Company_Cd Company_Name Count If #Count > 0 Print $Company (+1,1,5) Print $Name (,+1,20) Print #Count (,+1,5) Edit 99999 End-If Add 1 To #Idx End-While End-Procedure !******************************* The purpose of Test12C.sqr is to print an employee headcount report per company. In the beginning, the Load_Comp_Array procedure populates each element of 194 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! the Company array with the company code and company name from the Company_Tbl table. The Count field in each element of the Company array remains initialized to zero. Next, the Update_Comp_Array procedure reads the Job table, and for each selected employee record, searches the row with the proper company code in the Company array and increments the Count field in this row. The actual search is carried out by the Search_Comp_Array procedure that employs the binary search algorithm. This algorithm can be used because the Load_Comp_Array procedure populates the Company array in the ascending company code order. KEY POINTS 1 The Create-Array command creates an array of fields in the memory. 2 The Create-Array command can be used in any section of your SQR program. 3 You can specify an initial value for each field in an array. 4 Some fields in an array can be defined as multiple-occurrence fields. 5 Row numbers and field occurrence numbers in arrays start from zero. 6 Row numbers and field occurrence numbers can be numeric literals or numeric variables. 7 When using variables to refer to row numbers or occurrence numbers, be sure to keep the variable values within the boundaries defined in the Create-Array command. 8 All SQR arrays are created during the program compile stage. 9 The following commands can be used when working with arrays: • Create-Array • Clear-Array • Get • Put • Array-Add • Array-Subtract • Array-Multiply • Array-Divide • Let S EA RC HI NG D A T A I N A R RAY S TEAM LinG - Live, Informative, Non-cost and Genuine! 195 KEY POINTS (CONTINUED) 10 All fields used in arithmetic operations must be declared as Number, Decimal, Float, or Integer. 11 The Get command is used to retrieve one or more fields from an array. 12 The Put command is used to move one or more fields into an array. 13 The Clear-Array command is used to initialize an array. 14 Commonly known sort and search algorithms can be used to sort or search array elements. Specific algorithm selection should be made based on the array size and the program performance requirements. 196 CHAPTER 1 2 WOR KING W ITH ARR AYS TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 3 Creating multiple reports IN THIS CHAPTER • Declaring individual reports • Defining custom printer types • Directing the Print command output to specific reports • Naming conventions for the output report files 197 TEAM LinG - Live, Informative, Non-cost and Genuine! So far, we have discussed only single-report SQR programs, that is, programs that generate only one output report. There are cases however, when an SQR program may need to create more than one report. For efficiency’s sake, you may choose to produce multiple reports in a single program. With today’s databases covering thousands of megabytes of data, generating several reports with one pass on the database may result in substantial savings. (This is especially true when your program accesses remote databases over the network connections.) Another reason you might want to generate multiple reports in a single program would be if you needed to direct program reports to different printers. For instance, a payroll detail report might need to go to a secured printer, a check printing report to a special printer, while a payroll summary report could go to a shared printer. In many cases, writing one program that generates several business-related reports may also result in savings on the future program maintenance since you’ll have to support only one program. Be careful though. If multiple report logic makes your program too complex and difficult to understand, you may be better off with a few single report programs. In this chapter we’ll create a program that produces two different reports using one pass on data, but first, let’s look at some SQR commands that are used for creating multiple reports in one program. 13.1 Defining multiple reports Defining multiple reports In a multiple-report SQR program, each report must be defined with a separate Declare-Report command. This command is coded in the Setup section. In the Declare-Report command, you specify the following attributes: • the name of the report • the name of the layout to be used for this report • the type of the printer to be used for this report. Depending on the specific business requirements, each report can use its own layout or printer definitions, or some reports can share the same layout or printer definitions. If no Printer-Type is specified, the default is the LINEPRINTER for this report. If no layout is specified in the Declare-Report command, the Default layout is used. (See the command reference in appendix D for a complete description of the Declare-Report command.) Here is an example of coding the Declare-Report command: Declare-Report My_Report1 Layout=My_Layout1 198 CH APTER 13 C R E ATING MU LTIPLE REPOR TS TEAM LinG - Live, Informative, Non-cost and Genuine! Printer-Type=PS End-Declare Valid values for the Printer-Type argument in the Declare-Report command are HTML (HT), HPLASERJET (HP), POSTSCRIPT (PS), and LINEPRINTER (LP). The Declare-Layout command is also coded in the Setup section. This command allows you to specify the attributes of the layout for your output file and define as many layouts as you need for your application. If you do not code the DeclareLayout in your SQR program, a default layout with the name Default will be created. If you need to override its attributes, you can define a layout called Default in your program and list only the attributes you need to change. If you are defining multiple layouts in your program, each layout name must be unique. The following example shows the use of the Declare-Layout and DeclareReport commands when multiple reports are created in one SQR program: !Using the Declare-Layout and Declare-Report commands Begin-Setup Declare-Layout Empl_Layout Left-margin=1 Right-margin=1 End-Declare Declare-Layout Summary_Layout End-Declare Declare-Report Empl_Detail Layout=Empl_Layout Printer-Type=HP End-Declare Declare-Report Empl_Summary Layout=Summary_Layout Printer-Type=Postscript End-Declare End-Setup … Each printer type specified in the Declare-Report commands has a set of default attributes. If you need to override some of the default printer attributes, use the Declare-Printer command. This command is also coded in the Setup section. The Declare-Printer command allows you to create your own customized printer type and assign it a name. In order to make a Declare-Printer command applicable to a specific report, you have to use the For-Reports parameter with a specific report name defined in a Declare-Report command. If the For-Reports parameter is not coded, the printer type attributes specified in the Declare-Printer command will apply to D E FIN I N G M U LTI P LE R E P O RTS TEAM LinG - Live, Informative, Non-cost and Genuine! 199 all reports that use the printer type specified in the Type argument of this DeclarePrinter command. The Declare-Printer command does not specify a physical printer, it defines only a printer type. In most cases, reports are written to output files (under Windows, you have an option to direct your report to the Windows default printer without using an intermediate file). In order to print a report produced for a specific printer type, use the appropriate operating system commands. You can use only one Declare-Printer command for each printer type specified in Declare-Report commands. !Using the Declare-Printer command Declare-Printer My_HP_Printer ! for all reports that use HP printers Type=HP Font=4 ! Helvetica End-Declare Declare-Printer My_PS_Printer For-Reports=(My_Report)! only for the My_Report report Type=PS ! (this report must use PS printer) Font=5 ! Times-Roman End-Declare If no Declare-Printer is specified, the following default printer type names will be used: DEFAULT-LP for line printer, DEFAULT-HP for HP LaserJet, or DEFAULT-PS for PostScript. Please note that the names of custom printer types defined in the DeclarePrinter commands are not referred to explicitly in the Printer-Type parameter of the Declare-Report command. The Declare-Report command can specify only standard printer types. If you want a custom printer type to be used in certain reports, you have to use the For-Report parameter of the Declare-Printer command. Here is an example of using custom printer types in the Declare-Report commands: !Using custom printer types in the Declare-Report command Begin-Setup Declare-Printer HP1 For-Reports = (Total_Report) Type = HP Font = 4 End-Declare Declare-Printer HP2 For-Reports = (Detail_Report) Type = HP Font = 5 End-Declare 200 CH APTER 13 C R E ATING MU LTIPLE REPOR TS TEAM LinG - Live, Informative, Non-cost and Genuine! Declare-Report Total_Report Printer-Type = HP End-Declare Declare-Report Detail_Report Printer-Type = HP End-Declare You can change the printer type dynamically by using the Use-Printer-Type command. This command allows you to change the printer type used in the report currently being processed. Be aware that the Use-Printer-Type command must be issued before the first line of the output report is written, otherwise the command will be ignored. !Using the Use-Printer-Type command to specify printer type Use-Report My_Report Use-Printer-Type PS print (1, 1) 'Testing My Report ' If you are using the same printer type for all reports, you can specify this printer type in the SQR command line -Printer:xx flag. This will cause your SQR program to produce all your report output files for the specified printer type. For example, you can specify -Printer:HP to produce all output for HP LaserJet printers. Please keep in mind that if the -Printer command line flag is specified, it will override the printer types for all reports in your program. If you need to change certain printer characteristics for the current printer type used in the current report in any part of the program except the Setup section, you can use the Alter-Printer command. (Please refer to appendix D for a full description of the above discussed commands.) 13.2 Handling multiple reports in one program Multiple reports in one program If your program generates multiple reports, the Print commands need to know which report to direct their output to. The Use-Report command will allow your program to switch between reports. When this command is used, all Print commands issued after the Use-Report command will write the output to the specified report file, until the next Use-Report command is issued. For example, in the following code, the first Print command will direct its output to the Empl_Detail report. The second Print command will direct its output to the Empl_Summary report: !Using multiple Use-Report commands to redirect the output Use-Report Empl_Detail Print 'Emplid' (1,1) MUL TIPL E RE P OR TS IN ON E P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! 201 … Use-Report Empl_Summary Print 'Total Employees:' Print #Tot_Recs (,1) (+1,1) The above technique is used to print the report body in multiple report programs. Headers and footers are printed using a different approach. Technically, the same header or footer can be shared by multiple reports. In real business life, however, different reports usually have different information in their headers and, sometimes, footers. In these cases, you should code separate Heading and Footing sections for each report and use the For-Report parameter to assign each section to a specific report, for example: !Using the For-Reports argument in the Heading and Footing sections Begin-Heading 5 For-Reports=(Empl_Detail) Print 'Employee Detail Report' (1) Center Print … End-Heading Begin-Heading 3 For-Reports=(Empl_ Summary) Print 'Employee Summary Report' (1) Center Print … End-Heading Begin-Footing 1 For-Reports=(Empl_Detail) Print 'End Of Report' (1,1) End-Footing 13.3 An example of a multiple report program Multiple report program Suppose you are assigned a task to develop a program that will print two reports. The first report will list all employees who currently participate in any health benefit plan along with their plan type and coverage codes. In addition, the report will indicate which employees had their dependent’s coverage terminated. The second report will list only those employees and their dependents who had their benefits coverage terminated within the reporting period. In the following example, we will create the two separate reports in one program: !********************************************** ! TEST13A.SQR !A multiple report program !********************************************** !Set report column widths #Define col_emplid 11 !Employee ID #Define col_empl_name 15 !Employee Name #Define col_plan_type 5 !Plan Type #Define col_cov_cd 8 !Coverage Code #Define col_cov_drop_flag 5 !Dependent Coverage Termination #Define col_effdt 12 !Effective Date #Define col_dep_id 6 !Dependent Beneficiary ID #Define col_dep_name 15 !Dependent Name 202 CH APTER 13 C R E ATING MU LTIPLE REPOR TS TEAM LinG - Live, Informative, Non-cost and Genuine! #Define col_sep 1 !*********************** Begin-Setup !*********************** !Column Separator Declare-Layout EE_Data Left-Margin=1 End-Declare Declaring a layout for each report Declare-Layout Term_Dependents Orientation=Landscape Left-Margin=0.3 End-Declare Declare-Report EE_Data Layout=EE_Data End-Declare Using the Declare-Report command for each report Declare-Report Term_Dependents Layout=Term_Dependents End-Declare End-Setup !********************************** Begin-Program !********************************** Do Process_Main End-Program !********************************** Using the For-Reports parameter to specify the Begin-Heading 4 For-Reports=(EE_Data) report name !********************************** Print 'List of Employees by Plan Type' (1) Center Print 'Page' (,+9) Print #page-count (,+1) edit 999 Print 'EMPLID' Print 'Name ' Print 'Plan ' Print 'Effdt' Print 'Coverg' Print 'Dep. ' Print ' ' Print ' ' Print 'Type ' Print ' ' Print 'Code ' Print 'Term ' Print '-' (+1,1,{col_emplid}) (0,+{col_sep},{col_empl_name}) (0,+{col_sep},{col_plan_type}) (0,+{col_sep},{col_Effdt}) (0,+{col_sep},{col_cov_cd}) (0,+{col_sep},{col_cov_drop_flag}) (+1,1,{col_emplid}) (0,+{col_sep},{col_empl_name}) (0,+{col_sep},{col_plan_type}) (0,+{col_sep},{col_Effdt}) (0,+{col_sep},{col_cov_cd}) (0,+{col_sep},{col_cov_drop_flag}) (+1,1,60) Fill End-Heading MUL TIPL E RE P OR T P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! 203 !******************************************** Printing a heading for the Term_Dependents Begin-Heading 4 For-Reports=(Term_Dependents) report !******************************************** Print 'List of Terminated Dependents' (1) Center Print 'Page' (,+15) Print #page-count (,+1) edit 999 Print 'EMPLID' (+2,1,{col_emplid}) Print 'Name ' (0,+{col_sep},{col_empl_name}) Print 'Plan Type' (0,+{col_sep},{col_plan_type}) Print 'Effdt' (0,+{col_sep},{col_Effdt}) Print 'Dep ID ' (0,+{col_sep},{col_dep_id}) Print 'Dep Name' (0,+{col_sep},{col_dep_name}) Print '-' (+1,1,65) Fill End-Heading !*************************************** Begin-Procedure Process_Main !*************************************** !Select and print all Employees !who are currently covered by any Health Benefits !and who have Family, or Empl+1 Coverage Begin-Select A.Emplid A.Plan_Type A.Effdt A.Covrg_Cd B.Name Do Check_Dep_Termination If $TERM ='N' Let $TERM = ' ' End-If Do Print_EE_Data If terminated dependent is found, the record will go to the Term_Dependents report. Print the EE_Data report record. From Health_Benefit A, Personal_Data B Where A.Effdt =(Select Max(Effdt) From Health_Benefit Where Emplid = A.Emplid And Plan_Type = A.Plan_Type And Effdt <= Sysdate) And A.Coverage_Elect Not In ('T','W') And A.Emplid = B.Emplid Order By A.Emplid, A.Plan_Type End-Select End-Procedure !*************************************************** Begin-Procedure Check_Dep_Termination !*************************************************** 204 CH APTER 13 C R E ATING MU LTIPLE REPOR TS TEAM LinG - Live, Informative, Non-cost and Genuine! ! For each row retrieved in Step 1, compare between the previous ! maximum effective dated record and the current effective dated ! HEALTH_DEPENDNT row to determine a dependent that is no longer ! covered ! If dropped dependents are found, print the record to a ! separate report Move 'N' to $TERM Begin-Select C.Emplid C.Plan_Type C.Effdt D.Dependent_Benef Move 'Y' to $TERM Do Print-Terminated-Dependents Show 'Dep Term found=' &D.Dependent_Benef ' for Employee ' &A.Emplid From Health_Benefit C, Health_Dependnt D Where C.Emplid = &A.Emplid And C.Plan_Type = &A.Plan_Type And C.Emplid = D.Emplid And C.Plan_Type = D.Plan_Type And C.Effdt = D.Effdt And C.Effdt = (Select MAX(E.Effdt) From Health_Benefit E Where C.Emplid = E.Emplid And C.Plan_Type = E.Plan_Type And E.Effdt < &A.Effdt) And D.Dependent_Benef Not In (Select F.Dependent_Benef From Health_Dependnt F Where C.Emplid = F.Emplid And C.Plan_Type = F.Plan_Type And F.Effdt = &A.EFFDT) End-Select End-Procedure !*********************************** Begin-Procedure Print_EE_Data The Use-Report command !*********************************** tells SQR to direct all output to Use-Report EE_Data the EE_Data report. Let $EmplID=&A.Emplid Let $Date_Str= Datetostr(&A.Effdt,'mm/dd/yyyy') Print &A.Emplid (+1,1,{col_emplid}) On-Break Print=Change/Top-Page Print &B.Name (0,+{col_sep},{col_empl_name}) On-Break Print=Change/Top-Page Print &A.Plan_Type(0,+{col_sep},{col_plan_type}) Print $Date_Str (0,+{col_sep},{col_effdt}) MUL TIPL E RE P OR T P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! 205 Print &A.Covrg_Cd(0,+{col_sep},{col_cov_cd}) Print $Term (0,+{col_sep},{col_cov_drop_flag}) Add 1 to #ee_rcds End-Procedure !****************************************** Begin-Procedure Print-Terminated-Dependents !****************************************** Use-Report Term_Dependents Let $EmplID=&A.Emplid The Use-Report command tells SQR to direct all output to the Term_Dependents report. Do Get_Dependent_Name Let $Date_Str= Datetostr(&A.Effdt,'mm/dd/yyyy') Print &A.Emplid (+1,1,{col_emplid}) On-Break Print=Change/Top-Page Print &B.Name (0,+{col_sep},{col_empl_name}) On-Break Print=Change/Top-Page Print &A.Plan_Type(0,+{col_sep},{col_plan_type}) Print $Date_Str (0,+{col_sep},{col_effdt}) Print &D.Dependent_Benef(0,+{col_sep},{col_dep_id}) Print $DEP_NAME (0,+{col_sep},{col_dep_name}) Add 1 to #Dep_Rcds End-Procedure !************************************************ Begin-Procedure Get_Dependent_Name !************************************************ Let $Dep_Name=' ' Begin-Select Name Move &Name to $Dep_Name From Dependent_Benef Where EMPLID=&A.Emplid And Dependent_Benef=&D.Dependent_Benef End-Select End-Procedure !************************** End of Report *************************** In the Setup section, we declare two reports and the layout for each report. If the layouts were the same as the Default layout, we could have omitted these declarations. When creating multiple reports, however, it is a good idea to keep the layouts separate. It gives you better flexibility for future changes and uniquely identifies both reports, along with the report declarations. A separate heading is defined for each report. There are two Begin-Heading commands in the program. Each command specifies its report name using the ForReport parameter. 206 CH APTER 13 C R E ATING MU LTIPLE REPOR TS TEAM LinG - Live, Informative, Non-cost and Genuine! In the Process_Main procedure, the Health_Benefits and Personal_Data tables are joined to select employee information required for the employee report. For each row selected, the Check_Dep_Termination procedure and Print_EE_Data procedure are called. The Check_Dep_Termination procedure contains a little tricky WHERE clause in its SQL. This code is a good example of “effective-dated” logic, which allows the program to compare the effective dates in the current and prior rows to determine a dependent no longer covered by a health plan. For each row selected from the Health_Benefit table in the Process_Main procedure, the program selects the prior record in the Health_Benefit table and the matching Health_Dependent record, and also checks to make sure that this Dependent is not in the current Dependent_Benef record. If the terminated dependent record is found, the program calls the Print_Terminated_Dependents procedure. The Print_Terminated_Dependents procedure issues the Use-Report command to direct all print output into the Term_Dependents report. From this moment on, all Print commands will direct their output to this report until another UseReport command overrides this setting. In the Print_EE_Data procedure, another Use-Report command directs all output to the EE_Data report until the next Use-Report command is issued. The results of the program are the two report files: Test13a.lis (figure 13.1) and Test13a.l01 (figure 13.2). List of Employees by Plan Type Page 1 Plan Effdt Coverg Dep. Type Code Term ----------------------------------------------------------6601 Jones,Gladys 10 06/01/1996 1 11 06/01/1996 1 14 06/01/1996 1 6602 Peppen,Jacques 10 02/01/1997 1 Y 11 06/09/1996 1 14 06/09/1996 1 6603 Pitman,Earl 10 06/01/1996 4 11 06/01/1996 4 14 06/01/1996 4 7702 Atchley,Tamara 10 06/01/1996 1 11 06/01/1996 1 14 06/01/1996 1 7704 Riall,Alphonsin 10 12/21/1997 1 11 12/21/1997 1 14 12/21/1997 1 EMPLID Figure 13.1 Name A portion of the Employee List report MUL TIPL E RE P OR T P RO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! 207 List of Terminated Dependents Page 1 EMPLID Name Plan Effdt Dep ID Dep Name ----------------------------------------------------------------6602 7705 8200 8553 G011 G040 G200 G400 Figure 13.2 Peppen,Jacques 10 10 10 Holt,Susan 10 Albright,Arnold 10 Jackson,Sonya 11 11 11 Sherwood,Steven 11 O'Leary,Mary 10 11 Monroe,Henry 10 11 Sharpe,James 10 11 02/01/1997 02/01/1997 02/01/1997 02/01/1997 01/01/1998 12/24/1995 12/24/1995 12/24/1995 04/01/1998 12/01/1995 12/01/1995 01/04/1998 12/21/1997 01/04/1998 12/28/1997 03 02 01 03 01 03 02 01 01 01 01 02 02 02 02 Peppen,Annalise Peppen,Ronald Peppen,Marie Holt,Jarrett Arnold,Judy Jackson,Rachel Jackson,Anna Li Jackson,Gregory Sherwood,Nancy O'Leary,Lloyd O'Leary,Lloyd Monroe,Brett Monroe,Brett Sharpe,Jason Sharpe,Jason A portion of the Terminated Dependents report 13.4 Output files in SQR programs with multiple reports SQR programs with multiple reports We know that our program generates two report files, Test13a.lis and Test13a.l01, but which file name will be assigned to each program report? The file names are assigned in the order in which the Declare-Report commands are coded in the program. The first Declare-Report command in our program defines the EE_ Report. Therefore, this report is written to the Test13a.lis file. The report defined in the second Declare-Report command goes to Test13a.l01. By default, SQR creates output report files with the same names as your program name, but with different extensions. For single report programs, the extension is usually .lis or .spf. For multiple report files, the report file names are still the same as the program name, but the file extensions will be .lis, l01, l02, and so on. In case of the .spf output files (see chapter 14), the names will be same as the program name with the extensions: .spf , .s01, .s02, and so on. SQR version 4 offers an improved file naming scheme: the default report file names are controlled by the OUTPUT-FILE-MODE environmental variable in the DefaultSettings section of the SQR.ini file. If this variable is set to SHORT (the default setting), SQR uses the previously described naming conventions. If the variable is set to 208 CH APTER 13 C R E ATING MU LTIPLE REPOR TS TEAM LinG - Live, Informative, Non-cost and Genuine! LONG, the first report file name will be the same as the program name; the second report file name will be the program name post-fixed with _01; the third file name will be the program name post-fixed with _02, and so on. All file extensions in this case will be the same: .lis. In our program, the report file names will be Test13A.lis and Test13A_01.lis. Based on the described naming conventions, you may guess that you can have up to a hundred reports in a program, but who would need this many reports in one program, anyway? You can use the -F flags in the SQR command line to override the default file names with your own file output names. For example, if you want the Test13A.sqr program to generate the Empl.lis and Depd.lis report files, use the following -F flags: -FEmpl.lis -FDepd.lis Similarly, you can use the -F flag to create multiple .spf files. Keep in mind, however, that you must still specify the .lis extensions for these files. SQR will automatically rename the files to Empl.spf and Depd.s01. (More about SPF files in chapter 14.). KEY POINTS 1 In multiple report programs, each report must be defined with a separate Declare-Report command. 2 The Declare-Report and Declare-Layout commands must be coded in the Setup section. 3 Each report can use its own layout or printer type definitions, some reports can share the same layout and (or) type definitions. If no layout is specified, the Default layout is used. 4 If you define multiple layouts in your program, each layout name must be unique. 5 The Use-Report command allows your program to switch between reports. 6 SQR allows you to specify the Heading and Footing sections for each report using the For-Report argument in the Begin-Heading and Begin-Footing commands. SQR PR OGR AMS WITH MUL TIPL E RE POR TS TEAM LinG - Live, Informative, Non-cost and Genuine! 209 KEY POINTS (CONTINUED) 7 210 When creating multiple output files, the default names of the report files will be the program name with the extensions .lis, l01, l02, and so on. In case of the SPF output files, the file extension will be: .spf, .s01, .s02, and so on. CH APTER 13 C R E ATING MU LTIPLE REPOR TS TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 4 Creating SQR Portable Files IN THIS CHAPTER • How to create SPF files • The advantages of using SPF files • The SQR VIEWER • Using the SQR PRINT to convert SPF files to printer-specific files 211 TEAM LinG - Live, Informative, Non-cost and Genuine! In our examples to this point, the SQR programs have directed their report output to the regular SQR report output files with the .lis extension. This extension is a default extension for a single report program output file. (For multiple report programs, the file extensions are .lis, .l01, .l02, and so on.) LIS files are printer-specific output report files. In order to print these files, you have to use the proper operating system command or utility, for example, lp for UNIX, or copy to printer port for DOS. You have probably already noticed that this kind of output is not always convenient, especially if you would like to view the file before printing, or to email it to your users for review. In many cases, LIS files are impossible to preview without printing them and are very difficult to refine and, sometimes, quite a few trees may be destroyed before you can make your report perfect. SQR allows you to create SQR Portable Files (SPF); these files help to resolve all of the above-mentioned problems and add a great deal of flexibility to the task of creating SQR reports. The main idea of SPF files is that, with their help, your program can create printer-independent output files. These files can be viewed and printed by the SQR Viewer, or converted back to LIS files with the help of the SQR Printer. Both the SQR Viewer and SQR Printer will be discussed later in this chapter. End users can use SPF files to view, e-mail, fax, or print them on any available printing device. 14.1 How to create an SPF file SPF output files can be produced by SQR on any platform. You do not have to change your program to make it generate SPF output. All you need to do is to use one of the following flags in the SQR command line or the SQR Dialog Box: • -NOLIS will create SPF file output instead of LIS file output • -KEEP will allow you to create SPF output in addition to LIS output • -ZIV will invoke the SQR Viewer after creating an SPF file. This flag automatically invokes the -KEEP flag to create an SPF file. If your program creates multiple output files, the SQR Viewer will automatically display only the first report file. The other SPF files can be viewed or printed by using the SQR Viewer's file selection option. Here are some considerations for you to keep in mind when creating printerindependent reports: • When creating a report for different printers, make sure you are using fonts that are common among printers that may be used to produce your report. You may have to limit the fonts used to font numbers 3, 4, and 5 (Courier, Helvetica, and Times Roman, respectively) or PostScript, HP LaserJet printers in Windows environment. 212 CH APTE R 14 C REATING SQR POR TABLE FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! On some HP printers font 4 is not available, which leaves you with font 3 and 5 as common fonts. When in doubt, use font 3 to test your report. • There may be variations in the implementation of some graphical features between different printer types. • The best way to make sure that your program works with different printer types is to test it. For our readers who would like to take a look “behind the scenes,” it may be of some interest to know that, in many cases, SQR generates SPF output even when it is not asked to do so. When SQR needs to accumulate its output before creating the final reports, (for example, in multiple-report programs) it uses SPF files as intermediate output file storage. Another example occurs when the Last-Page command is used in a program (see chapter 5). As you already know, this command causes SQR to accumulate the entire report without printing it until the last page is processed. In these and other cases, SQR automatically creates SPF files and deletes them upon the program’s completion (unless the -KEEP flag is specified). By the way, SPF files generated by SQR behind the scenes may be a source of the following confusing error message: "(SQR 6003) Unexpected End-of-File while processing the printer file". This may happen when LPT1 is used in place of a report file name with the -F SQR command flag. In most cases, using this value in the -F flag is not a problem except when SPF output is created. When SQR creates SPF output, it takes the current output file name (in our case, it is LPT1) and adds the .spf extension to this name. As soon the LPT1.spf is created, Windows sends this file to the printer. Unfortunately, SQR considers LPT1.spf a normal file name and will try to open and read the file. To avoid this problem, direct your output to a file and print this file in a separate step. 14.2 Advantages of SPF files SPF files allow you to generate a report and store the produced SPF output for subse- quent electronic distribution or on-site printing. You do not have to know the type of printer belonging to each potential recipient. SPF files also permit you to view, search, or email the text portions of a graphic output by rendering a line-printer output. In addition, many problems with printing graphical objects on certain printer types can be solved by generating SPF files rather than directing the program output to a printer. ADVANTAGES OF SPF F ILES TEAM LinG - Live, Informative, Non-cost and Genuine! 213 You can also separate printing from report execution. For example, you might want to run the report on the database server computer, and then print it on another computer. The overhead associated with producing the graphics will be shifted to the computer where the report is printed. Since SPF format reports can be produced by SQR on any platform, you can take advantage of the Windows printing capabilities even though you created the SQR report file on another platform. For example, you can run a report on a UNIX server and then print the output on a PC using Windows. 14.3 Using the SQR Viewer The SQR Viewer allows you to view your report online before you print it. You can also take advantage of the Viewer’s Print Preview, Print Setup, Find, and Zoom options as well as the capability to email the report to your users (presently supported are Lotus Notes, E-mail, and Microsoft Mail). To view a report through the SQR Viewer, the report must be in the SPF format. These files normally have .spf or .snn extensions. In order to demonstrate the use of SPF files and the SQR Viewer, we will take an SQR program that we developed in the previous chapter and, without changing its code, create the SPF output. Figure 14.1 shows a snapshot of the SQR dialog box with the -NOLIS flag, which allows us to create SPF output for the Test13A.sqr program. We use the -NOLIS command line flag to create SPF output. Since the Test13A.sqr program creates two output files, the NOLIS option will make the program generate two SPF files: Test13A.spf and Test13A.s01. (If the OUTPUT-FILE-MODE Figure 14.1 Using the NOLIS flag to generate SPF output 214 CH APTE R 14 C REATING SQR POR TABLE FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! environmental variable in the SQR.ini file is set to LONG, the SPF file names will be Test13A.spf and Test13A_01.spf.) In order to view these files online, you can either double-click on the file in the appropriate directory, or invoke the SQR Viewer and open each file from the SQR Viewer menu. Figure 14.2. shows the Test13A.spf file viewed with the help of the SQR Viewer. Figure 14.2 Using the SQR Viewer to view an SPF file As you can see, it is very convenient to look at your report on-line. You can then print the report or send it to your users. (To exit from the SQR Viewer, select EXIT from the File menu.) Another convenient way of using the SQR Viewer is to run your program with the -ZIV command line flag. When you use this flag, SQR creates SPF output, automatically invokes the SQR Viewer, and opens the first SPF file created in the program. It is important to keep in mind that while SPF files can be created on any platform, the SQR Viewer can be used only on Windows. What if your program ran on a nonWindows platform, but you or your users need to view the SPF files generated by the US ING T H E S Q R V I E WE R TEAM LinG - Live, Informative, Non-cost and Genuine! 215 program? The simplest way to solve the problem is to use a file transfer tool (such as the FTP) to transfer the files to the Windows environment. Another option for programs run on UNIX is to use the UNIX uuencode and mail commands to email the SPF output to the client site. An example of an SQR code that can carry out this task is shown here: !TEST14A.SQR !Sending SPF output from UNIX to Windows Begin-Program Print 'My test email with ' (1,1) Unstring $sqr-report By '.' Into $Filename $Extension New-Report 'temp.lis' Let $Spf_File = $Filename || '.spf' Let $Command = 'uuencode ' || $Spf_File || ' ' || $Spf_File || > ' mailfile' Call System Using $Command #Status If #Status <> 0 Show 'Problem with redirecting SPF file' End-If Call System Using 'mail "SPF Mail" John_User@abcd.com < mailfile' #Status If #Status <> 0 Show 'Problem with sending SPF file=' #status End-If End-Program The code shown in Test14A.sqr demonstrates how to use the UNIX uuencode and mail commands to send the SPF files generated on UNIX directly to a user’s email system. If the SQR Viewer is a registered Windows application, the recipient will be able to start it from his/her email package. 14.4 Converting SPF files to printer-specific files When an SQR program which contains printer type specific commands generates an SPF file, the file becomes printer-independent. All printer type specific features are lost. Another product called SQR Print allows you to convert printer-independent SPF files to printer-specific LIS files. To invoke the SQR Print in the Windows environment, enter the following: SQRWP [file_name] [command_flags] 216 CH APTE R 14 C REATING SQR POR TABLE FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! where file_name is the SPF file name (possible flag values are listed in appendix B). If you run the SQR Printer on a server, use SQRP instead of SQRWP. Despite its name, SQR Print's main job is not to print its output, but rather to generate an LIS file with the same name as the report, using the .lis extension. You can override this name with the -F command line flag. You can use the -PRINTER command line flag to specify the type of printer for your LIS output. The available printer types are: Line Printer (LP), HP LaserJet (HP), HTML (HT), PostScript (PS), and Windows (WP). If your report includes graphics and you choose an LP, the graphic elements such as lines, boxes, and charts will be skipped. Only text portions of the report will be printed. Instead of invoking the SQR Print via a command line on Windows, you can use the SQR Print Dialog Box as in figure 14.3. Figure 14.3 Using the SQR Print Dialog Box As you can see, the product is very simple. You select FILE, PRINT, and the system will prompt you for other parameters as shown in figure 14.4. In FILE NAME, you specify the input SPF file to be converted. In Generate Output For, you specify the printer type. This is the equivalent of using the DeclarePrinter or Use-Printer-Type command. The default is SPF file. By default, the output file will be written to the same directory and have the same name as the input file but with the .lis extension. If you need to use a different output file name or directory, select PRINT TO FILE, and the system will prompt you for an output file name. C O N V E RT I N G S P F FIL ES T O P RI N T E R- S PE C I F IC FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! 217 Figure 14.4 Generating an LIS file via the SQR Print Dialog Box KEY POINTS 1 SQR Portable Files (SPF files) are printer-independent and offer a higher degree of flexibility for both program developers and end users. 2 You do not have to change your program to make it generate SPF files: it can be achieved by using the proper SQR command line flags. 3 SPF files can be generated on any platform. 4 The SQR Viewer allows you to view, print, and email SPF files. This product works in the Windows environment only. 5 The SQR Print converts SPF files to printer-specific files. This is a multipleplatform product. 218 CH APTE R 14 C REATING SQR POR TABLE FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 5 Generating letters IN THIS CHAPTER • The Document paragraph’s format • Using the Document paragraph in SQR programs • Document markers • Variable length sections in the Document paragraph • How to control pages in multiple page letters • How to use multiple Document paragraphs 219 TEAM LinG - Live, Informative, Non-cost and Genuine! Every time you need to generate a line in a report (even when the line consists of a plain text), you have to use the Print command. Often, your report may contain large portions of text with very few variables. In these cases, you may use the SQR Document paragraph to code the text portions of the report without using the Print commands. The SQR Document paragraph allows you to put information onto a report page as if you were using a word processor. This technique is especially useful when creating form letters. You can design your business forms or letters, and automatically populate them with data from the database. Suppose you are a company Human Resources (HR) Administrator, and you need to send a letter to every employee inviting them to a holiday party. The task may seem as simple as generating a standard letter template and inserting an employee name and address into each letter. But what if your employees have different party locations? In that case, your program will have to select a department Id for each employee and add program logic to determine the party location for each individual employee. We will show you how to accomplish this and similar tasks in this chapter. 15.1 Using the Document paragraph to create form letters As we learned in our earlier chapters, in order to print a document page, SQR first builds the page in the memory buffer. We were using the Print command to put information onto a report page, explicitly specifying the location (row, column, and length) on the page. There is, however, another way to do it. The Document paragraph allows you to combine the body of your standard letter with the values of variables or database columns. The Document paragraph can be used in an SQR Procedure section or in the Program section. It is advisable to place it in the Procedure section. It starts with the Begin-Document command and ends with the End-Document command. You can have multiple Document paragraphs in a single procedure. In the following example, we use a Document paragraph in the Print_Letter procedure: Document paragraph and form letters !Coding the Document paragraph Begin-Procedure Print_Letter Begin-Document (1,1) Dear friend: Use .b to print a blank line. .b This letter is written to let you know how simple it is to use the SQR Document Paragraph. .b Sincerely yours, .b .b 220 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! End-Document End-Procedure Coding the Document paragraph is somewhat different from using the Print command. There are certain rules you have to adhere to when using the Document paragraph: • Use spaces (not tabs) to indent text or field. • Use .b to indicate an entire blank line. • Use fixed or relative position to indicate the start of the paragraph on the page. • SQR commands (including the Print command) are not allowed within the Document paragraph. • A Document paragraph must fit within a single report page. In order to place a variable or column into your document, simply specify the variable or column name at the desired location as in the following example: !Placing a variable in a Document paragraph Begin-Procedure Print_Letter Let $My_String = 'My Document' Begin-Document (1,1) .b The value of $My_String will be $My_String printed at this location of the document. .b End-Document End-Procedure Now, that we better understand the purpose of using the Document paragraph, let’s put on our HR Administrator’s hat and create a program to generate the invitation letters mentioned earlier: !TEST15A.SQR !Using logic to assign values to variables in the Document paragraph !******************************* Begin-Program !******************************* Do Process_Employees End-Program !*********************************************** Begin-Procedure Process_Employees !*********************************************** Begin-Select A.Name A.Address1 A.City || ' '||A.State|| ' ' ||A.Zip &Addr B.Company DOCUMENT PARAGRAPH AND F ORM L E TTERS TEAM LinG - Live, Informative, Non-cost and Genuine! 221 B.Deptid Do Get_Location(&B.Deptid,$Location) Let $Location = Upper(Rtrim($Location,' ')) Show 'Location=' $Location Evaluate $Location When = 'CORP HQ' Let $Restaurant = 'Planet Hollywood' Break When = 'DP Center' Let $Restaurant = 'Hard Rock Cafe' Break When = 'PARIS' Let $Restaurant = 'Moulin Rouge' Break When-Other Let $Restaurant = 'Home' End-Evaluate If Rtrim($Restaurant,' ') <> 'Home' Let $Restaurant = Rtrim($Restaurant,' ')|| '.' Do Convert_Name(&A.Name,$First_Last_Name) Let $First_Last_Name_Col = $First_Last_Name|| ':' Do Print_Letter New-Page End-If From Personal_Data A, Job B Where A.Emplid=B.Emplid And B.Empl_Status = 'A' And B.Effdt =(Select Max(Effdt) From Job Where Emplid=B.Emlid And Empl_Rcd=B.Empl_Rcd) End-Select End-Procedure !******************************************************** Begin-Procedure Get_Location($Deptid,:$Loc) !******************************************************** Begin-Select L.Descrshort Move &L.Descrshort to $Loc From Dept_Tbl C, Location_Tbl L Where C.Deptid=$Deptid And C.Effdt=(Select Max(Effdt) From Dept_Tbl Where Deptid=C.Deptid And Effdt<=Sysdate) And C.Location=L.Location And L.Effdt=(Select Max(Effdt) From Location_Tbl Where Location=L.Location And Effdt<=Sysdate) End-Select 222 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! End-Procedure !******************************************************************** Begin-Procedure Convert_Name($Last_First,:$First_Last) !******************************************************************** !Input parameter includes: Last name,First name, MI !Output parameter includes: First name, MI, Last name Let $Last_Name=Substr($Last_First,0,instr($Last_First,',',0)-1) Let First_Name=Substr($Last_First,instr($Last_First,',',0)+1, Length($Last_First)- instr($Last_First,',',0)) Let $First_Last=Rtrim($First_Name||' '||$Last_Name,' ') Show '$First_Last=' $First_Last End-Procedure !**************************************** Begin-Procedure Print_Letter !**************************************** Begin-Document (1,1) .b .b .b $First_Last_Name The employee name and address &A.Address1 &Addr .b .b .b Dear $First_Last_Name_Col .b .b You are cordially invited to attend our annual Holiday party. It will be held at $Restaurant Please feel free to bring as many guests as you think is appropriate for the occasion. The party location .b .b .b Best wishes to you and your family in the coming Holiday season. .b Sincerely, HR Administrator .b .b /Mrs. Bayer/ .b End-Document End-Procedure In Test15A.sqr, we select all active employees in the Process_Employees procedure. We join the Personal_Data table with the Job table in order to select the DOCUMENT PARAGRAPH AND F ORM L E TTERS TEAM LinG - Live, Informative, Non-cost and Genuine! 223 Gladys Jones 1 South Main St. Los Alamos CA 94325 Dear Gladys Jones: You are cordially invited to attend our annual Holiday party. It will be held at Planet Hollywood. Please feel free to bring as many guests as you think is appropriate for the occasion. Best wishes to you and your family in the coming Holiday season. Sincerely, HR Administrator /Mrs. Bayer/ Figure 15.1 An example of the holiday party invitation letter company and employee name, and to make sure that employee status is active. We use the Evaluate command to select the restaurant the employee is invited to attend. After all information is prepared, we are ready to print the letter. Note that in the Document paragraph we use string variables as well as column variables, and place them exactly where we wanted them to be printed. Be careful with your letter space planning: if you place a variable in a Document paragraph, make sure it gets enough room for printing. If another variable must follow the first one, and you want to avoid blank spaces, concatenate your variables outside the Document paragraph. In Test15A.sqr, we use this technique to concatenate the employee full name stored in First_Last_Name with a colon and store the result of concatenation in the First_Last_Name_Col variable. 15.2 Document markers Another method of placing the values of variables or columns into your document involves using document markers. A document marker is a special variable whose name begins with an @. It marks a location in the document where you place data from areas Document markers 224 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! external to the Document paragraph parts of the program. Document markers defined in Document paragraphs can be referenced in the Position command outside the Document paragraph to establish the next printing position. (Remember, SQR commands cannot be used inside the Document paragraph). The use of document markers may be helpful in many cases. Let’s say you would like to print some portions of your document in bold. You may employ the following technique to accomplish this: !TEST15B.SQR !Using document markers !******************************* Begin-Program !******************************* Do Print_Doc Do Print_Bold End-Program !********************************** Begin-Procedure Print_Doc !********************************** Begin-Document (1,1) Dear colleague: This document is created just for a demo purpose. The following lines will be printed in Bold letters. .b Using a document marker to identify a @Start_Bold starting position in the Document paragraph .b The following lines will be printed underlined. @Start_Underline .b End-Document End-Procedure !********************************** Begin-Procedure Print_Bold !********************************** Position () @Start_Bold Print 'Printing the Bold Text' () Bold Position () @Start_Underline Print 'Printing the Underline Text' () Underline End-Procedure In this example, we create two document markers inside the Document paragraph. Then, in the Print_Bold procedure, we position our line at the location indicated by the @Start_Bold document marker, and use a regular Print command with the Bold qualifier to print at the current location. Another document marker, @Start_ Underline, is used to print an underlined text. Note that just naming a document marker @Start_Bold does not make SQR print the following text in bold. It is the Print command with the Bold qualifier that places data in bold at the location marked by our document marker. Now our program output will look like that in figure 15.2. DOCUMENT MARKERS TEAM LinG - Live, Informative, Non-cost and Genuine! 225 Dear colleague: This document is created just for a demo purposes. The following lines should be printed in Bold letters. Printing the Bold Text The following lines should be printed underlined. Printing the Underline Text Figure 15.2 Using document markers and Print commands to format output The next example demonstrates another way of using document markers. We hope you did not put your HR Administrator’s hat too far away as you will need it again. Let’s say you, as an HR Administrator, are going to send every department manager a list of all employees up for review. The list will include the review date printed for each employee. The program may look like the following: !TEST15C.SQR !An Employee Review Date Notification program !******************************* Begin-Program !******************************* Do Process_Main End-Program !*************************************** Begin-Procedure Process_Main !*************************************** Begin-Select The main query selects A.Manager_ID department managers. A.Deptid A.Descr B.Name Let #Rcds = 0 Show &A.Manager_Id ' ' &A.deptid Do Convert_Name(&B.Name,$First_Last_Name) Let $First_Last_Name_Col = $First_Last_Name|| ':' Do Print_Letter Get an employee list for Do Get_Employees each department manager. New-Page From Dept_Tbl A, Personal_Data B Where A.Manager_Id=B.Emplid And A.Company='CCB' And A.Effdt=(Select max(Effdt) from Dept_tbl where Deptid=A.Deptid and Effdt<=Sysdate) Order by A.Deptid 226 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! End-Select End-Procedure !******************************* Begin-Procedure Get_Employees !******************************* Show 'Get_Empl started...' Set position with the help Position () @EE_List of the document marker. Begin-Select C.Name (0,1,20) D.Review_Dt(0,+2) Add 1 to #Rcds Maximum ten employees If #Rcds >= 10 per page Let #Rcds=0 New-Page Do Print_Letter Show 'There are more than 10 Employees in Department:' &A.Deptid Position () @EE_List Else Position (+1) !Advance to the next line End-If From Personal_Data C, Job D Where C.Emplid=D.Emplid And D.Deptid=&A.Deptid And C.Emplid<>&A.Manager_Id And D.Empl_Rcd = 0 And D.Effdt = (Select Max(Effdt) From JOB Where Emplid=D.Emplid And Empl_Rcd=D.Empl_Rcd) End-Select End-Procedure !***************************************************** Begin-Procedure Convert_Name($Last_First,:$First_Last) !***************************************************** !Input parameter includes: Last name,First name, MI !Output parameter includes: First name, MI, Last name Let $Last_Name=Substr($Last_First,0,instr($Last_First,',',0)-1) Let $First_Name= Substr($Last_First,instr($Last_First,',',0)+1,Length($Last_First)Instr($Last_First,',',0)) Let $First_Last=Rtrim($First_Name||' '||$Last_Name,' ') End-Procedure !**************************************** Begin-Procedure Print_Letter !**************************************** Begin-Document (1,1) .b .b DOCUMENT MARKERS TEAM LinG - Live, Informative, Non-cost and Genuine! 227 $First_Last_Name Manager of &A.Descr Department .b .b .b Dear $First_Last_Name_Col .b .b This is a reminder that the following employees of your department will have their annual reviews: .b @EE_List .b .b .b .b .b .b .b .b .b .b .b .b Please prepare the appropriate documentation by their respective review dates. .b .b .b Sincerely, HR Administrator .b .b /Mrs. Bayer/ .b End-Document End-Procedure In the Main procedure of Test15C.sqr, we first select all managers of the company’s departments, one by one, with the company Id = 'CCB'. In order to obtain the manager’s names, we join Dept_Tbl with Personal_Data. For each selected row in the main query, we execute the Convert_Name procedure to rotate the employee name to the printable format. Then we call the Print_Letter procedure. This procedure contains a Document paragraph, which begins with the Begin-Document command and ends with the End-Document command. In the paragraph, we use the string variable $First_Last_Name, the column variable &Descr, and the document marker @EE_ List to mark the beginning of the employee list. Please note that we have not yet selected the list of employees, but we have already printed the document into a buffer. This is an important consideration: you must print a document before referencing a document 228 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! marker, since SQR may not know the actual marker location until the document is placed on the page. After the body of the document is created, we call the Get_Employee procedure that selects all employees for the specific department, and prints each employee name and review date at the position specified by the @EE_List document marker. There are several important points to mention. First, as you may have already noticed, we allocated ten blank lines for our employee list in the document paragraph. What will happen if the number of selected employees exceeds ten? If we did not have special logic to check the number of selected employees, the program would keep on printing employee names and review dates, thus overlaying next lines in the document. Therefore, we check the number of selected employees beforehand. If the number exceeds ten, we issue the New-Page command, which prints the current page and prepares the new one. Then, on the next page, we generate our document, reset the record count, and make sure that the next print position starts at the document marker. The output of the program will look like that in figure 15.3. Simon Schumacher Manager of the Office of the President Dear Simon Schumacher: This is a reminder that the following employees of your department will have their annual reviews: Bennett,William D. Elias,Jan Campenhout,Marc Dahling,Irene Rossalini,Bartholome Wagner,Gretchen Jordan,Robert Austin,Caroline Kidd,Kenneth Bird,Douglas 1998-12-31 1998-10-30 1998-10-20 1998-01-22 1998-02-12 1998-02-18 1998-03-28 1998-03-28 1998-04-15 1998-04-30 Please prepare the appropriate documentation by their respective review dates. Sincerely, HR Administrator /Mrs. Bayer/ Figure 15.3 The output of the Employee Review Date Notification program (continued on next page) DOCUMENT MARKERS TEAM LinG - Live, Informative, Non-cost and Genuine! 229 Simon Schumacher Manager of Office of the President Dear Simon Schumacher: This is a reminder that the following employees of your department will have their annual reviews: Smith,John Bronte,Katherine Fletcher,Leslie Drew,Suzanne Reagan,Deidre Alomar,Samuel Barfield,John Cone,Alton Bell,Anna Gruber,Jennifer 1998-05-10 1998-05-18 1998-05-31 1998-06-10 1998-06-12 1998-03-16 1998-02-28 1998-09-10 1998-12-15 1998-08-24 Please prepare the appropriate documentation by their respective review dates. Sincerely, HR Administrator /Mrs. Bayer/ Figure 15.3 (continued) The output of the Employee Review Date Notification program Let’s review the output of Test15C.sqr in figure 15.3. We avert the danger of printing employee names and review dates over the lower portion of the document body when the number of employees exceeds ten, but the program output still does not look right. Instead of generating one letter to each department manager, the program produces multiple letters on the same subject to the same department manager if the number of employees exceeds the limit. In section 15.3, we will employ another technique to fix this problem. What if you know for sure that the variable portion of your document will never exceed the designated space? Why bother with all these tricks of preventing something which will never happen? Our note of caution is this: despite present numbers, never assume that the maximum number of employees in every department will never exceed the limit. If your letter includes variable length parts, you need to include some page change control logic similar to the one presented in the previous example to provide for the future expansion. 230 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! 15.3 Using variable length sections As you see from our examples, using the Document paragraph can be a simple and straightforward method of creating form letters, but it has certain limitations. The main inconvenience is that one Document paragraph cannot cross over pages. You may solve this problem by taking advantage of the fact that there could be multiple Document paragraphs in one SQR program or even in one SQR procedure. The next example illustrates the use of multiple Document paragraphs for fixed portions of a letter, as well as how to print variable length information between different Document paragraphs. We will code the beginning of the letter in one Document paragraph in the Print_Letter_Beginning procedure, and the ending portion of the letter in another Document paragraph in the Print_Letter_Ending procedure. For the purposes of demonstration, we assume a maximum page length of thirty lines in this example. Let’s rewrite the previous example, using the multiple Document paragraph technique: variable length sections !TEST15D.SQR !Using multiple Document paragraphs in one Procedure section Begin-Setup Declare-Procedure Before-Page=Before_Page_Proc End-Declare Declare-Layout Default Max-Lines=30 End-Declare End-Setup !******************************* Begin-Program !******************************* Do Process_Main End-Program !******************************* Begin-Procedure Before_Page_Proc !******************************* If #page-count > 1 Print '(Continued)' (1,1) (+0,+2) Print 'Page: ' Page-Number (0,+1) Position (+1) End-If End-Procedure !******************************* Begin-Procedure Process_Main !******************************* Begin-Select A.Manager_ID VARI ABLE L ENGTH SECTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 231 A.Deptid A.Descr B.Name Let #Rcds = 0 Show &A.Manager_Id ' ' &A.Deptid Let #page-count=1 Do Convert_Name(&B.Name,$First_Last_Name) Let $First_Last_Name_Col = $First_Last_Name || ':' Do Print_Letter_Begining Do Print_Employees Making sure there is enough room on the If #current-line > 19 page for the second Document paragraph New-Page End-If Do Print_Letter_Ending New-Page From Dept_Tbl A, Personal_Data B Where A.Manager_Id=B.Emplid And A.Company='CCB' And A.Effdt= (Select Max(Effdt) From Dept_Tbl Where Deptid=A.Deptid And Effdt<=Sysdate) Order By A.Deptid End-Select End-Procedure !****************************************** Begin-Procedure Print_Employees !****************************************** Show 'Get-empl started...' Begin-Select C.Name (0,1,20) D.Review_Dt (0,+2) Add 1 to #Rcds Position (+1) From Personal_Data C, Job D Where C.Emplid=D.Emplid And D.Deptid=&A.Deptid And C.Emplid<>&A.Manager_Id And D.Empl_Rcd = 0 And D.Effdt = (Select Max(Effdt) From JOB Where Emplid=D.Emplid And Empl_Rcd=D.Empl_Rcd) End-Select End-Procedure !***************************************************** Begin-Procedure Convert_Name($Last_First,:$First_Last) !***************************************************** 232 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! !Input: Last,First MI !Output: First MI Last Let $Last_Name=Substr($Last_First,0,instr($Last_First,',',0)-1) Let $First_Name=Substr($Last_First,instr($Last_First,',',0)+1,Length($Last_First) instr($Last_First,',',0)) Let $First_Last=Rtrim($First_Name||' '||$Last_Name,' ') End-Procedure !************************************************ Begin-Procedure Print_Letter_Begining !************************************************ First Document paragraph Begin-Document (1,1) .b $First_Last_Name Manager of &A.Descr Department .b Dear $First_Last_Name_Col .b This is a reminder that the following employees of your department will have their Annual reviews: .b End-Document End-Procedure !**************************************** Begin-Procedure Print_Letter_Ending !**************************************** Second Document paragraph Begin-Document (+1,1) .b Please prepare the appropriate documentation by their respective review dates. .b Sincerely, HR Administrator .b /Mrs. Bayer/ End-Document End-Procedure In Test15D.sqr, contrary to the previous test, we do not have to count how many employees have been selected. We simply print the first Document paragraph in the procedure Print_Letter_Beginning, then all employees from the selected department, and (upon finishing printing the employee list) the second Document paragraph in the procedure Print_Letter_Ending. In the Print_Employee procedure, SQR takes care of the page overflow situation: whenever an end of the current page is reached, SQR opens a new page automatically. VARI ABLE L ENGTH SECTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 233 We still have to make sure that there are enough lines left for the second Document paragraph at the end of the entire letter. We do this by checking the SQR reserved variable #current-line and issuing the New-Page command if there is not enough room for the second Document paragraph on the current page. The output of our program will look like that in figure 15.4. Simon Schumacher Manager of Office of the President Dear Simon Schumacher : This is a reminder that the following employees of your department will have their Annual review: Bennett,William D. Elias,Jan Campenhout,Marc Dahling,Irene Rossalini,Bartholome Wagner,Gretchen Jordan,Robert Austin,Caroline Kidd,Kenneth Bird,Douglas Smith,John Bronte,Katherine Fletcher,Leslie Drew,Suzanne Reagan,Deidre Alomar,Samuel Barfield,John Cone,Alton Bell,Anna Gruber,Jennifer King,Russell Lamp,Claire 1998-12-31 1998-10-30 1998-10-20 1998-01-22 1998-02-12 1998-02-18 1998-03-28 1998-03-28 1998-04-15 1998-04-30 1998-05-10 1998-05-18 1998-05-31 1998-06-10 1998-06-12 1998-03-16 1998-02-28 1998-09-10 1998-12-15 1998-08-24 1998-08-03 1998-07-27 Figure 15.4 Using multiple Document paragraphs to generate letters with unlimited employee lists (continued on next page) 234 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! (Continued) Page: McGriff,Frank 2 1998-08-24 Please prepare the appropriate documentation by their respective review dates. Sincerely, HR Administrator /Mrs. Bayer/ Hugh Aitken Manager of Retail Servicesment Dear Hugh Aitken : This is a reminder that the following employees of your department will have their Annual review: Vierra,Ginaual Ling,David 1998-07-31 1998-08-05 Please prepare the appropriate documentation by their respective review dates. Sincerely, HR Administrator /Mrs. Bayer/ Figure 15.4 Using multiple Document paragraphs to generate letters with unlimited employee lists (continued) You may now be asking yourself these questions: was the usage of the Document paragraph justified in this specific case? Was it worth the trouble? Wouldn’t we be better off using the regular SQR report printing technique? The purpose of Test15D.sqr was to demonstrate how to deal with situations when the variable portion of the document exceeds the designated page space. It is up to our readers to decide how practical this approach is and if the standard SQR report generation technique is appropriate in a particular case. VARI ABLE L ENGTH SECTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 235 KEY POINTS 1 Use the Document paragraph to create form letters. 2 The Begin-Document and End-Document commands are used to define the Document paragraph. 3 The Document paragraph may be used inside any procedure. There may be multiple Document paragraphs within a program. 4 Do not use tabs inside a Document paragraph. Use spaces instead. For a full line of spaces specify .b. 5 SQR commands are not allowed within the Document paragraph. 6 A printed Document paragraph must fit into a single report page. 7 You can use document markers to designate special locations in the Document paragraphs where you can place data from external to the paragraph parts of the program. The Position and Print commands are used to move data to the Document paragraph from outside of the paragraph. 8 You must generate a document before placing information at the locations marked by document markers. 9 You can separate your letter into variable length sections to achieve greater flexibility. 236 CHAPTER 15 GENERATING LE TTER S TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 6 Using graphics IN THIS CHAPTER • Printing business graphics charts • Controlling parts of an SQR graphics chart • Using SQR arrays to feed data into business charts • Using images in reports • Printing bar codes and address labels • Changing fonts and letter sizes • Drawing boxes, horizontal lines, and vertical lines • Controlling the position on the page when working with graphical objects 237 TEAM LinG - Live, Informative, Non-cost and Genuine! As a reporting tool, SQR supports a rich variety of different graphical objects that can enhance the appearance of your reports as well as add a number of valuable features such as business graphs and charts, bar codes, address labels, signatures, and so on. Not all printer types will support all these features. In some cases, you may get a shadow box or just an empty space instead of the nice image you expected. Only PostScript printers or HP printers with HPGL support (generally, HPLaserJet 3 and higher) are capable of printing the image outputs. The surest way to generate your graphics is to direct the program output to an .spf file (more about .spf files in chapter 14). Another problem with printing most graphical objects in SQR is controlling the object’s position on the page. After a graphical object is printed, SQR brings the current position on the page back to the upper left hand corner of the object. If you do not take this into consideration, the subsequent Print commands may simply overlay the object. We will learn how to deal with these problems later in the chapter. Let’s start with the most exciting graphical objects: business graphs and charts. 16.1 Declaring a business chart Business charts, graphs, and histograms are very useful when you need to present complex quantitative data in the simplest and most cohesive way. Generally, readers of reports equipped with graphical charts read and understand the presented information more quickly and make fewer mistakes analyzing the printed materials. It is certainly worthwhile to make the extra effort of adding these useful features. This is especially true when your report includes tabular information with repeated rows of similar structure containing numerical data with values that depend on one or two parameters (sometimes called dimensions) such as time, geographical region, type of merchandise, and so on. Beware, however, of the fact that if the tabular information to be displayed with the help of a chart has too many cells, the graphical chart may become difficult to analyze. Segments in a bar or pie chart may overlay each other. In this case, you may be better off with the conventional tabular presentation. Depending on your program structure and the number of charts to be printed in one program, printing a business chart may be a one-step or a two-step procedure. The one-step approach uses the Print-Chart command; the two-step approach uses a combination of the Declare-Chart and Print-Chart commands. If your program has only one chart or a few different charts that have very little in common, you can utilize the one-step approach: use a fully-coded Print-Chart command to print each chart. The problem with this technique is that since you have to define all features of the graphical object to be printed in each Print-Chart command, you have a lot of coding to do. A mere syntax definition of this command takes more than a page in the SQR language reference manual (see appendix D). Of course, 238 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! not all the command parameters have to be coded, you can use the default values for certain parameters, but it is still a big job to code a business chart in an SQR program. The two-step approach comes in very handy when you have a number of same or similar charts in one program. In this case, you can use just one Declare-Chart command to define all basic features of the chart, and multiple Print-Chart commands to print each specific graphical object. When all the basic features of a chart are already defined by the Declare-Chart command, there is no need to code all the PrintChart parameters; you only have to code a few necessary ones. To better understand how to declare a business chart, let us consider the following situation: You need to produce a report that will list the number of employees in different regions during different time periods. The report should include a business chart depicting the number of employees by geographical region (Northeast, South, Central, West) from January 1998 to June 1998. In our particular case, we have two dimensions: regions and time. Before we start coding the chart, let us select the chart type. SQR offers a good choice of types, including • line graph • different kinds of bar charts • histogram • area / stacked area / 100%-area charts • XY-scatter plot • high-low-close graph A regular bar chart will do a very good job of displaying the employee numbers distribution. Let us use the Declare-Chart command to code our chart. As any other declarative command, the Declare-Chart command must be coded in the Setup section. !An example of the Declare-Chart command Begin-Setup Declaring a chart Declare-Chart Employees_By_Region Title='Employees by Region by Month' Type=STACKED-BAR Legend=Yes Legend-Placement=Upper-Right X-Axis-Label='Months' Y-Axis-Label='Number of Employees' End-Declare End-Setup In the example, we declare a chart named Employees_By_Region. We assign our chart a title as well as labels to be displayed along the X and Y axes. We define the chart legend placement: the legend will be placed in the upper-right corner of the chart. The DECL ARING A BU SINESS CHAR T TEAM LinG - Live, Informative, Non-cost and Genuine! 239 chart type is defined as STACKED-BAR, but later it will be redefined as BAR just to demonstrate how the chart parameters can be dynamically redefined. We did not code all the command parameters. The omitted ones will either be assigned their default values or be supplemented with the Print-Chart parameter values later. If you have been following us with coding and then tried to run the program, you were probably disappointed: nothing came up. The Declare-Chart command does not print charts, it is the Print-Chart command that performs the actual printing. 16.2 Creating an array No, this is not a misplaced subchapter. You will need to refresh your memory on the material covered in chapter 12. We are going to create an array. In SQR, arrays serve as data sources for charts, and there is a good reason for this: arrays contain repeated rows of similar structure. Let’s create an array to hold the employee numbers by region and months. We will use the Create-Array command to create an empty array, and a number of Put commands to populate the array with the chart data. This will be a twodimensional array with the following dimensions: month and region. !Creating and populating an array as a data source for a bar chart Begin-Setup Create-Array Name=Number_Of_Employees Creating an array to feed data to Size=12 the chart. Each array row will Field=Month:Char contain employee numbers in four regions for the month. Field=Empno:Number:4 End-Setup Begin-Program Put 'Jan' 20 22 28 32 Into Number_Of_Employees(0) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Feb' 10 16 12 18 Into Number_Of_Employees(1) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Mar' 12 15 18 22 Into Number_Of_Employees(2) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Apr' 30 26 19 23 Into Number_Of_Employees(3) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'May' 23 20 15 12 Into Number_Of_Employees(4) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Jun' 20 16 12 10 Into Number_Of_Employees(5) Month Empno(0) Empno(1) Empno(2) Empno(3) End-Program In real business, the array is usually populated with data from the database or from an external file. Each particular chart type needs its own specific array structure. In case of the BAR type (both the regular BAR and STACKED-BAR), the array’s structure, as well as the data placed into the array, must fit the two-dimensional pattern. The first 240 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! dimension (the month name) value is moved to a single-occurrence field Month of each row and the number of employees for each region is moved to each occurrence of the multiple-occurrence field Empno where every occurrence corresponds to one region (the region is the second dimension). Please note that while the first dimension values (months) are stored in the array explicitly, the second dimension values (regions) are not stored in the array: they are just implied based on their occurrence numbers. If we were to display a different type of chart, for instance, a pie chart, the array structure would be different. We will show you an example of a pie chart later in this chapter. Now with our array created, we are ready to print our chart. 16.3 Printing a chart In order to print a chart, SQR needs to know the chart’s graphical characteristics, the chart size and location on the page, and the source of data for the chart. The chart’s characteristics can be defined (fully or partially) in the Declare-Chart command. Later, this definition is finalized with the help of the Print-Chart command. The Print-Chart command defines the following: • all the chart characteristics if the chart has not been previously-defined or adds/ overrides previously defined characteristics • the chart location on the page and the chart size • the array which holds the data source for the chart. Let’s code the proper Print-Chart command to display the employee distribution by geographical region: !TEST16A.SQR !A bar chart generating program !*************** Begin-Setup !*************** Declare-Chart Employees_By_Region Title='Employees by Region by Month' Type=STACKED-BAR Legend=Yes Legend-Placement=Upper-Right X-Axis-Label='Months' Y-Axis-Label='Number of Employees' End-Declare Create-Array Name=Number_Of_Employees Size=12 Field=Month:Char PRINTING A CHAR T TEAM LinG - Live, Informative, Non-cost and Genuine! 241 Field=Empno:Number:4 End-Setup !******************* Begin-Program !******************* Put 'Jan' 20 22 28 32 Into Number_Of_Employees(0) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Feb' 10 16 12 18 Into Number_Of_Employees(1) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Mar' 12 15 18 22 Into Number_Of_Employees(2) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Apr' 30 26 19 23 Into Number_Of_Employees(3) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'May' 23 20 15 12 Into Number_Of_Employees(4) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Jun' 20 16 12 10 Into Number_Of_Employees(5) Month Empno(0) Empno(1) Empno(2) Empno(3) Print-Chart Employees_By_Region (2,5) In addition to previously defined chart Chart-Size=(50,30) parameters, the Print-Chart comData-Array= Number_Of_Employees mand specifies the chart size and Data-Array-Row-Count=6 location on the page, names the data Data-Array-Column-Count=5 array, and overrides some of the previLegend-Presentation=Outside ously declared chart parameters. Data-Array-Column-Labels= ('NORTH EAST','SOUTH','CENTRAL','WEST') Type=BAR End-Program In the Test16A.sqr program, we define our chart characteristics with the help of the Declare-Chart command, create an array to feed the chart with data, and use the Print-Chart command to print the chart. The Print-Chart command specifies the location of the chart on the page (2, 5) and the chart size (the first number in the ChartSize parameter specifies the chart horizontal length in SQR page columns; the second number specifies the vertical height of the chart in SQR page lines). Please note that the Print-Chart command overrides the value of one of the parameters defined in the Declare-Chart. The Type is defined as STACKED-BAR in Declare-Chart and is overridden as BAR in Print-Chart. In addition, the Print-Command specifies the location, the size, and the data source for the chart. The two parameters Data-ArrayRow-Count and Data-Array-Column-Count can limit the amount of data fed to the chart from the array. If the values of these parameters are lower than the corresponding array parameters, only the specified number of rows and/or columns will be used to feed the array data to the chart. In our case, the array Number_Of_Employees was defined 242 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! with twelve rows to hold data for all twelve months of the year, but the Print-Chart command uses only the first six rows. Please note that we specified Data-Array-Column-Count=5 even if the corresponding array definition included only two array fields, Month and Empno. When SQR generates a chart, it counts every array field as well as every occurrence of a field as a separate column. In our example, the four occurrences of the Empno field are considered four separate columns. The Data-Array-Column-Labels parameter tells SQR what values must be placed in the chart legend. In our case, the legend should display the regions. Finally, we add another parameter, Legend-Presentation, which places the chart legend outside the area defined by the two chart axes (but still inside the chart border). The output of our program is shown in figure 16.1. Figure 16.1 Generating a bar chart If we were to print some additional information below our chart or to add another chart, where do you think the current print position would be? Based on common sense, one would expect the position to be next to the chart’s lower right hand corner. Actually, after the chart is generated, the current position jumps back to the page position it occupied prior to the start of the chart generation. To solve this problem, you have to adjust the page position by adding the number of lines and columns occupied by the chart to the proper SQR page position parameters. This is true for all SQR graphical objects, not just for business charts. PRINTING A CHAR T TEAM LinG - Live, Informative, Non-cost and Genuine! 243 We will demonstrate the page position controlling technique in the following example. In this example, we will print a small report using the regular Print commands at the top of the page, then add two business charts, one beneath the other. The report will display the employee statistics by region and time in a tabular format. The first chart located under the report will be exactly the same as the one on figure 16.1. The second chart will be a pie chart displaying the total employee headcount for all regions for each month. We will need to make a number of modifications to our program: First, we define another array, Employees_By_Month, which will feed data into the pie chart. Every array element includes three fields: Month, Empno, and Explode. As we mentioned earlier, each chart type needs its special array organization. In case of the pie chart, every row in the array corresponds to one pie chart segment. The Month field is pretty similar to the Month field of the Number_Of_Employees array. The values of this field will be displayed in the chart legend. The Empno field will hold the total number of employees for every month. The values of this field will determine the size of each chart’s segment; these values will also be displayed next to every segment. The Explode field, special for pie charts, indicates whether the corresponding pie chart segment should be exploded. The second change to the program is to add a While loop in order to • print a table displaying employee numbers for every month by regions • calculate the total number of employees by regions for every month • for every month, move the month short name and the total number of employees by all regions into the proper element of the Employees_By_Month array. The third change to the program is the addition of another Print-Chart command to print the second business chart, the pie chart. As does the first Print-Chart command, the second Print-Chart command will reference the previously defined Declare-Chart command, and will add or override certain chart parameters. !TEST16B.SQR !Adjusting the print position after printing a business chart !******************** Begin-Setup !******************** Declare-Chart Employees_By_Region Title='Employees by Region by Month' Type=STACKED-BAR Legend=Yes Legend-Placement=Upper-Right X-Axis-Label='Months' Y-Axis-Label='Number of Employees' End-Declare 244 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! Create-Array Name=Number_Of_Employees Size=12 Field=Month:Char Field=Empno:Number:4 Create-Array Name=Employees_By_Month Size=12 Field=Month:Char Field=Empno:Number Field=Explode:Char End-Setup !******************* Begin-Program !******************* Do Create_Array Do Print_Table Do Print_Charts End-Program !************************************* Begin-Procedure Create_Array !************************************* Put 'Jan' 20 22 28 32 Into Number_Of_Employees(0) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Feb' 10 16 12 18 Into Number_Of_Employees(1) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Mar' 12 15 18 22 Into Number_Of_Employees(2) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Apr' 30 26 19 23 Into Number_Of_Employees(3) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'May' 23 20 15 12 Into Number_Of_Employees(4) Month Empno(0) Empno(1) Empno(2) Empno(3) Put 'Jun' 20 16 12 10 Into Number_Of_Employees(5) Month Empno(0) Empno(1) Empno(2) Empno(3) End-Procedure !************************************ Begin-Procedure Print_Table !************************************ Print ' Employees By Region by Months' (1,1) Print ' ' (+1,1) Print 'Month North East South Central West ' (+1,3) Print ' ' (+1,1) While #i < 6 Get $Month #Emp1 #Emp2 #Emp3 #Emp4 From Number_Of_Employees(#i) Month Empno(0) Empno(1) Empno(2) Empno(3) Print $Month (+1,3,5) Print #Emp1 (,+6) Edit 9,999 Print #Emp2 (,+6) Edit 9,999 Print #Emp3 (,+6) Edit 9,999 PRINTING A CHAR T TEAM LinG - Live, Informative, Non-cost and Genuine! 245 Print #Emp4 (,+6) Edit 9,999 Let #Emp_Total = #Emp1+#Emp2+#Emp3+#Emp4 Put $Month #Emp_Total 'N' Into Employees_By_Month(#i) Let #i = #i + 1 End-While Print ' ' (+1,1) End-Procedure !************************************** Begin-Procedure Print_Charts !************************************** Print-Chart Employees_By_Region (+1,5) Chart-Size=(50,25) Title='Employees by Region by Month' Data-Array= Number_Of_Employees Data-Array-Row-Count=6 Data-Array-Column-Count=5 Legend-Presentation=Outside Data-Array-Column-Labels=('NORTH EAST','SOUTH',' CENTRAL','WEST') Type=BAR Adjusting the current page position from the upper left corner to under the bottom of the previous chart Print-Chart Employees_By_Region (+27,5) Chart-Size=(50,15) Title='Total Headcount by Month' Data-Array= Employees_By_Month Data-Array-Row-Count=6 Data-Array-Column-Count=3 Legend-Placement=Lower-Right Legend-Presentation=Outside Data-Array-Column-Labels=Employees_By_Month Type=PIE End-Procedure Please note the difference between coding the print position parameters in the first and second Print-Chart commands. In the first Print-Chart command, the page line number is coded as +1, pretty much the same way as it would have been coded in a regular Print command when you need to start printing on the next line. In the second Print-Chart command, however, the page line number is coded as +27 even though the intent is to print the chart starting just two lines below the first chart. The reason is that SQR does not move the current position after printing any graphical object. You need to account for the number of lines occupied by the object (in our case the number of lines is twenty-five) and adjust the current page position accordingly. Now, let’s run the modified program and see the output in figure 16.2. 246 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 16.2 Using the previous chart size to adjust the starting position of the next chart 16.4 Using images in reports Image is another popular graphical object in SQR. You can use images to print corporate logos, signatures, pictures, and so on. As with business graphs and charts, you have a choice between a one-step process and a two-step process when programming images in your report. An image characteristics can be defined (fully or partially) in the US IN G IMAG ES I N RE P ORTS TEAM LinG - Live, Informative, Non-cost and Genuine! 247 Declare-Image command, and later this definition can be finalized with the help of the Print-Image command. Alternatively, images can be printed in one step using only the Print-Image commands. Because both image processing commands have substantially fewer operands, you save almost nothing by declaring an image and printing the image later, unless your program logic calls for printing exactly the same image in many places in the report (for example, a corporate logo). Another case when the two-step approach can prove useful is when several programs share the same image. In this case, it would make sense to declare the image only once and to place this declaration into a shared file. The file can be inserted into many programs using the #Include compile directive. Declare-Image and Print-Image have identical operands with the exception of the page position operand of the Print-Image command, which the Declare-Image command does not use. Let’s write a sample program to print an image using the Declare-Image and Print-Image commands: !TEST16C.SQR !An image printing program Begin-Setup Declare-Image Flower Type=BMP-FILE Source='C:\MSOFFICE\CLIPART\FLOWER.BMP' End-Declare End-Setup Begin-Program Let #Image_Length = 6 The length and height of the image are defined dynamically. Let #Image_Height = 8 Print 'Look at this flower: ' (2,2) Print-Image Flower (+2,5) Image-Size=(#Image_Length,#Image_Height) Let #Curr_Line_Adj = #Image_Height +3 Print 'Isn''t it lovely?' (+#Curr_Line_Adj,2) End-Program Figure 16.3 248 Printing an image Adjusting the current page position from the upper left corner of the image Figure 16.3 illustrates the output of our program. Please note that as with other graphical objects, SQR brings the current page position back to the upper lefthand corner of the image after printing the image. Therefore, the next Print command needs to account for the image height to adjust the current line number. In our example, instead of using a hard-coded image CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! height, we use the numeric variable #Image_Height and calculate the necessary adjustment dynamically. EPS images can only be printed on PostScript printers. BMP images can only be Note printed using Windows. GIF and JPEG images are only suitable for HTML output (more about HTML output in chapter 19). 16.5 Printing bar codes SQR supports a wide variety of bar code types. Bar codes are printed with the help of the Print-Bar-Code command. This command has a number of operands, the most important one being Type. Table 16.1 lists the available bar code types and their characteristics. Please note that some bar code types use only numbers while other types are more liberal and allow you to encode almost everything. The content of the text to be encoded by the Print-Bar-Code command must correspond to the bar code type. Some bar code types use check sums that can be calculated and printed in the bar code. In these cases, you need to code Checksum=Yes in the Print-Bar-Code command (for a complete syntax of the Print-Bar-Code command, please refer to appendix D). The text encoded in the bar code is specified by the Text operand. You can also specify some additional text to be printed without encoding with the help of the Caption operand. Table 16.1 Types of bar codes Type Description Text Length Text Type Checksum Y/N 1 UPC-A 11, 13, or 16 Numbers only N 2 UPC-E 11, 13, or 16 Numbers only N 3 EAN/JAN-13 12, 14, or 17 Numbers only N 4 EAN/JAN-8 7, 9, or 12 Numbers only N 5 3 of 9 (Code 39) 1 to 30 Numbers, Upper Case Letters, Punctuation Y 6 Extended 3 of 9 1 to 30 Numbers, Upper / Lower Case Letters, Punctuation, Control Characters Y 7 Interleaved 2 of 5 2 to 30 Numbers only Y PRINTING BAR CODES TEAM LinG - Live, Informative, Non-cost and Genuine! 249 Table 16.1 Types of bar codes (continued) Type Description Text Length Text Type 8 Code 128 1 to 30 Numbers, Upper / Lower Case Letters, Punctuation, Control Characters Checksum Y/N N 9 Codebar 1 to 30 Numbers only Y 10 Zip+4 Postnet 5, 9, or 11 Numbers only N 11 MSI Plessey 1 to 30 Numbers only Y 12 Code 93 1 to 30 Numbers, Upper Case Letters, Punctuation Y 13 Extended 93 1 to 30 Numbers, Upper / Lower Case Letters, Punctuation Y 14 UCC-128 19 Numbers only N 15 HIBC 1 to 30 Numbers only Y The following example shows how to generate a bar code with the type HIBC: !TEST16D.SQR !Generating an HIBC type bar code Begin-Program Print ' The type of this bar code is HIBC ' (2,2) Move '123456789' To $Text Let $Caption='ABCDEF - ' || $Text Print-Bar-Code (+2, 2) Type=15 ! Type=HIBC Height=0.5 Text=$Text Caption=$Caption Checksum=Yes Print 'End of bar code area' (+5, 2) End-Program In Test16D.sqr, the Print-Bar-Code command specifies the starting position of the bar code, the height of the bar code in inches, the text to be encoded and printed on the bar code, and an additional text to be printed under the bar code area (this text is not to be processed by bar code scanners). Please note that, as with other graphical objects, SQR will not adjust the current page position after generating the bar code. Subsequent Print commands must account for the bar code area height (including the caption area) when calculating the next print position. The height of bar code graphical objects is defined in inches, not in SQR print lines. The height must be between 0.2 and 2 inches. The Print-Bar-Code command 250 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 16.4 Printing a bar code does not specify the length of the bar code because the bar code scanners are tuned to certain distances between the lines, depending on the bar code type. Figure 16.4 shows the output of our program. 16.6 Drawing boxes and solid lines The Graphic command can be used to draw boxes as well as horizontal or vertical lines. It can also be used to change fonts, but this use of the Graphic command may be obsolete in the next SQR versions. Therefore, we recommend using the Declare-Printer or Alter-Printer commands to change fonts and letter sizes. We will demonstrate the font and letter size change technique in this subchapter. First, though, let’s use the Graphic command to print address labels for an employee mailing list. In the following example, our program will select employees from the Personal_Data table and, for each selected employee, generate an address label with a ZIP+4 Postnet bar code to support pre-sorted mailing. The address labels will be printed in rows, two labels per row, and every label will be framed in a box. To demonstrate the font and letter size change technique, the employee names will be printed using one letter font, while the address portion will be printed using a different font and letter size. In addition, we will demonstrate the line drawing technique by creating a solid frame around the entire page. The frame will be made up of two vertical and two horizontal lines: !TEST16E.SQR !Using the Graphic command !****************** Begin-Setup Declare-Procedure Before-Page=At_Page_Start End-Declare End-Setup !******************* Begin-Program !******************* Use-Printer-Type HP Columns 3 33 Do Main_Proc End-Program DRAW ING BOXES AND SO LID LINES TEAM LinG - Live, Informative, Non-cost and Genuine! 251 !*********************************** Begin-Procedure Main_Proc !*********************************** Begin-Select Name Address1 City State Zip Do Convert_Name(&Name,$First_Last_Name) Do Generate_Label From Personal_Data Where State = 'CA' End-Select End-Procedure !*********************************************************** Begin-Procedure Convert_Name($Last_First,:$First_Last) !*********************************************************** !Input parameter includes: Last name,First name, MI !Output parameter includes: First name, MI, Last name Let $Last_Name=Substr($Last_First,0,instr($Last_First,',',0)-1) Let $First_Name= Substr($Last_First,instr($Last_First,',',0)+1, Length($Last_First)-Instr($Last_First,',',0)) Let $First_Last=Rtrim($First_Name||' '||$Last_Name,' ') End-Procedure !********************************* Drawing a box around the Begin-Procedure Generate_Label address label !********************************* Setting the font to Times Roman Let #Label_Count = #Label_Count+1 Graphic (3,2,28) Box 10 5 After drawing the box, the Print ' ' (+1,1,27) Fill print position remains at the ! Set the font to Times Roman upper left of the box thus allowing for printing inside Alter-Printer Font=5 Point-Size=12 the box. Print $First_Last_Name (+1,3) Bold ! Change the font to Courier Changing the font Alter-Printer Font=3 Point-Size=12 Pitch=16.66 to Courier Roman Print &Address1 (+1,3) If length(&Zip) < 6 Let $Display_Zip = &Postal Else Let $Display_Zip = substr(&Zip,1,5) || '-' || substr(&Zip,6,4) End-If Let $City_State_Zip = &City || ', ' || &State || ' ' || $Display_Zip Print $City_State_Zip (+1,3) Print ' ' (+1,1,26) Fill Print '-' (+1,3,25) Fill Print ' ' (+1,1,26) Fill 252 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! Print-Bar-Code (+1,4) Type=10 ! Type = Zip+4 Postnet Height=0.8 Text=&Postal Next-Column If #Label_Count = 2 Let #Label_Count=0 Next-Listing Need=12 End-If End-Procedure !*************************************** Begin-Procedure At_Page_Start !*************************************** Graphic (1,1,60) Horz-Line 20 Graphic (1,1,58) Vert-Line 20 Drawing a page frame using vertical and horizontal lines Graphic (1,61,58) Vert-Line 20 Graphic (59,1,60) Horz-Line 20 Print ' ' (2,2,58) Fill End-Procedure In the Test16E.sqr we use all three types of the Graphic command. The Graphic Box command specifies the box position and length in parentheses and the box height and rule width following the Box keyword. The position, length, and height are coded in SQR print lines and columns. The rule width is coded in decipoints (there are 720 decipoints per inch). You can also code an additional positional parameter right after the width to specify shading. The shading is coded in percentage, from 1 (very light) to 100 (black). The Graphic Horz-Line and Graphic Vert-Line commands specify the position of the starting point of the line and the line length in the parentheses while the width is specified after the Horz-Line or Vert-Line keyword. The position and line length are coded in SQR print lines and columns, and the width is coded in decipoints. The same result could have been achieved by using the Graphic Box command, but we wanted to demonstrate the usage of the Graphic Horz-Line and Graphic VertLine commands. In addition to drawing boxes and lines, two Alter-Printer commands are used to control the fonts and letter sizes in the mailing labels. Due to the large output size, only a portion of the Test16E.sqr output is shown in figure 16.5. Please note that, in our program, we take advantage of the fact that SQR does not adjust the current page position after printing graphical objects. Our program first prints a box, and then places the address label information inside the box. DRAW ING BOXES AND SO LID LINES TEAM LinG - Live, Informative, Non-cost and Genuine! 253 Figure 16.5 Generating address labels using the Graphic, Alter-Printer, and Print-Bar-Code commands KEY POINTS 1 SQR offers a rich variety of graphical objects. 2 Not all printer types are capable of supporting all graphical objects, the surest way to generate graphical objects is to direct the program output to an .spf file. 3 You can print a business chart using the Print-Chart command without declaring the chart, or you can declare the chart using the Declare-Chart command and print this chart later using the Print-Chart command. 4 Business charts receive data from SQR arrays. Each chart type needs its special array organization. 5 When SQR prints a graphical object, it leaves the current page position at the upper left-hand corner of the object where the printing started. 6 When printing images, you can use the Declare-Image command to define an image in one source file and share this file between multiple programs. In this case, the Print-Image command performs actual printing of the previously defined image. 254 CH APTER 1 6 USING GRAPH ICS TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 7 SQR supports a wide variety of bar code types. 8 The Graphic command draws vertical or horizontal lines or boxes. 9 You can control fonts and letter sizes with the help of the DeclarePrinter or Alter-Printer commands. DRAW ING BOXES AND SO LID LINES TEAM LinG - Live, Informative, Non-cost and Genuine! 255 C H A P T E R 1 7 Working with flat files IN THIS CHAPTER • The Open, Read, Write, and Close commands • Working with fixed length records and variable length records • Checking the end-of-file condition • Reading into and writing from strings, binary data, and dates • Creating interface files for external applications • Using the Print and New-Page commands to output data to flat files • Using interface files to upload data into databases • Sorting flat files 256 TEAM LinG - Live, Informative, Non-cost and Genuine! 17.1 Files in SQR Files in SQR Many situations exist where, in addition to working with database tables and arrays, you may want to take advantage of regular sequential (often called “flat”) files. SQR supports all necessary input/output operations on flat files, including opening and closing files and reading from and writing to files. Please note that SQR does not provide random access input/output commands; all records are processed sequentially. You can use sequential files to • import data from other systems into your database tables • unload data from your database tables into sequential files, and use the files as interfaces to external systems • sort your data using the operating system commands or utilities. Flat files created in SQR are frequently used as interfaces between PeopleSoft applications and other systems. They are also used for generating comma-delimited (or any other symbol-delimited) files compatible with the Excel Spreadsheet format, or for creating inputs to Lotus Notes or E-mail. 17.2 Using the input/output operations in SQR Input/output in SQR Like most programming languages, SQR uses the Open, Read, Write, and Close commands to handle sequential files. 17.2.1 Opening a file Before accessing a file in SQR, you have to open it. A file can be opened as an input file for reading from an existing file, or an output file for writing to a new file or appending data to an existing file. The maximum number of open files at any time is 256. If your program includes more than 256, close the files you do not need at the moment. When a file is opened for reading, the record length specified in the Open command must be equal to or greater than the length of the longest record used in the file. The maximum record length is (32K - 1) bytes (32767). When a file is opened for writing, a new file is created unless the For-Append parameter is specified. If the same file already exists and no append mode is specified, the existing file will be overwritten. To better understand all the Open command operands, let us consider a few examples of the Open command: INPUT/OU TPUT IN S QR TEAM LinG - Live, Informative, Non-cost and Genuine! 257 1 2 3 4 5 6 7 Open 'My_File_1' as 1 For-Reading Record=80:Vary Open 'c:\temp\My_File_2' as 2 For-Reading Record=88:Fixed Open $File_1 as 3 For-Reading Open $File_2 as #k For-Writing Record=100 Open $File_3 as 4 For-Append Record=70 Open &A.File_Name as 5 For-Reading Record=120:Fixed_Nolf Let $File_4='c:\files\input\My_File_3' Open $File_4 as 6 For-Reading Record=150:Vary Status=#FileStatus If #FileStatus != 0 Show 'Error Opening File: ' $File_4 Else Show 'File ' $File_4 'Opened Successfully' End-If In the example in line 1, we opened a file named 'My_File_1' for Reading. The file name in SQR can be coded as a literal or stored in a string variable or column variable. The file name can be specified as a fully qualified name, including the drive and directory, or as a file name only. If the drive and directory are not specified, SQR will use the current directory as a default. The number 1 next to the file name in the Open command is the file number (often called the file handle), which is used by other input/ output commands to reference the file in the program. You can use any literal, variable, or column variable to specify the file number. File numbers are used in the program in the Read, Write, and Close commands instead of file names. These numbers can be any positive integers less than 64,000. The maximum record size in our example is eighty, which means that all bytes in the input record beyond eighty will be ignored. Since our record type was defined as Vary, all records in the file must be terminated by a line terminator (platform-dependent). It is important to note that files with type=Vary should not contain binary data. In line 2, the file 'c:\temp\My_File_2' is opened as a Fixed Length file. This means that all the records in this file have the same length=88. Each record is terminated with a line terminator. You can use this type of file to read binary data. Note that, for files with type Fixed, record length does not include the line terminator characters. In line 3, we opened a file with the name stored in the SQR variable $File_1. It is a good practice to use variables instead of hard coding file names. Doing so gives you the flexibility to change the file name in the program before you use the Open command. In line 4, the file with a name stored in the SQR variable $File_2 is opened for Writing with the file handle stored in the SQR numeric variable #k. The maximum record length is 100. Since the file type is not specified, SQR will be using the Vary type as a default. In line 5, the file with the name stored in the SQR variable $File_3 is opened for Append. This means that all output information will be placed at the end of the file. If the file does not exist, a new file will be created with the specified attributes: Record 258 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! Length=70, File Type=Vary (default file type). If the file does exist, make sure the file attributes specified in the Open command match the existing file attributes. Failure to do so may lead to unpredictable results. In line 6, the file with the name stored in the column variable &A.File_Name is opened for Reading. The file type is specified as Fixed_Nolf, which indicates that all the records in the file are 120 bytes in length with no line terminator at the end of the record. This file type is very useful when writing or reading binary data. Please note that the Fixed_Nolf qualifier is coded with an underscore rather than with a hyphen. This is one of just a few SQR command arguments that are coded with an underscore. The last example, in line 7, shows the way to check if your Open command was successfully executed. It is not only a good practice to check the Open command status, but it must become your programming habit. In most cases, when the returned status is not zero, it means that the file was not found. Verify that your file is, in fact, in the specified directory, and has the correct name and extension. Make sure that the correct syntax is used when specifying the directory. For example, you may want to run your program in both UNIX and Windows environments. When specifying the file directory, keep in mind that in Windows you use backslashes while in UNIX the directory must be specified with forward slashes, for example c:\Temp\Myfile.dat /tmp/Myfile.dat ! in Windows environment ! in UNIX environment 17.2.2 Closing a file SQR files stay opened until the Close command is used or your program successfully terminates. The SQR Close command is very simple. Here are a couple of examples: 1 2 Close 1 Close #k As you can see, the Close command uses only one argument, the file number, which is the number assigned to the file in the corresponding Open command. 17.2.3 Reading from a file SQR will allow you to read an input file only if the file was opened For-Reading. The Read command reads the next record from the file into the variable or variables specified in the command. INPUT/OU TPUT IN S QR TEAM LinG - Live, Informative, Non-cost and Genuine! 259 Read Into There are several ways to read a record from a file, and, depending on your program needs, you may choose the proper approach. Each record can be either read into one SQR string variable or a number of individual variables. To illustrate the point, let’s look at the following example: Read 1 Into $Input_Record:80 In this example, we read the whole record into a string variable $Input_Record. This gives us flexibility and, with the help of string functions, we can easily extract the needed fields, or parse the record by a field separator. This approach is especially useful when dealing with records containing variable length fields. Here are some parsing examples: Unstring $Input_Record By ',' Into $Last_Name $First_Name $Address Extract $First_Name from $Input_Record 0 20 Let $Last_Name=Substr($Input_Record,1,20) Another method of using the Read command is when the input record is read directly into the specified fields. Please note that this method is mostly used when you know the exact length of every field. Here is an example of this flavor of the Read command: Read 1 into $Last_Name:20 $First_Name:10 $Address:50 Detecting end of file When reading a file you should always check if the End-Of-File condition is reached. SQR internal variable #end-file is set to 1 when there are no more records to read. You should make sure that this condition is checked after each record is read. A common practice is to place the Read statement into a loop, then break from the loop when the End-Of-File condition is detected. Please note that not breaking from the loop will cause an extra record process. Here are some examples of how this check can be accomplished: … !********************************** Begin-Procedure Read-File !********************************** While 1 Check if the End-Of-File Read 1 Into $Record:80 condition is reached. If #end-file = 1 Break !Break from the While Loop End-If Do Process-Input-Record 260 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! End-While !At this point all the records are read from the file Close 1 End-Procedure In this example, we use the Read command within an infinite loop. After each record is read, we check for the End-Of-File condition, and, when True, exit from the While loop with the help of the Break command. Many C programmers would probably frown at this example. Using an infinite loop is often considered to be a bad programming technique. Even though While 1 expressions are widely used in SQR, we can easily avoid them. Let’s rewrite our previous example using the SQR reserved variable #end-file in the While expression: … !********************************** Begin-Procedure Read-File !********************************** While Not #end-file Read 1 Into $Record:80 Check for End-Of-File If #end-file condition in the While Statement. Break End-If Do Process-Input-Record End-While !At this point all the records are read from the file Close 1 End-Procedure … Our procedure becomes smaller and perhaps more acceptable for those who like to program with style. Verifying read status As an option you can designate a numeric variable in the Read command in order to obtain the read status from the operating system. The name of the variable is specified in the Status parameter of the Read command. SQR returns zero if the read is successful, otherwise, a system-dependent error number is returned. The following example illustrates the use of the Status parameter in the Read command: !********************************** Begin-Procedure Read-File !********************************** While Not #end-file Read 1 into $Record:80 Status=#Read_Stat If #end-file Break End-If INPUT/OU TPUT IN S QR TEAM LinG - Live, Informative, Non-cost and Genuine! 261 If #Read_Stat <> 0 Show 'Bad return from the Read command, errno=' #Read_Stat ' Record # = ' #Rcds Else Add 1 to #Rcds Do Process-Input-Record End-If End-While !At this point all the records are read from the file Close 1 End-Procedure Reading text data When reading text data (any string of characters), you should specify the variable name and number of bytes you want the program to read. Keep in mind that the trailing blanks are omitted when the record is read. The total length specified for all your read variables must be less than or equal to the length of the entire record read. Reading binary data In order to read binary data, the file must be opened as a file with record type equal to Fixed Note If you use binary numbers, the file may not be or Fixed_Nolf. Binary fields can be one, two, or portable across platforms, since four bytes in length. When reading binary numbinary number representation is bers, they must be placed into numeric variables. platform dependent. Binary numbers hold only integers. If you need to maintain a decimal portion of a number, convert the number to a string variable. The following is an example of reading binary numbers: Read 1 Into #Amount:2 #Hours:1 Reading date fields If a date field was written to a file in the SQR date variable format, you can read this field to either a date or a string variable. The date variable must be in one of the following formats: • the format specified by the SQR_DB_DATE_FORMAT environmental variable • your database specific format • the database independent format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'. Here is an example of reading a date field into a date variable: 262 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! Declare-Variable Date $Input_Date End-Declare !********************************** Begin-Procedure Read-File !********************************** While Not #end-file Read 1 Into $Input_Date:18 $Record If #end-file Break End-If End-While End-Procedure …. Reading a date into a date variable declared above When a date field is read into a text variable, you may use the strtodate() function to convert a string to a date. Declare-Variable Date $Date1 End-Declare !********************************* * Begin-Procedure Read-File !********************************** While Not #end-file Read 1 Into $String_Date:18 $Record If #end-file Break End-If Let $Date1 = strtodate($String_Date) End-While End-Procedure Reading a date field into a string variable, and converting a string to a date 17.2.4 Writing to a file SQR will allow you to write a record to a file only if the file was opened For-Writing or For-Append. The Write command writes a record to a file from the variable or variables specified in this command. When using SQR Write command, you can write to a file from a single literal, variable, or column, or from a list of literals, variables, or columns. The command operands are very similar to those of the Read command. You have to specify the file number, the source field(s), the length of the source field(s), and a variable to hold the Write command status. Only the file number and source field names are required parameters. The source field length and the status variable are optional. Let’s look at the following examples: INPUT/OU TPUT IN S QR TEAM LinG - Live, Informative, Non-cost and Genuine! 263 1 2 3 4 5 Write 1 From $Record:80 Status=#Write_Stat Write #H From $Record_2 Write 2 From $Last_Name:20 ',' $First_Name:10 ',' &Address:50 Write 3 From #Amount:2 #Number:1 Write 4 From $Date:18 In line 1 of our example, we write an entire record from the string variable $Record. Since the record length of eighty bytes is specified, only the first eighty bytes will be written. If $Record is longer than eighty bytes, the record will be truncated. If $Record is shorter than eighty, the remaining part of the record will be padded with spaces. Also, since the Status keyword is coded, SQR will move a zero to #Write_ Stat if Write is successful, or, in case of a Write command failure, a systemdependent error number will be moved to #Write_Stat. In line 2, the record is written from the string $Record_2. Since the length is not specified, the current length of the variable $Record_2 will be used. The file number previously specified in the Open command is stored in the #H variable. In line 3, the record is written from the three specified fields. Since we wanted this file to be comma-separated, we placed commas between the data fields. Line 4 illustrates the use of numeric variables as sources. In this case, the length argument is mandatory, and only one- two- or four-byte binary integers are allowed by SQR. If you need to write decimal or floating point numbers, you can convert them to string variables, then write from the string variables. In line 5, we write from a date variable. Before being written to a file, the date is converted to a string using the format specified by the SQR_DB_DATE_FORMAT environment variable, or if not set, your database-specific format. What would happen if the file record length specified in the Open command differs from the record length specified in the Write command? If the length in Open is smaller than the length in Write, an error will occur. If the record length in the Write command is smaller than the length in the Open command, the length in the Write command will be used. Please note that if you don’t specify the length of each field in your record, SQR will treat the file as having variable length records. Also, it is important to know that you cannot read or write NULL values to and from an ASCII file, since SQR strings are NULL-terminated. 17.3 Different techniques for creating flat file output Techniques for flat file output One of the most popular uses of flat files is downloading information from a database into a flat file. The file created can later be used by other applications outside the SQR environment. You can use one of two major approaches to accomplish this task: 264 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! • the Write command • the Print and New-Page commands. Using the Write command is a more traditional way of creating flat files. It is also a more flexible method. It involves the following steps: 1 Open a file For-Output or For-Append. 2 Select the required data from your database tables in the Begin-Select paragraph. 3 Perform the necessary data conversions and manipulations. 4 Use the Write command to place data into the file. 5 Repeat steps 2, 3, and 4 until the end of selection. 6 Close the output file. 17.3.1 Using input/output commands Let’s assume, that we want to create a list of all active employees and their phone numbers, to download this list to a file, and to use the resulting file as an interface to the E-mail system: !TEST17A.SQR ! The E-Mail Interface program !******************** Begin-Program Checking the platform and !******************** setting the right file path If $sqr-platform = 'WINDOWS-NT ' Let $FileName='c:\appldir\Employee.dat' Else ! UNIX Let $FileName='/tmp/appldir/Employee.dat' End-If Open $FileName as 1 For-Writing Record=100 Status=#OpenStat If #OpenStat != 0 Show 'Error Opening ' $FileName Open output file, and Else check its status. Do Process_Employees Close 1 End-If Display 'Total records exported: ' Noline Display #Tot_Recs 999,999,999 End-Program !********************************************* Begin-Procedure Process_Employees !********************************************* Move 0 To #Tot_Recs Begin-Select TECHNIQU ES F OR FL AT FIL E OU TPU T TEAM LinG - Live, Informative, Non-cost and Genuine! 265 A.Emplid A.Deptid B.Name B.Phone Do Write-Output-Record From Job A, Personal_Data B Where A.Emplid=B.Emplid And A.Empl_Rcd=0 And A.Empl_Status = 'A' And A.EFFDT = (Select MAX(Effdt) From Job Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt <= Sysdate) And A.Effseq = (Select MAX(Effseq) From Ps_Job Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt = A.Effdt) End-Select End-Procedure !********************************************** Begin-Procedure Write-Output-Record !********************************************** Write 1 From &A.Emplid:11 &A.Deptid:10 Write SQR columns with fixed lengths &B.Name:30 &B.Phone:10 Add 1 to #Tot_Recs End-Procedure In Test17A.sqr, we open a file for writing, select data from the Personal_Data and Job tables row by row in the Process_Employees procedure, and, for every record read from Job and Personal_Data, we call the Write_Output_Record procedure to output the record to the file. Please note that, at the beginning, the program checks the SQR predefined variable $sqr-platform to determine the platform under which it is running, in order to specify the correct fully qualified file name. This piece of logic makes your SQR program less platform-dependent and allows you to run it in UNIX or Windows environments without changing the program. Another important point to remember is to use the Status argument when opening a file. Why bother with checking the status when opening an output file? Wouldn’t SQR just create the file if it didn’t exist? Sure, but if the file name you specified is not valid, for example, if the directory or folder you coded in your program does not exist you would probably want to notify the operator and stop the program. A good program has to address all possible “if ” questions. 266 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 17.1 illustrates a sample portion of our output file. 6601 6602 6603 7702 7703 7704 7705 7706 7707 8001 8052 8101 8102 8113 8120 8121 Figure 17.1 10200 21700 10200 11000 21700 21700 10200 P9300 21700 10100 20100 10500 10900 20500 20900 10200 Jones,Gladys Peppen,Jacques Pitman,Earl Atchley,Tamara DeJackome,Jeanette Riall,Alphonsine Holt,Susan Quabin,Mark Adams,Bill Schumacher,Simon Avery,Joan Penrose,Steven Sullivan,Theresa Frumman,Wolfgang Jones,Theresa Gregory,Jan 415/376-38 604/376-38 415/284-72 415/376-29 604/284-72 604/376-29 415/837-44 A portion of the E-Mail Interface program output The Open statement in Test17A.sqr did not specify Record=Fixed, but the output file generated by the program came out with fixed length records because we specified the exact length for each variable in the output record. The total record length was simply calculated as the sum of all the record components. 17.3.2 Creating comma-separated file output Some applications, like the Microsoft Excel, work with comma- or other symbolseparated files. We can easily change our previous SQR program to create such a file. In order to do this, we would only need to rewrite the Write_Output_Record routine to look like this !************************************************ Begin-Procedure Write_Output_Record !************************************************ Write 1 from &A.Emplid ',' &A.Deptid ',' Creating a comma-separated file &B.Name ',' &B.Phone Add 1 To #Tot_Recs End-Procedure TECHNIQU ES F OR FL AT FIL E OU TPU T TEAM LinG - Live, Informative, Non-cost and Genuine! 267 We use a hard-coded string literal ',' as a field separator. It may be a better idea to define the separator as a variable at the beginning of the program, and then to use the variable instead of the literal. In this case, if you later decide to use another character for field separation, you would have to change it only once. In fact, in our particular example, it might be better to use a character other than a comma. Why? Let’s run the program, examine the output, and we’ll see the answer right away. As you probably noticed, the column variable &B.Name in figure 17.2 already contains a comma. Therefore, it would be better to use another character for field separation. The best solution depends, of course, on your business requirements. By now, you have probably recognized the ability of SQR to meet your business needs. 6601,10200,Jones,Gladys, 6602,21700,Peppen,Jacques, 6603,10200,Pitman,Earl, 7702,11000,Atchley,Tamara, 7703,21700,DeJackome,Jeanette, 7704,21700,Riall,Alphonsine, 7705,10200,Holt,Susan, 7706,P9300,Quabin,Mark, 7707,21700,Adams,Bill, 8001,10100,Schumacher,Simon,415/376-3848 8052,20100,Avery,Joan,604/376-3847 8101,10500,Penrose,Steven,415/284-7229 Figure 17.2 A portion of the comma-delimited file Starting with version 4.3, SQR offers a set of command flags (-EH_CSV, -EH_ CSV:file_name, EH-CSVONLY) that make SQR generate CSV output without making any changes to the program. These flags, however, still do not convert your report into a CSV file automatically. If you run an SQR program that was designed to generate a regular report with headers, footers, totals and so on, and use one of these flags, SQR will generate CSV output and will place all these headers, footers, and other unnecessary information into the output using every Print command output as a separate commaseparated field. Therefore, generating a program report and CSV output as two separate files is still a good idea. However, if your program generates only field-oriented output, the -EH_CSV* flags will save your time plus you will have an option to place an Excel icon pointing to your CSV file on your HTML output (please see chapter 19 and appendix D for details). 268 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! 17.3.3 Using the Print command to create a flat file Using the Write command to create an output file is the traditional way of creating a flat file. Many programming languages use very similar approaches. SQR allows you to employ another technique of writing data to an output file: using the Print command with which you have already become familiar. Keep in mind, however, that this method is not as flexible as using the Write command; it has a number of limitations which we will discuss later in this chapter. To use the Print command to output data to a file: • specify Max-Lines = l in the Declare-Layout command • define the Width in the Declare-Layout command equal to record size (the size of your output record) • specify Formfeed=No in the Declare-Layout command to make sure that SQR will not output form-feed characters • use explicit length in your Print command, thus placing each field at a fixed location in the record • use the New-Page command after each record is processed. The following example demonstrates the second technique of creating an output file with fixed length records: !TEST17B.SQR ! Using the Print and New-Page commands to generate and output file !******************** Begin-Setup !******************** Declare-Layout Default Formfeed=No ! Prevent form feeds between records. Max-Lines = 1 ! Output will be 1 line deep by 80 columns wide. Max-Columns=80 Left-margin=0 ! Make sure we start from the top Top-margin=0 End-Declare End-Setup !******************** Begin-Program !******************** Do Process_Employees Display 'Total records exported: ' Noline Display #Tot_Recs 999,999,999 End-Program TECHNIQU ES F OR FL AT FIL E OU TPU T TEAM LinG - Live, Informative, Non-cost and Genuine! 269 !********************************************** Begin-Procedure Process_Employees !********************************************* Move 0 To #Tot_Recs Begin-Select A.Emplid (1,1,11) A.Deptid (0,0,10) B.Name (0,0,30) B.Phone (0,0,10 ) Use the New-Page comNew-Page mand after each record. Add 1 to #Tot_Recs From Job A, Personal_Data B Where A.Emplid=B.Emplid And A.Empl_Rcd=0 And A.Empl_Status = 'A' And A.Effdt = (Select Max(Effdt) From Job Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt <= Sysdate) And A.Effseq = (Select Max(Effseq) From Job Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt = A.Effdt) End-Select End-Procedure As you can see from Test17B.sqr, our program becomes much simpler. We do not have to deal with any input/output commands. We just select the required data from the tables, and print them into a file. Note that your file will be created as an LIS file unless you use the -F SQR command line flag to specify another name for your program report file. This technique is very useful when you need to create a fixed-length character output, but will not work for variable or binary output. Also, if you need to print an audit report in addition to your output file, it may be a better idea to use regular input/output commands in order to distinguish between your report output and the file output. It is possible, however, to create more than one LIS file output by using techniques described in chapter 13. 17.4 Using flat files to import data into your database Importing data Another frequent task involves uploading data from a flat file into a database. You can develop a simple program that reads the file and inserts the information into your 270 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! database. Some business needs require the designing of more complex programs that not only read and insert, but also verify the information read from the file, and populate multiple tables at once. Your SQR program can do many things, including the following: • read records from the input file • edit input fields using SQR functions, table lookups, and queries • build logic to insert data into different tables based on the specified criteria • check for duplicates • print an audit report. The next program demonstrates the standard technique of loading data into a database table. You can easily modify it, depending on your business needs. PeopleSoft programmers may find this example useful when working on data conversion or interface projects. !TEST17C.SQR ! Using flat files to populate the database !************************ Begin-Program !************************ Open 'c:\temp\job.dat' As 1 For-Reading Record=3000 Status=#FileStat If #FileStat !=0 Show 'Error Opening Input File' Else Do Read_Input_File End-If End-Program !*********************** Begin-Setup !*********************** Begin-SQL On-Error=Stop Create Table Temp_Job_Tbl ( Emplid Varchar2 (11) Not Null, Empl_Rcd Number Not Null, Effdt Date Not Null, Effseq Number Not Null, Deptid Varchar2 (10) Not Null, Jobcode Varchar2 (6) Not Null, Empl_Status Varchar2 (1) Not Null, Annual_Rt Number (18,3) Not Null, Monthly_Rt Number (18,3) Not Null, Hourly_Rt Number (18,6) Not Null ) IMPORTING DA TA TEAM LinG - Live, Informative, Non-cost and Genuine! 271 End-Sql End-Setup !*************************************** Begin-Procedure Read_Input_File !*************************************** Display 'Inserting records from file c:\temp\job.dat intoTemp_Job_Tbl ...' Move 0 to #Inserts Move 0 to #Tot-Recs While Not #end-file Read 1 Into $Input:1000 If #end-file Break End-If Unstring $Input By $Sepchar Into $Emplid $Empl_Rcd $Effdt $Effseq $Deptid $Jobcode $Empl_Status $Annual_Rt $Monthly_Rt $Hourly_Rt Do Insert_Temp_Job End-While Close 1 Display 'Total records inserted: ' Noline Display #Tot_Recs 999,999,999 End-Procedure !***************************************** Begin-Procedure Insert_Temp_Job !***************************************** Begin-Sql On-Error=Insert_Error Insert Into Temp_Job_Tbl ( Emplid, Empl_Rcd, Effdt, Effseq, Deptid, Jobcode, Empl_Status, Annual_Rt, Monthly_Rt, 272 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! Hourly_Rt) Values ( $Emplid, $Empl_Rcd, $Effdt, $Effseq, $Deptid, $Jobcode, $Empl_Status, $Annual_Rt, $Monthly_Rt, $Hourly_Rt ) End-Sql If #sql-count=1 Add 1 to #Inserts If #Inserts >= 500 Commit Move 0 to #Inserts End-If Add 1 to #Tot_Recs End-If End-Procedure !************************************ Begin-Procedure Insert_Error !************************************ If #sql-status = -9 ! if duplicate (this code is for ORACLE only) Show 'Insert Error: Duplicate row is not allowed for emplid=' $Emplid Else Show 'Insert Error: ' $sql-error Show 'Error number: ' #sql-status Stop ! Halt Program and Rollback End-If End-Procedure 17.5 Using operating system commands to sort files Sorting files with system commands Often a need arises to sort the same data in two different ways within your program. Sometimes, internal arrays are used for this purpose, but you can also use a flat file when the required array exceeds the available memory. Also, with external files, you can use operating system commands or utilities to perform the sorting task rather than writing the sorting logic yourself. S ORTI N G F I LE S WI T H S Y S T EM CO M M A N DS TEAM LinG - Live, Informative, Non-cost and Genuine! 273 For example, suppose you need to produce a list of all employees in your organization sorted by their names. At the same time, you need to have another report sorted by ZIP codes. One way to accomplish this is to use two Select paragraphs with different Order By clauses, but, in this case, you would have to select your data twice. If we want to avoid double-reading the database, the task can be approached with the following code: !TEST17D.SQR ! Using the UNIX Sort command instead of selecting from the database ! twice. This Program Demonstrates the use of files for Sort purposes ! The UNIX Sort is used and is being invoked from SQR program !******************* Begin-Program !******************* Do Main End-Program !************************** Begin-Procedure Main !************************* Open 'file1.dat' as 1 For-Writing Record=80:Vary Begin-Select Emplid (+1,1) Name (,+1) State (,+1) Postal (,+1) Write 1 From &Emplid ':' &Name:25 ':' &State:2 ':' &Postal From Personal_Data Order By Name End-Select ! Close the file and call the UNIX Sort command Close 1 ! Sort the file starting at field # 4 using a colon as separator Call System Using 'sort -t: +3 file1.dat > file2.dat' #Status ! Open the newly created file and read it in. Open 'file2.dat' As 2 For-Reading Record=80:Vary New-Page Print ' Employee List by Zip Code ' (1,1) Print ' Zip Code Employee Id Employee Name (+1, 1) 274 CHAPTER 17 State ' WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! While Not #end-file Read 2 Into $String:80 If #end-file Break End-If Unstring $String By ':' Into $Emplid Print $Emplid (+1, 1, 8) Print $Name (, +1, 25) Print $State (, +1, 5) Print $Zip (, 1, 8) $Name $State $Zip End-While Close 2 End-Procedure In Test17D.sqr, we select data from the Personal_Data table, print the selected information into a report, and, at the same time, use the Write command to output each selected record into a flat file named file1.dat. When the selection is over, we close the file, and then call the UNIX Sort utility to sort our file1.dat. The result of the sort is placed into file2.dat. Since ZIP code is the third field in the record, the Sort utility sorts the file by ZIP codes. After the file is sorted, we simply read the file, record by record, and print the report in the sorted order. With this approach, our program creates two different reports with just one pass on data in the database. KEY POINTS 1 SQR provides the following Input/Output commands: Open, Read, Write, and Close. 2 Before accessing a file, it must be opened. If you are reading a file, it should be opened For-Reading. If you are writing to a file, it should be opened For-Writing. 3 The open For-Append parameter is used when you need to append the records to an existing file. If a file does not exist, it will be created. 4 The record type may be defined as Fixed, Vary, or Fixed_Nolf (fixed with no line terminator). The Fixed_Nolf type is often used for accessing binary data. S ORTI N G F I LE S WI T H S Y S T EM CO M M A N DS TEAM LinG - Live, Informative, Non-cost and Genuine! 275 KEY POINTS (CONTINUED) 5 When reading from or writing to a file, you refer to the file by the file number rather than by the file name. File names are specified only in the Open command. 6 SQR internal variable #end-file is set to 1 when there are no more records to read. 7 When reading text data, the trailing blanks are omitted. 8 In order to read binary data, the file must be opened as Fixed or Fixed_ Nolf. 9 You cannot read or write NULL values from or to an ASCII file. 10 The Print and New-Page commands can be used to create flat files with fixed length records. 11 You can use the operating system commands to sort files. 12 Files are closed either by the Close command, or upon the successful termination of your program. 276 CHAPTER 17 WOR KING WITH F LAT FIL E S TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 1 8 Interacting with operating systems and other applications IN THIS CHAPTER • Submitting SQR programs from the operating system command line • Creating and executing pre-compiled programs • Running SQR programs in batch mode • Issuing operating system commands from SQR programs • Invoking non-SQR programs from SQR • Invoking SQR programs from other SQR programs • Invoking SQR programs from non-SQR programs • Calling user functions from SQR programs • Generating Word documents from SQR 277 TEAM LinG - Live, Informative, Non-cost and Genuine! An SQR program can be invoked in a number of different ways. You already know how to start your program from the SQR Dialog Box in the Windows environment. You can also execute SQR programs from the operating system command line or call them from other programs. As an alternative, SQR programs can be run in batch mode under VAX/VMS, MVS, UNIX, MS-DOS, Windows NT, Windows 95 and OS/2 using DCL (VAX/VMS), JCL (MVS), shell scripts (UNIX), or batch files (MS-DOS, WINDOWS, OS/2). 18.1 Executing an SQR program from the command line Executing from the command line When executing an SQR program from the operating system command line, you need to enter • the name of the SQR engine (SQRW in Windows, SQR in UNIX, etc.) • your program name • the database connectivity string • SQR command line flags • application-specific arguments. Since .sqr is the default extension for all SQR source programs, you don’t have to specify it, unless your program has another extension. The database connectivity string contains important information needed to connect your program to the database, including user Id and password. The exact format of this string is database-specific. 18.1.1 SQR command line flags SQR allows you to change its default behavior by using the command line flags. Some of the flags have arguments. Please do not confuse the SQR command line flag arguments with command line arguments. SQR command line flag arguments are simply parameters related to each specific flag, for example, flag -ZIF (alternative INI file directory) requires the directory name as its argument. Command line arguments are application-specific input parameters that are retrieved by your program from the command line using the Input or Ask command. Examples include state code, as-of-run date, and so on. Command line flags always start with a dash (-). If a flag has arguments, the arguments must follow the flag with no spaces between the flag and its arguments. All SQR 278 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! command line flags are described in appendix B. In this chapter, we will discuss some of the most frequently used flags. Some command line flags are used to specify a directory, a file name or a path. In the following example, the -F flag is used to override the default SQR output file name and directory. By default, SQR names the output report file the same as your program name with an .lis extension and places this file into the current directory. The following examples show the usage of the -F flag in Windows and UNIX: 1 2 3 4 5 6 7 8 9 sqrw test01.sqr sqrbook/passwd -Fc:\temp\ sqrw test01.sqr sqrbook/passwd -Fc:\temp\Mysqr.lis sqrw test01.sqr sqrbook/passwd -Fc:\temp !Windows !Windows !Windows !(Incorrect Path) sqrw test01.sqr sqrbook/passwd -fc:\temptst !Windows sqr test01.sqr sqrbook/passwd -F/home/output/ !UNIX sqr test01.sqr sqrbook/passwd -F/home/output/output.lis !UNIX sqrw test02.sqr sqrbook/passwd -freport1.lis -freport2.lis -freport3.lis !Windows sqrw test02.sqr sqrbook/passwd -Keep -fc:\temp\ !Windows sqrw test03.sqr sqrbook/passwd -Keep -freport1.lis -freport2.lis !Windows In line 1 of our example, when the test01.sqr program is executed, the test01.lis output file will be created in the c:\temp directory. In line 2 when the test01.sqr program is executed, the output report will go to the c:\temp\Mysqr.lis file. In line 3 (during the program execution), when the output file is about to open, SQR will display the following message: “Cannot open the report output file c:\temp. Permission denied. Program aborted.” What happened? Since the backslash character at the end of the path was missing, SQR decided that temp is the output file name and tried to open the temp directory as an output file for writing, thus triggering the “Permission denied” error. In line 4, the program output was directed to the c:\temptst file. Note that, in this case, the file is created with no .lis extension because the file name was explicitly specified with no extension. In line 5, the test01.lis file is created in the UNIX directory /home/output. In line 6 we wanted to redirect the output file to the output.lis file in the /home/output directory. In line 7, a multiple report program, test02.sqr creates three output report files in the current directory. The first one goes to the report1.lis file; the second file is created as report2.lis; and the third file name is report3.lis. All three files are created in the current directory. In line 8, the same multiple report program is executed with the command line flag -KEEP. As we discussed in chapter 14, this flag tells SQR to create an SPF file in addition to EXECU TING FR OM THE COMMAND LINE TEAM LinG - Live, Informative, Non-cost and Genuine! 279 the regular LIS file. Since the program generates three output report files, the following six files will be created in the c:\temp directory: test02.lis, test02.l01, test02.l02, and test02.spf, test02.s01, test02.s02. Please note that, from SQR version 4 on, you may see different report file names. The report file names and extensions are now controlled by the OUTPUT-FILE-MODE parameter in the SQR.ini file. If the value of the OUTPUT-FILE-MODE parameter is set to SHORT, the output file names would be: test02.lis, test02.l01, test02.l02, and test02.spf, test02.s01, test02.s02. If the value of the OUTPUT-FILE-MODE parameter is set to LONG, the output file names would be: test02.lis, test02_01.lis, test02_02.lis, and test02.spf, test02_01.spf, test02_02.spf. In line 9, the following four files will be created in the current directory: report1.lis, report2.lis, report1.spf, report2.s01. Please note, that in this example, the -F flag arguments specify their respective file names with the .lis extensions even though the program will generate both the regular and the SPF output. Also, note some inconsistency in the file naming scheme for LIS files and SPF files. Like the -F flag, the -I flag specifies a list of directories where SQR will search for files specified in the #Include statements. If the #Include statement in your SQR program does not specify the search path, SQR will first look for the file in the current directory. If the files are not found in the current directory, SQR will search in the directory specified in the -I flag. You can specify multiple directory paths. The directory names must be separated by semicolon or comma. For example: -Ic:\sqr\user\include\;c:\sqr\include\ -I/$HOME/sqr/inc/,/$HOME/mysqr/inc/ !Windows !UNIX When you specify the directory for both -F and -I flags, you must use the right sub-directory separation character at the end. For Windows, it is a backslash, for UNIX, it is a forward slash. In order to override the directory and/or the name of the SQR.ini file, use the -ZIF flag with the file name following the flag. The default name is SQR.ini. This file contains the default settings and parameters used by SQR. The -O flag is used to override the default program log file name. If this flag is not used, the program log will be placed in the current directory. The default log file name is your program name with the .log extension. Please note that this flag can be used only in Windows. Use the standard file redirection method (“>”) in UNIX. In chapter 20, we will discuss the flags that may be used in testing and debugging, including: -Debug, -S, -T, -E, -C. 280 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 18.1.2 SQR command line arguments SQR command line arguments are the values that your SQR program expects to receive from the command line via the Ask or Input commands. Since all Ask commands in your SQR program are processed first, all arguments for the Ask commands should be listed before arguments for the Input command. All the arguments must be listed in the order they are prompted for in your SQR program. In the following example, the program reads two arguments from the command line: File_Name and As_Of_Date. The first argument is retrieved by the Ask command, the second one is read by the Input command: !TEST18A.SQR !Retrieving SQR command line arguments … Begin-Setup Ask File_Name 'Please Enter Input File Name' End-Setup Begin-Program Input $As_of_Date type=date … End-Program … The command line for this example will look like SQRW Test18a.sqr test/pswrd -Fc:\temp\ 'My_File' '09/01/1998' When retrieving arguments from the command line, SQR uses the following logic: • attempts to read the arguments from the command line • if no arguments are found in the command line, SQR looks for an argument file • if no argument file is found, SQR will prompt the user for input. Note that when the Input command uses the Batch-Mode parameter, SQR will not prompt the user for input. Specifying the command line arguments eliminates the need to prompt an operator. This is especially useful when you execute your SQR program in the batch mode or schedule your process to run automatically at night. 18.1.3 Using the argument files You can use a text file to pass arguments to your program in the command line. Each argument should be entered in the argument file, one argument per line. An argument file may include an unlimited number of arguments. SQR will process the arguments one by one. The argument file name must be specified on the command line with the @ EXECU TING FR OM THE COMMAND LINE TEAM LinG - Live, Informative, Non-cost and Genuine! 281 character preceding the file name. For example, if test18A.sqr program is invoked from the command line using an argument file to pass two parameters to this program, the command line may look like sqr test18A.sqr test/pswrd @arg1.dat where arg1.dat is an argument file containing !SQR argument file my_file 09/01/1998 In addition to program input parameters, SQR allows you to put your program name and the database connectivity string into the first two lines of an argument file. This comes in handy when you wish to execute your program from a command file or a batch script, and do not want to expose your database access password. The following example shows how to execute your program from the command line while keeping the program name, user name, and password in your argument file: sqr @arg2.dat where arg2.dat is an argument file containing !Placing both the user Id and password in an argument file test18a.sqr test/pswrd my_file 09/01/1998 You can optionally specify a question mark in your command line to prompt user for some of the command line arguments. For example, in the command sqr test18a.sqr ? @arg1.dat the program prompts the user for the database/password instead of reading this information from an argument file. SQR argument file can also be generated as an output from another SQR program, allowing two different programs to communicate via an argument file. One last note: when putting parameters in the argument file, do not include them in quotes. 18.2 Executing a precompiled SQR program Executing a pre-compiled program In chapter 5, we explained that SQR goes over the source program in two steps. The first step is compiling the program, and the second step is actual program execution. So far, we have 282 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! been compiling and executing our SQR programs in one step, but SQR allows you to separate these two steps. In order to compile an SQR program without executing, use the -RS SQR command line flag which allows you to create a pre-compiled version of your program. Precompiled programs are usually assigned the extension .sqt and are sometimes called SQT programs. After an SQT program is created, you can execute it at any time with the help of either the -RT SQR command line flag, or via the SQR Execute (SQRT for UNIX or SQRWT for Windows) program. The SQR Execute program performs only program execution. It allows you to specify most command line flags similar to the flags you specify when executing your program in two stages. Please refer to appendix B for a complete list of the SQR command line flags. The following examples show you how to pre-compile and then execute an SQR program: !Creating a pre-compiled SQR program and !executing this program in a separate step 1 sqrw test01.sqr sqrbook/passwd -RS 2 sqrw test01.sqt sqrbook/passwd -RT -Fc:\temp\ 3 sqrt test01.sqt sqrbook/passwd -F/tmp/ !compile test01.sqr !execute test01.sqt !execute test01.sqt !via sqrt program In line 1 of our example, we compile the test01.sqr program and save the compiled version of the program in the test01.sqt file. Line 2 shows how to execute a pre-compiled program using the SQRW and the -RT command line flag. Line 3 demonstrates how to execute a pre-compiled program using the SQR Execute or SQRT program. Compiling an SQR program once, and then executing the already-compiled file could improve the performance of your SQR program to some degree. However, there are some limitations that have to be taken into consideration when running a program in two separate steps. When your program is compiled and executed at the same time, it is not so important to know what is done at which stage, but as soon as you separate the two steps, you must be aware of what happens during each stage. A clear understanding of what SQR actually does during each of these two steps is very important. For example, all compiler directives (#Define, #If, #Include, etc.) as well as the commands in the Setup section (Ask, Declare-Layout, etc.) are processed at compile time. This means that when a pre-compiled program is executed, all Ask commands will be ignored. You will have to use the Input command, instead of the Ask command, to prompt for user input at run time. EXECU TING A PR E- COMPILED PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! 283 18.3 Executing your SQR programs in batch mode Executing in batch mode SQR programs can be executed in batch mode under MS-DOS, Windows, UNIX, VAX/VMS, or MVS. For example, an SQR command line can be included into a batch file or a UNIX shell script. PeopleSoft users are, probably, familiar with a special script named PRCS_SQR. This script was developed by PeopleSoft to run PeopleSoft SQR programs in the UNIX environment. It takes all user parameters from PeopleSoft on-line panels with the help of the PeopleSoft Process Scheduler, and dynamically builds the regular SQR command line described above. 18.4 Issuing operating system commands from an SQR program Issuing system commands from a program You can issue operating system commands within an SQR program. This feature gives you a great deal of flexibility, allowing you, for example, to use the same operating system commands that work with files and directories. This is as simple as issuing the same commands from the operating system command line. For example, if your program runs under UNIX and you would like to make a copy of a file, you may use the following code in your program: !Executing a UNIX command from an SQR program Let $Command_String='cp /usr/tmp/file1.dat /usr/tmp/file2.dat' Call System Using $Command_String #Status If #Status<>0 Show 'Error executing the command in Unix: '$command End-If As you can see, all you need to do is to move the command to a string variable using exactly the same syntax as you would use in the UNIX system command line. In fact, it’s always a good idea to test this command separately from a command line before you put the code into your program. After the string is built, use the Call System SQR command to execute the command in the $Command_String variable. The operating system returns the status of the command execution in the #Status numeric variable. The value returned in the status variable is system-dependent. (Please see the complete description of the Call System command and return statuses in appendix D.) You do not have to move the operating system commands to a string, you can hardcode them right in the Call System command. In the next example, the UNIX cp command is explicitly coded in the Call System command. Call System Using 'cp /usr/tmp/file1.dat /usr/tmp/file2.dat' #status 284 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! In order to execute a similar command in the Windows environment, the following command may be used: !Executing Windows commands from an SQR program Let $COMSPEC=getenv('COMSPEC') Let $Command=$COMSPEC|| ' /C copy c:\temp\testdate.lis c:\myoutput\testdate.lis' Call System using $Command #Status If #Status<>0 Show 'Error executing the command in Windows: '$Command End-If The following example shows how to print a report output file from an SQR program. The program checks for the platform and, depending on the result, executes the corresponding operating system command. !Printing a file from SQR on different platforms ********************************************************************* Begin-Procedure Print-file !******************************************************************** Show '$sqr-platform=' $sqr-platform Print $sqr-platform (+1,1) Move $sqr-report to $Save_Report ! to Close the current report output file New-Report 'new.lis' Let $Eval_Platform = substr($sqr-platform,1,7) Show $Eval_Platform Evaluate $Eval_Platform When = 'DOS' When = 'VMS' Let $command='print'||$Save_Report Break When='UNIX' Let $Command='lp '||$Save_Report Break When = 'WINDOWS' Let $Command=getenv('COMSPEC')|| '/C print '||$Save_Report Break When-Other Show 'Do not know how to print for ' $Eval_Platform End-Evaluate Call System Using $Command #Status End-Procedure The Call System command can also help you to create new directories and files, check for the existence of a specified file; delete, rename, and move directories and files; or perform other tasks. If your program runs under UNIX, you can take advantage of the rich variety of the UNIX operating system commands and utilities, such as sort, IS SU ING S Y STEM CO MMA NDS F ROM A PROG RAM TEAM LinG - Live, Informative, Non-cost and Genuine! 285 grep, and others. Be careful, however, if your program is written to run on multiple platforms. Before issuing a specific operating system command, it is always a good idea to check if the command matches the current platform. The Call System command can be invoked in either synchronous mode or asynchronous mode. If you would like to run an operating system command and execute the next SQR command in your program without waiting for the completion of the operating system command, specify the Nowait parameter in the Call System command. If SQR commands that follow the Call System command depend on the results of the Call System command execution, use the Wait parameter. Not every operating system will allow you this kind of flexibility: only Windows 95/98/2000, Windows NT, or VMS support both the synchronous mode and asynchronous mode of the Call System command. In UNIX, you can use only the Wait parameter. In Windows 3.1, you can only use Nowait. The default behavior of the Call System command also depends on the operating system used. In Windows 95/98/2000 and Windows NT, the default is Nowait, whereas in VMS, the default is Wait. Please keep in mind that SQR offers a few file manipulation functions: exists(), rename(), and delete(). If these SQR functions are sufficient for your task, use them instead of the operating system’s specific commands to ensure platform independence for your program. For instance, if you need to change a file name from file1.dat to file2.dat, there are two different ways to do this. The following code includes two SQR procedures: the first one renames a file using the UNIX mv command; the second one performs the same task with the help of the SQR rename() function: !Two methods of renaming a file using a UNIX command or an !SQR function !************************************** Begin-Procedure Unix_Rename !************************************** Let $Command_String='mv /usr/tmp/file1.dat /usr/tmp/file2.dat' Call System Using $Command_String #Status If #Status<>0 Show 'Error executing the command in Unix: '$Command_String End-If Using the Call End-Procedure System com… mand to rename file File1.dat !*************************************** to File2.dat Begin-Procedure SQR_Rename !*************************************** Let $File3='/usr/tmp/file3.dat' Let $File4='/usr/tmp/file4.dat' Using an SQR function to Let #Ret_Cd = exists($File4 ) re-name File3.dat to If #Ret_Cd = 0 File4.dat Let #Ret_Cd = delete($File4) 286 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! End-If Let #Ret_Cd = rename($File3,$File4) If #Ret_Cd <> 0 Show 'Error Renaming ' $File3 ' to ' $File4 ' Ret_cd=' #Ret_Cd End-If End-Procedure In the Unix_Rename procedure from the preceding example, the UNIX mv command is used to rename file1.dat to file2.dat. In the SQR-Rename procedure, we take advantage of the platform-independent function rename() to carry out the same task. Before calling this function, the program checks if the output file already exists, and if so, deletes it. We use two more SQR functions to do this: exists() and delete(). Using the SQR functions alone does not warrant platform independence. Perhaps you’ve already spotted a problem in this procedure that will prevent its successful run on Windows. The file names are coded with forward slashes making the procedure only good for UNIX. The problem can be easily fixed by generating fully-qualified file names depending on the current platform. Another way of solving this problem is to accept file names from user input. In chapter 17 we used the UNIX sort command in one of the chapter’s examples. You can use this command to sort file1.dat and place the result into file2.dat by using the following command: Call System Using 'sort file1.dat > file2.dat' #Status Another popular UNIX command, grep, can help you extract records from a file based on the record’s content. In the following example, this command is used to extract all records that contain a specific Paygroup code in any record position. !Using the UNIX grep command to extract records that !contain the specified text !******************************************************************** Begin-Procedure Extract_Paygroup($FileIn,$FileOut,$Paygroup) !******************************************************************** Let $Redirect=' >' Let $Command_Line= 'grep '||$Paygroup || ' '||$FileIn|| $Redirect||$FileOut Show '$Command_Line=' $Command_Line Call system using $Command_Line #status If #status <> 0 Show 'Unsuccessful Call to execute Unix Extract, #status=' #status End-If End-Procedure … IS SU ING S Y STEM CO MMA NDS F ROM A PROG RAM TEAM LinG - Live, Informative, Non-cost and Genuine! 287 The above example shows a local procedure that accepts the following input parameters: input file name, output file name, and Paygroup code. The value in the $Paygroup variable defines the selection criterion. The procedure does not specify the position of the Paygroup field in the record. The grep command will search over the entire record, and, if it finds a string that matches the value in $Paygroup, the record will be selected. Isn’t this amazing? We select records from a file without even opening the file! Try it on your system, and you will also notice that the processing time is unbelievable: this procedure can crunch a fairly large file in less than a second. The above procedure is capable of replacing an entire SQR program: leaving no need to open files, read and write records, use the string comparisons, and so on. You can also save on writing the error handling code. Hopefully, you are convinced by now that using the Call System command can be a good and efficient alternative to writing your own subroutines . 18.5 Calling external programs from SQR Calling external programs from SQR As you just learned, the Call System command is used to execute the operating system commands. SQR, however, sees no difference between an operating system command and any executable program that can be invoked from the command line. You can use the same command to execute any application program, compiled as executable file, to be run from the command line. You can also execute a batch file or a shell script from an SQR program. One important thing to consider is how the invoked external program gets connected to the database (in most cases, it is necessary). 18.5.1 Calling a Pro-Cobol program from an SQR program under UNIX The following example shows how to execute a program written and compiled in ProCobol: !Using the Call System command to execute a !program written in Pro-Cobol Call System Using '/home/user/cbl/PRG01' #Status If #Status <> 0 Show 'Unsuccessful Call to execute PRG01, #status=' #Status Let $Error_Msg = 'Unable to execute COBOL PRG01' Let #Ret_Cd=#Status End-If … In this example, the invoked program does not need to connect to a database, nor does it accept input parameters. If your program uses input parameters and works with 288 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! a database, the program call will have to include the database name, user Id, and password as well as the program input parameters. The following example shows one way of doing this: !Passing input parameters and database connectivity information !in a program written in Pro-Cobol Let $Connect_String=' user01/mypasswd@hrdev' Let $My_Command = '/home/user/cbl/prg002 '||$Company || ',' ||$Paygroup || ',' ||$Pay_End_Dt || ',' ||$Connect_String Call System Using $My_Command #Status Show 'Return from Call ' #Status … One thing that could be improved in the above example, is to get rid of hardcoded database connectivity information. The calling SQR program has already received these parameters so if the called program works with the same database, it can use the same parameters. There are several methods you may choose to pass the connectivity string from the main program to a subroutine. The method chosen depends on the method of the main SQR program invocation. If you call your SQR program from the command line, you can place the connectivity information in an argument file, and then read this file in your SQR program, get the database connectivity string, and pass it to the subroutine. If the main program is executed from a batch file or shell script, the connectivity string can be saved in a temporary environment variable for use in your program. The following example demonstrates this technique for an SQR program invoked from the PeopleSoft Process Scheduler. The Process Scheduler uses the prcs_sqr UNIX script to parse input parameters including the connectivity string, and then dynamically builds a command line to submit the SQR program. We made a few simple modifications to this script by saving the database connectivity string in an environment variable MYENV. #Saving the database connectivity string in an environmental variable # Parse and interpret parameters in the command line. parse_command_line() { CMDF_SYNTAX="Syntax: <process> <signon> <+p>|<+P>| <sqr option>[ <+p>|<+P>|<sqr option>]..." if [ $# -lt 3 ] then error_exit 1 "Too few arguments in command file" $CMDF_SYNTAX fi # Get the report parameter and go to the next argument. rpt='downshift_string $1' CALL ING EXTER N AL PR OGR AMS FR OM S QR TEAM LinG - Live, Informative, Non-cost and Genuine! 289 Suppose the connectivity string is saved in the MYENV variable. Now we can retrieve the value of the MYENV variable in our SQR program with the help of the getenv() SQR function. The connectivity string obtained from this variable is passed to the calling program: !Passing the connectivity string from main program to a subroutine Let $Connect_String = getenv('MYENV') The getenv() SQR function retrieves saved connectivity string. 18.5.2 Calling an SQR program from another SQR program under UNIX Just as we did when calling Pro-COBOL programs from SQR programs, we can use the connectivity information derived from either an argument file or a batch file. If we use the same technique to save and use an environment variable, calling an SQR program from another SQR program in UNIX environment may look like the following: Let $Out_Dir = '/usz/tmp/' Let $Connect_String = getenv('MYENV') Let $My_Command = '/opt/sqr/ora/workbench/bin/sqr ' || 'PS_HOME/sqr/mysqr.sqr ' || $Connect_String || ' -m $PS_HOME/sqr/allmaxes.max' || ' -F' || $Out_Dir || 'mysqr.lis' Call System Using $My_Command #Status Let $Print_Command = 'lp -d mp_mis1 ' || $Out_Dir || 'mysqr.lis' Call System Using $Print_Command #Status This example looks somewhat like the previous one. Please note, however, the difference: the command line contains an invocation of the SQR itself rather than the program name. This command line looks exactly like the one you would use to execute your program from the command line. The second Call System command is used to print the program output report. 18.5.3 Calling a PeopleSoft Cobol program from SQR This subchapter is intended for PeopleSoft developers only. It shows some tricks that may be used to run PeopleSoft Cobol programs from SQR. Why would someone ever want to call a PeopleSoft Cobol program from an SQR program? In some cases, it may be necessary. Let’s take for example the Leave Accrual process. This process must be run separately for every plan type. To speed up processing, users may want to specify all plan types at once, and submit all the processes together. In this and similar cases, calling PeopleSoft Cobol programs from SQR may prove useful. 290 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! There are several things that we have to take into consideration. First, the PeopleSoft Cobol programs are usually called from a batch file or shell script which, in turn, is executed from the Process Scheduler. Second, these programs usually work with databases, and, therefore, require the database connectivity string. The following example shows how an SQR program obtains all necessary parameters from PeopleSoft, saves these parameters in a temporary parameter file, builds a UNIX command line, and then uses this command line in the Call System command to invoke a PeopleSoft Cobol program: !TEST18B.SQR !Calling a PeopleSoft Cobol program from SQR !************************************* Begin-Procedure Process-Main !************************************* … Do Build-Command-Line … !The actual logic of a Select loop is omitted Do Call-PSPACCRL !This procedure is called in a Select loop … Do Delete-Param-File End-Procedure Process-Main !************************************ Begin-Procedure Build-Command-Line Preparing command !************************************ line parameters !Process Instance Move #prcs_process_instance to $Cur_Prcs_Inst 999999 Let $Cur_Prcs_Inst= Rtrim (Ltrim($Cur_Prcs_Inst,' '),' ') !Process Run Control Let $prcs_run_cntl_id=Rtrim(Ltrim($prcs_run_cntl_id,' '),' ') !Operator ID Let $prcs_oprid= Rtrim(Ltrim($prcs_oprid,' '),' ') !Database name Do Get-DBNAME !Operator password Let $PSWD = getenv('OPRPSW') Creating an argument file Do Create-Param-File !Command line Let $subroutine = 'PSRUN PSPACCRL <leave.par' End-Procedure Building a command line to !************************************ invoke the PSPACCRL proBegin-Procedure Get-DBNAME gram and to pass the pro!************************************ gram input parameters in the leave.par argument file Begin-Select dbname Let $DB_Name = &dbname from psprcsrqst where prcsinstance=$Cur_Prcs_Inst CALL ING EXTER N AL PR OGR AMS FR OM S QR TEAM LinG - Live, Informative, Non-cost and Genuine! 291 End-Select End-Procedure !****************************************** Begin-Procedure Create-Param-File !****************************************** Let $subroutine='cd $PS_HOME/bin' Using the Call System command to change the Call System using $subroutine #status current directory If #status <> 0 Let $Error_Msg = 'Unable to change Unix directory to ' ||$subroutine Do Error End-If Let $OpenError='N' Let #tot-recs-out = 0 Let $Param_File='leave.par' open $Param_File as 1 for-writing record=100:vary status=#filestat1 Move #filestat1 to $FileStat 9999 If #filestat1 <> 0 Let $Error_Msg = 'Error Opening File: '|| $Param_File||': ' || $filestat Do Error End-If If $OpenError='N' Write 1 from $sqr-database Write 1 from $DB_Name Creating an argument file containWrite 1 from $prcs_oprid ing the database name, operator Write 1 from $PSWD ID, password, the run control ID, Write 1 from $prcs_run_cntl_id and the process instance ID Write 1 from $Cur_Prcs_Inst Close 1 End-If End-Procedure !*************************************** Begin-Procedure Call-PSPACCRL !*************************************** Let #status = 0 Call system using $subroutine #status show 'Return from Call ' #status If #status <> 0 Let $Error_Msg = 'ERROR in COBOL Prgm PSPACCRL' Show $Error_Msg Else Print 'Records were successfully processed by PSPACCRL' (+1,1) End-if End-Procedure !****************************************** Begin-Procedure Delete-Param-File !****************************************** Using the Call System Let #status = 0 command to delete the Let $del = 'rm leave.par' argument file Call System Using $del #status 292 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! If #status <> 0 Show 'ERROR Deleting leave.par file '! not a critical error, ! do not stop execution. End-if End-Procedure … The main idea behind this example is to dynamically build a command line that is similar to the command line used by the PeopleSoft Process Scheduler. The Process Scheduler submits Cobol programs via its PSRUN utility. In our code we dynamically build the command line PSRUN PSPACCRL <leave.par, where PSPACCRL is the name of the Leave Accrual Cobol process, and leave.par is a parameter file built to run this process. The logic in the example may seem straightforward except for the method of passing the database password to the Cobol program. The program reads the password from the environmental variable OPRPSW using the SQR getenv() function. The trick is to set this environmental variable prior to the SQR program execution. In order to do this, the PeopleSoft-delivered PRCS_SQR UNIX shell script was changed to retrieve the password from the command line and save it in OPRPSW. (The PRCS_SQR script is used by the Process Scheduler to run SQR programs on UNIX.) When the process finishes, the OPRPSW variable will be deleted. As you can see from the example, the program dynamically builds a parameter file named leave.par to pass all the input parameters to the COBOL program. At the end of the process, this parameter file is deleted due to security reasons. 18.6 Calling SQR from other programs Calling SQR from other programs In order to call SQR programs from non-SQR applications, you can use the SQR Application Program Interface: SQR API. 18.6.1 Using SQR API In Windows environment, you can use SQR API via the Dynamic Link Library (DLL) calls. Any program in Windows environment that is capable of using DLL calls can invoke SQR with the help of SQR API. If an SQR program is called from a C or C++ program, a header file sqrapi.h has to be included in the program. In UNIX, a static link library SQR.a or SQR.lib is provided. Programs written in C or C++ must include a header file, sqrapi.h. When linking C or C++ applications, both the SQR API library and your database library must be included as well as two additional libraries: Bcl.a and Libsti.a. C ALL ING S QR FRO M OTHE R P RO GRAMS TEAM LinG - Live, Informative, Non-cost and Genuine! 293 You can call SQR programs from PowerBuilder, Visual Basic, Microsoft Access, and other applications that support VBA. SQR programs can also be invoked from Oracle Forms. Please refer to the application-specific technical documents for the detail steps required to link your application with SQR. The following steps are needed to invoke an SQR program from a C program on UNIX platform: 1 Code an SQR program call within your C program. Include the sqrapi.h header file in your program. Build the proper SQR command line parameters to support the call. Include the user ID, database password, and application-specific input parameters into the command line as in //Calling SQR from a “C” program in UNIX #include <sqrapi.h> my_func() { int stat = 0; stat = sqr ("mysqr userid/psswd@testdb"); if (stat == 0) printf ("\n\t\tSuccessfull SQR call\n"); else printf ("\n\t\tSQR call failed\n"); sqrend(); Releases the memory and } closes the cursors Call an SQR program via the SQR API. The call status is returned by the API upon program completion. 2 Compile your C program. 3 Change your make file to include all necessary SQR API libraries: Bcl.a, Libsti.a, SQR.a, plus other libraries required by your application. 4 Run the make file to link the programs to create an executable file. 18.6.2 Using PeopleCode to schedule an SQR program execution from PeopleSoft In PeopleSoft, SQR programs are usually submitted from a menu under the PeopleSoft Process Scheduler. In this case, the Process Scheduler performs actual program invocation, as well as passing parameters to the program. We will discuss this process in detail in part 3. Sometimes you may need to submit an SQR program from PeopleSoft in some nontraditional way, (if, for instance, a program call is triggered by certain events). Or you may want to give users an option to push a button and initiate an SQR program directly from the current PeopleSoft page, thereby avoiding the need for users to switch to the Run Control page and the Process Scheduler Request page that are normally used to submit an SQR program. By the way, in PeopleSoft, programs and jobs (sets of related programs that have to run in a certain order) are called processes. In order to implement 294 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! this, a PeopleCode function, ScheduleProcess, is used. This function accepts the program input parameters, validates them, and inserts a record into the PeopleSoft Process Request table, forcing the system to execute the process automatically. You can optionally schedule your process to run at any specified moment. You can also create recurring processes that can be scheduled to run automatically at specified intervals. If there are several related processes that have to be run sequentially or in parallel, you can create a job where you specify the individual SQR processes and the order in which they run. (Note, jobs are not supported on the client. Only API Aware processes are allowed in job definitions. Please refer to chapter 24 for detailed explanations on how to make your SQR program API Aware.) There are eight parameters that can be used when scheduling an SQR process from PeopleCode. The Process Type and Process Name are the only required parameters of the ScheduleProcess function. The complete ScheduleProcess function parameters are: • Process_Type (string), a required parameter that specifies the process type. Use "SQR Report" to schedule a single SQR program or "PSJob" to schedule a job. • Process_Name (string), a required parameter that specifies your SQR process or job name, as defined in your Process definition. Please note that the Process or Job definition must be created prior to scheduling this process. (Refer to chapter 23 for details on how to create Process definitions.) • Run_Location (string), an optional parameter that specifies where the process is run: “1” = client; “2” = server. Please note that depending on where your PeopleCode is running, you may or may not be able to change the run location. For example, if the PeopleCode script runs on client, your SQR processes can run on either client or server. If, however, the PeopleCode script is run on server, then your SQR process cannot be scheduled to run on client. • Run_Cntl_Id (string), an optional parameter that specifies a Run Control Id (a string that identifies your process for the Process Scheduler). • Process_Instance (number), a temporary variable which you provide as a placeholder to be filled by the ScheduleProcess function. This is an optional parameter, and if not passed, the process instance number is not returned to your PeopleCode script. • Run_Dttm (DateTime), an optional parameter that specifies the date and time to schedule your process. • Recurrence_Name (string), an optional parameter that specifies the recurrence name. (Refer to chapter 24 for details). • Server_Name (string), an optional parameter that specifies the server name (if any). C ALL ING S QR FRO M OTHE R P RO GRAMS TEAM LinG - Live, Informative, Non-cost and Genuine! 295 Here is an example of a PeopleCode script that schedules an SQR process: !Using PeopleCode to schedule an SQR program &PROCESS_NAME = "my_sqr"; &PROCESS_TYPE = "SQR Report"; &RUN_CNTL_ID= "My_Run_ID"; &RC = ScheduleProcess(&PROCESS_TYPE, &PROCESS_NAME, "2", &RUN_CNTL_ID, &PRCS_INST); IF &RC != 0 WinMessage(“Error: Error Scheduling SQR Process, rc="|&RC); End-If; In the preceding example, the value in &RC informs your code whether or not the process was successfully scheduled to run. Do not confuse it with a return code from your SQR program: your program has not even started to run yet. As soon as the PeopleCode function ScheduleProcess inserts a record into the Process Request table, PeopleCode returns to the next command and does not know anything about your SQR process execution. You can see your program run status on the Process Monitor page. The next example shows you how to use inline bind variables in the ScheduleProcess function. Inline bind variables allow you to avoid hard-coding some of the ScheduleProcess function parameters and to use the pertinent parameters from the current page. You can reference any parameter from the current component by using a combination of the record name and the parameter name and prefixing this combination with a colon. For example, to pass the value of Run Location and the Report name stored in the record MY_RECORD, use /*Using inline bind variable to schedule a process from PeopleCode*/ &PROCESS_NAME = :MY_RECORD.REPORT; &PROCESS_TYPE = "SQR Report"; &RUN_CNTL_ID= “My_Run_ID”; &RUN_LOCATION=:MY_RECORD.LOCATION &RC = ScheduleProcess(&PROCESS_TYPE, &PROCESS_NAME, &RUN_LOCATION,&RUN_CNTL_ID, &PRCS_INST); IF &RC != 0 WinMessage(“Error: Error Scheduling SQR Process, rc=”|&RC); End-If; … Keep in mind that before you schedule any SQR program to run from PeopleCode, you must create a Process definition for this program. Starting from PeopleSoft version 8, you can also use the CreateProcessRequest PeopleCode function instead of the previous method. 296 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 18.7 Linking with a user function written in C Linking with a “C” function SQR offers a rich variety of commands and built-in functions. Still, there are some functions or features that are native to other programming languages, but may be very difficult or even impossible to implement using SQR commands and functions. Fortunately, SQR provides a way to extend itself by integrating your own functions written in standard languages like C or utilizing libraries of vendor-supplied functions. SQR provides two ways of linking an SQR program with a function written in C. You can add your function to either the Ucall.c or Ufunc.c file. Depending on where the function was placed, there are two methods of the function invocation: • If the function was placed into Ucall.c, it can only be invoked via the SQR Call command (do not confuse this command with the Call System command). • If the function was placed into Ufunc.c, it must be used in the SQR Let command similarly to any native SQR function. For example: !Two methods of invoking a user function ! my_func1 was placed into the UCALL.C file: Call My_Func1 Using $Parm_In $Parm_Out ! my_func2 was placed into the UFUNC.C file: Let #Status = my_func2($Parm_In, $Parm_Out) The first method involves creating your subroutine, adding the code and function prototype to Ucall.c file, recompiling the Ucall.c, and re-linking it with SQR. In this chapter, we will concentrate on the second method of user function invocation, that is, calling the new user functions from SQR the same way you would call native SQR functions. Creating custom functions for SQR involves some effort. The most difficult part of the effort happens outside of SQR. In the next subchapter, we will create a sample C function, and then go over all steps that are necessary to incorporate this function into the SQR environment. 18.7.1 Creating a user function Our sample function will convert its input to a hexadecimal format. It may prove especially useful when working with file interfaces from non-SQR applications. Sometimes, developers working with programs written in different programming languages have problems with interpretation of the file interfaces between these programs. In other cases, technical documentation from interfacing systems may include inaccurate input LINKING W ITH A “C” FU NCTION TEAM LinG - Live, Informative, Non-cost and Genuine! 297 record layouts. This is especially frustrating when dealing with complex records containing mostly numeric data. The new function will help you to quickly produce a dump of any string, which may prove extremely helpful when debugging your programs. Our new function will accept an input string and return a string containing the input string in the hexadecimal format. This way, we will be able to demonstrate how custom-made functions read their input parameters and how they return the output results. After the function is created, it will be added to SQR as a user function and may be called within an SQR program as follows: Let $HexString= showsashex($Input_String) where $HexString represents the result string and $Input_String contains the function input string. We will start by writing a C function that will convert its input to the hexadecimal format. Thanks to C language flexibility, this task can be accomplished in a number of ways. The example which follows shows just one possible way (many readers will probably come up with different implementations of this logic): //An example of a user function written in “c” static void showashex CC_ARGL((argc,argv,result,maxlen)) CC_ARG(int, argc) /* Number of actual arguments */ CC_ARG(char*, argv[]) /* Pointers to arguments: */ CC_ARG(char*, result) /* Where to store result */ CC_LARG(int, maxlen) /* Result's maximum length */ { char *pIn,*pOut; int iLen; pIn=argv[0]; pOut=result; iLen=0; while (*pIn && iLen<maxlen) { sprintf(pOut,"%2.2x",*pIn); iLen+=2; pIn++; pOut+=2; } *pOut=0; return; } Note that the C function’s type is void even though we planned that the corresponding SQR function will return a string. As you will see later, communicating 298 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! between SQR and the underlying C functions is done indirectly using the standard SQR interface. 18.7.2 Integrating a user function with SQR SQR has a source file named Ufunc.c that contains all user-defined C functions. In order to integrate our new function with SQR, we need to perform the following steps: • create the function prototype and add this prototype to the function declaration list • add an entry to a global array USERFUNC to describe the new function • add the function code to Ufunc.c • re-link SQR using the supplied make file • test your new function. 18.7.3 Adding a function prototype In order to add a function prototype to Ufunc.c, we will be using the CC_ARGS macro that makes your code portable between different compilers. This is how it is done: static void showashex CC_ARGS((int, char *[], char *, int)); This line will be added to already defined prototypes in Ufunc.c: //Adding a function prototype static void max CC_ARGS((int, double *[], double *)); static void split CC_ARGS((int, char *[], double *)); static void printarray CC_ARGS((int, char*[], double *)); static void showashex CC_ARGS((int, char *[], char *, int)); /* new function prototype */ … As you can see, our function prototype is somewhat different from the three function prototypes. The reason is that there is a difference in passing parameters to functions that have string output (string functions) and functions that have numeric output (numeric functions). You have to adhere to the following rules: String functions must include: • the number of arguments (int) • an array of argument pointers to either char[] or double (char *), or (double *) • the address of the result string (char *) • the maximum length of the result string, in bytes (int). LINKING W ITH A “C” FU NCTION TEAM LinG - Live, Informative, Non-cost and Genuine! 299 Numeric functions must include: • the number of arguments (int) • an array of argument pointers to either char[] or double (char *) or (double *) • the address of the result numeric value (double *). As you can see from the description, the difference between the numeric and string functions, is that string functions have one additional argument: the maximum length of the result string. This argument prevents memory corruption by limiting the length of the output string. Another difference between string and numeric functions is that string functions return a character pointer address of the result string, whereas numeric functions return a double pointer to the address of the resulting numeric value. 18.7.4 Adding an entry to the USERFUNCS array In order to describe our function, we need to add an entry to an array of structures in Ufunc.c. The name of the array is USERFUNCS. Each array structure contains five entries for every function. We will add a structure for our new function and then explain each structure element. The following is the USERFUNCS array in Ufunc.c after the addition of the new function: //The USERFUNCS array userfuncs[] = { "max", 'n', 0, "n", PVR max, "split", 'n', 0, "C", PVR split, "printarray", 'n', 4, "cnnc", PVR printarray, "showashex", 'c', 1, "c", PVR showashex, /* Last entry must be NULL—do not change */ "", '\0', 0, "", 0 }; Let’s discuss each element in the array’s structure: The first element is the name of a user-defined SQR function. This is the name that will be used in the Let, If and While commands in the SQR program. The name must be in the lower case. Our function name will be showashex. Note that in SQR you will be able to code it in any case. The second element is the function’s return type: 'n' = numeric, 'c' = character. Our function will return a string; therefore the entry indicates 'c'. 300 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! The third element specifies the number of input arguments. If a function has a variable number of arguments, set this element to 0. Our function accepts 1 argument: the input string. The fourth element is a string that specifies the input argument types. Since our function accepts string arguments, this element must be coded "c". Please note that the order in which argument types are specified is important: first, you specify the type of the first argument, then the type of the second argument, and so on. The fifth element is a pointer to the C function. We are using the PVR macro that provides the proper cast for the pointer. For our function, the entry is PVR showashex. This entry refers to the function prototype that we defined in the first step. 18.7.5 Adding your C function to Ufunc.c The next step is to add your C function source code to Ufunc.c. Make sure the function is unit-tested outside of Ufunc.c, and then simply add the code of your function to Ufunc.c. Note that the CC_ARGL, CC_ARG, and CC_LARG macros are used to make our code portable. 18.7.6 Re-linking SQR and testing your new function Since we have modified the Ufunc.c file, we have to recompile this file and re-link it with SQR. SQR provides the make file that contains both the compilation and the linkage steps. The make file is located in the LIB (UNIX) or LIBW (Windows) sub-directory of SQR. After the program is compiled and linked successfully, let’s test our new function. We can create the following simple test program for this purpose: !Testing the new user-defined function called from SQR Begin-Program Let $InString = 'ABCD &^$#@!!(),.;' Let $HexString = ShowAsHex($InSring) Show 'Input string = ' $InString Show 'Hex string = ' $HexString End-Program Figure 18.1 shows the program run log. 18.7.7 Adding user-defined functions in Windows NT The process of adding a user-defined function in Windows NT is similar to that in Windows and UNIX. When integrating the new function with SQR under Windows NT, you need to use the Extufunc.c file instead of Ufunc.c. Although all basic steps are the same as described earlier, there are some specifics. For example, you will need to LINKING W ITH A “C” FU NCTION TEAM LinG - Live, Informative, Non-cost and Genuine! 301 Figure 18.1 The new user-defined function test log use different functions and different DLL files. Please refer to the Extufunc.c file for more information. In the beginning of this file, there are extensive comments and directions. The name of the SQR-provided make file under Windows NT is SQREXT.MAK. This file is used to compile and link your new function with SQREXT.DLL. 18.8 Generating a Word document Generating a Word document Generating Word documents from SQR is a fairly popular task. In this subchapter, we will generate PIN numbers that employees will use to access their HR records via a selfservice application. Our program will generate a letter in Word to each employee informing the employee about his/her new PIN number. We will need three main components: a Word file with the letter template, another Word file containing a macro that will carry out the mail merge and printing functions, and our SQR program. Let’s start with the template. This is the letter that we are going to send each employee (we will name it Tmp18C.DOC): Welcome to the ABCD Employee Self Service Center. In order to keep your personal information secure when accessing the telephone or online system, you will be required to enter your Employee ID and Personal Identification Number (PIN). Your PIN is provided below: Your PIN: «Pin» 302 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! After you access the system for the first time, you will need to change your PIN to one of your choice. You will be prompted to enter a new four-digit PIN. Please be sure to make it something not known to other people. «Name» «Address1» «Address2» «City», «State» «Zip» In our template letter named Tmp18C.DOC, we inserted seven MAILMERGE fields that will be populated with the employee PIN number, employee name, and various parts of the employee address. The second component is the Word macro: Sub TEST18CM() ' ' TEST18CM Macro ' Dim DirOut$ Dim DirDoc$ Dim OutFiles$ Dim DocFile$ Dim filenm$ Dim DataFiles$ Rem ============================================= Rem Employee PIN Notification Letter Rem ============================================= DirOut$ = "c:\sqrbook\output" DirDoc$ = "c:\sqrbook\examples" OutFiles$ = DirOut$ + "\TEST18C*.*" DocFile$ = "\TEST18C.doc" WordBasic.ChDir DirOut$ filenm$ = DirDoc$ + DocFile$ WordBasic.FileOpen Name:=filenm$, ReadOnly:=0 DataFiles$ = DirOut$ + "\TEST18c.lis" WordBasic.MailMergeOpenDataSource Name:=DataFiles$, ReadOnly:=0 WordBasic.MailMerge MergeRecords:=0, Destination:=0, MailMerge:=1 Rem Print the document Rem WordBasic.FilePrintDefault Rem Set cursor to top field WordBasic.StartOfDocument DataFiles$ = DirOut$ + "\EmplPIN.doc" WordBasic.FileSaveAs Name:=DataFiles$, Format:=0, LockAnnot:=0, Password:="", AddToMru:=1, WritePassword:="", RecommendReadOnly:=0, EmbedFonts:=0, NativePictureFormat:=0, FormsData:=0, SaveAsAOCELetter:=0 GENERATING A WORD DOCUMENT TEAM LinG - Live, Informative, Non-cost and Genuine! 303 On Error Resume Next End Sub This macro reads the in Test18C.LIS file that will be generated by our SQR program, and, for each employee, populates the mail merge fields in the template (PIN number, employee name, and employee address) from Test18C.LIS and generates the PIN notification letter, then, after the entire file of letters is complete, prints the complete file and closes the file and the Word template document. Therefore, the main task of our SQR program is to generate the right Test18C.LIS file with PIN, employee name, and employee address for each employee and to invoke the Word executable. This is our SQR program: !TEST18C.SQR !Calling Word from SQR !************************** Begin-Setup !************************** Declare-Layout Default Formfeed=No Max-Lines=1 Max-Columns=150 Left-Margin=0 Top-Margin=0 End-Declare End-Setup !************************** Begin-Program !************************** Let #Seed = 1 Do Process_Main If #Record_Count > 0 ! Close the current report by opening dummy report New-Report 'dummy.lis' Do Invoke_Word End-If End-Program !***************************************** Begin-Procedure Process-Main !***************************************** Do Write_Heading Do Extract_Data End-Procedure !****************************************** Begin-Procedure Write_Heading 304 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! !****************************************** ! Write Word Mail Merge Column Headings Let $Ioarea = 'Pin,Name,Address1,Address2,' Let $Ioarea = $Ioarea || 'City,State,Zip,End' Print $Ioarea (1,1) New-Page End-Procedure !*********************************************** ! Get employee data !*********************************************** Begin-Procedure Extract_Data Begin-Select P.Name P.Address1 P.Address2 P.City P.State P.Postal Let $Name = &P.Name Let $Address1 = &P.Address1 Let $Address2 = &P.Address2 Let $City = &P.City Let $State = &P.State Let $Zip = &P.Postal Do Generate_Pin ! Generate random PIN, move to $Pin Add 1 to #Record_Count Do Print_Data From Job A, Personal_Data P Where A.Emplid = P.Emplid And A.EMPL_Rcd = 0 And A.EFFDT = (Select Max(Effdt) From Job Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt <= Sysdate) And A.Effseq = (Select Max(Effseq) From Job Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt = A.Effdt) And A.Action In ('HIR', 'REH') And A.Action_Dt = Sysdate And A.Empl_Status = 'A' Order by P.Name GENERATING A WORD DOCUMENT TEAM LinG - Live, Informative, Non-cost and Genuine! 305 End-Select End-Procedure !*********************************************** Begin-Procedure Generate_Pin !*********************************************** Let #Next_Pin = (#Seed * 1103515245) + 12345 Let #Next_Pin = #Next_Pin / 65536 Let #Next_Pin = mod(#Next_Pin, 32768) Let $Pin = edit(#Next_Pin,'9999') Let #Seed = #Seed + 1 End-Procedure !*********************************************** Begin-Procedure Print_Data !*********************************************** Print '"' (+1,1) Print $PIN () Print '","' () Print $Name () Print '","' () Print $Address1 () Print '","' () Print $Address2 () Print '","' () Print $City () Print '","' () Print $State () Print '","' () Print $Zip () Print '","' () Print 'END' () Print '"' () New-Page End-Procedure !*********************************************** Begin-Procedure Invoke_Word !*********************************************** ! Run Winword to Create the Form in Word Let $WinWord='c:\Progra~1\Micros~3\office\winword.exe || 'c:\sqrbook\examples\TEST18C.doc || /mTEST18CM' Show 'winword=' $winword Call System Using $WinWord #Dos_Status Show 'Return code from Word = ' #Dos_Status End-Procedure !******************************************************************** In Test18C, we start with writing the mail merge headings to the output file. This will be the first record in the file. Next, we select the most current employee name and 306 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! address information for each employee from the database. These data are used to populate the pertinent mail merge data fields in the output record for each employee. In addition, the Generate_Pin procedure is used to generate a pseudo-random PIN number for each employee. As you can see, we use a combination of the Print and New-Page commands in place of the Write command to generate the output file. After the program’s output LIS file has been created, we use the New-Report command to close this file so that SQR could release the file before the program’s completion. Then the Invoke_Word procedure calls the Word executable and passes the name of a Word file that stores the mail merge macro and the name of the macro itself. The macro, in turn, reads in the output of our program (Test18C.LIS) and generates the PIN notification letters using the Word template named Text18C.DOC. As the result, our program will generate an output Word document partially shown here: Welcome to the ABCD Employee Self Service Center. In order to keep your personal information secure when accessing the telephone or online system, you will be required to enter your Employee ID and Personal Identification Number (PIN). Your PIN is provided below: Your PIN: 00909 After you access the system for the first time, you will need to change your PIN to one of your choice. You will be prompted to enter a new four-digit PIN. Please be sure to make it something not known to many people. Adams, Cynthia 812 Central Avenue Great Falls, MT 59405 Welcome to the ABCD Employee Self-Service Center. In order to keep your personal information secure when accessing the telephone or online system, you will be required to enter your Employee ID and Personal Identification Number (PIN). Your PIN is provided below: Your PIN: 17747 After you access the system for the first time, you will need to change your PIN to one of your choice. You will be prompted to enter a new four-digit PIN. Please be sure to make it something not known to many people. Aliverdi, Reza 201-7421 Fullerton St Syracuse, NY 132052011 GENERATING A WORD DOCUMENT TEAM LinG - Live, Informative, Non-cost and Genuine! 307 After this document is generated, the macro automatically prints and saves the document in EmplPIN.doc. KEY POINTS 1 You can execute an SQR program from a command line, start this program from the SQRW Dialog Box in the Windows environment, or call the program from other programs. 2 SQR allows you to change its default behavior by using its command line flags. SQR command line flags always start with a dash (-). Some flags may have additional arguments. In this case, the arguments must follow their respective flags immediately with no intervening spaces. 3 When flag arguments specify file directories, they must be ended with the operating system-specific directory character. For Windows, it is a backslash; for UNIX, a forward slash. 4 SQR command line arguments are the values that your SQR program expects to receive from the command line via the Ask command at compile time or the Input command at run time. 5 You can use an argument file to pass arguments to your program on the command line. Each argument must be entered in the argument file on a separate line. 6 SQR processes the program in two stages: the compilation stage and the actual program execution. You can use the -RS command flag to create a pre-compiled version of your program (with the .sqt extension). You can execute a pre-compiled version of an SQR program by specifying the -RT command flag or using the SQR Execute. 308 CHAPTER 18 INTERACTING WITH OTH ER APPLI CA TIONS TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 7 You can issue any operating system command within your SQR program. The Call System Using command is used to execute the operating system commands. 8 In addition to issuing the operating system commands, the Call System Using command can be used to invoke any executable program. 9 In order to call SQR programs from other applications, you can use SQR API. 10 SQR programs can be scheduled for execution from PeopleCode. 11 You can extend the SQR built-in functions spectrum by writing your own functions in “C” or “C++”. GENERATING A WORD DOCUMENT TEAM LinG - Live, Informative, Non-cost and Genuine! 309 C H A P T E R 1 9 Internet enabling IN THIS CHAPTER • The three ways to generate HTML from an SQR program output • HTML procedures • Controlling the output positioning in Internet-enabled SQR programs • Formatting the HTML output • Hypertext links and hypertext link anchors • Accepting arguments from the Internet environment 310 TEAM LinG - Live, Informative, Non-cost and Genuine! In this chapter you will learn how to convert existing SQR reports into the HTML compatible format and how to write SQR programs specifically designed to generate Internet reports or Web pages. Please keep in mind that our book is not about the Internet or HTML, we just touch some Internet topics related to SQR program Internet enabling. For any kind of comprehensive coverage of the Internet, please refer to the special books on this subject. 19.1 SQR and the Internet SQR and the Internet internet One of the new features in SQR version 4 is Internet enabling. You can generate program reports in an HTML compatible format and publish these reports on the Internet or an intranet. You can place hypertext links in your report, thus allowing the report viewers to switch from place to place within the report, or to jump to another report, or, even, to a different website. Alternatively, you can create hypertext link anchors in key places inside your report so that other web pages will be able to include pointers to these specific portions in the report. For example, you can include hypertext links and link anchors in two related reports. This way, the viewers will be able to jump back and forth between the two reports and better understand the relationships between the report data. There are three methods of generating HTML output from an SQR program. Each method requires a different programming effort—from very minimal effort, involving no program source changes, to rather substantial program code modifications. And, of course, different efforts bring different results. The more changes you make to your program the better functionality you’ll receive. Not all SQR features are presently supported by the Internet conversion. The following graphical features cannot be converted to HTML: • font selection • bar codes • lines and boxes • charts and graphs. 19.2 No program code changes no programcode No program codechanges changes Too good to be true? Actually, if your goal is just to publish an SQR-generated report on the Internet or an Intranet without any changes or special features, you do not need to touch a single line in your program! All you have to do is to run the program using the SQR command flag -PRINTER:HT. SQR will automatically convert the program output into the HTML format and will take care of positioning, HTML tags, font and letter size mapping, and so on. NO PRO GRAMCODE CHANGES TEAM LinG - Live, Informative, Non-cost and Genuine! 311 Another alternative involving a very minimal program change is to use the Declare-Printer command with the argument Type=HT or the Use-PrinterType HT command. When you use this technique, the program output is positioned on the web page according to the position coordinates specified in the Print or similar native SQR commands. The text is displayed using a fixed-width font such as Courier even if your program uses a different font. If your program code includes special characters that are used by HTML, such as < or >, the characters are converted into the corresponding HTML sequences. When converting your program output to HTML, SQR will create the same number of HTML pages as that for the regular SQR output: the pages will be generated automatically, based on the report layout page length or according to the SQR page control commands used. When you use the -Printer:HT command line flag, SQR automatically generates the following three output files: an HTML frame file, a report body file, and a table of contents file. The file names are generated as follows: The frame file name is your program name post-fixed with _frm; the table of contents file name is your program name post-fixed with _toc; the report body file name is your program name. The extensions to all three files are set to .htm. What if your program has more than one output report? In that case, SQR will generate three more files for each additional report and will insert _01, _02, and so on, into the file names of each file triplet. You can override the file names using the -F SQR command line flag. After the output files have been generated, you need to use the FTP utility, or a similar file transfer tool, to move these files to the appropriate web server directory. If users will be linked to your report from an HTML index file or from another web page, the links should be pointing to the frame file. The frame file displays the report in the following HTML FRAMESET format: the left frame shows pointers to all the report pages, whereas the right frame shows the report body with pointers to the first, previous, next, and last report pages displayed on the top of the report. You can point the cursor to any page pointer and the web browser will bring you to the selected report page. You can also scroll down the report using the browser’s scroll-down control. Your web browser must support the FRAMESET format. (Most browser products, including both the Netscape Navigator and the Microsoft Internet Explorer, support this format.) Let’s take one of the programs created in chapter 13, Test13A.sqr, and run this program with the -Printer:HT SQR command line flag to produce an HTML output (figure 19.1). 312 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! Because the Test13A.sqr program produces two output reports, it will generate the following six output files: Test13A.htm, Test13A_frm.htm, Test13A_toc.htm, Test13A_01.htm, Test13A_01_frm.htm, and Test13A_01_toc.htm. The Test13A.htm file is an HTML body file; the Test13A_frm.htm is an HTML frame file; and Test13A_toc.htm file is an HTML table of contents file for the Figure 19.1 Submitting an SQR first Test13A.sqr report. Figure 19.2 program to generate HTML output shows how the Test13A_frm.htm file is viewed on a web page. Because the program output report includes multiple pages, SQR automatically generates pointers to every report page and places these pointers in the left frame of the report. The report body is placed in the right frame of the report. Similarly, the other file triplet: Test13A_01.htm, Test13A_01_frm.htm, and Test13A_01_toc.htm belongs to the second program report. Figure 19.2 Viewing an SQR-generated HTML frame output via a web browser. NO PRO GRAMCODE CHANGES TEAM LinG - Live, Informative, Non-cost and Genuine! 313 As you can see, SQR uses a standard design to build the table of contents frame automatically. If you do not like this design, use the Declare-Toc command (introduced in version 4.2) to build your own table of contents. If your browser does not support the HTML FRAMESET format use the Test13A.htm file instead of that in Test13A_frm.htm. You will not see the page pointers, but the browser will still display the entire report body. Starting with version 4.3.1, you can use another SQR command flag, –PRINTER:EH, to generate HTML output without making any changes to your program. This flag allows you to generate enhanced HTML 3 output. The enhanced HTML output flag can also be used in conjunction with a number of new features available starting from version 4.3.2. You can have SQR generate a CSV file or a PDF file, specify the directories where an HTML browser should look for the referenced icons or images, set the language for the HTML navigation bar, move or copy the program output to a Zip file, define a scaling factor for your HTML output, and so on. For the complete description of all SQR command flags please see appendix B. Let’s run the Test13A.sqr program with the following SQR flags: –PRINTER:EH, EH_PDF, EH_CSV, EH_BQD. We are hereby requesting SQR to generate enhanced HTML output and place the three icons on the top of the report: an Adobe PDF icon, an Excel CSV icon, and a Brio BQD (Brio Query Format File) icon. Figure 19.3 shows the output of our program. Note the three icons on the top. Figure 19.3 314 Using the –PRINTER:EH flag to generate enhanced HTML output CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! 19.3 Using a table of contents using aa table Using table of ofcontents contents Strictly speaking, tables of contents are not just features of HTML-enabled reports. SQR allows you to generate a table of contents for any report. For example, you can generate a table of contents for a PDF-formatted report, for an SPF report or for a regular LIS report. But with an HTML-enabled report, you can take advantage of the hypertext links that SQR places in a table of contents. These links help report viewers with navigating between different portions of their reports. SQR automatically generates a rudimentary table of contents for any HTMLenabled report by dividing the report into pages (see figure 19.2.). You can use a combination of the Declare-Toc and Toc-Entry commands to divide your report into logical portions and build a symbolic table of contents for the report based on the report specifics. Depending on the report structure, you can build rather complex tables of contents with various levels of chapters and subchapters. In order to demonstrate full capabilities of this feature, we need an SQR program that generates output with multiple levels of data so that we would be able to build a table of contents with multiple levels. Let’s employ the Test10C.sqr program used in chapter 10 to generate a list of employees by company and paygroup. In chapter 10, we used this program to create breaks in the program output at two levels: level 1 breaks by company and level 2 breaks by paygroup within the same company. Now, we will modify it to build a table of contents with company names as chapters and paygroup codes as subchapters. This is the modified program renamed as Test19A.sqr: ! TEST19A.SQR ! Using Table of Contents !**************************** Begin-Setup Declare-Report Report19A End-Declare Declare-Toc Test19A For-Reports = (Report19A) Before-Toc=Print_Toc_Header End-Declare End-Setup Begin-Program !**************************** Show 'Program Started' Do List_Employees US ING A TABLE OF CONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 315 End-Program !**************************** Begin-Heading 3 !**************************** Print 'List of Employees by Company, Paygroup' (1,1) Print 'Page' (,+3) Print #page-count (,+1) edit 999 Print Company' (+1,1) Print 'Paygroup' (,+2) Print 'Emplid' (,+2) Print 'Name ' (,+4) Print 'Annual Salary' (,+15) Print ' (+1,1) End-Heading !*********************************** Begin-Procedure Print_Toc_Header !*********************************** Position (+1,1) Print 'Employees by Company, Paygroup' () Bold Position (+1,1) End-Procedure !******************************* Begin-Procedure List_Employees !******************************* Begin-Select Add 1 to #Row_Num Show 'Selected row# ' #Row_Num ' Comp=' &B.Company ' Paygrp=' &B.Paygroup B.Company (,1,7) On-Break Level = 1 Before=Company_Name After=Company_Totals Skiplines = 1 B.Paygroup(,10,8) On-Break Level = 2 Before=Paygroup_Group_Code After=Paygroup_Totals A.Emplid(,+2,8) A.Name(,+2,18) B.Annual_Rt(,+1,13) edit $$,$$$,$$$.00 Position(+1) Let #EE_Paygroup_Total= #EE_Paygroup_Total + 1 Let #Sal_Paygroup_Total= #Sal_Paygroup_Total + &B.Annual_Rt From Personal_Data A, Job B Where A.Emplid=B.Emplid And B.Effdt=(Select Max(D.Effdt) From Job D Where D.Effdt<=Sysdate And D.Emplid=B.Emplid And D.Empl_Rcd=B.Empl_Rcd) And B.Effseq = (Select Max(C.Effseq) From Job C Where C.Emplid=B.Emplid And C.Empl_Rcd = B.Empl_Rcd And C.Effdt=B.Effdt) 316 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! Order By B.Company, B.Paygroup, A.Emplid End-Select End-Procedure !*********************************** Begin-Procedure Company_Name !*********************************** Show 'Before Procedure Company_Name is invoked' Begin-Select C1.Descr Print 'Company Name: ' (+1,1) Print &C1.Descr (,+2) Position (+1) From Company_Tbl C1 Where C1.Company = &B.Company And C1.Effdt = (Select Max(Effdt) From Company_TBL Where Company = C1.Company) End-Select Move &C1.Descr To $Company_Name Let $Toc_Caption='Company: '||$Company_Name Toc-Entry Text=$Toc_Caption Level = 1 End-Procedure !*********************************** Begin-Procedure Paygroup_Group_Code !*********************************** Show 'Procedure Paygroup_Group_Code is invoked' Move &B.Paygroup To $Paygroup Let $Toc_Caption='Paygroup: '||$Paygroup Toc-Entry Text=$Toc_Caption Level = 2 End-Procedure !************************************ Begin-Procedure Company_Totals !************************************ Show 'After Procedure Company_Totals is invoked' Print 'Number of Employees in Company = ' (+1,5) Print #EE_Company_Total () edit 999999 Print 'Total Annual Salary Paid for Company = ' (+1,5) Print #Sal_Company_Total () edit $,$$$,$$$,$$$.00 Let Let #EE_Company_Total = 0 #Sal_Company_Total = 0 End-Procedure !************************************* Begin-Procedure Paygroup_Totals !************************************* US ING A TABLE OF CONTENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 317 Show 'After Procedure Paygroup_Totals is invoked' Print 'Number of Employees in Paygroup = ' (+1,5) Print #EE_Paygroup_Total () edit 999999 Print 'Total Annual Salary Paid for Paygroup = ' (+1,5) Print #Sal_Paygroup_Total () edit $,$$$,$$$,$$$.00 Position (+1) Let Let #EE_Company_Total = #EE_Company_Total + #EE_Paygroup_Total #Sal_Company_Total = #Sal_Company_Total + #Sal_Paygroup_Total Let #EE_Paygroup_Total = 0 Let #Sal_Paygroup_Total = 0 End-Procedure !******************************** In Test19A.sqr we made the following modifications: • added the Declare-Toc command in the Setup section • added the Toc-Entry command in the Company_Name section to generate level 1 table of contents entries • added another Toc-Entry command in the Paygroup_Group_Code section to generate level 2 table of contents entries. The Declare-Toc command defines the look of the table of contents, relates program reports to the table of contents, and allows you to specify procedures that will be called before and after generating the table of contents, before and after generating every page, or before generating each entry of the table of contents. For a single-report program, you need only one Declare-Toc command. For a multiple-report program, you may still use one Declare-Toc command if the look and structure of the table of contents can be same for all reports or you may specify separate tables of contents for each report or for the groups of reports. In our case, we use the Before-Toc operand of the Declare-Toc command to specify a name of the procedure that will be invoked before generating the table of contents. We use this procedure to print a title for our table of contents. It is really a “nice to have” feature: SQR would have generated the table of contents without the BeforeToc operand, but a table of contents with a title looks more professional, plus this is a good chance to demonstrate how to use this operand. The Toc-Entry command makes SQR generate an entry in the table of contents. The Text argument of this command defines the exact content of this entry. SQR generates each table of contents entry automatically at the level specified and takes care of indentation, inserting the proper HTML hypertext link, building the hierarchy, tree and so on. 318 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! If you want to build your table of contents entries yourself, you can use the Entry argument of the Declare-Toc command. This argument specifies the name of a procedure that is invoked to process each entry of the table of contents. SQR passes the text of each entry (generated by the Toc-Entry command) in the $SQR-Toc-Text reserved variable. SQR also passes the level of entry in the #SQR-Toc-Text reserved variable and the current page number in the #SQR-Toc-Page reserved variable. In most cases, you do not need to use this argument: you can trust SQR with generating table of contents entries. Figure 19.4 shows the HTML output of Test19A.sqr. Figure 19.4 Using table of contents 19.4 Bursting your HTML output bursting your Bursting your HTML HTML output When you generate output without using the bursting option, SQR creates the report body as one HTML file. When viewing the report via an HTML browser, the entire report body is loaded into the memory. To speed the process of loading and opening of large reports, SQR allows you to divide the report body into pages or other logical BUR S TING YOUR HTML OU TPU T TEAM LinG - Live, Informative, Non-cost and Genuine! 319 pieces. This way, your users will be able to preview the report’s table of contents without opening the entire report. By clicking on specific pages or other entries in the table of contents, they will cause the loading of only selected pieces of the report. Generating your report by pieces is called bursting. When using this option, you can divide the entire report into many pieces and have SQR generate only the pieces you need. You can divide your report into physical pages and have SQR generate only certain pages or page ranges (this option is called demand paging). Another option is to use the Declare-Toc command to generate the report’s table of contents and burst your report on a desired level: generate only table of contents, table of contents and level 1 entries, table of contents and level 2 entries, and so on. To use report bursting, you need to use two SQR command flags: –PRINTER:HT (or –PRINTER:EH) and –BURST. The arguments of the –BURST flag control the level of bursting. For example, you can ask SQR to generate pages 1, 2, 10, 11, and 12 by specifying –BURST:P1,1,2,10-12. The first argument of the –BURST flag (P) denotes how many changes each HTML output file will have, the second argument lists the page numbers to be generated. Another option that can be used only if your report has a table of contents is to have SQR generate only the table of contents by using –BURST:T or the table of contents and report entries at the desired level by using –BURST:Si where i is the level number. Let’s run Test19A.sqr with the –PRINTER:HT and –BURST:P2,1-6 flags. This means that SQR will generate only first six pages of the report and split the output into three files containing two pages each. Figure 19.5 shows the output in this case. You can see that although SQR generated the entire table of contents for the report, only entries representing pages from 1 to 6 have hypertext links to the report body (these entries are underlined). The report body frame shows only the first two pages of the report. To see the remaining pages, you have to use the page controls on the top. The output directory will have three report body files, test19A_1.HTM, test19A_2.HTM, and test19A_3.HTM, each file will be two pages long. Let’s run the Test19A.sqr program with the –PRINTER:HT and –BURST:S1 flags. This will make SQR generate a table of contents file and split the report body into separate HTML files with each file containing only data for a specific company. The program output will look like that shown in figure 19.6. Please note that when you open the report, SQR loads only the report’s table of contents and the first report body file. When you click on some entry in the table of contents, only this portion of the report body is loaded into the memory by the browser. Also note that all table of contents entries have links to all parts of the report body because with this option, we do not limit the size of the report body. 320 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 19.5 Using demand paging Figure 19.6 Bursting the Test19A.sqr program output on the company level BUR S TING YOUR HTML OU TPU T TEAM LinG - Live, Informative, Non-cost and Genuine! 321 If we were to use the –BURST:S2 flag, SQR would generate a table of contents file and split the report body into many HTML files with each file containing only data for a specific paygroup. 19.5 Defining a title and background image defining aatitle Defining title and and background backgroundimage image Now we are going to make a few improvements to our HTML output. The first improvement will be a visual one: we are going to define a background for the report to make it better fit the rich Internet graphical environment. The second change will be more important: we will add a title to the report. Do not confuse this title with the report header. In some browsers, the title appears in the title bar of the browser window; in others, the title is centered at the top of the screen. The title is used in search indexes as well as in the browser’s history lists and bookmarks. Both improvements require very minimal program changes. All you will have to do is: • add the Html.inc file to the program using the #Include command • invoke the following two SQR procedures: Html_Set_Head_Tags and Html_ Set_Body_Attributes. The procedures should be invoked at the program start using the SQR Do command. For example, in case of Test13A.sqr, the beginning and the end of the program will look like that in Test19B.sqr. !TEST19B.SQR !Adding webpage title and background Begin-Setup !*********************** . . . End-Setup !********************************** Begin-Program !********************************** Let $Title = '<TITLE>ABCD Company. List of Employees by Health Plan</TITLE>' Do Html_Set_Head_Tags($Title) Do Html_Set_Body_Attributes('Background="bkgrnd2.GIF"') Do Process_Main End-Program . . . #Include 'html.inc' 322 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! selecting a background image for a report, avoid sharp pictures, text, or Note When any distinct objects as parts of the image. Otherwise, they will multiply over the page and reduce the visibility of your report. The input parameter for the Html_Set_Head_Tags procedure is a string that includes the title text surrounded by the <TITLE> and </TITLE> tags and, optionally, the META information about your document. The META information may contain several sections, including the keywords used by search engines, document description, rating, etc. An example of the Html_Set_Head_Tags procedure with the META section is: Html_Set_Head_Tags('<TITLE>My Title </TITLE> <META NAME="keywords" CONTENT="SQR, SQL, PeopleSoft"> <META NAME="description" CONTENT="A comprehensive guide to SQR"> <META NAME="rating" CONTENT="General"> ') The input parameter for the Html_Set_Body_Attributes procedure is a string that includes the name of the report background image file. You can use GIF or JPEG files for images. All image files used in your reports must be placed in the same directory with the reports. Alternatively, you can place the image files in a separate directory and specify this directory name in the -EH_IMAGES SQR command flag (see appendix D). As an alternative to a background image, you can use the same procedure to specify a background color for your report, for example: Html_Set_Body_Attributes('BGCOLOR=SILVER') In order to convert the program output to the HTML format, you still have to run the program using the SQR command line flag -PRINTER:HT. SQR will use the same approach to print positioning, font and letter size mapping, special HTML character replacement, etc., as the approach used in the previous method involving no program changes. The Test19B.sqr program will generate the same number of output files: Test19B.htm, Test19B_frm.htm, Test19B_toc.htm, Test19B_01.htm, Test19B_01_frm.htm, and Test19B_01_toc.htm. After adding the title and the background, the program output now will look as shown in figure 19.7. As you can see, the specified title is displayed on the browser title bar, not on the report. The report is shown in the HTML Frameset format with pointers to the report pages shown on the left frame. Please keep in mind that there must be one, and only one, title in each HTML document. If your program generates more than one report, each report will carry the same HTML title, but you can generate different headers for each report. DEFINING A TITL E AND BACKGROUND I MA GE TEAM LinG - Live, Informative, Non-cost and Genuine! 323 Figure 19.7 Adding a title and a background image to your report foreign letters or special characters in the title. Use the HTML reserved Note Avoid names or number codes instead. For example, use &amp or &#38 in place of the ampersand symbol (&). Please refer to special HTML manuals for a complete list of HTML reserved names and codes for foreign letters or special characters. 19.6 Adding more HTML features adding more Adding more HTML HTML features features In the previous examples, the look of our report did not change much, the only change made was adding the report background. Now we are going to add more HTML features to the report, which will enhance the report appearance and allow you to use HTML graphics and hypertext link pointers and anchors. To add more HTML features to your program, you have to use the SQR HTML procedures. These procedures are delivered in the Html.inc file. Additionally, you have to run your program with the already mentioned SQR command line flag: -PRINTER:HT 324 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! The SQR HTML procedures are turned on by calling the Html_On procedure in the beginning of the program. Invoking this procedure alone changes your program behavior drastically. All position qualifiers in the native SQR printing commands are ignored, and if you do not account for this, the output of your program will be messed up as in figure 19.8. Figure 19.8 Invoking the Html_On procedure makes SQR ignore all print positioning qualifiers. To fix the problem, you have to use the SQR HTML tabular procedures to control the field positions on your report by converting the report into an HTML table. Also, the Max-Lines parameter of the Declare-Layout command must specify a very large number of lines per page because SQR automatically inserts the page navigation hypertext links and HTML page break tags at page breaks. If this happens somewhere inside an HTML table, the output may display incorrectly. In addition to the SQR HTML tabular procedures, a number of other HTML procedures can help in controlling the report output on the web page. These procedures fall into the following categories: • general purpose procedures • heading procedures A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 325 • tabular procedures • highlighting procedures • list procedures • hypertext links procedures. For obvious reasons, we will not be able to discuss all SQR HTML procedures in this book, but only the most frequently-used ones. For a complete list of the SQR HTML procedures, please refer to the SQR technical manuals. 19.6.1 HTML tabular procedures This is how the SQR HTML tabular procedures can be used to ensure the correct output positioning in our program: !************************* ! TEST19C.SQR !Using the HTML tabular procedures to control output positioning !************************* !Set report column widths #Define col_emplid #Define col_empl_name #Define col_plan_type #Define col_cov_cd #Define col_cov_drop_flag #Define col_effdt #Define col_dep_id #Define col_dep_name #Define col_sep !*********************** Begin-Setup !*********************** Declare-Layout EE_Data Max-Lines=3000 Left-Margin=1 End-Declare 11 20 5 8 20 12 6 15 1 !Employee ID !Employee Name !Plan Type !Coverage Code !Dependent Coverage Termination !Effective Date !Dependent Beneficiary ID !Dependent Name !Column Separator Specify a very large number of lines in a page to make the entire report one page. Declare-Layout Term_Dependents Max-Lines=200 Orientation=landscape Left-Margin=0.3 End-Declare Declare-Report EE_Data Layout=EE_Data End-Declare Declare-Report Term_Dependents Layout=Term_Dependents 326 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! End-Declare End-Setup !********************************** Begin-Program Define the report title. !********************************** Do Html_On() Do Html_Set_Head_Tags('<TITLE>ABCD Company. List of Employees by Health Plan</TITLE>') Do Html_Img(' SRC="edition.GIF" ') Do Html_Set_Body_Attributes('Background="bkgrnd2.GIF" ') Do Process_EE_Heading Do Process_Dep_Heading Do Process_Main End-Program Define an image ribbon and a background for the report. !************************************************* Begin-Procedure Process_EE_Heading !************************************************* Use-Report EE_Data Do Html_Table('BORDER') Use the SQR HTML procedures to define the table characteristics. Do Html_Caption(' ') Print 'List of Employees by Plan Type' (1) Center Do Html_Caption_End Define the table !Print 'Page' (,+15) header cells. !Print #page-count (,+1) edit 999 !Print ' ' (+2,1,{col_emplid}) Do Html_Tr(' ') Do Html_Th(' ') Print 'EMPLID' (+1,1,{col_emplid}) Do Html_Th(' ') Print 'Name ' (0,+{col_sep},{col_empl_name}) Do Html_Th(' ') Print 'Plan' (0,+{col_sep},{col_plan_type}) Do Html_Th(' ') Print 'Effdt' (0,+{col_sep},{col_Effdt}) Do Html_Th(' ') Print 'Cov Type' (0,+{col_sep},{col_cov_cd}) Do Html_Th(' ') Print 'Dep Cov Terminated' (0,+{col_sep},{col_cov_drop_flag}) Do Html_Th_End !Print '-' (+1,1,70) Fill End-Procedure !************************************************************* Begin-Procedure Process_Dep_Heading !************************************************************* Use-Report EE_Data Use-Report Term_Dependents A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 327 Do Html_Table('BORDER') Do Html_Caption(' ') Print 'List of Terminated Dependents' (1) Center Do Html_Caption_End !Print 'Page' (,+15) !Print #page-count (,+1) edit 999 !print ' ' (+2,1,{col_emplid}) Do Html_Tr(' ') Do Html_Th(' ') Print 'EMPLID' (+1,1,{col_emplid}) Do Html_Th(' ') Print 'Name ' (0,+{col_sep},{col_empl_name}) Do Html_Th(' ') Print 'Plan_Type' (0,+{col_sep},{col_plan_type}) Do Html_Th(' ') Print 'Effdt' (0,+{col_sep},{col_Effdt}) Do Html_Th(' ') Print 'Dep ID ' (0,+{col_sep},{col_dep_id}) Do Html_Th(' ') Print 'Dep Name' (0,+{col_sep},{col_dep_name}) !Print '-' (+1,1,70) Fill End-Procedure !************************************* Begin-Procedure Process_Main !************************************* !Select and print all Employees !who are currently covered by any Health Benefits !and who have Family, or Empl+1 Coverage Begin-Select A.Emplid A.Plan_Type A.Effdt A.Covrg_CD B.Name Do Check_Dep_Termination If $Term = 'N' Let $Term = ' ' End-If Do Print_EE_Data From Health_Benefit A, Personal_Data B Where =(Select Max(Effdt) From Health_Benefit Where Emplid = A.Emplid And Plan_Type = A.Plan_Type And Effdt <= Sysdate) And A.Coverage_Elect Not In ('T','W') And A.Emplid = B.Emplid Order By A.Emplid, A.Plan_Type 328 A.Effdt CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! End-Select End-Procedure !************************************************** Begin-Procedure Check_Dep_Termination !************************************************** ! For each row retrieved in Step 1, compare between the previous ! maximum effective dated record and the current effective dated ! HEALTH_DEPENDNT row to determine a dependent that is no longer ! covered. If dropped dependents are found, print the record to a ! separate report Move 'N' To $Term Begin-Select C.Emplid C.Plan_Type C.Effdt D.Dependent_Benef Move 'Y' to $Term Do Print_Terminated_Dependents Show 'Dep Term found=' &D.Dependent_Benef ' for Employee '&A.Emplid From Health_Benefit C, Health_Dependnt D Where C.Emplid = &A.Emplid And C.Plan_Type = &A.Plan_Type And C.Emplid = D.Emplid And C.Plan_Type = D.Plan_Type And C.Effdt = D.Effdt And C.Effdt = (Select Max(E.Effdt) From Health_Benefit E Where C.Emplid = E.Emplid And C.Plan_Type = E.Plan_Type And E.Effdt < &A.Effdt) And D.Dependent_Benef Not In (Select F.Dependent_Benef From Health_Dependnt F Where C.Emplid = F.Emplid And C.Plan_Type = F.Plan_Type And F.Effdt = &A.EFFDT) End-Select End-Procedure !******************************************* Begin-Procedure Print_EE_Data !******************************************* Use-Report EE_Data Let $EmplID=&A.Emplid Let $Date_Str= Datetostr(&A.Effdt,'mm/dd/yyyy') Do Html_Tr(' ') A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 329 Do Html_Td(' ') Define each table Print &A.Emplid (+1,1,{col_emplid}) detail column cell. On-Break Print= Change/Top-Page Do Html_Td(' ') Print &B.Name (0,+{col_sep},{col_empl_name}) On-Break Print=Change/Top-Page Do Html_Td(' ') Print &A.Plan_Type (0,+{col_sep},{col_plan_type}) Do Html_Td(' ') Print $Date_Str (0,+{col_sep},{col_effdt}) Do Html_Td(' ') Print &A.Covrg_Cd (0,+{col_sep},{col_cov_cd}) Do Html_Td (' ') Print $Term (0,+{col_sep},{col_cov_drop_flag}) Do Html_Td_End Do Html_Tr_End Add 1 to #Ee_Rcds End-Procedure !********************************************************* Begin-Procedure Print_Terminated_Dependents !********************************************************* Use-Report Term_Dependents Let $EmplID=&A.Emplid Do Get_Dependent_Name Let $Date_Str= Datetostr(&A.Effdt,'mm/dd/yyyy') Do Html_Tr(' ') Do Html_Td(' ') Print &A.Emplid (+1,1,{col_emplid}) On-Break Print=Change/Top-Page Do Html_Td(' ') Print &B.Name (0,+{col_sep},{col_empl_name}) Print=Change/Top-Page Do Html_Td(' ') Print &A.Plan_Type (0,+{col_sep},{col_plan_type}) Do Html_Td(' ') Print $Date_Str (0,+{col_sep},{col_effdt}) Do Html_Td(' ') Print &D.Dependent_Benef (0,+{col_sep},{col_dep_id}) Do Html_Td(' ') Print $DEP_NAME (0,+{col_sep},{col_dep_name}) Do Html_Td_End Do Html_Tr_End On-Break Add 1 to #Dep_Rcds End-Procedure !************************************* Begin-Procedure Get_Dependent_Name !************************************* 330 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! Let $Dep_Name=' ' Begin-Select Name Move &Name to $Dep_Name from Dependent_Benef where EMPLID=&A.Emplid and Dependent_Benef=&D.Dependent_Benef End-Select End-Procedure #include 'html.inc' In Test19C.sqr, we made a number of program changes. These changes helped to improve the report appearance and eliminated the problem of positioning the report elements. The first improvement we made was to place an image on the top of the report. The image can be a corporate logo, a picture, a banner, or simply a multiple-color ribbon. Use the Html_Img procedure to insert an image in your report. For example, this is how we placed the Online Edition banner on the top of the report: Do Html_Img(' SRC="edition.GIF" ') The Html_On procedure is called at program start to turn all other SQR HTML procedures on. For each of the program’s two reports, the Declare-Layout command is used to make the number of lines in the page exceed the entire report length, placing all output into one large page. A number of SQR HTML tabular procedures is used to control the output field positions. Let’s discuss these procedures in more detail. • The Html_Table and Html_Table_End procedures are used to mark the start and end of the table. The BORDER argument of the Html_Table procedure causes a border to be displayed around each table cell. • The Html_Caption and Html_Caption_End procedures specify the table caption. Immediately after the Html_Caption procedure call, you have to use the Print command to display the table caption. • The Html_Tr and Html_Tr_End procedures mark the start and end of a new table row. You need to code these procedures for each table row, including the header row, the detail rows, and the total row (if there is one). • The Html_Th and Html_Th_End procedures mark the start and end of each table column header. You have to code as many Html_Th procedures as the number of columns in the table. The Print command following the Html_Th procedure call must display the column heading. You do not have to change the Print command position parameters. They will be ignored anyway. A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 331 • The Html_Td and Html_Td_End procedures mark the start and end of each column cell in the table detail body. As with column headers, you have to code as many Html_Td procedures as the number of columns in the table. The Print command following the Html_Td procedure call must display the content of the column cell. Figure 19.9. shows how a portion of the first program report will look on a web page. (The second report will look similar.) Figure 19.9 Using the SQR HTML tabular procedures Please note that the web browser displays the report as one table with no page breaks and no page navigation pointers. This is because we defined the entire report as one large page. 19.6.2 HTML heading procedures The SQR HTML heading procedures help you to display certain portions of the report more prominently than other portions. You can use up to six header levels, with level one as the most prominent and level six as the least prominent. Most reports will seldom need more than three levels of headers. You do not have to start from level one heading. Use the level which best fits your report design. 332 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! The Html_Hn and Html_Hn_End (where n can be from 1 to 6) procedures are used to specify the start and end of each level heading. You can also specify the proper HTML heading attributes as the Html_Hn procedure parameters as shown in Test19C.sqr. In this example, we are using two heading levels: level one and level two. Place the Print command between the Html_Hn and Html_Hn_End procedures to specify the heading text. !************************* ! TEST19D.SQR !Using HTML headings !************************* . . . !********************************** Begin-Program !********************************** Do Html_On() Do Html_Set_Head_Tags('<TITLE>ABCD Company. List of Employees byHealth Plan</TITLE>') Do Html_Img(' SRC="edition.GIF" ') !Do Html_Set_Body_Attributes('Background="bkgrnd2.GIF" ') Do Process_EE_Heading Do Process_Dep_Heading Do Process_Main End-Program !************************************************* Begin-Procedure Process_EE_Heading !************************************************* Use-Report EE_Data Define level one header. Do Html_H1(' ') Print 'Employee Health Benefit Coverage Report' (1,1) Do Html_H1_End Do Html_Table('BORDER') Do Html_Caption(' ') Define level two header. Do Html_H2(' ') Print 'List of Employees by Plan Type' (+1) Center Do Html_H2_End Do Html_Caption_End . . . End-Procedure !************************************************************* Begin-Procedure Process_Dep_Heading !************************************************************* Use-Report Term_Dependents Do Html_H1(' ') Print 'Employee Dependents with Health Benefit Coverage Terminated' (1,1) Do Html_H1_End A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 333 Do Html_Table('BORDER') Do Html_Caption(' ') Do Html_H2(' ') Print 'List of Terminated Dependents' (+1) Center Do Html_H2_End . . . End-Procedure !************************************* . . . #include 'html.inc' Figure 19.10 shows how both these headings will look on a web page. Figure 19.10 Using the SQR HTML heading procedures HTML procedures can be made transparent: if you do not use the Note SQR PRINTER:HT SQR command flag, all HTML procedures in your program will be ignored, and SQR will generate regular LIS files as output reports. 334 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! 19.6.3 HTML hypertext link procedures The SQR HTML hypertext link procedures make your report look like a true web report. You can use them to either: • specify a link to another part of the current web page or an external web page or • define an anchor within your report so that the same or another web page can have a link to this part of your report. The same pair of SQR HTML procedures, Html_A and Html_A_End, can perform either of the previously mentioned functions. What the procedures actually do depends on the Html_A procedure’s input argument. To define a hypertext anchor in your report, use the NAME argument in the Html_A procedure. This argument specifies the name of the anchor. For example, the following code defines an anchor named TAGB in the program output report: Do Html_A('NAME=TAGB') Do Html_A_End Print 'Annual Report. Section B' () Please note that, if other documents include links to the TAGB anchor, clicking on these links will bring viewers right to the beginning of the 'Annual report. Section B' statement. To define a hypertext link to another place, use the HREF argument in the Html_A procedure. There are two possible methods of coding hypertext links. If the point of reference is at the beginning of a separate document, use an absolute or relative URL address of the entire document. For example, the following code specifies a hypertext link to the ABCD company home page which is external to your program report: Do Html_A('HREF=http://www.abcd.com') Print 'Return to ABCD company home page' () Do Html_A_End To define a hypertext link to some place inside a document, you need to specify the name of a previously defined anchor located in this place in the document. You can code hypertext links pointing to anchors within the same document or anchors defined in web pages external to your program report. The following example demonstrates the use of the Html_A procedure to create a pointer to the anchor Anchor1 defined in another web page: Print 'Click below to review the employment opportunities at ABCD company: ' A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 335 Do Html_A('HREF="http://www.abcd.com/homepage/employment.htlm#anchor1" ') Print 'The current list of openings' () Do HtmlA_End We will use HTML hypertext links and anchors to connect the two output reports in our program. Let’s recall that the first program report lists all employees and their health benefit plans. The second report includes only those employees whose dependents lost their benefits within the period covered by the report. In the first report, employees listed in the second report are marked with a “Y” in the “Dep Cov Terminated” column. Wouldn’t it be nice to insert hypertext link pointers in the first report so that, for the employees with terminated dependent health benefits, the links would bring the viewer to the corresponding places in the second report? Also, we can walk an extra mile and place similar link pointers in the second report that will bring the viewer back to the first report, right to the previously viewed portion in this report! The improvement will require a few additional changes to our program: !************************************* ! TEST19E.SQR !Using HTML hypertext links procedures !************************************* !Set report column widths #Define col_emplid #Define col_empl_name #Define col_plan_type #Define col_cov_cd #Define col_cov_drop_flag #Define col_effdt #Define col_dep_id #Define col_dep_name #Define col_sep !*********************** Begin-Setup !*********************** Declare-Layout EE_Data Max-Lines=3000 Left-Margin=1 End-Declare 11 20 5 8 20 12 6 20 1 !Employee ID !Employee Name !Plan Type !Coverage Code !Dependent Coverage Termination !Effective Date !Dependent Beneficiary ID !Dependent Name !Column Separator Declare-Layout Term_Dependents Max-Lines=200 Orientation=landscape Left-Margin=0.3 End-Declare Declare-Report EE_Data Layout=EE_Data 336 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! End-Declare Declare-Report Term_Dependents Layout=Term_Dependents End-Declare End-Setup !********************************** Begin-Program !********************************** Do Html_On() Do Html_Set_Head_Tags('<TITLE>ABCD Company. List of Employees by Health Plan</TITLE>') Do Html_Img(' SRC="edition.GIF" ') !Do Html_Set_Body_Attributes('Background="bkgrnd2.GIF" ') Do Process_EE_Heading Do Process_Dep_Heading Do Process_Main End-Program !************************************************* Begin-Procedure Process_EE_Heading !************************************************* Use-Report EE_Data Do Html_H1(' ') Print 'Employee Health Benefit Coverage Report' (1,1) Do Html_H1_End Do Html_Table('BORDER') Do Html_Caption(' ') Do Html_H2(' ') Print 'List of Employees by Plan Type' (+1) Center Do Html_H2_End Do Html_Caption_End Do Html_Tr(' ') Do Html_Th(' ') Print 'EMPLID' Do Html_Th(' ') Print 'Name ' Do Html_Th(' ') Print 'Plan' Do Html_Th(' ') Print 'Effdt' Do Html_Th(' ') Print 'Cov Type' Do Html_Th(' ') Print 'Dep Cov Terminated' Do Html_Th_End (+1,1,{col_emplid}) (0,+{col_sep},{col_empl_name}) (0,+{col_sep},{col_plan_type}) (0,+{col_sep},{col_Effdt}) (0,+{col_sep},{col_cov_cd}) (0,+{col_sep},{col_cov_drop_flag}) End-Procedure !************************************************************* A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 337 Begin-Procedure Process_Dep_Heading !************************************************************* Use-Report Term_Dependents Do Html_H1(' ') Print 'Employee Dependents with Health Benefit Coverage Terminated' (1,1) Do Html_H1_End Do Html_Table('BORDER') Do Html_Caption(' ') Do Html_H2(' ') Print 'List of Terminated Dependents' (+1) Center Do Html_H2_End Do Html_Tr(' ') Do Html_Th(' ') Print 'EMPLID' (+1,1,{col_emplid}) Do Html_Th(' ') Print 'Name ' (0,+{col_sep},{col_empl_name}) Do Html_Th(' ') Print 'Plan_Type' (0,+{col_sep},{col_plan_type}) Do Html_Th(' ') Print 'Effdt' (0,+{col_sep},{col_Effdt}) Do Html_Th(' ') Print 'Dep ID ' (0,+{col_sep},{col_dep_id}) Do Html_Th(' ') Print 'Dep Name' (0,+{col_sep},{col_dep_name}) End-Procedure !************************************* Begin-Procedure Process_Main !************************************* !Select and print all Employees !who are currently covered by any Health Benefits !and who have Family, or Empl+1 Coverage Begin-Select A.Emplid A.Plan_Type A.Effdt A.Covrg_CD B.Name Do Check_Dep_Termination If $Term = 'N' Let $Term = ' ' End-If Do Print_EE_Data From Health_Benefit A, Personal_Data B Where A.Effdt =(Select Max(Effdt) From Health_Benefit Where Emplid = A.Emplid And Plan_Type = A.Plan_Type 338 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! And Effdt <= Sysdate) And A.Coverage_Elect Not In ('T','W') And A.Emplid = B.Emplid Order By A.Emplid, A.Plan_Type End-Select End-Procedure !************************************ Begin-Procedure Check_Dep_Termination !************************************ ! For each row retrieved in Step 1, compare between the previous ! maximum effective dated record and the current effective dated ! HEALTH_DEPENDNT row to determine a dependent that is no longer ! covered. If dropped dependents are found, print the record to a ! separate report Move 'N' To $Term Begin-Select C.Emplid C.Plan_Type C.Effdt D.Dependent_Benef Move 'Y' to $Term Do Print_Terminated_Dependents Show 'Dep Term found=' &D.Dependent_Benef ' for Employee '&A.Emplid From Health_Benefit C, Health_Dependnt D Where C.Emplid = &A.Emplid And C.Plan_Type = &A.Plan_Type And C.Emplid = D.Emplid And C.Plan_Type = D.Plan_Type And C.Effdt = D.Effdt And C.Effdt = (Select Max(E.Effdt) From Health_Benefit E Where C.Emplid = E.Emplid And C.Plan_Type = E.Plan_Type And E.Effdt < &A.Effdt) And D.Dependent_Benef Not In (Select F.Dependent_Benef From Health_Dependnt F Where C.Emplid = F.Emplid And C.Plan_Type = F.Plan_Type And F.Effdt = &A.EFFDT) End-Select End-Procedure !******************************************* Begin-Procedure Print_EE_Data A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 339 !******************************************* Use-Report EE_Data Let $EmplID=&A.Emplid Let $Date_Str= Datetostr(&A.Effdt,'mm/dd/yyyy') Do Html_Tr(' ') Do Html_Td(' ') Print &A.Emplid (+1,1,{col_emplid}) On-Break Print=Change/Top-Page Do Html_Td(' ') Print &B.Name (0,+{col_sep},{col_empl_name}) On-Break Print=Change/Top-Page Do Html_Td(' ') Print &A.Plan_Type (0,+{col_sep},{col_plan_type}) Do Html_Td(' ') Print $Date_Str (0,+{col_sep},{col_effdt}) Do Html_Td(' ') Print &A.Covrg_Cd (0,+{col_sep},{col_cov_cd}) Defining an Do Html_Td(' ') anchor in the Print $Term (0,+{col_sep},{col_cov_drop_flag}) Employee If $Term = 'Y' Benefits report Let $Emp_Anchor_Str='NAME='||'"'||'Ea' || &A.Emplid||'"' Do Html_A($Emp_Anchor_Str) Do Html_A_End Let $Dep_Anchor = 'HREF='||'"' Defining a link to the ||'TEST19D_01.HTM#Da' || &A.Emplid || '"' Dependents report Do Html_A($Dep_Anchor) Print 'Go to Dependents List' () Do Html_A_End End-If Do Html_Td_End Do Html_Tr_End Add 1 to #Ee_Rcds End-Procedure !********************************************************* Begin-Procedure Print_Terminated_Dependents !********************************************************* Use-Report Term_Dependents Let $EmplID=&A.Emplid Do Get_Dependent_Name Let $Date_Str= Datetostr(&A.Effdt,'mm/dd/yyyy') Do Html_Tr(' ') Do Html_Td(' ') Print &A.Emplid (+1,1,{col_emplid}) On-Break Print=Change/Top-Page Let $Dep_Anchor = 'NAME=' ||'"'|| 'Da' || &A.Emplid || '"' Do Html_A($Dep_Anchor) Defining an anchor in the Do Html_A_End Dependents report If $Emplid <> $Save_Emplid let $Save_Emplid = $Emplid 340 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! Let $Emp_Anchor = 'HREF='||'"' ||'TEST19D.HTM#Ea' || &A.Emplid || '"' Do Html_A($Emp_Anchor) Print 'Go back to Employee List' () Do Html_A_End End-If Do Html_Td(' ') Print &B.Name Defining a link to the Employee Benefits report (0,+{col_sep},{col_empl_name}) On-Break Print=Change/Top-Page Do Html_Td(' ') Print &A.Plan_Type (0,+{col_sep},{col_plan_type}) Do Html_Td(' ') Print $Date_Str (0,+{col_sep},{col_effdt}) Do Html_Td(' ') Print &D.Dependent_Benef (0,+{col_sep},{col_dep_id}) Do Html_Td(' ') Print $DEP_NAME (0,+{col_sep},{col_dep_name}) Do Html_Td_End Do Html_Tr_End Add 1 to #Dep_Rcds End-Procedure !************************************* Begin-Procedure Get_Dependent_Name !************************************* Let $Dep_Name=' ' Begin-Select Name Move &Name to $Dep_Name from Dependent_Benef where EMPLID=&A.Emplid and Dependent_Benef=&D.Dependent_Benef End-Select End-Procedure #include 'html.inc' As you can see, the program inserts HTML hypertext anchors in the second report for each listed employee. When this employee record is processed in the first report, the program inserts a hypertext link to the corresponding anchor in the second report. Similarly, the program generates anchors in the first report for employees listed in the second report and inserts links to these anchors in the second report. Both the anchor names and the hypertext links include the employee Id, thus ensuring uniqueness of each pair of references. Figure 19.11 shows a portion of the first report with a pointer to the second report for employee Martina Griffiths whose dependents lost their benefits during the reporting period. A D D I N G M O R E HT M L FE A T U RE S TEAM LinG - Live, Informative, Non-cost and Genuine! 341 This hypertext link points to Griffiths, Martina’s dependent list in the second report. Figure 19.11 A hypertext link points from employee Griffiths, Martina in the first report to her dependents’ information in the second report. This hypertext link brings viewer back to the Employee List report Figure 19.12 After clicking on the link in the first report, the viewer is brought to this place in the second report. Clicking on the link next to the employee name brings the viewer back to the employee information in the first report. 342 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! If the report viewer clicks on the link, the browser will bring the viewer to the corresponding place in the second report where the employee dependents with terminated benefits are listed. To return back to the first report, one just needs to click on the link to the first report next to the employee name as in figure 19.12. 19.7 How an SQR program accepts parameters from the Internet accepting parameters Accepting parameters from from the the internet internet You can use the regular SQR Input command to accept input parameters from a CGI (Common Gateway Interface) script. Passing input parameters to an SQR program over the Internet involves creating an HTML fill-out form and writing a CGI script to call your SQR program and to pass input parameters to the program. After the SQR finishes, the CGI script copies the program output file to the standard output. Let’s modify our program to limit the employee selection to the specified state only and to accept State as an input parameter. !************************* ! TEST19F.SQR ! Accepting input parameters from a CGI script !************************* Begin-Setup !*********************** . . . End-Setup !****************************** Begin-Program !****************************** Accepting State as an input Do Html_On() parameter from a CGI script Input $State Maxlen=2 ' ' Do Html_Set_Head_Tags('<TITLE>ABCD Company. List of Employees by Health Plan</TITLE>') Do Html_Img(' SRC="edition.GIF" ') Do Process_EE_Heading Do Process_Dep_Heading Do Process_Main End-Program . . . !************************************* Begin-Procedure Process_Main !************************************* !Select and print all Employees !who are currently covered by any Health Benefits !and who have Family, or Empl+1 Coverage Begin-Select ACCEPTING PARAMETER S FRO M THE INTER NET TEAM LinG - Live, Informative, Non-cost and Genuine! 343 A.Emplid A.Plan_Type A.Effdt A.Covrg_CD B.Name Do Check_Dep_Termination If $Term = 'N' Let $Term = ' ' End-If Do Print_EE_Data From Health_Benefit A, Personal_Data B Where A.Effdt =(Select Max(Effdt) From Health_Benefit Where Emplid = A.Emplid And Plan_Type = A.Plan_Type And Effdt <= Sysdate) And A.Coverage_Elect Not In ('T','W') And B.State = $State The program selects only And A.Emplid = B.Emplid employees who reside in the state specified. Order By A.Emplid, A.Plan_Type End-Select End-Procedure !************************************ . . . #include 'html.inc' In Test19F.sqr, the modified program accepts State as input parameter, moves this value to the $State string variable, and uses $State as a bind variable in the Select paragraph of the Main procedure. The logic used to read input parameter is not different from any regular input parameter read code in an SQR program. The next step is to create an HTML fill-out form. This form will include a dropdown selection list to help the user to select the state. The following example presents a sample of a simple HTML fill-in form that can serve our purpose: <HTML><HEAD> <TITLE> Employee Health Benefit Report </TITLE></HEAD> <BODY> <FORM METHOD=POST ACTION=”http://yoursite.com/cgi-bin/benefit_report.sh”> <HR> <STRONG>Please select the State to run the report by: </STRONG> <SELECT STATE=”State” Size=”3”> <OPTION VALUE=”CA”>California <OPTION SELECTED VALUE=”CA”>California <OPTION VALUE=”CT”>Connecticut <OPTION VALUE=”NY”>New York </SELECT><HR> 344 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! <INPUT TYPE=”submit” VALUE=”Submit report”> <INPUT TYPE=”reset”> </FORM> </BODY></HTLM> Figure 19.13 shows how our HTML fill-in form will look on a web page. Figure 19.13 An HTML fill-in form that is used to pass input parameters to a CGI script As you can see, the form contains a state selection drop-down list and a button to submit the report after the user selects the state. The form calls the benefit_report.sh CGI script and passes the selected state code to this script. The script, in turn, calls our SQR program and passes the selected value to this program as input parameter. CGI scripts can be written in different languages: Perl, Visual Basic, C++, UNIX Shell script, and so on. Next we present an example of a UNIX shell script that sets the ORACLE environmental variables, accepts the state code value from the HTML fill-out form, invokes our SQR program via the command line method, and passes the state code as input parameter to the program. #! /bin/sh #Set the appropriate ORACLE environment variables ORACLE_SID=oracle7; ACCEPTING PARAMETER S FRO M THE INTER NET TEAM LinG - Live, Informative, Non-cost and Genuine! 345 export ORACLE_SID ORACLE_HOME=/usr2/oracle7; export ORACLE_HOME SQRDIR=/usr2/sqr/bin; export SQRDIR #identify the output as being HTML format echo "Content-type: text/html" echo "" # get values from fill-out form using the POST method read TEMPSTR STATE=`echo $TEMPSTR | sed "s;.*STATE=;; s;&.*;;"` #invoke the SQR program sqr /usr/reports/test19e.sqr dbaorauser/orapssw -F/usr/tmp/emprpt.lis -F/usr/tmp/deprpt.lis "$STATE" >/usr/tmp/test19e.out 2>&1 < /dev/null if [ $? -eq 0 ]; then # display the output cat /usr/tmp/emprpt.lis else # display the error echo "<HTML><BODY><PRE>" echo "failed to run SQR program" cat /usr/tmp/test19e.out echo "</PRE></BODY></HTML> fi The script retrieves the selected state code into the STATE variable and passes this parameter to the SQR program. The script also redirects the standard input from /dev/ null to prevent the program from hanging when it requires any input, then copies the generated report file to the standard output stream. KEY POINTS 1 You can generate program reports in an HTML compatible format and publish these reports on the Internet. 2 There are three main methods to generate HTML from an SQR program output. 3 You always have to use the -PRINTER:HT, -PRINTER:EH, or -EH_* SQR command line parameter to have your program generate HTML output. 346 CHAPTER 19 IN T E R NE T E N A B LIN G TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 4 All SQR HTML procedures are delivered in the Html.inc file. 5 Executing the Html_On procedure turns on all SQR HTML procedures, but causes SQR to ignore all print position qualifiers. 6 The SQR HTML table procedures can be used to control the report-element positioning. 7 The SQR HTML hypertext link procedures allow you to specify links to other parts of the current web page or to an external web page. Alternatively, you can use these procedures to define anchors within your report so that the same or other web pages can have links to these anchors. 8 Your program can use the regular SQR Input command to accept input parameters from another HTML script. 9 If you run an SQR program without the PRINTER:HT, -PRINTER:EH, or -EH_* command flag, SQR will ignore all SQR HTML procedures in the program and will generate regular LIS files as the program’s output. ACCEPTING PARAMETER S FRO M THE INTER NET TEAM LinG - Live, Informative, Non-cost and Genuine! 347 C H A P T E R 2 0 Debugging techniques IN THIS CHAPTER • The Show and Display commands • Methods of debugging encapsulation • SQR command line flags that are frequently used in debugging 348 TEAM LinG - Live, Informative, Non-cost and Genuine! As happens with any programming language, working with SQR frequently involves tracking down errors, especially when your program logic is complex. Even if your program runs to the end without an abend, you still may want to make sure that all the program variables were assigned the expected values. It is a good idea to display the content of some variables at the important points of your program, or just to trace the program control flow at certain strategic places. We always think that this can be done later, after the program is created, but the best time to plan on program debugging is the time when you write the program. 20.1 Using the Show and Display commands Show and Display commands Using the Show and Display commands Whenthe using it comes show and to debugging display commands an SQR program, there are no fancy step-through debuggers. Your main weapons are the two SQR commands: Show and Display. You can use these commands anywhere in your program except the Setup section. Both the Show and Display commands display the specified texts and variables on the screen and also write the same information into the program log file. If, for example, you ran and completed a program successfully, but your output is not what you expected, then you need to find out whether your program had some logic flaws, or whether the unexpected results have been caused by an incorrect selection in the program’s query. The simplest way to find this out is to add some Show or Display commands to the program: !Using the Show command to display selected columns Show 'Select Started' Begin-Select A.Emplid A.Name Show 'Selected Emplid = ' &A.Emplid ' Name= ' &A.Name From PERSONAL_DATA Where Name Like 'Abr%' End-Select In the example, each selected record is displayed on the screen and written to the program log file. If you do not see any records displayed on the screen, you may have a problem with your Select statement. There can be many reasons: the selection criteria in the Where clause may be too restrictive; the database tables do not have the expected information; or (most likely), your Select statement is incorrect. At this point, the Display or Show commands are not very helpful, and the best way to track down the problem is to use a database on-line access tool like SQL Plus for Oracle, SQL Programmer for Sybase, or QMF for DB2. In PeopleSoft, you can use the PS Query tool to debug your query, and then paste the Select statement into your SQR program. Please keep in mind that because different databases have slightly different Select S HO W A ND DI SP L AY C O MM A NDS TEAM LinG - Live, Informative, Non-cost and Genuine! 349 statement syntax, this statement may need some minor adjustments after you paste it into an SQR program. The Display command is somewhat similar to the Show command, but it has less power: you cannot display multiple fields in one Display command. For example, to display the same information as we did in the preceding example using the Display command, you may need four Display commands (the Noline qualifier prevents the line advancement; thereby keeping all four displayed fields on one screen line): Display 'Selected Emplid = ' Display &A.Emplid Noline Display ' Name= ' Noline Display &A.Name Noline In addition to its ability to display multiple fields, the Show command allows you to control the cursor position on screen, send beeping signals, make the screen fields blink, display the screen fields in bold, underline the screen fields, and so forth. In fact, it may be used as a crude screen design tool, although it cannot compete with today’s systems like PowerBuilder or Visual Basic. As you can see, the Show command is more the Display and convenient to use, but no matter what command Note Both Show commands allow you use for debugging—Display or Show— you to use edit format masks they both can be instrumental in program testing. when displaying the content of Careful planning and placement of the Display the program variables. or Show commands in your program will help you to identify potential problems while spending as little time and effort as possible. 20.2 Using conditional compiler statements Conditional compiler statements allow you to modify your program based on the values of certain SQR command line flags or substitution variables. Note that these statements are checked at compile time, not at run time. conditional compiler statements Conditional compiler statements Using conditional compiler statements 20.2.1 The #Debug command You can optionally use the #Debug conditional compiler directive by executing the Display, Show, Print, or other commands in your program during testing, and then deactivating them when the program is released to production. This approach is called debugging encapsulation. Any SQR command that immediately follows the #Debug statement will be compiled only when the corresponding -Debug flag is specified in the SQR command line. The #Debug command may be appended (suffixed) with one or more letters or 350 CHAPTER 2 0 DEBU GGING TECHNIQU ES TEAM LinG - Live, Informative, Non-cost and Genuine! numbers, e.g.; #Debugxy, to provide additional flexibility in debugging. You may optionally specify up to ten different suffix letters or numbers. When you use a specific suffix letter in the -Debug command line flag, all #Debug statements with the same suffix in the program will be activated by the compiler. This is in addition to all #Debug statements with no suffix. For example, if the flag -DEBUGxyz is used on the command line, SQR automatically creates four substitution variables: debug, debugx, debugy, and debugz. This causes all commands following the #Debug, #Debugx, #Debugy, or #Debugz statements to be activated: #Debug Show 'Start Proc1' #Debugx Show 'Start Proc2' #Debugy Display 'Start Proc3' #Debugz Display 'Start Proc4' If a statement like: #Debuga Show 'Test Started' is coded in the program, and no -DEBUGa flag is specified in the command line, then this statement will not be processed during the compile stage, and therefore, will not be executed. Table 20.1 shows the examples of the -Debug flag in the SQR command line, #Debug statements, and the results of compilation. Table 20.1 Using the #Debug commands Debug flag #Debug statement in the program The result of program Comment compilation -DEBUG #Debug Show 'A' Show 'A' -DEBUG #Debuga Show 'B' -DEBUGabcd #Debuga Show 'A' #Debugb Show 'B' #Debugc Show 'C' #Debugd Show 'D' #Debug Show 'N' #Debugx Show 'X' The -DEBUG flag was defined with no suffix, the command following the #Debug command is activated. Because no -DEBUGa flag was specified the command following the #Debuga command is ignored. Show 'A' Show 'B' Show 'C' Show 'D' Show 'N' All commands except #Debugx are activated. 20.2.2 Other conditional compiler directives When using the #Debug directive, you have to place this command before any SQR statement that you would like activated or deactivated. The #Debug directive is not very CONDITIONAL COMPIL ER STATEMENTS TEAM LinG - Live, Informative, Non-cost and Genuine! 351 convenient when you need to turn entire sections of code on or off for debugging. It can be achieved by using the following commands: #Ifdef or #Ifndef /#End-If #If/#Else/#End-If In fact, it is a good idea to code the program control flow trace statements and encapsulate them into the conditional compiler directive blocks when you write a program. For example, a code such as the following can be very useful to trace down potential problems that may occur in the future: !Using the #Ifdef command Begin-Procedure Get-Employees #Ifdef Debug Show 'Procedure Get Employees Started' #Endif Begin-Select Select Emplid Name SSN #Ifdef Debug Show 'Employee:' &Emplid ' Name: ' &Name ' Selected ' Add 1 to #Rcds_selected #End-If From Personal_Data Where … End-Select End-Procedure … In the example, we used the conditional compiler directives #Ifdef/#End-If to encapsulate SQR statements that we want executed only if the Debug substitution variable is defined in the program. This variable can be defined explicitly using, for instance, the #Define Debug 1 statement in the beginning of your program, or implicitly by specifying the -DEBUG flag in the command line. The second option is preferable, especially after your program is released to production, since, in this case, you will not have to change the program every time you want to run it in the debugging mode. Remember, you can specify the -DEBUG flag with a suffix, thereby creating not only one substitution variable Debug, but also a few additional substitution variables. For example, the -DEBUGabc flag in the command line causes the creation of four substitution variables: Debug, Debuga, Debugb, and Debugc. All these variables can be checked 352 CHAPTER 2 0 DEBU GGING TECHNIQU ES TEAM LinG - Live, Informative, Non-cost and Genuine! by the #Ifdef commands, thus providing a good deal of flexibility during both the program testing and maintenance. As with the #Ifdef/#End-If directive, you can use #If/#Else/#End-If conditional compiler directives to split the encapsulated code into conditional blocks: !Using the #If/#Else/#End-If command #Define Test 'Y' … #If Test = 'Y' Show 'Test Started' #Else Show 'This is a Production Run' #End-If … 20.3 Using SQR command line flags to enable SQR debugging information using flags to enable debugging enabling SQR debugging information In addition enabling SQR to the debugging -DEBUGinformation command line flag, a few other command line flags may prove helpful in tracking down different problems. Depending on the particular situation, you can use the SQR command line flags or their combinations shown in table 20.2. Table 20.2 Additional SQR command line flags used in debugging SQR Command Purpose Line Flag Comments -S To show the status of all program cursors. Upon program completion, displays the following information: • text of each SQL statement • number of times each SQL statement was compiled and executed • total number of rows selected All this information will be displayed on the screen and placed into the program log file (see figure 20.1). Note that the bind variables will not be shown. -Tnn To generate only nn pages of the program output report. All ORDER BY clauses in the SELECT statements will be ignored. For multiple report programs, only the first nn pages of the first program report are produced. -C To create the Cancel Dialog This option will enable you to cancel the program Box during the program run. execution when necessary (and avoid using CNTRLALT-DELETE). US ING F LAGS TO ENABLE DEBUG GING TEAM LinG - Live, Informative, Non-cost and Genuine! 353 Table 20.2 Additional SQR command line flags used in debugging (continued) SQR Command Purpose Line Flag -E[file] To direct all the error messages to a file. Comments You can direct all SQR-generated messages to a specified file. The default file name will be the name of your SQR program with an extension .err, e. g., Test01.err. This way, you can direct all the Show and Display command output to a LOG file and all SQR-generated messages to an ERR file. If SQR generates no messages, no ERR file is created. To illustrate the use of the -S flag, let’s run one of the programs created in chapter 11, Test11F.sqr. with this flag. After the program finishes, the program log file will have the information shown in figure 20.1. Cursor Status: Cursor #1: SQL = select A.Emplid, A.Hire_Dt, B.Name, B.State From Employment A, Personal_Data B Where A.Emplid=B.Emplid And A.Termination_Dt is Null And [$Where] Compiles = 2 Executes = 1 Rows = 175 SQR: End of Run. Figure 20.1 A program log file with cursor information As you can see from the log file displayed in figure 20.1, the information about cursors can be helpful in identifying potential problems with SQL statements in your program. Unfortunately, it is not very comprehensive. For example, the report does not show the SQL statement as it is passed to your database engine for execution. Depending on the problem that needs debugging, you may need to supplement the -S flag with a few Show or Display commands, or use other techniques described earlier in this chapter, but do not discard the output produced with the help of the -S flag. It could help you, not only in finding SQL-related problems, but also in tuning your program’s performance. Your data base administrator may use this report to find a way to increase the database efficiency. 354 CHAPTER 2 0 DEBU GGING TECHNIQU ES TEAM LinG - Live, Informative, Non-cost and Genuine! You may find the -Tnn command flag useful when testing large reports. It allows you to generate only the specified number of pages for your report output. The program will stop execution after the first nn pages of output are created. Note that because this flag suppresses all Order By clauses in the Select statements, your database records will not be sorted when using this option. This may lead to incorrect processing. For example, if you use the break logic in your report, this flag may cause incorrect processing of breaks. KEY POINTS 1 The Display and Show commands are the main instruments in debugging SQR programs. 2 The -DEBUG SQR command line flag in combination with the #Debug, #IfDef, and #IfNdef statements, can help to encapsulate the debugging logic in your program. 3 The -S, -T, -C, and -E SQR command line flags can provide additional help during the debugging process. US ING F LAGS TO ENABLE DEBUG GING TEAM LinG - Live, Informative, Non-cost and Genuine! 355 C H A P T E R 2 1 Good programming practices IN THIS CHAPTER • Supporting multiple national environments • Developing and maintaining platform and database independence • Handling error conditions • Making your programs easier to build, test, and maintain 356 TEAM LinG - Live, Informative, Non-cost and Genuine! In this chapter, we will not introduce any new commands, parameters, or flags. Instead, we are going to discuss a few programming techniques designed to address a number of problems and challenges that many SQR programmers may face while working on real projects. The approaches and techniques presented in this chapter should be viewed as suggestions and ideas rather than strict technical recommendations. We are sure that our readers will be able to come up with alternative solutions to the discussed problems (perhaps, even better ones), and possibly, add to the list of the subjects discussed in this chapter. Your actual business environment will determine your particular priorities and programming standards. We feel, however, that at least, some of the discussed problems are common, and, therefore, worthy of discussion. 21.1 Going global Today’s business trend towards a global economy puts additional pressure on business software developers. Many large corporations have scores of subsidiaries in different countries involving different languages, alphabets, currencies, ways of displaying dates, times, and so forth. Obviously traditional methods of writing programs designed for just domestic use are becoming outdated. In today’s world, any computer language not capable of addressing these issues puts itself at risk of losing market share and, ultimately, becoming obsolete. SQR addresses the problems of global development by arming both program developers and system administrators with a number of options. The available options include special system settings that can be used to control the system defaults, as well as commands that allow program developers to dynamically switch from one set of system parameters to another. In addition, the most recent releases of SQR (4.2 and higher) include support for double-byte character manipulations and the Japanese Imperial Era calendar. SQR provides different ways to write programs capable of working in different national environments. Which way is the best depends on a number of factors. The most important factor concerns the programming standards at your site and the stringency of their enforcement. SQR provides program developers with a high degree of flexibility when it comes to printing or manipulating data. When printing or manipulating dates, money, or numbers, programmers can choose between the endless combinations of different edit format mask characters. This flexibility may backfire if you need your program to switch from one national environment to another without program changes, depending on some input parameter of system settings. For example, if you use hard-coded currency format masks, like $,$$$,$$$V99, switching to another currency sign or another way of displaying decimals will involve GOING G LOBAL TEAM LinG - Live, Informative, Non-cost and Genuine! 357 making changes to the program or, worse yet, creating different versions of the same program. Another example occurs when your program uses hard-coded date format masks, for example, 'MONTHbDD,YYYY'. This is the traditional American way of displaying dates, and will display the New Year’s date as JANUARY 1, 1998. In France, the same date will be more likely displayed as 1 janvier 1998. If your program uses a hard-coded date mask (common in the USA) MM/DD/YYYY, April 10, 1998 will be displayed as 04/10/1998. In most European countries, the recipients of this report will read this date with the month and date transposed, i.e. October 4, 1998: they expect the day’s portion of a date to precede the month’s portion. As with hard-coded currency format masks, hard-coded date masks make multiple language support very difficult. The best way to eliminate the problem is to use special SQR keywords Date, Money, and Number when printing or manipulating dates, monetary fields, or numbers. These are preset edit masks that format dates, money or numbers according to the settings in the SQR.ini file. The downside of using the keywords is that they impose certain limits on the way your programs will display and manipulate data. For example, if DATE-EDIT-MASK is defined in your INI file as Mon-DD-YYYY, you will need to make an additional effort to squeeze this date into an 8-character field to make it fit some of your overly populated reports. On the other hand, if you are involved in a global development project, using these keywords must be a part of the programming standards in your team. Assuming that the use of edit format keywords is enforced, you can use SQR locales to make your programs automatically adapt to national environments. An SQR locale is a set of local preferences for language, currency, and presentation of dates and numbers. For example, the US locale will use English names for days and months, the dollar sign for currency, the American way of inputting dates (month first, day second), the American way of displaying dates, the twelve-hour cycle for time, numbers with commas separating the thousands, and a period for the decimal place. The French locale will use French names for days and months, F (French franc) for currency, the French way of inputting dates (day first, month second), military time, and the European way of displaying numbers (periods separating thousands, and a comma for the decimal place). If you use this approach, switching to another national environment will require no programming changes. All you need to do is to make sure that users running your reports have the right INI files on their machines. SQR provides predefined locales such as US-English, UK-English, German, French, Spanish, or Japanese. If this is not enough, you can easily create additional locales and place them into the SQR.ini file. For example, this is how the US-English locale looks: NUMBER-EDIT-MASK MONEY-EDIT-MASK MONEY-SIGN 358 = '999,999,999.99' = '$$9,999,999.99' = ' $' CHAPTER 21 GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! MONEY-SIGN-LOCATION THOUSAND-SEPARATOR DECIMAL-SEPARATOR EDIT-OPTION-NA INPUT-DATE-EDIT-MASK DATE-SEPARATOR TIME-SEPARATOR EDIT-OPTION-AM EDIT-OPTION-PM EDIT-OPTION-BC EDIT-OPTION-AD DAY-OF-WEEK-CASE DAY-OF-WEEK-FULL DAY-OF-WEEK-SHORT MONTH-CASE MONTH-FULL MONTH-SHORT = Left = ' ,' = ' .' = 'n/a' = 'MM/DD/YYYY' = ' /' = ' :' = 'am' = 'pm' = 'bc' = 'ad' = Edit = ('Sunday','Monday','Tuesday', 'Wednesday','Thursday', 'Friday','Saturday') = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat') = Edit = ('January','February','March','April','May', 'June','July','August','September','October', 'November','December') = ('Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec') Usually, the SQR.ini file contains a few locales. One locale is considered a default locale, and its name is specified in the [Default-Settings] section of the SQR.ini file. If the name of the default locale is not specified in the SQR.ini file, SQR will use a locale named System. This locale is also present in the SQR.ini file. By assigning users in different countries to different locales, you can make your programs switch automatically to the necessary environment without changing a single line of code. Of course, your program will only change the way it displays dates, money, and numbers. You will still have to take care of things like social security numbers, postal codes, addresses, telephone numbers, and so on, but these, too, can be automated by storing the relevant edit format masks in a database and using the current locale name as a key to retrieve the proper masks. The SQR predefined variable $sqr-locale can be used to determine the name of the current locale. A similar approach can be employed by storing report headers and other text strings translated into different languages in a database. In many cases, your program may need to switch to another locale dynamically. This may happen when SQR programs are run on a server and share the same INI file, or when the same program must generate multiple reports for different countries. These programs may accept the country code as an input parameter or they may read it from an input file or a database. The Alter-Locale SQR command then can be used to change the current locale assignment at any moment. You can use this command as many times as you want. In addition to switching the current locale, you can also use this command to override certain parameters in the existing locales. The example next demonstrates the use of this command: GOING G LOBAL TEAM LinG - Live, Informative, Non-cost and Genuine! 359 ! TEST21A.SQR !Dynamically changing locales Begin-Setup Declare-Variable Date $Date_Time End-Declare End-Setup Begin-Program Let $Date_Time = datenow() Let #Salary = 120000 Alter-Locale Locale = 'US-English' ! Switching to US locale DATE-EDIT-MASK = 'Month DD, YYYY' Print 'Country USA ' (1, 1) Print 'Date, Time: ' (+1, 1) Print $Date_Time (, +1) Date Print 'Salary: ' (, 35) Print #Salary (, +1) Money Alter-Locale Locale = 'UK-English' ! Switching to UK locale Print 'Country Great Britain ' (+1, 1) Print 'Date, Time: ' (+1, 1) Print $Date_Time (, +1) Date Print 'Salary: ' (, 35) Print #Salary (, +1) Money Alter-Locale Locale = 'German' ! Switching to German locale Print 'Country Germany ' (+1, 1) Print 'Date, Time: ' (+1, 1) Print $Date_Time (, +1) Date Print 'Salary: ' (, 35) Print #Salary (, +1) Money Alter-Locale Locale = 'French' ! Switching to French locale Print 'Country France ' (+1, 1) Print 'Date, Time: ' (+1, 1) Print $Date_Time (, +1) Date Print 'Salary: ' (, 35) Print #Salary (, +1) Money Alter-Locale Locale = 'French' ! Making French locale European Money-Sign = ' EU' ! switching currency to euro Print 'European Market ' (+1, 1) Print 'Date, Time: ' (+1, 1) Print $Date_Time (, +1) Date Print 'Salary: ' (, 35) Print #Salary (, +1) Money Alter-Locale Locale = 'Spanish' Print 'Country Spain ' (+1, 1) Print 'Date, Time: ' (+1, 1) 360 CHAPTER 21 ! Switching to Spanish locale GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! Print $Date_Time (, +1) Date Print 'Salary: ' (, 35) Print #Salary (, +1) Money End-Program The output of Test21A.sqr appears in figure 21.1. Figure 21.1 Using different locales to print dates and money in different languages Starting with version 4.2, SQR made another step in supporting multiple languages by introducing double-byte character manipulations. To enable this feature, the ENCODING environmental variable in the SQR.ini file must be set to either SJIS or JEUC. The default value of this variable is ASCII. Two new SQR commands, Mbtosbs and Sbtombs, can be used to convert double-byte strings to their single-byte equivalents, and single-byte strings to their double-byte equivalents, respectively (please see appendix D). A number of double-byte string manipulation and conversion functions have been added to the list of SQR built-in string functions. In addition, two new date edit format codes can now be used in the Print command to support the Japanese Imperial Era calendar. Both codes are supported only when the ENCODING environmental variable is set to enable double-byte character manipulations. The ER format code returns the name of the Japanese Imperial Era in the appropriate kanji (e.g., “Heisei” is the current era). The EY format code returns the current year within the Japanese Imperial Era. GOING G LOBAL TEAM LinG - Live, Informative, Non-cost and Genuine! 361 21.2 Creating platform-independent programs One of the strong points of SQR is its ability to run on multiple platforms. Today’s corporate-wide enterprise systems usually include components running on different operating systems from Windows to mainframe. Making a program portable between different platforms has always been a developer’s dream. SQR is inherently designed to support platform independence. Because SQR programs are usually compiled and executed in one invocation, they can be moved between platforms at source level, thereby requiring no changes (at least in theory). Even precompiled SQR programs remain portable: when an SQR program is pre-compiled, the resulting SQT file is not a true executable file. It represents a pseudo-code, interpreted by the SQR Execute (SQRT or SQRWT). For example, you can use SQRT on UNIX to run an SQT file created on Windows. The fact that SQR is capable of producing multiple platform programs does not mean that any SQR program can be moved between different operating systems without special effort. Certain program functionality requirements can limit a program’s portability, sometimes making it nearly impossible due to numerous technical difficulties and high development costs. To avoid possible confusion, let’s call a program platform-independent if it is portable across a few of the most popular operating systems. In fact, many developers would consider an SQR program platform-independent when the program can be executed on both client and server without any changes. Making an SQR program platform-independent involves careful planning and selfdiscipline. At times you won’t need to do anything to make your program portable. In other cases, creating platform independence will require additional programming effort. Let’s talk about a few major factors that may affect your program’s platform independence. When an SQR program contains fully qualified file names, they should not be hard-coded. This is true for input/output files, #Include files, image files, printer startup files, report files used in the New-Report command, file names used in SQR builtin functions, and so on. Hard-coded fully qualified file names include file directories which are platform-specific. For example, a fully-qualified file name must be coded differently for different operating systems: c:\temp\myfile /temp/myfile dsn=temp.myfile( ! DOS, Windows, Windows NT ! UNIX ! MVS A few relatively simple techniques exist to solve the problem. 362 CHAPTER 21 GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! One method is to include all required file names in the program’s input parameter list. The parameters can be input via user entry (not the best idea since it involves an extra load for users and a high likelihood of entry mistakes) or placed into the program’s argument file. This method works well when your program deals with a small number of files. When the number of files is substantial, the number of input parameters becomes equally substantial (a fact that your data center administrator may not appreciate). Another method for addressing coding differences for fully qualified file names is to define a directory for all files as a substitution variable in an SQC file. Depending on the platform, the proper platform-specific SQC file can be placed in the program working directory. This approach is used, for example, by PeopleSoft in its Setenv.sqc file. The program logic in this file checks the current platform defined in the platform-specific SQC files and sets a substitution variable {Fileprefix} based on the platform. For example, for Windows NT, the substitution variable is set to ‘c:\temp’, whereas, for UNIX, this variable is set to ‘/usr/tmp’. The fully qualified file names can be built into the program by concatenating the directory and file names. You can define more than one directory: for example, one directory for image files, and another directory for input/output files. Here is an example of using three substitution variables to define file directories: Let $My_File = '{fileprefix}' || $My_File || '.OUT' Let $Input_File = '{io_dir}' || $File_In Let $Logo_File = '{image_dir}' ||$Logo Please keep in mind that, if you use a substitution variable for a directory, the fully qualified file names will be resolved at compile stage, therefore, you cannot use this method to create a pre-compiled program on one platform and later run it on another platform. The problem can be easily solved by using input parameters instead of substitution variables. The third method used when working with full file names is to check the $sqrplatform SQR predefined variable that contains the operating system name. Use the Evaluate command to build the proper file names depending on the value of this variable. Rule number one: avoid hard-coded fully qualified file names in platform-independent programs. In chapter 18, we discussed the Call System command, which allows you to issue native operating commands from an SQR program. While this command increases your program’s power and flexibility, you should try to avoid using it in platformindependent programs. SQR provides a few functions that perform a similar task to that of many platform-specific operating system commands. These SQR functions are platform-independent and you should definitely choose them over the Call System command. We hope that SQR will add more functions in its future releases. One SQR C RE A T I N G P L A T FOR M -IN D E P E N DE N T P R O G RA M S TEAM LinG - Live, Informative, Non-cost and Genuine! 363 function you have to be careful about is: getenv(). This function allows you to retrieve the value of the specified environmental variable. If you use this function, make sure that you specify only variables that are valid for any operating system your program might run on. This suggests your rule number two: avoid calling operating system commands from SQR programs. Use similar SQR functions instead. What if SQR functions cannot do what you need? Use a method similar to the file name building technique: check the $sqr-platform SQR predefined variable and, depending on its value, use the appropriate platform-specific operating system command. This approach does not make your program platform-independent from a purely scientific point-of-view, but, practically speaking, the program can still be considered portable. If, for example, you can improve your program performance by calling a sort utility, do not discard this just because this call will make the program platformdependent. It is worth the extra programming effort to call the proper sort utility, depending on the $sqr-platform variable value. There are many other factors that may affect your program portability: not all printer types are supported by all operating systems; not all databases can run on any platform; not all image file types are valid across different platforms; and the return codes from SQR file processing commands are platform-specific. Only a comprehensive testing of your program on different platforms can ensure your program platform independence. Rule number three: test your programs on different platforms. 21.3 Database-independent programs Many programming tools claim that they can work with any database, but SQR is one of the few that can really do this. Its database versatility is phenomenal: SQR can work with Oracle, MS SQL Server, Sybase, SQLBase, DB2, Informix, Ingres, Allbase, Rdb, as well as with other databases via the ODBC. Does this make your program database-independent automatically? You probably already know the answer. As with platform independence, database independence involves additional programming effort. Rule number one: do not use database-specific functions. This rule was a bit difficult to follow in prior versions of SQR. Version 4 introduced a number of date-processing functions that eliminated the need to use similar database-specific functions. Even with prior versions, you could use a combination of SQR commands and built-in string functions to replace nearly all database-specific functions. One limitation on the use of SQR commands and built-in functions remains: they cannot be used in the Where clause. If you need to use data conversions and other 364 CHAPTER 21 GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! built-in functions in the Where clause, your choice is limited to database-specific SQL functions. Rule number two: avoid using built-in functions in the Where clause. Please keep in mind that in many cases, built-in functions in the Where clause can be very instrumental in reducing the SQL query sizes and improving performance. A reasonable compromise can be found in coding separate SQL logic for each database used in your company and calling the appropriate code, depending on the value of the $sqr-database predefined variable. The following two examples are designed to demonstrate the pros and cons of using database-specific functions. Both programs do the same job of selecting all employees who were hired or rehired during the specified year. Test21B.sqr uses the datetostr() SQR built-in function to convert dates to strings, and therefore, is database-independent: !********************************* !Test21B.SQR !A database-independent SQR program !********************************* Begin-Program !********************* Do Main End-Program !********************* Begin-Procedure Main !********************* Begin-Select Emplid Hire_Dt Rehire_Dt If &Hire_Dt > &Rehire_Dt Let $Serv_YYYY=datetostr(&Hire_Dt,'YYYY') Else Let $Serv_YYYY=datetostr(&ReHire_Dt,'YYYY') End-If If $Serv_YYYY = '1998' Print &Emplid (+1,1,10) Print &Hire_Dt (,+1,10) Print &Rehire_Dt (,+1,10) End-If From Employment End-Select End-Procedure The second program Test21C.sqr, employs the Oracle database functions To_ Char and Decode to carry out date-to-string conversions. Both functions are placed in the Where clause: DATABASE-INDEPEND ENT PR OGRAMS TEAM LinG - Live, Informative, Non-cost and Genuine! 365 !**************************************** !Test21C.SQR !Using Oracle database-specific functions !**************************************** Begin-Program !********************* Do Main End-Program !********************* Begin-Procedure Main !********************* Begin-Select Emplid (+1,1,10) Hire_Dt (,+1,10) Rehire_Dt (,+1,10) From Employment Where to_char(decode(rehire_dt,Null,hire_dt,rehire_dt),'YYYY')= '1998' End-Select End-Procedure As you can see, the second program is much smaller and straightforward. It is also more efficient because it restricts the employee selection in its Where clause. But it is not database-independent! Today’s RDBMS offer ever-improving performance and flexibility. Strong competition between the relational database vendors has forced the vendors to offer more and more database-specific features to attract new customers and retain the existing ones. This has resulted in less compatibility between different databases. If you want your programs to be database-independent, stick to the common denominator: ANSI SQL standards. This way, you can be sure that at least the SQL parts of your programs will work the same way on different databases. This is your rule number three: use the ANSI standards in your SQL. So why doesn’t everyone use ANSI standards? For two reasons: first, the ANSI standards are too restrictive, and second, many programmers do not know them. Working with dates presents another problem for database independence. As we mentioned earlier in this chapter, using hard-coded date masks makes it difficult for your program to work in different language environments. Using no edit mask in dateto-string conversions will make SQR utilize the default date mask. This default date mask is controlled by the SQR environmental variable SQR_DB_DATE_FORMAT. If this variable is not defined, SQR will use the first database-specific default date format. Consequently, the String command may produce different results, depending on the database-specific format used: String &Emplid &Name &Effdt &Address Into $Empl_Record 366 CHAPTER 21 GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! Assuming the &Effdt value is March 15, 1998, the command will move the value shown in table 21.1 to the date portion of the output record. Table 21.1 Database-dependent default date formats Database Output Oracle 15-MAR-1998 Sybase MAR 15 1998 DB2 1998-03-15-00.00.00.000000 Informix 1998-03-15 00:00:00.000 SQLBase 1998-03-15-00.00.00.000000 To avoid problems, move &Effdt to a string variable using an edit mask (same for all databases) prior to invoking the String command. Rule number four: do not rely on database-specific default date formats. It is always a good idea to check the database return code after each SQL operation. But can you always expect the same return codes when working with different databases? Will the databases return the same error messages? The answer is no! It may be a good idea to code separate error-handling routines for different databases and to use the $sqr-database predefined variable to call the right routine. Rule number five: do not assume that all databases return the same error codes and messages. Many SQR commands have database-specific parameters and flags or use different syntax with different databases. Here are a few examples: • -C, -XP, -NR, -SORT, -DB, -LOCK flags in the Begin-Select and Begin-SQL commands • -B flag in the Begin-Select command • the Execute command. Rule number six: avoid database-specific parameters and flags in SQR commands. 21.4 SQL or procedure calls? In some cases, your program may include rather complex database queries. Should you go crazy by making your SQL more and more complex or should you take advantage of SQR procedure calls instead? There is no single answer to this question. Each of these approaches may have both advantages and disadvantages, depending on your specific situation. S Q L O R P R O C E DU RE C A L LS ? TEAM LinG - Live, Informative, Non-cost and Genuine! 367 In many cases, confining all database queries to one SQL may result in a better program performance by reducing the number of open cursors and minimizing the communication between SQR and the database. It also makes your program shorter. On the other hand, there may be a number of advantages of making your main SQL simpler by shifting a substantial portion of work to procedures that you can invoke from the main SQL. One of the main drawbacks of very complex “one covers all” SQL statements is that they tend to be very difficult to program and debug. By its nature, SQL is not a procedural language and when your query involves complex logic, you have to create numerous sub-selects, thus increasing a possibility of making an error. It also makes your program difficult to read and understand by other programmers. One single change to your SQL may cause unexpected changes in the query results. Another problem is that complex SQL statements may have to join many large tables, choking your database engine, especially when you have many outer joins performed against large tables. And in many cases you need outer joins to ensure no data loss when records in joint tables do not match. Employing one SQL also causes programmers to use more database-specific functions, which makes your program less database-independent. Using SQR procedure calls from main SQL can make your program much simpler to debug. It will also improve the program’s readability. Changes to the program logic are much easier to make and these changes are much more predictable. After all, didn’t we say that the main strength of SQR is its ability to combine SQL and procedural logic? Why not use this approach all the time? The main problem is performance. Each query in a called procedure creates a database connection and opens a cursor, which slows your program. In many cases, you may face this dilemma when working on a master-detail program. In this case, a reasonable compromise may be found in using the Load-Lookup command to load the necessary detail data into the program memory and call an SQR procedure to retrieve these data from memory instead of making another SQL call. 21.5 Using the top-down approach In most examples in our book, we have tried to demonstrate the modular approach to writing SQR programs. We usually started with the main procedure that called other procedures. Each called procedure encapsulated certain functionality, thereby avoiding creating spaghetti-like code. Limiting each procedure to just one function is very important when it comes to program maintenance. Another suggestion is to never combine the Select and SQL paragraphs in one procedure: this makes your program difficult to test and debug. 368 CHAPTER 21 GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! Using procedures allows you to create programs by starting from the main procedure and gradually adding lower level procedures. This approach is called top-down construction. What is good about this method is that you do not have to code the entire program at once. The yet-to-be-written procedures can be made dummy by coding the Begin-Procedure and End-Procedure statements and hard-coding variables that will be populated in these procedures. This way, you can start testing your program’s top-level functionality almost from the moment you start the program code. This is especially important if your program is just one of several related programs in a job or script. Another benefit of the top-down construction is that it forces you to think of the program-testing process well before you have finished with coding. If you work in a team, other team members do not have to wait until you deliver your program—you can give them an unfinished program that may support certain application’s functions while you continue working on the remaining procedures. One problem with the top-down approach is that different procedures often communicate by using shared variables. This creates a good deal of confusion and may be one of the main sources of programming errors. A good way to solve this problem is to use local procedures. 21.6 Local procedures vs. global procedures Depending on your previous programming experience, you may have differing opinions about the benefits of using local and global procedures. C programmers usually prefer to use local procedures as much as possible. Others may argue that using local procedures involves too much overhead. We shall discuss advantages and disadvantages of local procedures, and let our readers be the final judges. As you already know, all procedures in SQR are considered global by default. In order to make a procedure local, it must have parameters or must be explicitly declared local. Variables that appear in local procedures are not available outside of their respective procedures. This way, the same variable name can be used in different local procedures without creating an error situation. At the same time, local procedures can communicate with other local and global procedures via parameters. Since global variables defined outside of a local procedure can be accessed only by using special naming conventions (see chapter 5), there is no risk that some variable values can be accidentally overridden. As you can see, local procedures can be instrumental in top-down construction. On the other hand, they have not become popular among the SQR programming community. Using local procedures is definitely the way to go when: • the same procedure is called more than once from other procedures (or is called recursively) and uses only its parameters to communicate with the outside world LOCAL PRO CEDU RES VS. GL OBAL PROCEDUR ES TEAM LinG - Live, Informative, Non-cost and Genuine! 369 • a procedure resides in an #Include file and is designed to be used by different programs. In other cases, the use of local procedures by inexperienced programmers may result in additional programming effort and problems with debugging. If you decide to try using local procedures, please keep in mind the following: • All variables defined within local procedures are considered local variables, and are not available to other procedures. If you use a variable name that was defined in another local procedure, SQR will not alert you and will consider this variable a new local variable. • In order to access global variables or columns, they have to be prefixed with an underscore (_) between the first character and the variable or column name (e.g., &_A.Emplid, or $_FileName). • Local variables are not initialized every time the procedure is called, but retain their values from the previous procedure call. Taking these rules into consideration, it’s very easy to misspell a variable name or forget about the underscore, making your program prone to errors that are sometimes difficult to trap. Keep in mind that SQR will not alert you since declaring variables is not enforced in SQR. A misspelled variable will simply be considered a new one. 21.7 Handling error conditions Coding error-handling routines allows you to alert users of any unusual situation, avoid system crushes, and prevent a possible database corruption. A well-written program should have enough error-condition handling to cover both programming errors and unexpected external environmental problems (erroneous input, server crushes, etc.). A number of SQR commands allow you to use the On-Error argument to explicitly specify the name of an error-handling procedure. Among these commands are • Begin-Select • Begin-Sql • Connect • Execute (For Sybase, Microsoft SQLServer and Ingres) The procedure specified in the On-Error argument will be called if the command fails. If no procedure name is specified, and the command fails, SQR halts with an error message. 370 CHAPTER 21 GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! If your Select paragraph does not contain dynamic variables, there is no need to code the On-Error argument, since it will be checked for syntax errors at the compile stage. When dynamic variables are used, this argument is recommended. If the Begin-SQL command is used in the Setup section, the On-Error argument specifies the action (Stop, Warn, or Skip), rather than the name of an errorhandling procedure. The On-Error argument in the Divide command works differently. It allows you to set the result to High or Zero when a zero-division occurs. If this argument is omitted, and a zero-division is attempted, SQR halts with an error message. Since zerodivision could also happen in the Let command by simply using the “/” sign, you should either check the divisor before division, or use the Divide command with the On-Error argument instead of /. Please note that the Array-Divide command does not have the On-Error argument. In addition, the following SQR commands use the Status argument to return the status of their execution: Input, Open, Read, Write, Call System. You should check the status variable after executing these commands. SQR built-in functions do not have the Status argument, but many return a numeric value that can be checked for the status of their execution. Use the Let command to assign this value to a numeric variable, for example: Let #File_Exists = exists($File_Name) If #File_Exists <> 0 Show 'File ' $File_Name ' ' does not exist. ' Else Do Main_Process End-If It is difficult to overstate the importance of error handling. The effort spent to code the error-handling routines is never a wasted time. You will be awarded by a lower maintenance cost, and a higher degree of your data integrity. 21.8 Other useful suggestions Every SQR programmer has his/her own programming style, usually an amalgam of the programmer’s personality and experience, as well as the business and technical environment. It is impossible to present any sort of carved-in-stone rules and recommendations. Perfect programs do not exist nor do absolutely correct ones. Often, budget constraints and time pressures prevail and programmers have to cut corners. It is hard to follow all rules when you have just two hours to write a program. Throughout this book, we tried to emphasize the importance of error handling, thorough testing, and other good programming habits. Here are a few additional suggestions: OTHE R U SE F UL SU GGE S TION S TEAM LinG - Live, Informative, Non-cost and Genuine! 371 • Do as little hard-coding as possible. Try to make your programs flexible by using substitution variables, dynamic columns, and argument files. Use the SQR predefined variables instead of hard-coded values (see chapter 4 for examples). • Place encapsulated debugging statements in your program when you write it, not after you have a problem. Using the -DEBUG SQR command flag will allow you to turn the debugging on when necessary; otherwise, these debugging statements will be simply ignored by the compiler (see chapter 20 for details and examples). • Do not forget about comments. Do not rely on program documentation: it gets lost or misplaced and, sometimes, does not provide the necessary answers to specific questions about the program code. • Use meaningful variable names. Avoid assigning program variables the same names as database columns. For instance, someone could easily confuse &Effdt for $Effdt. KEY POINTS 1 To support global development: • avoid hard-coding date, currency, and numeric edit format masks: use SQR special keywords Date, Money, and Number instead • use SQR locales. 2 To ensure your program’s platform independence: • do not use fully qualified hard-coded file names • use the $sqr-platform predefined variable to determine the current platform • avoid calling operating system commands from SQR programs; use similar SQR functions instead • test your programs on both client and server. 372 CHAPTER 21 GOOD PROGR AMMING PR ACTICES TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 3 These rules will help you to write database independent programs: • do not use database-specific functions, use similar SQR functions instead • avoid using built-in functions in the Where clause • use the ANSI standards in your SQL • do not rely on database-specific default date formats • do not assume that all databases return the same error codes and messages • do not use database-specific command parameters and flags. 4 Using SQR procedures in the Begin-Select instead of sub-queries gives your move flexibility and improves readability of your program, but may negatively impact performance. 5 The top-down approach to writing programs allows you to write and test highlevel procedures while deferring the creation of some lower-level procedures. It is a good idea to limit every program procedure to just one function. 6 Local procedures may help to avoid errors caused by incorrect usage of previously defined variables. 7 Coding error-handling routines allows you to alert users of any unusual situation, avoid the system crushes, and prevent a possible database corruption. 8 Avoid hard-coding in your programs. 9 Place encapsulated debugging statements in the program code. 10 Do not forget about comments. 11 Use meaningful variable names. OTHE R U SE F UL SU GGE S TION S TEAM LinG - Live, Informative, Non-cost and Genuine! 373 TEAM LinG - Live, Informative, Non-cost and Genuine! P A R T 3 SQR and PeopleSoft TEAM LinG - Live, Informative, Non-cost and Genuine! TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 2 2 Running SQR in PeopleSoft applications IN THIS CHAPTER • Running SQR programs in PeopleSoft applications • Monitoring the execution of SQR programs submitted from PeopleSoft • Viewing the log file and output of your SQR program 377 TEAM LinG - Live, Informative, Non-cost and Genuine! 22.1 SQR and PeopleSoft PeopleSoft belongs to a few companies that develop enterprise resource planning and management software. PeopleSoft packages or modules present comprehensive solutions capable of supporting business functionality of an entire corporate structure or large parts of it. PeopleSoft applications offer a wide range of query and reporting tools that enable users to access the necessary information for both day-to-day and long-term business decisions. For each product (HRMS, Payroll, Financials, etc.), PeopleSoft delivers a set of standard canned reports as a part of its basic package. At the same time, PeopleSoft offers a number of tools designed to help developers with customizing existing reports as well as creating new ones. PeopleSoft has selected SQR as one of its main reporting and processing tools because SQR provides a flexible and robust report-writing environment. SQR works beautifully when your report needs complex procedural logic or a tricky database manipulation; when you need to run your report on multiple platforms; when your report structure is complex with multiple breaks; or when you need to combine data base retrieval with special row processing. SQR is included with all PeopleSoft packages. As you learned earlier in this book, you can run your SQR programs from the SQR dialog box, or you can execute them from the operating system command line. PeopleSoft-delivered reports are usually executed from an online page and run with the help of the PeopleSoft Process Scheduler. This does not mean that you cannot execute PeopleSoft-delivered SQR programs from the SQR dialog box or a command line. You just need to have access to the database and to include the proper flags and arguments into the dialog box or the command line. Programs in PeopleSoft can be written in different languages: SQR, Crystal Reports, Cobol, Application Engine, etc. All these programs can run under the PeopleSoft Process Scheduler. The Process Scheduler works with processes and job streams. A process is any program that runs under PeopleSoft Process Scheduler. It can be a reporting program, a file generation program, a database update program, or a combination of all three. You can run processes on your workstation or a server. The PeopleSoft Process Scheduler can help you to schedule the execution of processes so that they can run automatically. Previously, we used to call SQR programs “programs” or “reports”. In this and subsequent chapters, we will be using the terms “process,” “program,” and “report” interchangeably to mean any SQR program that runs under PeopleSoft. The PeopleSoft Process Scheduler allows you to bundle processes into job streams and schedule them to run at a specific date and time. Let’s go over the PeopleSoft’s standard way of initiating and running SQR programs, monitoring the program execution and analyzing the results. We’ll start with the 378 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! user’s view of the process. In subsequent chapters, you will learn what happens behind the scenes, as well as how to create your own Process Scheduler definition in order to allow execution of your SQR program from a PeopleSoft page. Please note that starting from release 8, online panels are called pages and panel groups are called components. 22.2 A high-level view Let’s start by discussing the way PeopleSoft interacts with SQR programs at a very conceptual level without going into many details. (Subsequent chapters will cover every part of this process at a more detailed level.) In most cases, PeopleSoft users initiate their requests for reports via PeopleSoft pages. These pages can be delivered by PeopleSoft or developed by application programmers. When a page information is filled in, PeopleSoft generates process request parameters. These parameters usually include User Id, Run Control Id, Run Location, Output Destination, File/Printer name, plus application-specific parameters, for example, Company Id, FromDate, To Date, and so on. After the process request parameters have been read from a page, PeopleSoft passes them to the Process Scheduler (please see figure 22.1): Process Request Parameters Process Scheduler SQR Command Line Flags & Arguments SQR Program Program Run Status Information Figure 22.1 Process Monitor Program Run Status Information Interaction between PeopleSoft and SQR The Process Scheduler generates the SQR command line with flags and arguments that are required to run the requested SQR program, invokes SQR, and passes the flags and arguments to SQR. When the input from the page is saved, the system updates a number of tables that are used by SQR to communicate with the Process Scheduler and the Process Monitor via the special PeopleSoft API. A HIGH- LEVEL VIEW TEAM LinG - Live, Informative, Non-cost and Genuine! 379 The requested SQR program is executed. It may generate reports, update the database, create flat files, or print its reports directly on the specified printer. Users are kept informed about the program status with the help of the PeopleSoft Process Monitor. The Process Monitor receives the program feedback via the PeopleSoft API parameters and displays the program status on the Process Monitor page. 22.3 PeopleSoft objects PeopleSoft uses a number of objects to control both user-interface and internal processing of the invoked SQR programs. These objects are shown in figure 22.2. All these objects will be discussed in this or subsequent chapters. Here, we will just present a brief overview of each one: • PeopleSoft fields are basic units of information. In PeopleSoft, fields are defined as stand-alone objects. If any of a field’s properties (size, data type, description, etc.) is changed, the change will affect all PeopleSoft objects that include this field. • PeopleSoft records are built by grouping PeopleSoft fields together. Building a record definition is the first step in creating a PeopleSoft record. In this step, you define some basic record characteristics and the record key structure. Figure 22.2 PeopleSoft objects When the record definition is complete, you can use a special PeopleSoft tool to execute an SQL CREATE statement to build the corresponding database table. • PeopleSoft pages are used for data entry and view purposes. In our discussion we will be focusing only on one category of pages: the Run Control pages. These pages are used to enter application-specific parameters necessary to execute your SQR program. 380 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! • PeopleSoft components are used to link pages to PeopleSoft menus. Once a page is created, it must be added to a component. A component can be composed of a single page or a set of pages. All pages in a component must have the same key structure. • PeopleSoft menus help users to select the report they need to run. When an SQR program is added to the system, it can be attached to an existing menu or to a new menu. • PeopleSoft Process definitions contain information that is necessary to schedule and run your process. They specify the process type, name and description, and the components for which the report is selected. • an SQR program can be a PeopleSoft-delivered program or a custom-written program. In theory, any SQR program can run under the Process Scheduler, however, as you will learn later, in order to maintain a productive two-way communication between your program and PeopleSoft, you have to make certain changes to the program. • SQC files are the #Include files that can be added to your SQR program source code. PeopleSoft-delivered SQC files help you to standardize interfaces between your program and the Process Scheduler. In addition, you can write your own SQC files. 22.4 Selecting a menu PeopleSoft-delivered reports are usually displayed under the Report or Process menu bar item. In Note Please do not confuse the programs listed most cases, programs that generate output for printunder the Process on the menu ing or displaying are listed under Report, while probar with the PeopleSoft Process grams that manipulate the database records or Scheduler processes: any progenerate flat files are listed under Process. This is gram run under the Process not a carved-in-stone rule: some programs marked Scheduler is considered a process. as reports may update the databases, and some programs marked as processes may generate reports. Many programs do many jobs: print reports, update the databases, and generate files. To run a report, select it from the appropriate menu: the Report menu or the Process menu. First, you have to know, of course, which report you need to execute. The HR Manager’s hat will help you with this task. Once you put it on, you will immediately realize that you would like to take a look at all available reports within the Administer Workforce (GBL) menu. To begin, navigate to the following page: Navigation: Home ➠ Administer Workforce ➠ Administer Workforce (GBL) ➠ Report (figure 22.3). To print a list of all employee birthdays, for example, click on the Employee Birthdays hyperlink (figure 22.3). S EL E C TI NG A ME NU TEAM LinG - Live, Informative, Non-cost and Genuine! 381 Link to the Employee Birthdays report Figure 22.3 Selecting a list of all reports available in Administer Workforce (GBL) menu At this point the system asks you to specify the Run Control ID value or to add a new value (figure 22.4). The PeopleSoft system is using a special Run Control ID that along with your User ID and a Process Instance, uniquely identify this report. Figure 22.4 382 Selecting the Employee Birthdays report for execution C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! 22.5 Run control When we run a process via the Process Scheduler, we need to supply it with a number of parameters like: the run location, output destination, output format, file/printer name, etc. This information is stored in the PeopleTools Run Control record PSPRCSRUNCNTL. In addition, each process maintains its own Application Run Control record to store the process-specific input run-time parameters, for example, As-Of-Date, Company Code, or State. The Run Control Id along with the Operid are the key fields in both Application Run Control records and PeopleTools Run Control records. Let’s get back to our previous page (figure 22.4). In this page, the system asks you to create a PeopleTools Run Control record or to select an existing one. If an existing record fits your process execution requirements, find the proper record, and reuse it. Otherwise, you will need to click on the Add a New Value link, and add a new Run Control Id. Let’s select the Add action (figure 22.5). Figure 22.5 Adding a new Run Control record The system prompted for a Run Control ID. Let’s enter TEST1 as shown in figure 22.5 and click on Add. If you already established a Run Control ID, enter it or use the Find an Existing Value link to search for it. The system displays the next screen called the Run Control page (figure 22.6). What you see in figure 22.6 is the Run Control page for the Employee Birthdays report. The Run Control ID is the TEST1 value you entered in the previous page. RU N C O NT ROL TEAM LinG - Live, Informative, Non-cost and Genuine! 383 Figure 22.6 The Run Control page for the Employee Birthdays report Click on the Run button. At this moment the report parameters are saved, and the Application Run Control record is created. This record, identified by Run Control ID TEST1 and Operator ID (PS), stores the report input parameters. It will allow you to reuse these parameters in the subsequent report runs. The next time you need to run this report, you simply select the proper Run Control ID (in our case, it is TEST1), and the system automatically retrieves the settings. (Please be aware that some HRMS applications may delete their Application Run Control records upon successful execution). Note that clicking on the Run button in the Run Control page does not submit Note the report yet. It brings us to the Process Scheduler Request dialog page. 22.6 The Process Scheduler Request dialog The Process Scheduler Request dialog (figure 22.7) is used to specify where you want to run your report, the destination type and format of your output, and the time of the report execution. Let’s take a closer look at this page and its parameters. 384 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 22.7 Process Scheduler Request dialog In this page (figure 22.7) you tell the system how to handle your process request. You select the Server Name you run your report on, the Recurrence definition, the Run Date, and Time. The Recurrence parameter allows you to define your process as a recurring process that may be executed on a periodic basis. By default, the Run Date and Run Time parameters are set to the current run date and time. You can change these parameters to indicate that you want to execute your process at a later time. You can specify a Time Zone in which your process will run. For instance, you could be in Eastern Standard Time (EST) zone and schedule a process to run in PST (Pacific Standard Time) zone. The Reset to Current Date/Time button will set the Run Date and Run Time to current date and time. Please note that starting from release 8.0, the setup control for Recurrence has been removed from this page. You can still select an existing Recurrence definition for your process but in order to add or modify one, you need to use the development environment. We’ll discuss this in details in chapter 26 of this book. This change was made to simplify the Process Request dialog for end users. Let’s examine the Process List portion of the Process Scheduler Request page. Under Process List you could have more than one process as long as they all share runtime parameters specified in the same Run Control record and page. The Select check box lets you chose one or more processes to run together. If this box is not checked, the process will not be included into the run. T HE P R O C E SS S C H ED U LE R RE Q U E S T D I A LO G TEAM LinG - Live, Informative, Non-cost and Genuine! 385 By changing the Type parameter you can choose to send your output to the Web (default), a file, a printer or email. The Format box will allow you to specify the format of your output file. The following formats are now available as output types for SQR: • Adobe Acrobat (*.pdf ) • Comma Delimited (*.csv) • PostScript (*.ps) • HP (*.lis) • HTML (*.htm) • Line Printer(*.lis) • SQR Portable (*.spf ) The default output format for SQR with output type Web is Adobe Acrobat (.pdf ). As you can see there are several kinds of file output types that you can choose for your process. There is a variety of possible output formats depending on what output type you have selected. Table 22.1 shows formats you can choose for each Type selected. Table 22.1 Output file types and the corresponding file formats PDF - Acrobat (*.pdf) (Must have Acrobat Reader installed to read these files.) File Email X X Printer Web X HP - (*.lis) X X X X LP - Line Printer (*.lis) X X X X SPF - SQR Portable Format (*.spf) X X PS - PostScript Files (*.ps) X X CSV - Comma Delimited (*.csv) X X X HTML (*.htm) X X X X X X If you select File or Printer as your output type, you will be able to specify the Output Destination that will appear on the screen once the File or Printer is selected. For Email or Web you will be able to specify the destination on the Distribution Detail page by clicking on the Distribution icon that appears only when output type is Web or Email. The Distribution Detail page allows you to select the recipients of your process output. If the process that you are running allows output that can be emailed 386 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! (for example, Adobe Acrobat [.pdf ] files), you can enter an email subject and message and send the output to a group of email addresses. Those who used the system prior to version 8 may also notice that the Run Location box is not available when submitting your report from a browser. The Windows version of the Process Scheduler Manager still exists for a backward compatibility and you can submit your reports on the client when using the application in Windows. PeopleSoft recommends that you run your processes from a browser using the PeopleSoft Internet Architecture (PIA). During the course of this book, we will be executing our processes from a browser using PIA. This architecture gives you more flexibility in distributing and viewing your output in Report Manager. If you submit your process using the Windows client, the Process Request Dialog page appears after you select File, Run or click on the traffic light button from a PeopleSoft application. When you submit a process request from PIA, you execute the request using the Process Scheduler Server Agent. If you use Windows, you still have the option of submitting your request through the Server Agent or running it locally on your workstation. After your Process Scheduler Request page is filled, click on the OK button (figure 22.7.) The PeopleTools Run Control record (PSPRCSRUNCNTL) is updated. Also a record is inserted into the PSPRCSRQST table (Process Request Table) and other PeopleTools tables. This gives the Process Scheduler the necessary information to run and monitor the process request. How does it work? The Process Scheduler Server Agent polls the PSPRCSRQST table for incoming process requests. When the row is found, the Server Agent invokes the special program, called PSSQR wrapper, which then calls SQRW.EXE or SQR.EXE depending on the platform the SQR Report runs on. The Process Scheduler Server Agent also updates the Run Status of this process instance to Initiated and the Session ID with the process instance of that process. Figure 22.8 shows you that the report is submitted for execution. How can we be sure that the report has been submitted? Take a look at the line under the Run button. It shows the Process Instance number. As soon as your report is submitted, the system automatically generates the Process Instance value. A Process Instance number displayed under the Run button indicates that your Note process has been submitted for execution. T HE P R O C E SS S C H ED U LE R RE Q U E S T D I A LO G TEAM LinG - Live, Informative, Non-cost and Genuine! 387 The Process Instance number displayed here indicates that the process has been submitted Figure 22.8 The Employee Birthdays report is submitted for execution. 22.7 The Process Monitor The Process Monitor not only allows you to check the status of your process, but also to delete completed report requests from the queue or cancel process requests that are currently running or have been initiated. If you click on the Process Monitor hyperlink (figure 22.8), the system will bring you to the Process Monitor page as shown in figure 22.9. The Refresh button is used to update this page. The Process Monitor shows the processes by User (Operator ID). You can modify this process list by narrowing the selection down to a specific Server, Type (SQR Report, SQR Process, Application Engine, Crystal, etc.), or Run Status (Success, Error, Initiated, Canceled, etc.). Also, you can use the Last edit box to see your reports within a specified time range in Days, Hours, Minutes. Usually, you view by your own user ID. But if you leave this field blank the system lets you view all the processes that you are authorized to see. The View Job Items checkbox lets you view the individual items, or process requests, that make up a job. If you want to see the entire job and not the process requests within it, leave this checkbox blank. 388 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 22.9 The Process Monitor page Sometimes, if the process you started just sits in the input queue with its status equal to Initiated or Queued, and you wonder what’s going on, the first thing to do is to click on the Details link to find the Server name your process was assigned to. Then you can check to see whether the appropriate Server Agent is up and running by selecting the Server List tab (figure 22.10). As you can see, the PSNT Server Agent is up and running. But if it is down, you would need to contact your systems administrator to start the right Server Agent for your process. Let’s go back to the Process List tab (figure 22.9). Our process has been successfully executed. Click on the Details link. The system will display the Process Detail page as shown in figure 22.11. This page (figure 22.11) gives you more information about the process request. The Process Detail page shows the process Description, Type, the Run Control ID, and the Server name. You can check to see how long your report took to run by looking at the begin and end date/time stamps. Knowing these details can be very useful in troubleshooting. T HE P R O C E SS M O NIT O R TEAM LinG - Live, Informative, Non-cost and Genuine! 389 Figure 22.10 The Server List tab The Actions group box Figure 22.11 390 The Process Detail tab C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! 22.7.1 Deleting, cancelling, or putting on hold report request The Process Detail page gives you a certain level of control over your process. You can cancel, delete, restart or put the process on hold. Depending on the current status of your process, the system will allow you to select a valid action. You will be able to: • Delete the process request if its current status is one of the following: Error, Cancelled, Successful, Unsuccessful, Not Posted. • Cancel the process request if its current status is Hold, Queued, Initiated, Processing • Hold process requests that are Queued. • Queue process requests you’ve put on Hold. • Resend the Not Posted process requests. Since our process has been successfully executed, the system will allow us to delete the request. All other actions are grayed-out. In order to update the Process Request status, you have to select the valid action and then click on the OK button. Depending on your system security setup, you may be able to update the status of the processes submitted by other operators. 22.7.2 Viewing the Process Request parameters In the lower right corner of the Process Detail page (figure 22.11) there is an Actions group box. It contains links to other pages that display additional details about your process request. Click on the Parameters link. The system will show you the Process Request Parameters page as in figure 22.12. Figure 22.12 displays all parameters that are passed to your process, as well as the command line used during the process execution. Take a closer look at the Command Line entry. It shows the exact command line that your operating system uses to launch PSSQR.EXE to run SQR. As we already mentioned, starting from release 8, SQR is executed with the help of the wrapper program PSSQR.EXE. This very useful information will help you to eliminate any configuration problems you might have. You could copy these parameters and then try to run the process outside Process Scheduler to isolate a problem. Please note that you would have to manually provide the access ID and password since they are not exposed on this page for security reasons. Also keep in mind that some delivered PeopleSoft programs prevent you from T HE P R O C E SS M O NIT O R TEAM LinG - Live, Informative, Non-cost and Genuine! 391 Figure 22.12 The Process Request Parameters page executing them outside the Process Scheduler for security reasons. We’ll discuss this in chapter 27. The Working Dir displays the directory in which the database connectivity software is installed. Click on the Return button to get back to the Process Request Detail page. 22.7.3 Reviewing the Message Log and Trace files From the Process Detail page (figure 22.11) click on the View Log/Trace link. The system will display the Report/Log Viewer page as shown in figure 22.13. This lets you view the message log, trace file, or the output of the report in PDF format. Please note that the View Log/Trace link appears on the Process Detail page (figure 22.11) when the following two conditions are met: • the process request output destination is set to Web • the report and log files were successfully posted to the Report Repository by the Distribution Agent. The process must have a run status of Successful. 392 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 22.13 The Report/Log Viewer page In case the report hasn’t been transferred to the Report Repository, the run status of the process request will remain Posting and the View/Log link will not be active. In this case you can check the Message Log for any messages from the Distribution Agent indicating if there were problems transferring files to the Report Repository. The View Log/Trace link is only available from the Web. If you are accessing the Note Process Detail from Windows version of PeopleTools this option will not be available. Let’s click on the Message Log link (figure 22.11). The system displays a page as shown in figure 22.14. This page (figure 22.14) gives you more detailed information about your process including the database name and type, run control parameters and flags, output destinations, etc. It also can be very useful in troubleshooting. Let’s get back to the Report/Log Viewer page (figure 22.13) by selecting the Back arrow on the Internet Browser menu bar, or by closing this page. The Trace File link brings you a page with the log file. Any Display or Show statements used in your SQR program will be shown in this file. It will also show you any errors produced by the program. If your SQR program has no Display or Show statements and runs with no errors, you may see an empty file in this case. This is exactly what happened in our case. As you may have noticed, the size of the trace file in figure 22.13 is zero. T HE P R O C E SS M O NIT O R TEAM LinG - Live, Informative, Non-cost and Genuine! 393 Figure 22.14 The Message Log page 22.7.4 Viewing the report output If your program produced a report with an output destination type of Web, you should be able to click on the report link from the Report/Log Viewer page (figure 22.13) and view your report via a browser as shown in figure 22.15. Another way to view your report is by using the Report Manager. You can use the following path: PeopleTools>Report Manager>Inquire>Report List. The page will display all reports you (or all operators that are authorized to view) have executed within the specified time interval. For example, figure 22.16 shows all reports executed by operator PS within the last 2 days. From this page you can also view your report output by choosing the View link for the report you’d like to view. Have you noticed that the status of our report on figure 22.16 is Posted? Don’t be alarmed. Status on the Report Manager page is different from Run Status on the Process Request page. Posted here indicates that our report file has been successfully posted to the Report Repository. 394 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 22.15 The output report file In fact, a process with an output destination type of Web will have several different statuses during the time between the process initiation and report posting. These run statuses are described in table 22.2. Table 22.2 The Run Status and Distribution Status The Process Request Status Run Status in Process Distribution Status in Report Monitor Manager New Process Request created. Queued Scheduled Process Request is initiated by a Pro- Initiated cess Scheduler Server Agent. Processing The program for the process request Processing started. Processing Program has completed. Generated Posting Distribution Agent attempts to trans- Posting fer the files to the Report Repository. Posting T HE P R O C E SS M O NIT O R TEAM LinG - Live, Informative, Non-cost and Genuine! 395 Table 22.2 The Run Status and Distribution Status (continued) Run Status in Process Distribution Status in Report Monitor Manager The Process Request Status Distribution Agent failed to transfer file to the Report Repository and hasn’t reached the Maximum of Transfer Retries. Posting Posting All files are successfully transferred to the Report Repository Successful Posted The Distribution Agent failed to trans- Not Posted fer files to the Report Repository and has used up the Maximum of Transfer Retries. Not Posted Note that status changes described in table 22.2 apply to process requests that have output destination type of Web. Log files are transferred to the Report Repository when you select the option of transferring log files to the Report Repository in the Server Definition page. For process types with output destination other than Web, you can track status through the Message Log. For example, if you execute the same report with the Output Figure 22.16 All reports in the Report Manager submitted by Operator PS within the last 2 days 396 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 22.17 Report/Log Viewer Destination as File and then look at the Report Log/Viewer page, you will not find any links to your output report as shown in figure 22.17. When a report’s output type is other than Web, the Report/Log Viewer does not show a link to the report’s output file. In order to find the output destination, just click on the Message Log link as shown in figure 22.17. The next page (figure 22.18) shows that the report output destination is PDF. Figure 22.18 The Message Log contains output destination and other important parameters T HE P R O C E SS M O NIT O R TEAM LinG - Live, Informative, Non-cost and Genuine! 397 Figure 22.18 shows that we executed our report with Output Destination Type of File and Output Destination Format as PDF. Also notice that system automatically assigned the output report name in the following format: ProcessName_Instance.Format, in our case, it is PER002_209.PDF. KEY POINTS 1 PeopleSoft-delivered reports are usually executed from online pages and run with the help of the PeopleSoft Process Scheduler. 2 The Process Scheduler works with processes and job streams. 3 PeopleSoft recommends executing processes and jobs via the PeopleSoft Internet Architecture (PIA). It allows you more flexibility in distribution and viewing the report output in Report Manager. 4 The Process Scheduler controls process execution with the help of the control records: PeopleTools Run Control and the Application Run Control. The Run Control ID along with the Operator ID are the key fields in these records. 5 Clicking on the Run button in the Run Control Page does not submit a report. It brings you to the Process Scheduler Request page. 6 The Process Scheduler Request page is used to specify where you want to run your report, the destination and the type and format of your output, and the time of the report execution. 7 The Process Instance number displayed under the Run button on the Run Control page indicates that your process has been submitted for execution. 8 SQR is executed by the wrapper program PSSQR.EXE. This is an executable module of a program written in C that is responsible for handling your process request. 9 The Trace File hyperlink in the Report/Log Viewer page brings you a page with the log file. Any Display or Show statements used in your SQR program will be shown in this file. It will also show you any errors produced by the program. 398 C HA PTE R 2 2 S QR IN PE OP L E SO FT AP P LI CA TION S TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 10 The View Log/Trace link appears on the Process Monitor Detail page if the following two conditions are true: • the process request output destination is set to Web • the report and log files were successfully posted to the Report Repository by the Distribution Agent. The process must have a run status of Successful. 11 If your program produced a report with an output destination type of Web, you would be able to click on the report link from the Report/Log Viewer page and view your report online. For process types with output other than Web, you can track their status through the Message Log. 12 The Status on the Report Manager page differs from the Run Status on the Process Request page T HE P R O C E SS M O NIT O R TEAM LinG - Live, Informative, Non-cost and Genuine! 399 C H A P T E R 2 3 Attaching an SQR program to PeopleSoft objects IN THIS CHAPTER • The PeopleSoft Internet Architecture and the development environment • Selecting Run Control records • Selecting Run Control pages • Selecting components and adding pages to the components • Selecting a menu for your report • Granting security access to the newly created menu items • Creating a Process definition for your program • Testing your changes 400 TEAM LinG - Live, Informative, Non-cost and Genuine! 23.1 Behind the scenes Behind the scenes Behind the scenes In the previous chapter, you learned how to run the PeopleSoft-delivered SQR reports from the web pages using the Process Scheduler. Now we will take a look at what is happening behind the curtain and learn how you can attach your own reports to the PeopleSoft Process Scheduler. You will need to use PeopleSoft PeopleTools, an application development environment that allows programmers to develop, customize, maintain, and implement pages, components, records, and menus. Please be advised that while we will touch on aspects of PeopleTools pertinent to SQR, a thorough discussion of PeopleSoft objects would go beyond the scope of our book. Here we will concentrate on PeopleTools objects that are used in attaching an SQR program to the PeopleSoft Process Scheduler. To learn more about the PeopleSoft development process, please refer to the corresponding PeopleSoft technical manuals. PeopleSoft delivers a number of standard reports, records, pages, and menus, and has always recommended that the best way to add new functionality is to clone already developed similar application objects. We will be using this commonly accepted approach in attaching custom reports to PeopleSoft. In the previous chapter, we discussed the process of selecting, scheduling, running, monitoring, and viewing an existing report. What steps are necessary to add a new report to the PeopleSoft Process Scheduler? The best way to answer this question is to use one of the sample programs created in the previous chapters of this book. In chapter 17, we created the E-Mail Interface program, Test17A.sqr. With this as our custom SQR program, we will determine what needs to be done so that users can run this program under PeopleSoft. Let’s review how an existing program (for example, the Employees Birthdays report) is accessed in PeopleSoft, and, also what PeopleTools objects are involved in this process. These or similar objects will be the ones you need to create or modify for the new program. To access any report under PeopleSoft, you need to know the menu to which this report belongs. For the Employees Birthdays report, the menu is Administer Workforce (GBL). When you go to Administer Workforce (GBL) and select Reports (see figure 22.3 in chapter 22), all reports relevant to this task are listed under this menu. For a new report, you either have to create a new menu or attach the report to an existing menu. Let’s go back to figure 22.3. When you select the Employees Birthdays report and specify a Run Control Id for the report, the system displays the Run Control page for the selected report. Every report needs this kind of page, which contains the Operator ID, the Run Control ID, and the application-specific parameters (if any). Therefore, the new report will either need a new Run Control page or use an existing one. B E HIN D T H E S CE N E S TEAM LinG - Live, Informative, Non-cost and Genuine! 401 In PeopleSoft, a page is linked to a menu with the help of a component. Therefore, a new component has to be created or an existing one can be modified. After all application-specific parameters (if any) are entered and the Run Control page is saved, the system inserts this information into the Application Run Control record for the report. In order to attach your report to a Run Control page, a Process definition has to be created for this report. To summarize, the following PeopleTools objects have to be created or reused when you need to attach a custom report to the PeopleSoft Process Scheduler: • Menu • Run Control page • Component • Application Run Control record • Process definition Now, that you know all the steps involved in attaching a program to PeopleSoft, let’s do this for the E-Mail Interface program. One special feature of this program is that it uses no input parameters. Later, we will discuss how to handle input parameters in programs run under the Process Scheduler. Note that the listed objects do not have to be created in the order shown. In fact, our steps in attaching the E-Mail Interface program to the Process Scheduler will have to be taken in a different order as we’ll explain later in this chapter. 23.2 PeopleSoft Internet Architecture Internet architecture The development environment In PeopleSoft 8, access to the system by end users is based on the PeopleSoft Internet architecture (PIA). In PIA, end users do not have any PeopleSoft-specific software installed on their machines. They use standard Internet browsers to connect to their respective Web server, which, in turn, interacts with the Application server. The PeopleSoft Web server handles the standard tasks of communication between the Application server and user’s browser, such as sending data back and forth, encryption, hosting of static web pages and images, and so on. In addition to the standard tasks, this server hosts a number of PeopleSoft-specific Java servlets designed to handle various PeopleSoft-specific transactions. The Application server hosts most of the processing logic. There are two main parts of this server: the Tuxedo middleware and the PeopleSoft server processes. Tuxedo’s Java-enabled component Jolt manages communication between the Web server and Application server. The PeopleSoft server processes run the bulk of the PeopleSoft 402 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! business logic. When processing each user request, the Application server processes connect to the Database server, submit the database query, and receive the query result set. The Application server processes then analyze and process the result set, generate the output stream, and send it to the Web server via Jolt, which ultimately sends it back to the browser (see figure 23.1). The Application server also communicates with the Batch server. Figure 23.1 The PeopleSoft Internet Architecture (PIA) The Database server stores all PeopleSoft tables. There are three main types of tables on this server: system catalog tables, PeopleTools tables and PeopleSoft application data tables. The Batch server controls the execution of batch processes. The most important component of this server is the Process Scheduler. The Process Scheduler submits and monitors your SQR programs as well as various other processes such as Application Engine processes, Cobol programs, etc. Depending on your specific environment, the Batch server may be a separate physical server or a part of the Application server. The PIA schema supports the most reliable and secure business environment for end users. PeopleSoft developers and system administrators do not need such a complex schema. They can use the traditional client/server two-tier architecture for development or system administration and use the PIA mostly for testing purposes (a three-tier architecture may also be a part of this schema). In this case, all PeopleSoft software is installed on developers’ machines or networks. This approach reduces much of the PIA overhead while providing developers with access to all system catalog tables, PeopleTools tables, and application tables (see figure 23.2). INTERN ET AR CHITECTUR E TEAM LinG - Live, Informative, Non-cost and Genuine! 403 Figure 23.2 The development environment The fact that PeopleSoft developers can use the traditional two-tier architecture does not mean that nothing has changed. In fact, version 8 is so revolutionary that not only the architecture changed, but tools, methods of development and even names of some PeopleTools objects changed. What used to be panels are now pages, panel groups are now components. In this and subsequent chapters, we will be using the development environment to create or modify various PeopleTools objects and the PIA to access the PeopleSoft utilities, administer the PeopleSoft security, or run your processes. 23.3 Selecting a Run Control record Run control record Selecting a run control record As you have learned, the Application Run Control records are used to save the input parameters for processes. PeopleSoft developed a number of Application Run Control records that can be used if these records have the necessary fields for your program. For example, the Temporary Employees report uses As-Of-Date as an input parameter. This report uses an Application Run Control record named RUN_CNTL_HR. The structure of this record is shown in figure 23.3. Are you surprised to see many more fields than just ASOFDATE? Yes, this record is a placeholder for most HRMS report input parameters. This does not mean that all the record fields have to be used in every single report. If you know that your report input parameters are among the fields in this record, you can safely use the record as your report Application Run Control record. Can we use the RUN_CNTL_HR record for the E-Mail Interface program that needs no input parameters? Yes, we can, but using this record may impact the performance. 404 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.3 The RUN_CNTL_HR Run Control record There is always some overhead with handling a long list of input parameters in an Application Run Control record. If a process uses no input parameters, it is more efficient to use the PeopleSoft-delivered Application Run Control record designed for this kind of process. The record name is PRCSRUNCNTL. Many standard PeopleSoft reports, for example the Emergency Contacts report, use this record. Figure 23.4 shows the record structure. Figure 23.4 The standard Run Control record with no input parameters RU N C ON TROL RE C OR D TEAM LinG - Live, Informative, Non-cost and Genuine! 405 The fields OPRID and RUN_CNTL_ID are the PRCSRUNCNTL record key fields. The LANGUAGE_CD and LANGUAGE_OPTION fields are used in global development projects. The default value of the LANGUAGE_CD field depends on your OPRID. The LANGUAGE_OPTION tells the system if you are allowed to change the LANGUAGE_CD field. As you can see, the PRCSRUNCNTL record structure fits the purpose of the E-Mail Interface program. We will be using this record rather than creating a custom Run Control record. 23.4 Selecting a Run Control page Run control page selecting a run control page Instead of creating an Application Run Control record for the E-Mail Interface program, we have managed to find an existing PeopleSoft-delivered one. A similar approach can be used for a Run Control page. We will try to find an existing standard Run Control page that suits our purpose and will reuse the page for the E-Mail Interface program. One way to select a page is to choose one that you have already worked with since you will be familiar with its input parameters and Run Control record. If you are not sure about the page name, find a page associated with a report that uses the same input parameters as your program does. For example, we can take advantage of the fact that the Emergency Contacts report does not accept input parameters. This gives us a hint that the page used in this report can be good for the E-Mail Interface program. All we need to do is to obtain the page name for the Emergency Contacts report and make sure that this page works with the same Application Run Control record. Select the Emergency Contacts report from the Administer Workforce (GBL) menu and enter any Run Control ID. The Run Control page for the Emergency Contacts report will appear. Select View, Page Name as shown in figure 23.5. After clicking on Page Name, you will see the necessary page displayed (figure 23.6). You can see the page name at the bottom right portion of the page: PRCSRUNCNTL. If we want to reuse this page for the E-Mail Interface program, it must (1) use no input parameters and (2) be linked to the same Run Control record (in our case, it is PRCSRUNCNTL). We already know that the page uses no input parameters. To make sure that the page we are going to use for our program is linked to the PRCSRUNCNTL record, let’s check the page structure in the Application Designer. Select Go, PeopleTools, Application Designer. Do File, Open, enter Page as object type, and enter PRCSRUNCNTL as a page name. Click Enter. You will see the page that appears in figure 23.7. 406 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.5 Displaying a page attached to a report The page name is PRCSRUNCNTL Figure 23.6 The PRCSRUNCNTL Run Control page RU N CONTROL PAGE TEAM LinG - Live, Informative, Non-cost and Genuine! 407 Figure 23.7 The standard Run Control page with no input parameters The page contains a subpage named PRCSRUNCNTL_SBP. All the PRCSRUNCNTL page fields are located inside of this sub-page. If you select Layout, Test Mode, or click on the Test Mode button , you will be able to see all the page fields (see figure 23.8). Let’s find out what records are behind this sub-page. First you need to get out the Test mode. To accomplish this, click on the Test Mode button again. Then point your mouse on the sub-page and right mouse-click. Select the first option, View Definition. Figure 23.8 The PRCSRUNCNTL page in Test mode 408 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! You can achieve the same by selecting the sub-page and using the toolbar: View ➠ View Definition as shown in figure 23.9 Figure 23.9 Viewing the definition of the sub-page The definition contains two tabs: Page Designer and Order tab. The Page Designer tab is shown in figure 23.10. Figure 23.10 Displaying the Page Designer tab of the sub-page RU N CONTROL PAGE TEAM LinG - Live, Informative, Non-cost and Genuine! 409 Let’s select the Order tab and examine the fields and records that are behind this page (figure 23.11). Figure 23.11 The Order tab shows all the fields and records that the sub-page is built from As you can see from figure 23.11, the Process Run Control sub-page contains two key fields from the PRCSRUNCNTL record and the links to Report Manager, Process Monitor, and Run Control dialog. At last, we verified that this Run Control page is linked to the PRCSRUNCNTL Run Control record. Note that both the record and the page have the same name. This is not a requirement. In most cases, the names are different, and often, a page is linked to not one, but several records. (For more information about building PeopleSoft records and pages please refer to the corresponding PeopleTools manuals.) Now that you know for sure that you are working with the right page, you can use the PRCSRUNCNTL page as an Application Run Control page for the E-Mail Interface program. 23.5 Different methods of searching for a Run Control page searching for a run control page searching for a run control page The preceding method of finding an existing page is good if you know of some PeopleSoft-delivered reports that are similar to your report. But what if this is not the case? One helpful method is to use the Record Cross Reference utility. All Run Control pages are linked to one or more Run Control records. If a page is linked to the right record, you can use it. For example, if your report uses the As-Of-Date as its input 410 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! parameter, you can look for standard pages that are linked to the RUN_CNTL_HR Run Control record and contain the Asofdate field. The Record Cross Reference utility can be accessed via the PIA or the development environment. Let’s use the PIA. Navigation: PeopleTools ➠ Utilities ➠ Use ➠ Record Cross Reference. Enter RUN_CNTL_HR as the Record name (figure 23.12). Click the Search button. Figure 23.12 Selecting record cross reference list for RUN_CNTL_HR Figure 23.13 shows all objects that use the RUN_CNTL_HR record. RUNCTL_ASOFDATE is among the pages that use RUN_CNTL_HR Figure 23.13 All pages that use the RUN_CNTL_HR record S EA RC HI NG FOR A R UN C O NTR OL PA GE TEAM LinG - Live, Informative, Non-cost and Genuine! 411 If you scroll down the Referenced on Pages scroll area on the cross-reference page, you will see a lot of pages that use the RUN_CNTL_HR record. This is because this record, besides the As-Of-Date, has a number of other fields. The fact that a page is listed here does not necessarily mean that it has the fields you need to use. For example, not all the listed pages display the As-Of-Date field. Our obvious choice is the RUNCTL_ASOFDATE page because the page name suggests that it has the Asofdate field. In order to make sure that this page is the right one, let’s open the page via the development environment and review it (figure 23.14). Figure 23.14 The RUNCTL_ASOFDATE Run Control page Now you can see that the RUNCTL_ASOFDATE page is the one we were looking for: it includes the right input parameter. Let’s go back to figure 23.13 and scroll down the list of pages. You can see that there are other standard HR pages that can be reused. Take for example, the RUNCTL_ FROMTHRU page. It was developed to accept the FromDate and ThruDate parameters. This page uses the same RUN_CNTL_HR record, but it displays two different parameters from the record as shown in figure 23.15. As figure 23.16 illustrates, Run Control pages work like filters between the user and Run Control records: they show only the necessary fields of the records on the screen. 412 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.15 The RUNCTL_FROMTHRU page Figure 23.16 Run Control pages show only some fields of the entire Run Control record. Another way to find a Run Control Page for reuse is to search the entire list of all available pages. You can do partial name search. For example, if you need to list all pages with names starting with RUN, go to Application Designer, Open, Page. Type RUN in the Name and press Enter. The list of all pages with names starting with RUN will appear. Browse through the list (figure 23.17) to find a match for your program. S EA RC HI NG FOR A R UN C O NTR OL PA GE TEAM LinG - Live, Informative, Non-cost and Genuine! 413 Figure 23.17 List of available pages with names starting with Run The third, and probably the fastest way to find a page you are looking for, is to use Application Designer’s Object References. As long as you know at least one object name, for example the field name, you can select Edit, Find Object References, and all objects that contain this field will be listed, as shown in figure 23.18. Figure 23.18 Using the Application Designer’s Object References to find the page that contains the AsOfDate field 414 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! 23.6 Creating a component creating a component creating a component After a page is selected or created, it must be added to a component before you can attach it to a menu. Component is actually a link between a Run Control page and a menu. Until now, we were trying to reuse the existing PeopleSoft delivered objects. This time we will create a new custom component for our report. Why couldn’t we follow the same PeopleSoft recommendation and try to find a suitable delivered component for our page? Let’s think for a moment and get back to the design stage. How do we want to execute our report? Most probably we would want to access our report in a way similar to the way we selected the Employee Birthdays or Emergency Contacts reports. And therefore, it will have to have its own place in the menu. And since we already know that a component is a bridge between a page and a menu, we need to create a component. The page will be added to the component, and then the component will be added to the menu. Let’s create a component by selecting File, New (in the Application Designer), and double-clicking on Component in the New dialog (figure 23.19). Now, we need to add our page (PRCSRUNCNTL) Figure 23.19 Creating a component to the component in figure 23.20. Figure 23.20 The Component screen To do this, click on the Insert Page button into Component (figure 23.21). on the toolbar, or select Insert, Page CREATING A COMPONENT TEAM LinG - Live, Informative, Non-cost and Genuine! 415 Figure 23.21 Selecting a page to add it to a component Enter PRCSRUNCNTL as the page name, and press Enter. Select the PRCSRUNCNTL page from the list of all pages and click on the Insert button in order to add this page to the new component. If you need to add more than one page to the component (in our case, it is not necessary), repeat the preceding steps for all additional pages. When all the pages you wanted to add to your component have been inserted, click on the Close button. You will see all the pages in your new component (in our case, it is just one page, PRCSRUNCNTL) (figure 23.22). Figure 23.22 416 The PRCSRUNCNTL page is added to the new component CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Each page in a component has a set of properties. The PRCSRUNCNTL page has been added to the component with its properties set to their default values. Let’s change these values to make them more meaningful. The Item Name (figure 23.22) is used for informational purposes only, but it must be unique within the component. We’ll leave it the same as the page name (the default value). The next column is Hidden. You only set this value On if you need the page to be hidden from user’s view. We’ll leave the value of this column Off. You can have several pages in a component with the Hidden value set to Off, and one or more pages with the value set to On. This technique is used when you need to bring to the buffer certain fields from some pages, but you don’t want to display these pages to users. The Item Label column is your page name as it will appear on the menu. It will also be displayed at the bottom of the page and as the default folder tab label. Right now it is named PRCSRUNCNTL, which is not very meaningful. Let’s call it E-mail. The Folder Tab Label is used to identify the folder tab when the component is selected. Let’s name it E-Mail Interface for our task. The last column, Allow Deferred Processing, indicates whether the deferred processing has been turned off or on at the Page Properties level. On this page it is for information purposes only, and therefore it is marked as read-only field. PeopleSoft created the deferred processing mode to increase the efficiency of processing transactions. To maximize online performance, PeopleSoft sets the default for both page fields and pages to allow for deferred processing. After we enter all the values, our component definition will look like the one in figure 23.23. Figure 23.23 Setting page properties for the new component CREATING A COMPONENT TEAM LinG - Live, Informative, Non-cost and Genuine! 417 Before you save the newly created component, we need to add additional important information. We have to set the properties for the entire component, including: search records, update and data entry actions, and detail page information. Select File, Object Properties, or click on the Properties button in the toolbar, or right-click on the Component definition and select Component Properties from the pop-up menu. You will see the Component Properties dialog window. The window has three tabs: General, Use, and Internet. Let’s fill in the first tab as shown in figure 23.24. Figure 23.24 The Component Properties dialog (the General tab) The second and the third tab of the component shown in figure 23.25 require more knowledge to fill in. Please refer to the PeopleTools technical documentation for more information about the Search Records, Three Tier Execution, and Detail Page selections. Let’s do the following selections in the Use tab page: select PRCSRUNCNTL as both Search Record and Detail Page. Allow the following actions: Add and Update/Display (figure 23.25). Remember, that these actions will be displayed on the Process Scheduler menu when the interface menu is selected. As previously discussed, these actions mean adding or updating a Run Control record. Figure 23.25 The Component Properties dialog (the Use tab) 418 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Now is the time to save our component. Let’s select File, Save, and enter the new Component name as Interfaces (figure 23.26). Figure 23.26 Saving the Interfaces component 23.7 Selecting a menu for your report selecting a menue Selecting a menu for your report The next decision is which menu to run your report under. In order to find an appropriate menu for your report, let’s discuss what options you have and the advantages and disadvantages of each option. The first option is to add your new report to an existing menu item with the same input parameters. If you use this option, your report will be added to the existing report list in the Process Scheduler Request page similar to the one shown in figure 23.27. This option is the simpler one to implement. You don’t need to create a menu or component. You only have to create a Process definition, that links your SQR program to an existing component. The disadvantage of this method is that, because the new report or process is not going to be a separate item in a menu, users may find Figure 23.27 The Process Scheduler Request page S EL E C TI NG A ME NUE TEAM LinG - Live, Informative, Non-cost and Genuine! 419 navigation to this report rather difficult. For example, in order to get the Process Scheduler Request page for the Temporary Employees report, users need to select Process, AsOf-Date Request. Users then have to enter the proper Run Control ID, and get the Run Control page, which will not yet show a list of reports or processes (figure 23.28). Figure 23.28 The Run Control page for all reports using the As-Of-Date input parameter Only after clicking on the Run button can users see the Process Scheduler Request page with all the processes listed, as shown in figure 23.27. As you can see, this is not a very intuitive method. You may, however, choose this option in addition to placing the new program under a separate menu (as PeopleSoft did, for example, for the Temporary Employees report). The second option is to add your report or process as a separate menu item to an appropriate menu and give it a meaningful name to make sure the report or process can be easily found. This option is more user friendly. We can use the second option and create a separate menu item named Interfaces, attaching our E-Mail Interface program to it. We should also keep in mind that, in the future, there will be some other interfaces attached to the same menu item. Per our discussion in chapter 22, it makes sense to attach this program under 420 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.29 Adding a new menu item to the Process menu the Process menu rather than attaching it under the Report menu. We are going to add a new menu item named Interfaces to the menu Process in the Administer Workforce (GBL) menu. To create a new menu item, switch to the development environment. Then, go to the Application Designer, and select File, Open, Menu, Administer Workforce (GBL). Click on the Process menu bar item, and then double-click on the empty rectangle at the bottom of the menu. The Menu Item Properties dialog will appear as shown in figure 23.29. On Menu Item Properties, click on Select and add our Interfaces component to the Interfaces menu item. After the new menu item is created, you have to decide who will be able to see it. Only users who belong to the proper operator class should be granted access to the new menu item. 23.8 Granting security access to a new menu item granting security access Granding security to a menu item Since we created Interfaces under the Process menu in the Administer Workforce GBL page, we have to allow certain users to access this menu item and the corresponding page. Let’s see how to use the PeopleSoft Security maintenance tools to accomplish this task. PeopleSoft delivered a robust application that allows us to create and maintain multiple levels of security. For our specific task, we will be concerned with online security, and, particularly, with user security. This type of security is managed with the help of security definitions. In order to sign on to PeopleSoft, each user ID must be associated with a user security definition. User security definition includes three related types: user profile, roles, and permission lists. Each PeopleSoft user has an individual profile. This profile is linked to one or more roles (employee, supervisor, manager, security administrator, etc.). Permission lists control access to menus, components and pages for each role. It also controls user’s actions such as Add, Update, Correction. Roles act as links between user profiles and permission lists. GRA NTING SECU RITY ACCESS TEAM LinG - Live, Informative, Non-cost and Genuine! 421 In order to make it clear, let’s consider the following example. Suppose we have three employees, John Smith, Simon Schumaker, and Ann Rader. John is a manager of Human Resources, he also takes care of security administration, Simon is a payroll supervisor and Ann is a computer programmer in Finance. Our Security Administration created the following four roles: Security Admin, Manager HR, Payroll Supervisor, and Employee. Let’s assume that our very small HRMS system consists of eight pages: A, B, C, D, E, F, G, H. Pages A through F belong to HR, including the Employee Self Service pages A, B, C. Pages E, F, G, H belong to Payroll. Let’s see who is linked to what role and what permissions each role should have. We will start with permissions. Security Admin should have access to all pages and, therefore, should be linked to all permissions. Employee can only access pages A, B, and C: this role should only be linked to the Employee Self-Service permission. Manager HR should have access to all HR pages and will be linked to the HR permission. Finally, Payroll Supervisor should have access to pages E, F, G, H and be linked to the Payroll permission. All users are employees no matter what positions they hold, therefore, Ann, John, and Simon are linked to the Employee role. In addition, John is linked to the Security Admin and HR manager roles while Simon is linked to the Payroll Supervisor role. Figure 23.30 will show how the three security objects, permission lists, roles, and user profiles are linked together: Of course, our picture is much simplified. Permission lists are lists of authorizations that you assign to roles. Besides access to pages, Permission lists store Sign-On times, Figure 23.30 PeopleSoft security objects 422 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! menus (or components), access to PeopleTools. Be aware that changes to permission Lists will impact all users that are linked to that permission list via their roles. And now that we understand the concept of user security definitions, let’s use PeopleTools to grant security to our new menu item. As we just discussed, we need to grant access to users by assigning our new pages and components to permission lists. Each permission list should be linked to one or more roles. Let’s see how to find these three components and link them. Let’s assume that user IDs and roles already exist. We know, for example, that at least the PS user ID exists because we used it to login and execute the delivered PeopleSoft reports. This PS user ID and its profile may be linked to a number of roles. Our first step, therefore, is to identify a role that is appropriate. Then, we will have to link this role with the proper permission list to grant security access to our new component. Login into PeopleSoft (in PIA mode) and take a look at the User Profiles for the PS user ID (figure 23.31). Navigation: Figure 23.31 Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ User Profiles Identifying a role to use for security access to our new page GRA NTING SECU RITY ACCESS TEAM LinG - Live, Informative, Non-cost and Genuine! 423 Let’s switch to the Roles tab and see the roles that are assigned to this user profile. Since our new page belongs to HR, the HR Administrator role seems appropriate. Our goal is to find a permission list that we will use for our newly created component. If one does not exist, we can create a new permission list for our new component, and then link this list to the HR Administrator role. Our next step is to examine the permission lists linked to the HR Administrator role (figure 23.32). Navigation: Figure 23.32 Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ Roles Selecting the HR Administrator role From the HR Administrator Role tab let’s switch to the Permission Lists tab (figure 23.33). If we scroll down the permission lists and look at their descriptions as shown in figure 23.33, we can identify the Report Workforce permission list as the one we might be able to use. Copy the permission list name (CPHR3350) to the clipboard. We will be using it in a moment. Our next step is to modify the permissions in this list. Let’s go to the Permission Lists (Home>PeopleTools ➠ Maintain Security ➠ Use ➠ Permission Lists), and paste our clipboard value into the Permission Lists search dialog (Figure 23.34). 424 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.33 Identifying the permission lists to be used Figure 23.34 Selecting permission list for CPHR3350 Press the Enter key and switch to the Pages tab (figure 23.35). The Edit Components link (figure 23.35) will bring you to the list of all the components within the Administer Workforce GBL menu (figure 23.36). On this page, not all of the components have a check mark on their left. Only the checked components are GRA NTING SECU RITY ACCESS TEAM LinG - Live, Informative, Non-cost and Genuine! 425 Figure 23.35 Reviewing the Pages tab Figure 23.36 Finding our new component (Interfaces) in the list of all components for the Administer Workforce GBL menu 426 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! the ones that are authorized for user access. Our new component, Interfaces, does not have a check mark, and therefore, is not authorized. Now, let’s click on the Edit Pages link next to our new component, Interfaces (figure 22.36). This brings us to the Page Permissions screen (figure 23.37). Figure 23.37 Authorizing access to the E-Mail page On this page we specify the user actions, choose between the Display Only and full access, and decide whether to authorize each page of the component. Please note that the Authorized and Display Only options apply to each individual page in the component. On the other hand, the actions apply to the whole component. Remember that these actions will tell the system to add or reuse the Run Control record when selecting the Run Control page. Let’s click on Add and Update Display and Authorized check boxes. Then click the OK button. This will bring us back to the Permission Lists page. But this time we can see our new component marked as authorized (figure 23.38). In order to finish the component update, scroll down and click the OK button. This brings us to the very last step of saving of our modifications, as shown in figure 23.39. GRA NTING SECU RITY ACCESS TEAM LinG - Live, Informative, Non-cost and Genuine! 427 Figure 23.38 The Interfaces component is authorized Figure 23.39 Saving the permission list update 428 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! After the Save button is clicked (figure 23.39), our permission list is modified, and should include the proper access to our new objects: menu, component, and page. Before we start testing our new menu item, let’s recall what objects we modified to grant our users access to our new objects. If we look back, we’ll notice that we only modified the permission list. In order to find the right permission list, we did some research to make sure that the User ID PS is in fact linked to that permission list via the HR Administrator role. But what if some other users are also linked to the CPHR3350 permission list? Well, as we warned you before, all users that are linked to this permission list will be able to execute our E-Mail Interface program. In our particular case it is a valid solution. But when this is not the case, we would need to create a new permission list, and a new role. And then link this role to all users that will be responsible for our E-Mail Interface. Next are the steps you have to take in order to add a new permission lists and/or role. Step1. Add a new permission list Navigation: Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ Permission List 1.1. Click on Add a New Value – Specify a new permission list name, for example, HRINT – Click Add 1.2. General tab – Enter description: HR interfaces 1.3. Page Tab – Click on the Prompt button and enter the Administer Workforce(GBL) menu – Select Edit Component on the right to the Menu name. This will bring you to the Component Permission screen – Select Edit Pages – Click Select All, OK, and OK again on the next page 1.4. Save the permission list Step 2. Add a new role: Navigation: Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ Roles 2.1. Select Add a New Value – Enter role name: HR Interfaces – Click Add 2.2. Select General tab – Enter description: Manage HR Interfaces – Click Save. The role is created 2.3. Select Permission List tab GRA NTING SECU RITY ACCESS TEAM LinG - Live, Informative, Non-cost and Genuine! 429 Enter the permission list name created in the Step 1, HRINT, press the Tab key – Click Save. The permission list is linked to the HR Interfaces role – Step 3. Add a role to a user profile: Navigation: Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ User Profiles 3.1. Enter User ID: PS 3.2. Select Roles tab – Click on (+) to add a new row – Enter: HR Interfaces and press the Tab key – Click Save. User PS is assigned to the HR Interfaces role that has permissions to execute our new interface, E-Mail Interface. No matter what method we used to grant the appropriate access to the PS User ID, we are now ready to test the new menu item (figure 23.40). Navigation: Figure 23.40 Home ➠ Administer Workforce ➠ Administer Workforce (GBL) ➠ Process >Interfaces Selecting the Run Control ID to use in the Run Control page Enter the TEST1 Run Control ID and press the Enter key. This will bring us to the Interfaces Run Control page (figure 23.41). After the page is selected we can execute our process. Let’s give it a try. Selecting the Run button (figure 23.41) should bring us to the Process Request page. Take a close look at figure 23.42. We see a Process Scheduler Request page with no process attached. What happened? The answer is simple. We haven’t yet created a Process definition for our process. 430 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.41 The Application Run Control page for the Interfaces menu item Figure 23.42 The Process Scheduler Request page with no Process definition attached Our next step should be clear: we need to create a Process definition for our new process and attach it to the newly created component. GRA NTING SECU RITY ACCESS TEAM LinG - Live, Informative, Non-cost and Genuine! 431 23.9 Creating a Process definition for your program creating a process definition Creating a process definition Every process run under the PeopleSoft Process Scheduler needs a Process Definition to specify the process attributes and link the process to the appropriate component. To create a Process definition, we will be using the Process Scheduler Manager. This component of PeopleTools, similar to the Maintain Security tool, is accessed via PIA. Navigation: Home ➠ PeopleTools ➠ Process Scheduler Manager ➠ Use ➠ Process Definitions The system displays the Process definitions search dialog (figure 23.43). Since we are going to create a new Process definition, click on the Add a New Value link. Figure 23.43 The Process definitions search dialog As you can see from figure 23.44, we have to select the proper type for our process. Please note that the valid type for our process is SQR Report, not SQR Process. If you select SQR Process, the Process Scheduler will not pass the Operator ID and Run Control ID to your program and the program will not work correctly unless you specify Operator ID and Run Control ID as additional parameters. (Please refer to the PeopleTools technical manuals for additional information about process types.) The Process Name must be the same as your program name: Test17A. Note that the .sqr extension is not needed (figure 23.45). 432 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.44 Adding a new Process definition Figure 23.45 No extension is needed when entering the program name. After you have pressed OK and assigned the type to your process, the system displays the Process Scheduler Process definitions component (figure 23.46). This component consists of the following six pages: • Process Definition • Process Definition Options • Override Options • Destination CREATING A PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 433 • Page Transfer • Notification The Process Definition and the Process Definition Options pages are the only ones you have to fill in; the other four pages are optional. Figure 23.46 The Process Definition page 23.9.1 The Process Definition page After the system displays the Process definition component, you have to enter the information about your process. It is very important to input the correct information and to understand the meaning of every field. If your definition is not correct, your process will stay Queued or Initiated forever. Let’s look at all the fields on the page shown in figure 23.47. • The Description will later appear along with your process name on the Process Request Scheduler page, so make it meaningful. • The Long Description (Optional) is used for your process description. • The Priority can be set to Low, Medium, or High. If several processes are queued on a particular server, the system will be using this priority to decide which process should be initiated first. This parameter is applicable for processes that run on a server only. • An API Aware process is a process that updates the Process Request table (PSPRCSRQST) with the process run status (Error, Success, etc.), completion code, message set, and message number. This allows the system to perform Commit or 434 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Rollback, depending on the run status. Based on the process execution results, the system displays a standard or custom message on the Process Monitor’s Process Request Detail page. Not every program is API Aware, you have to add certain logic to your SQR program to make it API Aware. Turning the API Aware flag On does not automatically make the process API Aware. We’ll discuss the process of making an SQR program API Aware in detail in the next chapter, but please note that, if your program is not API Aware, the flag must be turned Off. For our current program, you have to turn this setting off, since we did not place any special code to make our program API Aware—yet. (We’ll cover this in the next chapter.) • The Log Client Request is On by default for all API Aware processes. If turned on, every time the process is run on the client, the system logs the request on the Process Request table. This is useful as an audit trail. Note that, for all server run requests, logging is always performed. • The SQR Runtime is checked when you want the system to append the .sqt extension to the process name (used for pre-compiled SQR programs). It will use the SQT working directory. For our E-Mail Interface program, this option should be turned off. After all fields in the Process Definition page are filled, the page will look like the one in figure 23.47. Figure 23.47 The Process Definition page for the E-Mail Interface process CREATING A PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 435 23.9.2 Process Definition Options Let’s switch to the next tab on the Process Definition component, the Process Definition Options page, as shown in figure 23.48. Figure 23.48 The Process Definition Options Page The run location can be Server, Client, or Both. If selected, it specifies the run location for your process request. Note that this selection will take precedence over the Process Scheduler Request specification. This means that, if you select Server here, the process will be scheduled to run on Server only, regardless of what the user specifies in the Process Scheduler Request dialog. Note that Client processes can not be run from your Internet Browser application, but only from the File, Run menu on your Windows client. The Server Name (Optional) is specified if you always have to run your process on a particular server. If you do not want to be so restrictive, leave it blank. If, for example, you have both the UNIX and the NT Server available to run your process, and you do not specify the server name on this page, the users will be able to select the server of their choice on the Process Request page. If a user leaves the server name blank on the Process Request page, the system will automatically find the first available server that can process 436 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! the request for this process class. The server name can be specified only if the run location is Server. The Recurrence Name (Optional) can be selected only for processes that run on a server. The recurrence definitions are created in the Process Scheduler Manager, Use menu. All previously created definitions are shown in the lookup list for the Recurrence Name field. Note: if you enter the Recurrence Name here, this does not mean that the process will automatically start and run according to the specified recurrence definition. It has to be started manually for the first time. The component is used to specify the component from which you want to run your process. In our case, it is the Interfaces component because we created this component to run the E-Mail Interface program. Note that, in order to link your process to a component, this component has to be created prior to creating the Process Definition. It is important to enter the correct component name. PeopleSoft does not edit this field: if you misspell it, users will not be able to run your process. To avoid the problem, you can search for a valid component. The system will display a list of all available components. If you do not remember the name of the component, you need to find it. Go to PeopleTools, Application Designer, File, Open, Menu, Administer_Workforce (GBL). Find the Interfaces menu item, highlight it, select View and View Definition (see figure 23.49). You will see the name of the component (Interfaces). You can optionally specify more than one component for your process. In this case, the process will appear on all selected components. The process groups must be entered to identify which operators have permission to submit this process. At least one process group must be specified. You can allow multiple process groups to run your process. You have to specify the process groups that belong to Figure 23.49 Finding the name of a component CREATING A PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 437 your user. If you specified a process group here, but did not give a permission to some operators to use this group, the process will not be visible to these operators. Please remember that the process groups must be linked to a permission list. Users can run only those processes through Process Scheduler that belong to process groups assigned to their role. Let’s select the HRALL process groups (figure 23.50) and then verify our selection. Figure 23.50 The Process Definition Options Page for the E-Mail Interface process We need to make sure that the permission list we selected to use for the Interfaces menu item has the right process groups permissions that will allow execution of our E-Mail Interface process. Click on the New Window hyperlink in the upper right corner of the page (figure 23.50), and open the Report Workforce permission list, (CPHR3350). Switch to the Processes tab as shown in figure 23.51. As you can see from figure 23.52, our permission list allows one to execute the HRALL process group processes. Now we can be sure that the parameters in our process definition Options page are valid and linked properly to the PS operator ID. 438 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.51 Verifying the process groups permissions Figure 23.52 HRALL is assigned as a process group for Report Workforce permission list CREATING A PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 439 23.9.3 Override Options page The third page on the process definitions component (figure 23.53) is optional. For our process, we are not going to use any of the page fields. We know, however, that you may be curious about the meaning of each field. You may also wonder when the use of these fields could be helpful. This page is used to modify the process parameter list, command line, or working directory. Figure 23.53 The Override Options page The drop-down lists for each parameter allow you to preface, append, or override each parameter for your process. For example, if you would like to add the -A SQR flag to append the output file to an existing output file of the same name and also define -DebugXYZ flags, your Override Options page may look like that in figure 23.54. 23.9.4 Destination page The Destination page allows you to control which output destination a user may select. Setting source to User Specified permits users to provide an output destination at run 440 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.54 An updated Process Definitions Options page time. For SQR processes, the Destination Source should be set to User Specified as shown in figure 23.55. Figure 23.55 The Destination page CREATING A PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 441 23.9.5 Page transfer The page shown in figure 23.56 is the fourth page in the process definitions component. This page is also optional. It allows you to transfer to the specified page from the Process Monitor after your process is successfully completed. You can specify the directions of transfer and the menu actions in this page. For our program, we will leave this page unchanged. Figure 23.56 The Page transfers page Please note that parameters on this page will not be modified automatically when you change your menu or page definitions in the Application Designer. 23.9.6 Notification page This page allows you send messages to your users based on the results (error or success) of your process execution. The messages can be sent to either a group of people by selecting the role as ID Type or to individual users by selecting User in the ID Type drop-down field. 442 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.57 Notification page You can also select multiple ID Types as shown in figure 23.58. Figure 23.58 Notifying multiple users about the process completion status CREATING A PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 443 23.10 Placing the program into the right directory placing in the right directory Place the program in the right directory Your last task is to place your program into the right directory so that the Process Scheduler will be able to run it. But how do we know where the Process Scheduler expects to find the program? SQR reports are located on the file server on your network. There are four environment variables that are used by PeopleSoft. The Process Scheduler uses these variables to search for your SQR program. PeopleSoft recommends using the following settings during development: • %PSSQR1%—your workstation user SQR directory • %PSSQR2%—your workstation PS delivered SQR directory • %PSSQR3%—network file server user SQR directory • %PSSQR4%—network file server PS delivered SQR directory While developing and testing your program it is convenient to keep your program files on your workstation. If you use these settings, the Process Scheduler will first look at your workstation directory and then will search on the network file server. After your program is tested and ready for production, you can then move it to the file server’s user SQR directory (%PSSQR3%). If you execute your program in a two- or three-tier developer’s mode (but not from PIA), the PeopleSoft Configuration Manager will help you to find out the right directories where your SQR program source file should reside or to modify them. You can only access Configuration Manager from the developer’s mode. From the Administer Workforce GBL menu, select Edit/Preferences/Configuration. Click on the Profile tab, select the profile and then click on the Edit button. If you switch to the Process Scheduler tab, you’ll see a page like the one in figure 23.59. Take a look at the SQR portion on this page. Here we see all the flags and directories that are used during the SQR program execution. These environment variables can be easily customized for your needs just by editing the fields in the Configuration Manager page. Please note that the settings will be written to your workstation’s Registry and will only apply to your workstation. For your production environment, these values will be supplied by the settings made during your Batch Server configuration. Any settings made here will not apply to the PIA. 444 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.59 The Process Scheduler tab in the Configuration Manager 23.11 Testing your Process Definition Testing your Process Definition testing your process definition We performed a lot of object manipulations in this chapter. Let’s retrace our steps in attaching our E-Mail Interface program to the PeopleSoft Process Scheduler: 1 Selected the PRCSRUNCNTL record as our Run Control Record 2 Selected the PRCSRUNCNTL page as our Run Control page 3 Created the new component: Interfaces and added our page to this component. 4 Added the new menu item: Interfaces to Administer_Workforce_(GBL), Processes menu. 5 Granted security access to the Component. 6 Created the Process Definition Test17A for our SQR program Test17A.sqr Now we are ready to run our SQR program. In the PIA mode, go to the Administer Workforce (GBL), Process, Interfaces menu. Select TEST1 as the Run Control and click on the Run button. You’ll get a Process Scheduler Request page with our TEST17A SQR process attached (fig. 23.60). Select the PSNT server and click OK to submit the TEST17A process for execution. Click on the Process Monitor hyperlink and check the Run Status. As you can see from figure 23.61, the Run Status indicates that the process run status is Error. TESTING YO UR PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 445 Figure 23.60 The Process Scheduler Request page with our TEST17A.SQR attached Let’s click on the Details link (figure 23.61) in order to find out what caused the error. This will bring us to the next page (Figure 23.62). Now we can examine the Message Log file. Click on the Message Log link under the Actions group (Figure 23.62), and then on the Explain button (figure 23.63). Figure 23.61 446 Viewing the Process Request run status CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 23.62 The Process Detail page The description of the problem (figure 23.63) shows exactly what happened. Our process failed to update the status in the Process Request table. Remember, that we just Figure 23.63 Reviewing the Message Log TESTING YO UR PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 447 took our SQR program that was developed with no PeopleSoft interface code, and plugged it into the PeopleSoft Process Scheduler. This allowed us to initiate and to run the program from the PeopleSoft page. Even the E-Mail Interface file has been created, but the Process Monitor’s Process Status has not been updated, and therefore, PeopleSoft has no idea about the return code of this process. In the next chapter, you will learn how to solve the problem by making your SQR program API Aware. KEY POINTS 1 PeopleSoft delivers a number of records and pages that can be reused for your custom processes. 2 Any Run Control record must have the Operator Id and the Run Control Id as its key fields. 3 A Run Control record may contain additional fields that are not used in your program. 4 A Run Control page should be specific to your application and should contain (or display) only the fields that you want your user to enter. The same Run Control page may be used by many processes as long as they share the same input parameters. 5 A component acts as a link between a Run Control page and a menu. There may be multiple pages in a single component. 6 You can add your SQR program to an existing menu item or create a new one. 7 The appropriate security access must be granted to allow all users to see the new menu item. 8 Each PeopleSoft user has an individual profile, called User Profile which is linked to one or more roles. 9 Permission lists control access to menu, components, and pages for each role. Roles act as links between User Profiles and permission lists. 448 CH APTER 23 ATTACH ING AN SQR PRO GRAM TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 10 An API Aware process updates the Process Request (PSPRCSRQST) table with the process run status, completion code, and message parameters. 11 In order to make your SQR program run under the Process Scheduler, the following PeopleSoft objects have to be created, modified, or reused: i. Application-specific Run Control record ii. Application Run Control page iii. Component iv. Menu Item v. Security access to the new component vi. Process definition TESTING YO UR PROCESS DEFINITION TEAM LinG - Live, Informative, Non-cost and Genuine! 449 C H A P T E R 2 4 Making an SQR program API Aware IN THIS CHAPTER • PeopleSoft-delivered SQC files • How does SQR program know that it was invoked from the Process Scheduler? • API variables as communicators between the program and the Process Scheduler • The Process Scheduler and error situations 450 TEAM LinG - Live, Informative, Non-cost and Genuine! In the previous chapter we discussed the process of attaching an SQR program to a PeopleSoft online page as well as ways to run this program under the PeopleSoft Process Scheduler. You also learned that, in most cases, SQR programs run under PeopleSoft need certain changes. While any SQR program can be executed by the Process Scheduler, only programs that include special code are capable of communicating their status back to the Process Scheduler. In order to allow the Process Monitor to reflect your program status, you have to make your program API Aware. Making an SQR program API Aware involves adding a program code to update the Process Request table (PSPRCSRQST) with the program run status (Error, Success, etc.), completion code, error message set, and error message number. 24.1 Using PeopleSoft-delivered SQC files PeopleSoft-delivered SQC files using peoplesoft-delivered SQC files PeopleSoft provides a number of routines that handle the communication between SQR programs and the Process Scheduler. In order to make your SQR program API Aware, you have to use the #Include operators to add the STDAPI.sqc and SETENV.sqc files to your program. The STDAPI.sqc, in turn, uses the nested #Include operators that refer to other important API files (figure 24.1). Let’s look at two of these files: PRCSDEF.sqc and PRCSAPI.sqc. The PRCSDEF.sqc file includes the Define-Prcs-Vars procedure. This procedure initializes all the fields used in API. The PRCSAPI.sqc file includes two procedures: Get-Run-Control-Parms and Update-Prcs-Run-Status. The first procedure Get-Run-Control-Parms retrieves the input parameters (Process Instance, Operator Id, and Run Control Id) and updates the run status of the process request to 'Processing'. Another procedure in PRCSAPI.sqc, Update-PrcsRun-Status, is designed to update the Process Request table (PSPRCSRQST) upon the program completion. When you run your program from the Process Scheduler, the control parameters that identify your process (Database Name, Process Instance, Operator ID and Run Control ID) are passed as a part of the command line. The application-specific input parameters are not passed to the program—these parameters are saved in the Run Control table. (In the next chapter you will learn how to retrieve parameters from the Run Control table.) When you run the same program from the SQR dialog box or from the command line rather than from the Process Scheduler, the Get-Run-Control-Parms API procedure will not detect any input values and will instead identify the process as being run from outside the Process Scheduler. Figure 24.1 lists PeopleSoft delivered API SQC files and procedures and also shows the location of each API procedure and SQC file. PEOPLE SOFT- DEL IVERED SQC F ILE S TEAM LinG - Live, Informative, Non-cost and Genuine! 451 STDAPL.SQC STDAPI-INIT STDAPI-TERM PRCSAPI.SQC STDVAR.SQC PRCSDEF.SQC DEFINE-STANDARD-VARS DEFINE-PRCS-VARS GET-RUN-CONTROL-PARMS UPDATE-PRCS-RUN-STATUS UPDATE-PROCESS-STATUS GET-PRCSRQST-INFO GET-JOB-INSTANCE GET-LAST-MESSAGE-SEQ PROCESS-MESSAGE-LOG CHECK-MESSAGE-PARMS PARSE-MESSAGE-PARMS INSERT-MESSAGE-LOG INSERT-MESSAGE-LOG-PARM SET-CLIENT-INFO MS-SET-CLIENT-INFO TRANCTRL.SQC PRCSLANG.SQC GET-LANGUAGE-CODE GET-OPERATOR-LANGUAGE GET-POSITION-LANGUAGE GET-CURRENT-LANGUAGE Figure 24.1 EOJ.SQC SUCCESSFUL-EOJ SQLERR.SQC CURDTTIM.SQC SQL-ERROR GET-CURRENT-DATETIME BEGIN-TRANSACTION COMMIT-TRANSACTION ERRCOMMIT ROLLBACK-TRANSACTION The Process Scheduler API SQC files and procedures 24.2 Incorporating SQC files into your program Incorporating SQC files incorporating SQC files into your program To make our E-Mail Interface program API Aware, let’s add the SQC files we just discussed to the program code. (The updated program will be called Test24A.sqr): !TEST24A.SQR !Making the E-Mail Interface program API Aware !E-Mail Interface #Include 'setenv.sqc' !Set environment !******************** The Stdapi-Init procedure in STDAPI.sqc calls Define-Prcs-Vars and Get-Run-Control-Parms Begin-Program to initialize API variables, gets control parameters from the !******************** command line and updates the run status to 'Processing' Do Stdapi-Init Let $FileName='c:\book\Employee.dat' Open $FileName As 1 For-Writing Record=100 Status=#OpenStat 452 CHA PTER 24 MAKING AN SQ R PROGR AM API A WARE TEAM LinG - Live, Informative, Non-cost and Genuine! If #OpenStat != 0 Show 'Error Opening ' $FileName Else Do Process_Employees Close 1 End-If Display 'Total records exported: ' Noline Display #Tot_Recs 999,999,999 Do Stdapi-Term The Stdapi-Term procedure in STDAPI.sqc calls the Successful-Eoj procedure from EOJ.sqc which updates the run status to "Successful" End-Program !********************************************* Begin-Procedure Process_Employees !********************************************* Move 0 to #Tot_Recs Begin-Select A.Emplid A.Deptid B.Name B.Phone Do Write-Output-Record From PS_Job A, PS_Personal_Data B Where A.emplid=B.emplid And A.Empl_Rcd=0 And A.Empl_Status = 'A' And A.Effdt = (Select Max(Effdt) From Ps_Job Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt <= Sysdate) And A.Effseq = (Select Max(Effseq) From Ps_Job Where Emplid = A.Emplid And Empl_Rcd = A.EmpL_Rcd And Effdt = A.Effdt) End-Select End-Procedure !********************************** Begin-Procedure Write-Output-Record !********************************** Write 1 From &A.Emplid ',' &A.Deptid ',' &B.Name ',' &B.Phone Add 1 to #Tot_Recs INCORP OR ATING SQC F ILES TEAM LinG - Live, Informative, Non-cost and Genuine! 453 End-Procedure !********************************** #Include 'datetime.sqc' #Include 'stdapi.sqc' This SQC file includes all necessary API code. !Routines for date and time formatting At the program start, the STDAPI-Init procedure is invoked. This procedure is a part of the PeopleSoft-delivered SQC file STDAPI.sqc. Stdapi-Init, invokes two more procedures in turn. The first one, named Define-Prcs-Vars, is located in PRCSAPI.sqc. Its job is to initialize all API variables. The second procedure, Get-RunControl-Parms, determines whether the program is called from the Process Scheduler and, if yes, promotes the run status from 'Initiated' to 'Processing'. If you are curious how the Get-Run-Control-Parms procedure knows that the program is invoked from the Process Scheduler, take a look at the procedure source code shown in the following example. We’ll list just a portion of the Get-Run-Control-Parms procedure of PRCSAPI.SQC that deals with input parameters. begin-procedure Get-Run-Control-Parms … input $database_name 'Database Name (Optional, Press ENTER to continue)' input $prcs_process_instance 'Process Instance (Optional, Press ENTER to continue)' if not isnull($prcs_process_instance) let #prcs_process_instance = to_number($prcs_process_instance) input $prcs_oprid 'Operator ID (Optional, Press ENTER to continue)' input $prcs_run_cntl_id 'Run Control (Optional, Press ENTER to continue)' else let #prcs_process_instance = 0 end-if if #prcs_process_instance > 0 let #prcs_first_time = {True} do Get-Language-Codes do GetTimeZones let #prcs_run_status = #prcs_run_status_processing do Update-Prcs-Run-Status let #prcs_run_status = #prcs_run_status_successful end-if As you can see the procedure code contains four Input commands with request to supply values for the following variables: Database Name, Process Instance, Operator ID 454 CHA PTER 24 MAKING AN SQ R PROGR AM API A WARE TEAM LinG - Live, Informative, Non-cost and Genuine! and Run Control ID. All these values are optional. The program instructs you to Press Enter to continue. This is actually done to accommodate two different modes for your program execution: from the command line or SQRW window and from the Process Scheduler. If the program is invoked from SQRW or command line (which usually happens during the program’s testing), the operator will receive the prompts and as instructed will press the Enter key. The Process Instance variable ($PRCS_PROCESS_INSTANCE) will then be populated with NULL value, which works as an indicator to the code that this program is being executed outside of the Process Scheduler. If the program is invoked from the Process Scheduler, the $prcs_process_ instance variable receives its value from the parameter list string passed from the Process Scheduler. The parameter list string, besides the Process Instance value, also includes the Database Name Operator Id, and the Process Run Id. Figure 24.2 shows the Process Request Parameters page for the Employee Birthdays program. You can see the program parameters on the page in the command line. You can also see that for this particular program run, the Database Name is HR8DMO, the Process Instance equal to 76, the Operator Id is PS, and the Process Run Id is Test1. Figure 24.2 Run Control parameters passed to an SQR program INCORP OR ATING SQC F ILES TEAM LinG - Live, Informative, Non-cost and Genuine! 455 Let’s return to Test24A.sqr. At the end of the main section, the program calls the Stdapi-Term procedure, which is a part of STDAPI.sqc. The purpose of this procedure is to update the PSPRSCRQST table with the Process Run Status, Message Parameters, and Return Code. The program logic in Stdapi-Term may seem a bit convoluted. The chart in figure 24.3 will help you figure out how this procedure communicates the program status to the Process Scheduler. Your SQR Program STDAPI-TERM (in STDAPI.SQC) UPDATE-PROCESS-STATUS SUCCESSFUL-EOJ (in EOJ.SQC) UPDATE-PRCS-RUN-STATUS (in PRCSAPI.SQC) Updates PSPRCSRQST Table to Set: - Run Status = #prcs_run_status - Return Code - Message Parameters COMMIT-TRANSACTION (in TRANCTRL.SQC) Figure 24.3 The STDAPI-TERM procedure logic As you can see, the Run Status in the PSPRCSRQST table is updated based on the #prcs_run_status variable value. This variable determines the Run Status that you can see on the Process Monitor page. It is important to remember that in case of an error, the value of the #prcs_run_ status variable must be updated by the application program. In a normal run, PeopleSoft promotes the process Run Status in the following order: 'Queued'→'Initiated'→'Processing'→'Success'. Please note that #prcs_run_status is a numeric variable. It cannot be assigned the above-discussed text values directly. The PRCSDEF.sqc file includes a number of predefined numeric status variables that can be used to assign the #prcs_run_status variable the right status value (table 24.1). 456 CHA PTER 24 MAKING AN SQ R PROGR AM API A WARE TEAM LinG - Live, Informative, Non-cost and Genuine! Table 24.1 Predefined numeric Run Status variables Predefined Variable Name Numeric Value Status #prcs_run_status_error 3 Error #prcs_run_status_processing 7 Processing #prcs_run_status_successful 9 Success #prcs_run_status_unsuccessful 10 Used in PRCSAPI.sqc to set the proper error message #prcs_run_posting 14 Posting As soon as your program is scheduled to run, the Process Scheduler sets the run status on the Process Monitor to Queued. Next, if all parameters in the Process Definition are resolved, and the appropriate Server Agent is up and running (for server processing only), the Process Scheduler changes the status to Initiated. If your program fails to get through the compilation stage, the status on the Process Monitor panel will depend on where the program runs. If your program runs on client, the status will remain Initiated. If it runs on server, the status will be changed to Error by the SQR invocation script. The Stdapi-Init procedure (which must be called in the beginning of every API Aware program) changes the status to Processing, and updates the PSPRCSRQST table. At this moment, you will see the status set to Processing on the Process Monitor page. The Stdapi-Init procedure then sets the #prcs_run_status variable to Success (#prcs_run_status_successful) in the program memory only, but will hold on to the PSPRCSRQST table update until either the Stdapi-Term or Sql-Error procedure is called. Therefore, you will still see the Processing status on the Process Monitor page. If your SQR program runs to the end, and then calls the Stdapi-Term procedure as shown in figure 24.3, this procedure will update the Process Status to Success. In case of an error, it is your program’s responsibility to call a PeopleSoft-delivered errorhandling routine SQL-Error or to code a similar logic in your program. Otherwise, the status on the Process Monitor will either remain set to Processing if your program aborted during execution or, worse yet, will be set to Success if the program ran to the end and called Stdapi-Term regardless of the error situation. If your program uses a PeopleSoft-delivered error handling routine (part of which is shown in the example below), you do not have to worry about updating the API variables in an error situation. If, however, your program uses its own error processing logic, the program must include a code to set all the API variables to the proper values and INCORP OR ATING SQC F ILES TEAM LinG - Live, Informative, Non-cost and Genuine! 457 update the Process Request table PSPRCSRQST. An example of the PeopleSoftdelivered SQL error-handling procedure is shown below: !A part of the SQL Error !procedure in SQLERR.SQC This procedure is usually referenced in the On-Error parameter of the #include 'sqlstat.sqc' Begin-Sql statement. if #prcs_process_instance > 0 let #prcs_message_set_nbr = #prcs_msg_set_nbr let #prcs_message_nbr = #prcs_msg_nbr_sql_error let #prcs_run_status = #prcs_run_status_error Updating API variables let #prcs_rc = #sql-status let $prcs_message_parm1 = $sql-error let #prcs_continuejob = 0 do Rollback-Transaction Updating the if $prcs_in_update_prcs_run_stat <> 'Y' PSPRCSRQST table do Update-Prcs-Run-Status do Commit-Transaction end-if end-if let #return-Status = 1 stop As you can see, the error processing logic in an API Aware program should include updating a set of API variables and calling the Update-Prcs-Run-Status procedure that updates the Process Request table for your program. After the Process Request table is updated, the Commit-Transaction function makes this table change permanent. Later in this chapter we will show you how to create your own error handling routine similar to the one above. A process request with an output destination type of Web will have several different statuses. As soon as the program successfully finishes its execution, the Process Scheduler Distribution agent will attempt to transfer the files to the Report Repository. The process Scheduler Server Agent will set the Run Status to Posting at this moment. If all files are successfully transferred to the Report Repository, the Run Status is changed to Success while the Distribution Status in Report Manager is changed to Posted. If Distribution Agent failed to transfer file to the Report Repository and hasn’t reached the Maximum Transfer Retries the status remains set as Posting. But when The Distribution Agent failed to transfer files to the Report Repository and had used up the Maximum Transfer Retries the Run Status will be changed to Not Posted. As you can see, it is very important to understand how the system works. It will help you in troubleshooting the errors. Your program may run to success, but if something is blocking the way to transfer the files, the status may not be successful at all. Please refer to table 22.2 in chapter 22 for the complete list of the Process Monitor and Report Manager Statuses. 458 CHA PTER 24 MAKING AN SQ R PROGR AM API A WARE TEAM LinG - Live, Informative, Non-cost and Genuine! 24.3 Testing your changes Testing your changes testing your changes Now that you better understand how an API Aware SQR program interacts with the Process Scheduler, let’s run our program from the Interfaces menu created in chapter 23. Before we start testing, we need to make one more change. Since our program name has changed from Test17A.sqr to Test24A.sqr, a new Process definition will have to be created as we did in chapter 23. Just remember that this time our process must be marked as API Aware (see figure 24.4). Figure 24.4 Creating the Process definition for the E-Mail Interface program Now we are ready to execute our program. In the Web mode, let’s go to the Interface Menu item, and select the TEST1 Run Id. Then click on Run and select our process from the Process Scheduler Request page (figure 24.5). As you can see, we now have two processes under the same menu item. Our old TEST17A and the new one, TEST24A, which is changed to become an API Aware process. Select the second process and then click on the OK button. Let’s then click on the Process Monitor hyperlink to examine the process run status (fig 24.6). At this point, you may want to ask, Is Success always a success? In other words, does the fact that the Run Status field on the Process Monitor page is set to Success mean that the process finished successfully? The best way to verify the program run results is to check the program’s log and trace files. Figure 24.7 shows the answer to our questions. TESTING YO UR CHANGES TEAM LinG - Live, Informative, Non-cost and Genuine! 459 Figure 24.6 Examining the process Run Status on the Process Monitor page Figure 24.5 Selecting the API Aware process to run 460 CHA PTER 24 MAKING AN SQ R PROGR AM API A WARE TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 24.7 The Test24A.SQR Trace file How can the Process Monitor say Success when the program hasn’t yet written a single record to the output file? The Success in the Status field can sometimes be misleading. Apparently, the program isn’t communicating its status back to the Process Monitor. Why not? You will find the answer in a moment. 24.4 Communicating errors back to the Process Scheduler communicating errors Communicating errors to the process Scheduler Let’s reexamine our program again and see if we missed any of the error situations. One point of concern is the output file opening code shown here: !The E-Mail Interface program code !with no comprehensive error handling Do Stdapi-Init Let $FileName='c:\book\Employee.dat' Open $FileName as 1 For-Writing Record=100 Status=#OpenStat If #OpenStat != 0 Show 'Error Opening ' $FileName Else Do Process_Employees Close 1 End-If Display 'Total records exported: ' noline Display #tot_recs 999,999,999 Do Stdapi-Term Upon the error, the program called the Stdapi-Term … procedure, but offered no clue that the program failed. COMMUNICATING ER ROR S TEAM LinG - Live, Informative, Non-cost and Genuine! 461 By now, you probably have realized that we did not do a good job in error handling. The program sent an error message to the log file, but failed to inform the Process Scheduler about the output file opening error. The Process Scheduler had no way of knowing the status of our process. The best way to correct the situation is to clone the PeopleSoft-delivered code in the SQL-Error procedure, and to modify this code according to our program logic. Here is one way to do this: !TEST24A.SQR !The E-Mail Interface program with enhanced error handling Do Stdapi-Init Let $FileName='c:\book\Employee.dat' Open $FileName as 1 For-Writing Record=100 Status=#OpenStat If #OpenStat != 0 Let $Error_Text=Rtrim('Error Opening ' ||$FileName,' ') Let #Ret_Code=#OpenStat Do File-Error Else Do Process_Employees Close 1 End-If display 'Total records exported: ' noline display #tot_recs 999,999,999 Do Stdapi-Term … In this portion of code, !*********************************** the error-related inforBegin-Procedure File-Error mation is moved to the !*********************************** appropriate API fields if #prcs_process_instance > 0 and is made available to Show $Error_Text the Process Scheduler. Print $Error_Text (+1,1) let #prcs_message_set_nbr = #prcs_msg_set_nbr let #prcs_message_nbr = 30 !set to blank let #prcs_run_status = #prcs_run_status_error let #prcs_rc = #Ret_Code let $prcs_message_parm1 = $Error_Text let #prcs_continuejob = 0 End-if End-Procedure Let’s incorporate this code into our TEST24A.SQR. We will use the same program name to avoid unnecessary PeopleSoft changes. Now we can execute the program. You will see the improvement right away (figure 24.8). Yes, the Run Status is now shown as Error. And what about the error log message? Click on the Detail hyperlink and select to view the Message Log (figure 24.9). Our error message is shown on the PeopleSoft Process Request Message Log page. Now you know that, should an error situation occur, your program will not only send the error messages to the program log file, but will also communicate the error 462 CHA PTER 24 MAKING AN SQ R PROGR AM API A WARE TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 24.8 The Run Status is changed to Error Figure 24.9 The Error message is displayed on the Process Request Message Log page COMMUNICATING ER ROR S TEAM LinG - Live, Informative, Non-cost and Genuine! 463 information to the Process Scheduler by updating the proper fields in the Process Request table. The Process Monitor will check this table and both update the process status and display the program’s error message on the Process Request Message Log page. KEY POINTS 1 An API Aware process is a process that updates the Process Request table (PSPRCSRQST) with the process run status (Error, Success, etc.), completion code, message set, and message number. 2 PeopleSoft-delivered SQC file STDAPI.sqc helps you to make your SQR program API Aware. 3 The Define-Prcs-Vars procedure in PRCSDEF.sqc initializes all the fields used in the API. 4 The Get_Run_Control_Parms procedure in PRCSAPI.sqc retrieves input parameters: Database Name, Process Instance, Operator Id, and Run Control ID. 5 It is your SQR program’s responsibility to communicate errors back to the Process Scheduler. 464 CHA PTER 24 MAKING AN SQ R PROGR AM API A WARE TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 2 5 Accepting input parameters from PeopleSoft pages IN THIS CHAPTER • Using the PeopleSoft-delivered SQC files to accept input parameters • Creating your own SQC files or modifying the existing SQC files • Changing an SQR program to work with SQC files • Creating your own Run Control records and pages • Testing your changes 465 TEAM LinG - Live, Informative, Non-cost and Genuine! In chapter 24 you learned that the PeopleSoft-delivered SQC files can help SQR programs in accepting the Run Control parameters from the Process Scheduler and in keeping the Process Scheduler informed as to what is going on with the programs. There is, however, another level of communication between PeopleSoft and an SQR program: accepting application-specific parameters from PeopleSoft. In PeopleSoft, users enter application-specific parameters via Run Control pages. In chapter 23, we discussed how the Process Scheduler passes input parameters to processes. You learned the difference between the PeopleTools Run Control records and the Application Run Control records. Now you will learn how an SQR program accepts and handles application-specific parameters from PeopleSoft Run Control pages. 25.1 Using application-specific SQC files to obtain input parameters obtaining input parameters PeopleSoft using application-specific delivers a number SQC of files application-specific SQC files that are used to read input parameters from Application Run Control records. Usually, two SQC functions are involved in reading the parameters: one function selects the input parameters while another one formats the selected parameters and moves them to the designated SQR program variables. You can either use the PeopleSoft-delivered SQC files or develop your own, depending on the parameters your SQR program needs to accept. Let’s first learn how PeopleSoft-delivered SQR programs work with input parameters. Later, you will learn how to use a similar approach in your program. Let’s take a look, for example, at the PeopleSoft-delivered Temporary Employees report and examine how this program works. This program accepts As-Of-Date as its input parameter. 25.1.1 How the Temporary Employees program accepts its input parameters The name of the program that generates the Temporary Employees report is Per007.sqr. If you open Per007.sqr, and scroll down to the end of the program code, you will see that it uses the following SQC files: !SQC files that are used to obtain input parameters in the !Temporary Employees report #include 'hrrnctl1.sqc' !Get run control parameter values #include 'hrgetval.sqc' !Get values mask routines #include 'askaod.sqc' !Ask As Of Date input This is how the input parameter read section of Per007.sqr looks: !Procedures used in reading input parameters in PER007.sqr 466 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! begin-procedure Init-Report move 'PER007' to $ReportID do Stdapi-Init if $prcs_process_instance = '' do Ask-As-Of-date else do Select-Parameters end-if … end-procedure begin-procedure Get-Values let $language_cd = $PRCS_LANGUAGE_CD do Get-As-Of-Date end-procedure Check if ran from the Process Schedule. If yes, call Select-Parameters; otherwise call the Ask-As-Of-date procedure. This procedure is called from the Select-Parameters procedure. In the previous chapter, you learned that the Stdapi-Init procedure initializes API variables and obtains the Process Scheduler command line parameters (if any). Next, the program determines the method of its invocation: it may be initiated by the Process Scheduler or invoked some other way (submitted via the SQR dialog box, executed from the command line, or called by another application). Based on this check result, the program calls the proper subroutine to obtain the application-specific input parameters. If the program is not run under the Process Scheduler, the $prcs-processinstance variable remains empty and the regular SQR Input command is used in the Ask-As-Of-date subroutine to read the input parameters from user input. The AskAs-Of-date code is located in the Askaod.sqc file. If the program is invoked by the Process Scheduler, the $prcs-processinstance variable is assigned the process instance number value, and the SelectParameters subroutine is called to retrieve the input parameters from a specific application Run Control table. In case of the Temporary Employees report, the program is designed to work with the Run Control table named PS_RUN_CNTL_HR, but the procedure logic is a typical example of the communication between an SQR program and a PeopleSoft Run Control page. Let’s examine the Select-Parameters procedure. The procedure code is located in the HRRNCTL1.sqc file: !A typical input parameters read procedure in HRRNCTL1.SQC begin-procedure select-parameters BEGIN-SELECT RUN_CNTL_HR.OPRID RUN_CNTL_HR.RUN_CNTL_ID RUN_CNTL_HR.ASOFDATE RUN_CNTL_HR.FROMDATE RUN_CNTL_HR.THRUDATE OBTAINING INPUT PARAMETER S TEAM LinG - Live, Informative, Non-cost and Genuine! 467 RUN_CNTL_HR.CALENDAR_YEAR RUN_CNTL_HR.SERVICE_YEARS RUN_CNTL_HR.AD_STEP RUN_CNTL_HR.AD_STEP_ENTRY_DT RUN_CNTL_HR.AD_COMPRATE RUN_CNTL_HR.AD_HOURLYRT RUN_CNTL_HR.AD_MONTHLYRT RUN_CNTL_HR.AD_ANNUALRT !… !… RUN_CNTL_HR.AD_CHANGEAMT RUN_CNTL_HR.AD_CHANGEPCT RUN_CNTL_HR.EEO_REPORT_TYPE If your program includes the HRRNCTL1.sqc file, a procedure named Get-Values should be coded within your program. do Get-Values from PS_RUN_CNTL_HR RUN_CNTL_HR where RUN_CNTL_HR.OPRID = $prcs_oprid and RUN_CNTL_HR.RUN_CNTL_ID = $prcs_run_cntl_id end-select end-procedure In this procedure, developed by PeopleSoft, the application-specific input parameters are selected from the PS_RUN_CNTL_HR table for a given combination of Operator ID ($prcs_oprid) and Run Control ID ($prcs_run_cntl_id). As you learned in the previous chapter, these two variables came from the Process Scheduler parameter list. An important and not-to-be-missed part of the Select-Parameters procedure is a call to the Get-Values procedure. This procedure moves and edits the selected input parameter values to the designated variables in an SQR program. If your program uses the HRRNCTL1.sqc file, the name of the input parameter edit subroutine must be Get-Values. If you code the input parameter retrieval logic yourself, the name of this subroutine (if any) can be different. In case of the Temporary Employees report, a subroutine named Get-Values is a part of the Per007.sqr code. You can see this subroutine in our previous example depicting procedures used to read input parameters. Because the Per007.sqr program accepts only two application-specific parameters, Language Code and As Of Date, the Get-Values subroutine in this case is very simple: it moves the Language Code value to its designated program variable and calls the Get-As-Of-Date procedure to format As Of Date and to move it to its designated variable: begin-procedure Get-As-Of-Date let $AsOfDate = RTRIM(&RUN_CNTL_HR.asofdate, ' ') if $AsOfDate = '' 468 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! move $AsOfToday to $AsOfDate end-if end-procedure The flow-chart shown in figure 25.1 will help you to better understand how the Temporary Employee program (Per007.sqr) retrieves its application-specific input parameters. 25.1.2 Changing your SQR program to accept from and thru dates as input parameters Now that we reviewed how a PeopleSoft-delivered program retrieves its applicationspecific input parameters, let’s apply this knowledge to the E-Mail Interface program. One problem exists: this program accepts no input parameters. It can be modified, however. For example, the employee selection can be limited to the effective dates This procedure calls Define-Prcs-Vars to initiate API variables, and then calls Get-Run-Control-Parms to obtain: $prcs_process_instance, $prcs_oprid, $prcs_run_cntl from the command line and to update Run Status to 'Processing'. STDAPI-INIT (from STDAPI.SQC) Is SQR program run under the Process Scheduler? No Yes Ask-As-Of-Date (ASKAOD.SQC) Prompt for Input Parameters Select-Parameters (HRRNCTL1.SQC) Get Values Get-As-Of-Date (HRGETVAL.SQC) Figure 25.1 Select Run Control parameters from the Application Run Control table PS_RUN_CNTL_HR This procedure is located in PER007.SQR It is called from the Select-Parameters procedure Formats and moves AsOfDate to the designated variable ($AsOfDate) Using SQC files to obtain input parameters in the Temporary Employees report OBTAINING INPUT PARAMETER S TEAM LinG - Live, Informative, Non-cost and Genuine! 469 within a user-specified date range. Assuming that the date range is defined by two parameters—FromDate and ThruDate—our program can use the PeopleSoftdelivered Application Run Control record PS_RUN_CNTL_HR, which includes the two necessary parameters. This will make our task much simpler. (Later, you will learn how to create your own Application Run Control records). The changed E-Mail Interface program is named Test25A.sqr: !TEST25A.SQR !The E-Mail Interface program modified to accept the !From and Thru dates as input parameters !E-Mail Interface #include 'setenv.sqc' !Set environment !******************** Begin-Program !******************** Do Init-Report Let $FileName='c:\book\Employee.dat' Open $FileName as 1 For-Writing Record=100 Status=#OpenStat If #OpenStat != 0 Let $Error_Text=Rtrim('Error Opening ' ||$FileName,' ') Let #Ret_Code=#OpenStat Do File-Error Else Do Process_Employees Close 1 End-If Display 'Total records exported: ' Noline Display #Tot_Recs 999,999,999 Do Stdapi-Term End-Program !******************************* Begin-Procedure Init-Report !******************************* Do Stdapi-Init If $prcs_process_instance = '' Do Ask-From-Thru-Date Else Do Select-Parameters End-if Check if run from the Process Scheduler. If yes, then call Select-Parameters; otherwise call the Ask-From-ThruDate procedure (in ASKFTD.SQC). End-procedure !***************************** Begin-Procedure Get-Values !***************************** 470 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! Do Get-From-Thru-Date End-Procedure Call Get-From-Thru-Date from HRGETVAL.SQC !*********************************** Begin-Procedure File-Error !*********************************** if #prcs_process_instance > 0 Show $Error_Text Print $Error_Text (+1,1) Let #prcs_message_set_nbr = #prcs_msg_set_nbr Let #prcs_message_nbr = 30 Let #prcs_run_status = #prcs_run_status_error Let #prcs_rc = #Ret_Code Let $prcs_message_parm1 = $Error_Text Let #prcs_continuejob = 0 End-if End-Procedure !Set to blank !********************************* Begin-Procedure Process_Employees !********************************* Move 0 to #Tot_Recs Begin-Select A.Emplid A.Deptid B.Name B.Phone Do Write-Output-Record From PS_Job A, PS_Personal_Data B where A.emplid=B.emplid And A.empl_Rcd=0 And A.Empl_Status = 'A' And A.Effdt = (Select Max(Effdt) Select the dates between From PS_JOB the From and Thru dates. Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt Between $FromDate and $ThruDate) And A.Effseq = (Select Max(Effseq) From PS_JOB Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt = A.Effdt) End-Select End-Procedure !********************************** Begin-Procedure Write-Output-Record !********************************** Write 1 from &A.Emplid ',' &A.Deptid ',' OBTAINING INPUT PARAMETER S TEAM LinG - Live, Informative, Non-cost and Genuine! 471 &B.Name ',' &B.Phone Add 1 to #Tot_Recs Make sure to include additional SQC files. End-Procedure !********************************* * #include 'datetime.sqc' #include 'stdapi.sqc' #include 'hrrnctl1.sqc' #include 'hrgetval.sqc' #include 'askftd.sqc' !Routines for date and time formatting !Get run control parameter values !Get values mask routines !Ask From-Thru Date input As you can see from the program source code, the changes make the input retrieval parameter logic look very similar to that in Per007.sqr. The only difference is that Per007.sqr accepts As Of Date as its input parameter, whereas our program accepts From and Thru dates. Therefore, the program uses Get-From-Thru-Date, and AskFrom-Thru-Date in place of Get-As-Of-Date as well as a different PeopleSoft-delivered SQC file: Askftd.sqc. 25.1.3 Adding unique input parameters to your SQR program In the previous example, an application program uses input parameters that are already present in one of the PeopleSoft-delivered Application Run Control records. We take advantage of this fact and reuse the PeopleSoft-delivered input parameter retrieval code. Our only problem involves finding a code that will handle the input parameters your program needs to use. If your program uses some parameters that are not included in any existing Run Control record, however, you have to develop your own record, or modify an existing one. Let’s consider a situation where an input parameter in your program cannot be found in any PeopleSoft-delivered Run Control record. For example, let’s add another input parameter to the E-Mail Interface program: the name of the program output file. As a result, the program will have three input parameters: FromDate, ThruDate, and FileName. There are several different approaches to accomplish this. One solution is to add the new field to an existing Run Control record and an existing Run Control page and change the input parameter retrieval code in all related SQC files. This solution is the quickest one from a development point of view. If you consider the future release upgrades, however, it may not be the wisest one since, in this case, you would have to reapply all your customizations to all the PeopleSoft objects you have changed. Also, if you are going to change a PeopleSoft-delivered page, you have to keep in mind that this same page may be used by another application, and, therefore, additional PeopleCode statements will have to be added to the related records either to hide 472 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! the existing fields your application does not need or to hide the new field when the page is used in other applications. Another solution is to create a new Run Control record and a new Run Control page and to write a code to retrieve and process the input parameters, optionally placing this code in your SQC files. This approach will require a little bit more work, but it will pay off when it comes to the future release upgrades. You might also be able to use the newly created record for other custom programs by adding the required fields to it. We recommend and will be using the second approach for our task. 25.2 Creating your own Run Control records and pages creating Run control records The easiest Creating Run way Control to create records your and ownpages Application Run Control record is to find a similar record and save it under a different name, allowing you to modify the record by adding the new fields and deleting the fields your program does not need. Since we already know that the RUN_CNTL_HR Run Control record contains both the FromDate and ThruDate fields, we will clone this record. If you do not know which record to clone, you can browse all records with names that start with “Run,” or, better yet, use Object Reference to find all records that contain the FromDate and ThruDate fields. Here is how you do this: Click on File, Open and specify Field as the object type in the Application Designer panel. Type FromDate in the Selection Criteria box and press Select. Select Edit, Find Object References. You will get the screen shown in figure 25.2 listing all PeopleSoft objects that use the FromDate field. Figure 25.2 Selecting a Run Control Record that contains the FromDate field CREATING R UN CO NTR OL RECOR DS TEAM LinG - Live, Informative, Non-cost and Genuine! 473 Figure 25.3 Creating a new Application Run Control record as a clone of RUN_CNTL_HR. Assuming that the RUN_CNTL_HR Run Control record is the right one to be cloned, let’s open this record and save it as MY_RUN_CNTL_HR (figure 25.3). In this record you must keep the key fields: OPRID and RUN_CNTL_ID. Another pair of fields you will need are FromDate and ThruDate. Let’s also leave the Language_ Cd and AsOfDate fields for potential future use. All other fields can be deleted. If, in the future, you need more fields, you will always be able to add them. that are used in PeopleSoft records are not defined within the scope of Note Fields each record. Each field is defined globally throughout the entire database and, therefore, can be used in many records. After deleting the unnecessary fields, you can add an additional field, FILENAME, to store the value of the respective input parameter. You can either create a field or reuse the existing one if the field attributes of your field are identical to the existing field. Keep in mind that each field definition’s properties are common across the entire database. For our task, we’ll use the existing field FILENAME, and add it to our record using the Insert Field menu option (see figure 25.4). Figure 25.4 Adding a field to an Application Run Control record We now have built a new Application Run Control record that will be used to pass input parameters to the E-Mail Interface program. After you select File, Save, there is another step to take. 474 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! But before that, we should not forget to add our newly created object to the Project. The Projects are used in PeopleSoft to keep track of all customizations and to promote your modifications to other databases. You can press the F7 key or select Insert Current Object into Project. You can also instruct the system to do it automatically by selecting Tools, Options, and clicking Insert Object into Project when Object is modified and saved. Let’s use this option as shown in figure 25.5 Figure 25.5 Keeping track of all system modifications After our first custom object, the record definition, is saved to a project, let’s save the project as MY_PROJECT (figure 25.6) Figure 25.6 Saving our custom object into the MY_PROJECT project CREATING R UN CO NTR OL RECOR DS TEAM LinG - Live, Informative, Non-cost and Genuine! 475 So far, we have just created a Record definition of the new Run Control record, sometimes called a PeopleTools-level record. Now, you will have to create a physical table. Sometimes this step is called creating a database-level record. Let’s select Build, Current Object as shown in figure 25.7 and build the new MY_RUN_CNTL_HR table. Since we are creating a new table, we can safely use this option. Otherwise be careful: this option will destroy all data in the table Figure 25.7 Creating a database-level MY_RUN_CNTL_HR record As you can see from figure 25.7, we request the system to create the MY_RUN_ CNTL_HR table. The key of the newly created table is a combination of the Operator Id and the Run Control Id. When the E-Mail Interface program is run under the Process Scheduler, the operator can select either New or Update/Display option (see, for example, figure 22.3 in chapter 22). When New is selected, a new record is inserted into the MY_RUN_CNTL_HR table, otherwise, an existing record is updated. When creating your own Run Control record, do not forget to mark both OPRID and Run_Cntl_Id as key fields. You must also make sure that the Operator Id will be stored in the record by adding the commonly used PeopleCode script which will be triggered by the RowInit event for the OPRID field. This script can be copied from any Run Control record. (The script is very simple and we will explain it shortly.) PeopleCode is a programming language designed by PeopleSoft specifically Note for use with PeopleTools. PeopleCode scripts are attached to the record fields, and are triggered by specific events. Please refer to the PeopleCode manual for more information. 476 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! The PeopleCode script in the RowInit event for the OPRID field moves the operator ID to the OPRID field in our record. You access this script from the Application Designer panel, by opening the MY_RUN_CNTL_HR record, double-clicking on the OPRID field, and selecting View PeopleCode. Figure 25.8 shows the script. Figure 25.8 The PeopleCode script in the RowInit event for Oprid moves the Operator Id from the system variable %OperatorId to the OPRID field in the record. The code shown in figure 25.8 will be executed when the operator selects the MY_ RUN_CNTL page from the Interfaces menu. It places the Operator ID into the OPRID field of the MY_RUN_CNTL_HR record. 25.2.1 Building a custom Run Control page As in the case with the Run Control record, we need to find a suitable Run Control page for use as a new page template. In chapter 23, we discussed different methods of selecting the appropriate page. You can look for pages that use specific Run Control records or specific fields within Figure 25.9 Creating a new Run Control page by the records, or simply select pages by the page name. Since cloning an existing page our program’s input parameters are: FromDate, ThruDate, and File Name, the PeopleSoft-delivered RUNCTL_FROMTHRU page will be ideal to clone. Let’s open it and save this page as MY_RUN_CNTL (figure 25.9). Now we can change the page by adding the FILENAME field to it. PeopleSoft pages can have different types of visual objects: radio buttons, check boxes, drop-down lists, edit boxes, etc. In our case, an edit box is the most suitable because it allows users to CREATING R UN CO NTR OL RECOR DS TEAM LinG - Live, Informative, Non-cost and Genuine! 477 Figure 25.10 Adding a new field to MY_RUN_CNTL panel Figure 25.11 Associating a panel field with a field in a Run Control record 478 enter the program output file name. Select Insert, Edit Box from the Application Designer page (figure 25.10). After the newly created edit box is added to the page, it is still a dummy field with no connection to any Run Control record. We need to associate this dummy object with our record. Click on the page field and select Edit, Page Field Properties from the Application Designer menu (or merely click the right mouse button while pointing to the field) to get the Page Field Properties page shown in figure 25.11. Select MY_RUN_CNTL_HR record as Record Name, and FILENAME as Field Name (figure 25.11). After you click OK, our new page will contain the new field: File Name (figure 25.12). Do not forget that you created the new Run Control page by cloning an existing page, RUNCTL_FROMTHRU. The RUNCTL_ FROMTHRU page’s two existing fields, FROMDATE C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 25.12 Modifying the MY_RUN_CNTL_HR page Figure 25.13 Changing the relationship between a panel field and a Run Control record and THRUDATE, are attached to the RUN_CNTL_ HR Run Control record. After cloning, these two fields remained attached to the same record. Now you will have to attach both fields to the new record, MY_RUN_CNTL_HR. You can do this by clicking on each of the fields, selecting Edit, Page Field Properties, and reassigning the field to MY_RUN_CNTL_HR. Let’s start with FROMDATE. Click on this field and select Edit, Page Properties. The system will display the Page Field Properties dialog window. Replace the old record name by selecting MY_RUN_CNTL_HR in Record Name (figure 25.13). Repeat the same procedure to update the field properties for the THRUDATE field. Our new page has been built. It has all the input parameters that your modified application program will need. Let’s save this page. CREATING R UN CO NTR OL RECOR DS TEAM LinG - Live, Informative, Non-cost and Genuine! 479 25.2.2 Modifying the component If you want the newly created page to become accessible from the Interfaces menu, you will have to modify the Interfaces component. You will also have to link the MY_RUN_ CNTL page to this component. Remember, originally the PRCSRUNCTL page was linked to this component. The PRCSRUNCTL page now has to be replaced with the new one. In the Application Designer, select File, Open, Component, Interfaces. Delete the old page name and replace it with the MY_RUN_CNTL page name (figure 25.14). Figure 25.14 Attaching the MY_RUN_CNTL page to the INTERFACES component Are you ready to run the program now? Well, almost … One problem remains: Every time a component is changed, the corresponding security setup needs to be adjusted. 25.2.3 Creating the Process definition Since our program has been modified and we saved it under a different name, the new Process definition has to be created. Simply create a TEST25B Process Definition the very same way you created the Process definition for TEST25A.SQR. 25.2.4 Granting Permissions In chapter 23, we recommended to use the Internet mode login in order to grant the security access to the new menu, component, and page. But you can use your developer’s 480 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! two- or three-tier mode to do it as well. Since we already logged into the PeopleSoft in developer’s mode, let’s change the security this way. Our goal is to authorize our new page in the CPHR3350 (Report Workforce) Permission list. Navigation: Go ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ Permission Lists ➠ Pages Figure 25.15 Selecting the Report Workforce Permission List Click on Edit Components and scroll down to Process Interfaces. Click on Edit Pages. Select Authorized as shown in figure 25.16 Figure 25.16 Authorizing the E-Mail Interface page CREATING R UN CO NTR OL RECOR DS TEAM LinG - Live, Informative, Non-cost and Genuine! 481 Press OK, OK, and then save the permission list changes. You now are done with all the online changes. The next step is to create all the necessary SQC files and to modify the E-Mail Interface program accordingly. 25.3 Creating your own SQC files creating SQC files creating In order your to create own new SQCapplication-specific files SQC files, we will be using our proven technique of cloning the existing SQC files. You already know that you need to have one SQC file to select the input parameter values from the appropriate Run Control record and another one to format the selected values and move them to designated variables in your program. Keeping in mind that your application program should retain an ability to be executed from the SQR dialog box or the command line, you have to provide the code to prompt the user for input parameters as well. In our previous examples, we used the Askaod.sqc and Askftd.sqc files, which provided the code to prompt users for As Of Date and From-Thru Date. For an additional File Name prompt, you will have to create an SQC file or place the code directly in the program. Please note that you do not have to place all input parameter retrieval logic into SQC files. It is just a convenient and modular way to read the input parameters. You could also place this logic in the application program. 25.3.1 Creating an SQC file to select parameters from the Run Control record To create a new input parameter retrieval SQC file, we’ll clone the existing HRRNCTL1.sqc file. Let’s bring this file in, save it as MYRNCTL.sqc, and change the file to make it work with the MY_RUN_CNTL_HR Run Control record: !******************************************************************** ! MYRNCTL.SQC: Retrieve Run Control parameters !******************************************************************** begin-procedure select-parameters BEGIN-SELECT MY_RUN_CNTL_HR.OPRID MY_RUN_CNTL_HR.RUN_CNTL_ID MY_RUN_CNTL_HR.ASOFDATE MY_RUN_CNTL_HR.FROMDATE MY_RUN_CNTL_HR.THRUDATE do Get-Values from PS_RUN_CNTL_HR MY_RUN_CNTL_HR 482 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! where MY_RUN_CNTL_HR.OPRID = $prcs_oprid and MY_RUN_CNTL_HR.RUN_CNTL_ID = $prcs_run_cntl_id end-select end-procedure 25.3.2 Creating an SQC file to format selected input parameters We will use the existing HRGETVAL.sqc file as a basis when creating the new input parameter formatting file MYGETVAL.sqc. All you will need to do is to delete the commands that format unused input parameters, add logic to format, and save the new File Name parameter. The changed program saved as MYGETVAL.sqc is: !******************************************************************** ! MYGETVAL.SQC: Mask Run Control Value * !******************************************************************** !-------------------------------------------------------------------! ! Procedure: Get-As-Of-Date ! ! Description: Get the entered as of date. ! !-------------------------------------------------------------------! begin-procedure Get-As-Of-Date let $AsOfDate = &MY_RUN_CNTL_HR.asofdate if $AsOfDate = ’’ move $AsOfToday to $AsOfDate end-if end-procedure !-------------------------------------------------------------------! ! Procedure: Get-From-Thru-Date ! ! Description: Sets the defaults for the From and Thru Dates. ! !-------------------------------------------------------------------! begin-procedure Get-From-Thru-Date let $FromDate = &MY_RUN_CNTL_HR.fromdate let $ThruDate = &MY_RUN_CNTL_HR.thrudate do Century-Begin-Date if $FromDate = ’’ move $Century_Begin_Dt to $FromDate end-if if $ThruDate = ’’ move $AsOfToday to $ThruDate CREATING SQC F ILE S TEAM LinG - Live, Informative, Non-cost and Genuine! 483 end-if end-procedure !-------------------------------------------------------------------! ! Procedure: Century-Begin-Date ! ! Description: Sets century begin date to ’1900-01-01’ ! !-------------------------------------------------------------------! begin-procedure Century-Begin-Date do Format-DateTime(’19000101’,$Century_Begin_ Dt,{DEFCMP},’’,’native’) end-procedure !-------------------------------------------------------------------! ! Procedure: Get-File-Name ! ! Description: Get the entered file name ! !-------------------------------------------------------------------! begin-procedure Get-File-Name let $FileName = Rtrim(&MY_RUN_CNTL_HR.FileName,’ ’) end-procedure As you can see, the modified program includes the changed names of the column variables for AsOfDate, FromDate, and ThruDate. In addition, a new Get-FileName procedure is added to get the additional parameter, File Name. The E-Mail Interface program will be using only two procedures in MYGETVAL.SQC: Get-FromThru-Date and Get-File-Name. The other two procedures are left in the file for future use. 25.4 Changing your SQR program to accept parameters from the Run Control record accepting parameters Finally, the accepting parameters last program fromchange! the runThe control change record will include a few modifications to the E-mail Interface program to make it work with the newly created Run Control record and to include the new SQC files: MYRNCTL.sqc and MYGETVAL.sqc. Let’s save the modified program as Test25B.sqr: !TEST25B.SQR !The E-Mail Interface program with customized SQC files !E-Mail Interface #include 'setenv.sqc' !Set environment !******************** 484 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! $FileName is the Begin-Program new input parameter. !******************** Do Init-Report Open $FileName as 1 For-Writing Record=100 Status=#OpenStat If #OpenStat != 0 Let $Error_Text=Rtrim('Error Opening ' ||$FileName,' ') Let #Ret_Code=#OpenStat Do File-Error Else Do Process_Employees Close 1 End-If Display 'Total records exported: ' Noline Display #Tot_Recs 999,999,999 Do Stdapi-Term End-Program !******************************* Begin-Procedure Init-Report !******************************* Do Stdapi-Init Check if run from the Process Scheduler; If yes, call SelectParameters; otherwise, call the Ask-From-Thru-Date and Ask-File-Name procedures. If $prcs_process_instance = '' do Ask-From-Thru-Date do Ask-File-Name Else Located in MYRNCTL.sqc do Select-Parameters End-if End-procedure !******************************* Begin-Procedure Get-Values !******************************* Do Get-From-Thru-Date Call the Get-From-Thru-Date and Get-File-Name procedures from MYGETVAL.sqc. Do Get-File-Name End-procedure !*********************************** Begin-Procedure File-Error !*********************************** If #prcs_process_instance > 0 Show $Error_Text Print $Error_Text (+1,1) Let #prcs_message_set_nbr = #prcs_msg_set_nbr Let #prcs_message_nbr = 30 !Set to blank Let #prcs_run_status = #prcs_run_status_error Let #prcs_rc = #Ret_Code Let $prcs_message_parm1 = $Error_Text Let #prcs_continuejob = 0 ACCEPTING PARAMETER S TEAM LinG - Live, Informative, Non-cost and Genuine! 485 End-If End-Procedure !********************************* Begin-Procedure Process_Employees !********************************* Move 0 To #Tot_Recs Begin-Select A.Emplid A.Deptid B.Name B.Phone Do Write-Output-Record From PS_Job A, PS_Personal_Data B Where A.emplid=B.emplid And A.Empl_Rcd=0 And A.Empl_Status = 'A' And A.Effdt = (Select Max(Effdt) Restrict the date range From PS_JOB between the From and Where Emplid = A.Emplid Thru dates. And Empl_Rcd = A.Empl_Rcd And Effdt Between $FromDate and $ThruDate) And A.Effseq = (Select Max(Effseq) From PS_JOB Where Emplid = A.Emplid And Empl_Rcd = A.Empl_Rcd And Effdt = A.Effdt) End-Select End-Procedure !********************************** Begin-Procedure Write-Output-Record !********************************** Write 1 From &A.Emplid ',' &A.Deptid ',' &B.Name ',' &B.Phone Add 1 To #Tot_Recs End-Procedure !*************************************** Prompt for the user input if Begin-Procedure Ask-File-Name not run under the Process Scheduler. !*************************************** Input $FileName Maxlen=8 Type=Char 'Please enter File Name' End-procedure #include 'datetime.sqc' !Routines for date and time Include the new #include 'stdapi.sqc' SQC files. #include 'myrnctl.sqc' !Get run control values #include 'mygetval.sqc' !Get values mask routines #include 'askftd.sqc' !Ask From-Thru Date input 486 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! To summarize, the following steps were undertaken to allow our E-Mail Interface program to accept input parameters from the Process Scheduler: 1 The MYRNCTL.sqc file was created to read the program input parameters from the MY_RUN_CNTL_HR table record. 2 The MYGETVAL.sqc file was created to format the input parameters, and to move them into the designated program variables. 3 The E-mail program was changed to: • include the MYRNCTL.sqc and MYGETVAL.sqc files • add the Ask-File-Name procedure to get program input from the SQR dialog box or the command line if the program is not executed from the Process Scheduler • modify the Init-Report and Get-Values procedures to reflect the new SQC functionality • change the output file opening code to open a file with a name that was accepted as an input parameter • alter the Where clause in the Process_Employees procedure to select records with the effective dates between the From and Thru dates 25.5 Testing your SQR program testing your SQR program Now, with Testing yourallSQR the changes, program you are ready to test your program. Before we start testing, let’s list all the objects that were created or modified to make the E-mail program accept input parameters from the Process Scheduler: • A new Run Control record, MY_RUN_CNTL_HR, was created • A new Run Control page, MY_RUN_CNTL, was built • The INTERFACES was modified to replace the old panel name with the MY_ RUN_CNTL page • The INTERFACES menu security was restored • The MYRNCTL.sqc file was created to select input parameters from the MY_RUN_ CNTL_HR record • The MYGETVAL.sqc file was created to format the selected parameters and to move them to the designated program variables • The E-Mail Interface program Test25B was modified • A new Process definition for Test25B was created. TE STIN G YO UR S QR P R OGR AM TEAM LinG - Live, Informative, Non-cost and Genuine! 487 The next step is to create a test plan to outline the order of testing and to specify how each object will be tested: Table 25.1 The E-Mail Interface program test plan Test Step Object Method of Testing 1 Test the updated menu INTERFACES menu Make sure that the menu appears in the right place and is visible for operators who are allowed to access it 2 Test the new page Run Control page: MY_RUN_CNTL 3 Test the new Run Control record Run Control record: Run the following query: MY_RUN_CNTL_HR SELECT OPRID, RUN_CNTL_ID, FROMDATE, THRUDATE, ASOFDATE, LANGUAGE_CD, FILENAME FROM PS_MY_RUN_CNTL_HR WHERE OPRID='PS' AND RUN_CNTL_ID='MyTest'. If the record is not found, verify the record name and rebuild it, specifying: Execute SQL Now. Repeat step 2. Make sure all the fields are in place, enter parameters, and save. Make sure that all the input values for your OPRID and RUN_CNTL_ID are saved correctly. Pay attention to OPRID. If it’s not what you entered, or it is set to spaces, check if your Run Control record has the correct PeopleCode statement. 4 Test the new Test25B Process Process Definition Definition After selecting on the Run Control page, your Process Request page should appear with the Test25B process attached. If it’s not there, make sure that the process definition points to the correct component. Also, verify that the Process Definition name is same as your SQR program name. 5 Test the E-Mail Interface program Inspect all output and log files. Test25B.sqr program Now let’s execute our test plan. Select Administer_Workforce (GBL), Process, Interfaces. Add Run Control Id: MyTest. The system will display the newly created MY_RUN_CNTL dialog page shown in figure 25.17. On the page, enter the values for FromDate, ThruDate, and FileName. Let’s save the values and execute the SQL SELECT statement described in step three to verify our Run Control record (figure 25.18). Once we see that our Run Control record is populated correctly, we can run our SQR program. From the Run Control page, click the Run button. The system will display the already familiar Process Request page (figure 25.19). 488 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 25.17 Entering the application-specific parameters for the E-Mail Interface program. Figure 25.18 Selecting the fields from the MY_RUN_CNTL_HR Run Control record Click the OK button, and our program is up and running! Let’s check the program run status. As you can see from figure 25.20, the program run status is Success. This is a good sign and a necessary condition to consider our test successful, but it is not sufficient. You still need to check to see if the file in the specified directory was created and, also, to make sure that the file output meets the specified business requirements. TE STIN G YO UR S QR P R OGR AM TEAM LinG - Live, Informative, Non-cost and Genuine! 489 Figure 25.19 The Process Request page for the modified E-Mail Interface program Figure 25.20 Checking the E-Mail Interface program run status 490 C HA P TE R 25 A CCE PTING INPUT PA RAM E TE R S TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS 1 To accept input parameters from PeopleSoft online pages, you can either use the existing PeopleSoft-delivered SQC files or develop your own, depending on the parameters your SQR program needs to accept. 2 Your program should support both types of input parameter retrieval logic: retrieving the parameters from the Process Scheduler, or accepting them from the SQR Dialog Box, or the command line. 3 Usually, there are two SQC files involved in accepting program input parameters from a PeopleSoft online page. One file should contain a procedure to select all required fields from the proper Run Control record. Another one should include procedures to edit the selected fields and place them into designated SQR variables. 4 Your SQR program must be changed to include the proper SQC files, and a code to call the input parameter retrieval procedures. 5 A Run Control page that contains all the input parameters should be developed, or an existing page should be used or customized. 6 The changed SQR program has to be thoroughly tested to make sure that the input parameters are passed and accepted correctly. TE STIN G YO UR S QR P R OGR AM TEAM LinG - Live, Informative, Non-cost and Genuine! 491 C H A P T E R 2 6 Using process recurrences and job streams IN THIS CHAPTER • Creating a recurrence definition • Scheduling programs for execution on a recurring basis • Creating job streams that contain multiple processes 492 TEAM LinG - Live, Informative, Non-cost and Genuine! During the course of this book we discussed the execution of PeopleSoft-delivered and custom processes under the PeopleSoft Process Scheduler. We demonstrated how to create a Process Definition to execute SQR programs, and how to monitor the status of process execution. You also learned how your program can communicate with the Process Scheduler via the API programs. However, there are some other important aspects of the PeopleSoft Process Scheduler that we did not cover. 26.1 Recurrence definition recurrence definition When you execute Recurrence definition your programs on the server, you can schedule them to run at predefined intervals. A special Recurrence definition has to be created and assigned to the process. When the Recurrence definition is created, it may be assigned to the process through its Process definition or from the Process Request Dialog page at runtime. Let’s see, for example, how we can schedule our custom program TEST25B.SQR for execution every day at 9:00 PM. As we already mentioned, starting from release 8 PeopleSoft moved the creation of Recurrence definitions to the Process Scheduler Manager. Navigation: Figure 26.1 PeopleTools ➠ Process Scheduler Manager ➠ Use ➠ Recurrence Definition Recurrence Definitions page This page (figure 26.1) allows us to add a new recurrence or reuse an existing one. Let’s create our own Recurrence Definition. Click on the Add a New Value link. Then choose a meaningful name for your process. Since our goal is to execute the TEST25B.SQR every day, let’s call it TEST25B_DAILY as shown in figure 26.2. RE C U RR EN C E D E FIN I T I O N TEAM LinG - Live, Informative, Non-cost and Genuine! 493 Figure 26.2 Adding a new Recurrence Definition, TEST25B_DAILY Click on the Add button (figure 26.2). This brings us to the Recurrence Definitions page (figure 26.3). Figure 26.3 494 Setting up the Recurrence Definition page CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! In the page shown in figure 26.3, we added a Description to our process recurrence, clicked on Daily to specify the Recurrence pattern, then selected the starting day and time of the cycle under the Start Request column. The Repeat column indicates how many times the process will repeat within the For timeframe. For example, you could specify that the process should run every 15 minutes within an hour. We also selected the option to Start Next Recurrence when the prior recurrence has completed. This means that the next process will be queued only when the previous process completed successfully. If you would like it to run regardless of the completion of prior runs, click on the Next Recurrence is Scheduled option. Also note that we selected to run our process every day by clicking on the Everyday check box. There is another option that we could have utilized if needed, that is to execute our process every weekday. In this case if we click on the Every weekday check box, the system would automatically select days Monday thru Friday. We can also execute our report weekly as shown in figure 26.4. Figure 26.4 Selecting the Weekly pattern In this case the system allows us to mark the days of the week we want to execute our report on. In the example shown in figure 26.4, the report will be executed every Monday and Friday. We can also select the Monthly option (figure 26.5). Figure 26.5 Selecting the 1st Sunday every month pattern When selecting Monthly, the system allows us to set a specific date every month or you can set a recurrence of the 1st, 2nd, 3rd, 4th, or Last Day of the month. The Day can also be any day of the week. For example, in figure 26.5 we selected our process to run every 1st Sunday of the month. Let’s have our process run daily at 9:00 PM and click Save. Our new Recurrence definition is created. But in order to utilize it properly, we will need to go through a few more steps discussed in the next subchapter. RE C U RR EN C E D E FIN I T I O N TEAM LinG - Live, Informative, Non-cost and Genuine! 495 26.2 Scheduling programs for execution on a recurring basis scheduling programs We just created scheduling execution a Recurrence on a recurring definition basis for our TEST25B.SQR process requesting that our process needs to be scheduled for execution every day at 9:00 PM. Our next step is to assign this Recurrence definition to the process. There are two ways of doing this. The Recurrence definition may be assigned to the process through its Process Definition or from the Process Request page at runtime. Let’s use the first method. Navigation: PeopleTools ➠ Process Manager ➠ Use ➠ Process Definitions Select the TEST25B Process Definition and switch to the Process Definition Options page, as shown in figure 26.6. Figure 26.6 Process Definition Options page with Recurrence name grayed-out Now we can type in our Recurrence definition name into the Recurrence Name field on the Process Definition page (figure 26.6). The only problem is that this field is grayed-out. The system does not allow us to specify the Recurrence. Why? The answer is simple. A program can only be scheduled for a recurrence execution if it is run on Server. Let’s change the Run Location from Both to Server, and then enter or search for a valid Recurrence definition (figure 26.7). 496 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 26.7 Assigning a Recurrence Definition to TEST25B Now we can save our modified Process definition and start testing. Navigation: Administer Workforce ➠ Administer Workforce (GBL) ➠ Process Select any existing Run Control ID, and fill in the Run Control page with your input parameters as shown in figure 26.8. Let’s examine our Run Control page again (figure 26.8). Since we are planning to schedule this process for execution every day, we have to supply the appropriate parameters to our SQR program every time the program runs. In our case, the parameters are: Start Date, End Date, File Name. When the program is executed manually, users enter the parameters when they submit the process for execution, as we just did in figure 26.8. We need to find a way to automatically fill in our Run Control table. There are several methods you can use depending on your business needs. The simplest one is to use the System date as the Start Date/End Date parameters. The standard method is to make your program default to System date if the date in the Run Control record is blank. If the System date technique is not applicable to your process, you would have to develop your own custom method to automatically supply the correct date to your program at each program execution. The third parameter, File Name, can be entered only once, and the Run Control record will retain its value for all subsequent runs. For our test, we’ll leave the Start Date and the End Date as is. Let’s click now on the Run button. This will bring you to the Process Scheduler Request page (figure 26.9) S CH E DUL ING PROGR AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 497 Figure 26.8 Filling in the Run Control page Figure 26.9 Process Scheduler Request page 498 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Notice that here we do not have any Recurrence specified. Let’s leave the Server and the Recurrence fields blank and see how the system will schedule our process. Remember that even though we haven’t selected any Recurrence definition here, we still have the Recurrence attached to our Process Definition. Let’s submit our process by clicking on the OK button, the system will bring you to the Process List tab on the Process Requests page shown in figure 26.10. Figure 26.10 Submitting TEST25B for execution automatically schedules same process execution on the following day Notice that we only submitted the process with instance number 82. As soon as this process has been successfully executed, the next process with instance number 83 is queued for the future execution. Let’s click on the Details link next to the queued process. This will bring you to the Process Request Details page (figure 26.11) On this page we can see that the Process Scheduler used the Recurrence definition TEST25B_DAILY and therefore, has already scheduled our process for the next day execution. Also notice that even though our process had been queued for next day execution, the time of the next day run is not 9:00 PM as we would expect. Take a look at the S CH E DUL ING PROGR AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 499 Figure 26.11 Verifying the Process Request details Process Request page in figure 26.9. The Run Time parameter specified on this page is exactly the time that the process is scheduled to run. We have our answer now. In order to schedule the process at the right time, we need to specify the Recurrence definition on the Process Request page. Let’s do this. For our next test, we’ll fill in the Recurrence field with the Recurrence definition that we prepared before as shown in figure 26.12. Notice that as soon as we selected the Recurrence definition, the Run Time field became grayed-out. Click on the OK button to submit the process. As you can see from figure 26.13, our process has been scheduled for execution at 9:00 PM. This is exactly what we specified in our recurrence definition. We’ve done lots of experimenting here. What have we learned so far? • In order to schedule a process for recurrent executions, a Recurrence Definition has to be created. • If a Recurrence Definition is attached to a Process Definition, then as soon as this process is submitted for execution, the next recurrence is created. Note that this will happen even if the Recurrence is not specified on the Process Request page. In this case the time of execution will be the time specified on the Process Request page. 500 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 26.12 Specifying the Recurrence definition on the Process Request page Figure 26.13 The process has been queued for execution at 9:00 PM S CH E DUL ING PROGR AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 501 • If you specify Recurrence on the Process Request page, the time of your process execution will be as defined in your Process Recurrence. • You do not have to always attach a Recurrence definition to the Process definition. You do it only when you want to protect the execution of a particular process on a recurrent basis from any user modifications. • In order to submit a process for recurrent executions, simply select the proper Recurrence definition on the Process Scheduler Request page and submit the process manually for the first time only. 26.3 Using Job Streams using job streams So far job using in this streams book we have been executing single processes from the PeopleSoft Process Scheduler. Oftentimes, your business requires the execution of multiple processes one after another or in parallel. PeopleSoft allows you do this if you run your processes on a Server. The Job definition is used to accomplish this task. A Job (or Job stream) in PeopleSoft usually consists of two or more processes. You can combine your SQR and Cobol programs into one Job to be executed in a parallel or serial mode. When scheduling your Job to run in a serial mode, all processes within the Job will be executed sequentially, one after another. Otherwise, they will be executed in parallel mode without any specific order. Similar to an individual process, you can schedule a Job to run at a later time or on a recurring basis. It is always a good approach to combine all processes that should be executed at a specific time into a Job stream, and schedule this Job stream for execution at predefined time intervals (e.g., every night). Let’s take, for example, one of the PeopleSoft delivered processes, the Refresh Employees Application Agent process (PER099) and schedule it to run together with our Email interface (TEST25B.SQR). Both processes need to be executed every night. In order to schedule any Job for execution, a Job definition has to be created. In our case, we will need to create a Job Definition that will contain PER099 and TEST25B.SQR. As we already know, when executing a process from the Process Scheduler, the process accepts its input parameters from the on-line pages. A Process definition is linked to a specific page through a Component. Likewise, the Job definition also requires a Component to be specified. Therefore, all processes in your Job stream will accept input parameters from this particular Component that may consist of several pages. To illustrate this point, let’s create a Component for our Job stream. 502 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! 26.3.1 Creating a Component for a Job stream Since both processes in our Job stream are already designed to run under the Process Scheduler, our task is very simple. We just need to combine their Run Control pages into one Component. Let’s find out what pages are used as Run Control pages for PER099.SQR and TEST25B.SQR. Log in to the developer’s environment. Navigation: Go ➠ Define Business Rules ➠ Administer HR System ➠ Process ➠ Refresh PS_Employees Table ➠ Update/Display Figure 26.14 Finding the name of the Run Control Page for the Refresh PS_Employees Table process You can see the name of the run control page at the bottom of the window (figure 26.14). If you don’t see the page name, click on View ➠ Page Name from the menu. The Run Control page that is used to run the Refresh Employees Table process is RUNCTL_ASOFDATE. When scheduling this program for execution in a Job stream, we obviously want to preserve all the functionality of the job’s components, including the input parameters processing. Therefore, we need to include this page into our new component. We know from the previous chapter that the name of the Run Control page for our Email process is MY_RUN_CNTL. That’s all we need to know in order to create our job Component. Let’s now create our new Component. Navigation: Go ➠ PeopleTools ➠ Application Designer ➠ New ➠ Component US ING JOB STR E AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 503 Figure 26.15 Creating a new Component for a Job Stream Our new Component should include both the MY_RUN_CNTL and RUNCTL_ASOFDATE run control pages. After inseting these pages and typing in some meaningful labels for each page in the Item Label fields, we need to specify the Component’s Properties (figure 26.16). The Search record and the Detail page for our Run Control Component should be the same as that for a Component in a single process. Figure 26.16 Specifying Component Properties for our Job stream 504 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 26.17 Saving Component for new Job Stream Now, we save the new object as MY_HR_NIGHTLY (figure 26.17). Do not forget to add your new object to the Project by clicking on F7 function key on your keyboard. After clicking on the OK button, we are ready to add our job to a Menu. 26.3.2 Creating a Menu Item for our new Job stream In order for our users to access a Run Control page, we need to attach this page to the proper Menu Item via a Component. Let’s use the existing Administer HR System menu, and create a new menu bar, named Job Stream, and use it to add our new Job stream menu item and for all future Job streams related to HR tasks. Figure 26.18 Creating a new Menu Bar and Menu Item As you can see in figure 26.18, we created a new menu bar, Job Stream. Then, just by clicking on an empty rectangle under this Menu Bar, we created a new menu item, HR Nightly, and linked our MY_HR_NIGHTLY Component to this Menu Item. After clicking on the OK button, we will modify the security to allow access to our new Menu item. US ING JOB STR E AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 505 26.3.3 Granting security access to the new objects Instead of adding our new items to the existing Permission Lists as we did before, let’s follow the steps described in chapter 23.8 and create a new Permission List. Navigation: Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ Permission List Let’s click on Add a New Value and specify the new Permission List name as HRJOBS. Click on the Add button. After specifying the description of our new Permis- sions, switch to the Pages tab and select the Administer_HR_System menu as shown in figure 26.19. Figure 26.19 Adding a menu to the new Permission list Let’s select Edit Component to the right of the Menu name. This brings us to the Component Permission screen. Scroll down and find our new component, Job Stream. Select Edit Pages next to the Job Stream Component. Click on Select All and then OK as shown in figure 26.20. Click on the OK button again and save our new Permission List. 506 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Figure 26.20 Authorizing our new component and pages within it The new Permission list HRJOBS has been created. We need to attach it to the existing Role or create a new Role. Let’s create a new Role. Again, we can follow the steps described in chapter 23.8 of this book. Navigation: Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ Roles Let’s select Add a New Value and enter a new Role Name, My HR Role. Click on the Add button. After entering the role’s description on General tab, click on the Save button. The Role is created. Switch to the Permission List Tab and enter the Permission List name, HRJOBS created in the previous step as shown in figure 26.21. Click on Save. The Permission List is now linked to My HR Role (figure 23.21). Our next step is to add the newly created role to the user profile. Navigation: Home ➠ PeopleTools ➠ Maintain Security ➠ Use ➠ User Profiles Let’s modify our profile to test the application, and then we will do the same for all users who are going to use our new objects. Enter User ID as PS and switch to the Roles tab. Scroll down the page and click on the (+) sign to add a new row. Enter: My HR Role and press the tab key (figure 26.22). US ING JOB STR E AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 507 Figure 26.21 Creating a new Role, MY HR Role Figure 26.22 Adding a role to a User Profile 508 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Let’s save our changes. User PS is assigned to My HR Role that has permissions to execute our new job stream. Now, we can test our new menu. Navigation: Figure 26.23 Home ➠ Define Business Rules ➠ Administer HR System ➠ Job Stream The new menu bar and menu item are created for our job stream Are we ready to run our job? Not yet. We need to create a Job Definition first. 26.3.4 Creating a Job definition When creating a new Job definition, use a unique Job definition name across Process definition names and Job definition names. PeopleSoft does not allow the same names within both definition, i.e.; Process definitions and Job definitions. Navigation: Go ➠ PeopleTools ➠ Process Scheduler Manager ➠ Use ➠ Job Definitions Click on Add A New Value and add MYHRJOB. Unlike the process definition creation, when you add a new job definition, the job name does not have to match any of your processes. You can give any name to your job. US ING JOB STR E AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 509 The process of creating a Job definition is similar to that of creating a Process definition. Let’s take a close look at what is involved in this process, and discuss the meaning of each field in the Job definition (figure 26.24). Figure 26.24 Creating a Job definition In Job description, you specify the Job definition description that will be displayed on the Process Scheduler Request page. Run mode can be either Serial or Parallel. If you want all processes in your job run sequentially, select Serial mode, otherwise, use Parallel. We will run our processes in a Parallel mode since the second process does not depend on the first process execution. Job priority can be High, Medium, or Low. This information is used by the Process Scheduler to initiate jobs with higher priorities first. We’ll specify our job priority as Medium. In the lower portion of your Job Definition page, below Process List, enter the processes that will be included into your job. If you’ve selected a Serial mode your processes have to be listed in the order they will be executed, otherwise, list them in any order. You should turn On the Run Always flag if you want your processes to be executed even if one of the previous processes failed. Since we selected to execute our process in 510 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Parallel mode, this field is not available for edits. The system automatically turns this flag On if the processes are run in a parallel mode. Also note that users can not run the Job’s individual processes if the Run Always flag is set to On. Let’s switch to the next tab, Job Definition Options (figure 26.25). Figure 26.25 The Job Definition Options tab Recurrence Name is used to specify a recurrence schedule that you previously set up. This parameter is optional, and can also be specified for your Job on the Process Scheduler Request page. We need to specify a Component your Job should be attached to. For our Job we will specify the name of the MY_HR_NIGHTLY Component that we previously created to run this Job. For each component you specify Process Groups for users who should be allowed to run your job. Now we can save our job definition. There are two more optional pages in this group, the Job Distribution and Job Notification pages. We’ll not use these pages for our test. Our Job Definition is created. Let’s save it and see how we can submit our Job for execution. US ING JOB STR E AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 511 26.3.5 Scheduling a Job for Execution Let’s add a new Run Control for our job, MYJOB. The Run Control Component appears. It consists of two pages, one for each process. Let’s enter the input parameters for each process. (figure 26.26). Navigation: Figure 26.26 Home ➠ Define Business Rules ➠ Administer HR System ➠ Job Streams ➠ HR Nightly Process Populating the Run Control page with required parameters for Email Interface Note that the first page in our component is the Email interface page and the second one is the Refresh Employees Process run control page. Does this mean that if you have ten processes in your job definition you would need ten pages in your Run Control Component? Not necessarily. Some of your processes may not even require any input parameters and, therefore, would not need additional Run Control pages. Others may need the same input parameters, and in this case one page can be used for several processes. It all depends on the records your processes are using to get the input parameters from. Let’s take, for example, the Years of Service program. It accepts two parameters: As of Date and Years of Service. If you have a job that includes this program and another one that only needs As of Date as its input parameter (for example the Pending Future 512 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! Actions report), you can use the same page to run both reports. This is all, of course, under a condition that both programs are using the same Run Control record. In the second page of our Component, the Refresh Employees Process Run Control page, we also use the As of Date input parameter. But since the run control records for both our processes are different, you have to include both pages in our Component (figures 26.26 and 26.27.) Figure 26.27 Entering input parameters for the Refresh Employees process After all parameters in the Run Control pages are entered, we are ready to execute our job. Click on the Run button to submit the job (figure 26.28) As you can see in figure 26.28, as soon as the job is submitted for execution, a number of process instances are created. In this case, there are three process instances: 206, 207 and 208. If you click on the Process Monitor, you will see why the system created three unique process instance numbers (figure 26.29). On the Process Request page click on the View Job Items check box. The page will display the job process instance (206) and all process instances within it (207, 208). As you can see, all the processes in our job successfully finished their execution. US ING JOB STR E AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 513 Figure 26.28 Submitting our job for execution Figure 26.29 Process Request page 514 CH A P T E R 26 PR O CE SS RE CU RR EN CE S & JO B S TR E A M S TEAM LinG - Live, Informative, Non-cost and Genuine! In many cases jobs are created to run automatically on a recurrent basis. We can schedule our nightly job for a recurrent execution the same way as we did for individual process in the beginning of this chapter. KEY POINTS 1 You can schedule your programs that run on Server for execution on a recurring basis. 2 A job (or job stream) may include one or more processes. 3 In order to schedule a job for execution, a Job definition has to be created. Like a Process definition, a Job definition needs to be associated with a Component. 4 You can include processes of different types (SQR program, COBOL, Application Engine) into one job. 5 You need to create a page in the job’s Component for each run control record used by a process in the job. 6 If you want your processes to be executed sequentially, you should select the Serial mode on your Job Definition page, otherwise, use the Parallel mode. US ING JOB STR E AMS TEAM LinG - Live, Informative, Non-cost and Genuine! 515 C H A P T E R 2 7 Implementing security in SQR IN THIS CHAPTER • Why SQR needs security • Preventing SQR from executing outside the Process Scheduler • Implementing row-level Security in SQR 516 TEAM LinG - Live, Informative, Non-cost and Genuine! 27.1 Why SQR needs security Why SQR needs security In chapter Why SQR needs 23 of this security book we already discussed the importance of building and maintaining data security. We learned how PeopleSoft Security Maintenance tools could help us to accomplish this task. At that point we were concerned with online security, and particularly with user security. Based on certain permissions, our users were able to access the menu, component and page that we created in order to execute our SQR report. As we already mentioned, PeopleSoft provides many different levels of security. Let’s discuss now why we need SQR security. Let’s say, we developed an SQR program that prints salary information for all employees in the corporation. This corporation, as many others, consists of different departments. The department managers are allowed to see their respective department information. Also, a head of the HR department is allowed to see the entire report output. When we developed the report, we placed it under a certain menu item and allowed access to this report to all department and HR managers, so they can execute it whenever the need arises. But how can we make sure that data from different departments are visible only to their respective managers and the head of the HR department? For tasks like this, the row-level security is the answer. Row-level security is used to control access to specific rows of data in the database. PeopleSoft delivers applications with row-level security. PeopleSoft uses security search views to provide on-line row-level security when accessing pages and via PS Queries. These views are also used in some of the PeopleSoft-delivered SQR programs. The security search records are, in fact, regular SQL views designed with security in mind. You can design your own security search views or use the PeopleSoft-delivered ones. After such a view is created, PeopleTools lets you attach the view to the corresponding PeopleSoft record and component. PeopleSoft delivers different security search mechanisms based on the application. For example, the built-in Department security is delivered with PeopleSoft HRMS package, while PeopleSoft Financial applications secure financial transactions by business units and ledgers. In the following sub-chapters we will learn how to use and implement row-level security in custom-made SQR programs. Before we jump into the details of implementing the row-level security in SQR, let’s look at another security aspect: how to prevent SQR program execution outside the PeopleSoft Process Scheduler. 27.2 Preventing an SQR from running outside the Process Scheduler Running outside the Process Scheduler SQR program Running outside canthe beProcess submitted Scheduler in different ways: from the Process Scheduler, from the SQRW dialog box, or from the command line. If you are running your program from RU N NI N G O UT S I D E T H E P RO C E S S S CH E D UL ER TEAM LinG - Live, Informative, Non-cost and Genuine! 517 the Process Scheduler, its execution is controlled by various levels of PeopleSoft security. First, you must be an authorized PeopleSoft user to login to the PeopleSoft system. Second, the menu security is in place to prevent unauthorized access to a particular menu or page. Third, when creating a Process definition, you must specify authorized process security groups, thus restricting the execution of a particular program to a specific group or groups of users. All these guards, stand between a user and SQR. Once the user passes through the PeopleSoft online checkpoints, the system, in most cases, will run SQR using SYSADM as user ID, which means that your program will have full, unrestricted access to data. In order to prevent this, you need to implement the row-level security in your SQR program. If your SQR program is not run under the Process Scheduler, your security is at a much bigger risk: the database access password alone is not sufficient to maintain the proper security. In order to prevent SQR execution outside the PeopleSoft Process Scheduler, PeopleSoft offers now a simple and efficient solution. This technique is used in most PeopleSoft delivered SQR programs for HRMS application. Let’s take a look, for example at the Emergency Contacts report, PER004.SQR: !Part of the PER004.SQR that restricts the SQR execution outside !the Process Scheduler … begin-procedure Init-Report #define Year4 '1' move 'PER004' to $ReportID do Stdapi-Init If the value of $prcs_oprid is Null, if $prcs_oprid='' SQR detects that it is not executed from the Process Scheduler display '' display 'REPORT CAN NOT BE EXECUTED OUTSIDE OF PEOPLESOFT,PLEASE USE PROCESS SCHEDULER.' display '' goto last1 end-if do Security-Param if $prcs_process_instance = '' !No Prompt else do Select-Parameters end-if do Init_Printer do Init_Report_translation ($ReportID, $language_cd) do Append_Report_Translation ('HR') LAST1: end-procedure 518 C HA P T E R 2 7 IM P LE M E N T I NG S E C UR IT Y I N S Q R TEAM LinG - Live, Informative, Non-cost and Genuine! As you can see from the Init-Report procedure of the PER004.SQR, the program checks the $prcs_oprid variable, which is populated by the API code (in the Get-Run-Control-Parms procedure) only if the report was initiated from the Process Scheduler. If the value of $prcs_oprid is Null, SQR displays an error message and exits the program. This simple code can be easily implemented in any custom program, thus ensuring program execution under the PeopleSoft Process Scheduler only. 27.3 Using PeopleSoft Security views in SQR to implement row-level security Row-level security in SQR PeopleSoft Security incorporated viewsrow-level security in some of the HRMS application programs. Let’s examine how SQR security is implemented, for example, in the Emergency Contacts program, PER004.SQR. !The excerpt from PER004.SQR that deals with Security … begin-procedure Init-Report #define Year4 '1' move 'PER004' to $ReportID do Stdapi-Init if $prcs_oprid='' display '' display 'REPORT CAN NOT BE EXECUTED OUTSIDE OF PEOPLESOFT,PLEASE USE PROCESS SCHEDULER.' display '' goto last1 end-if do Security-Param Call the Security-Param procedure from HRSECTY.SQC if $prcs_process_instance = '' !No Prompt else do Select-Parameters end-if do Init_Printer do Init_Report_translation ($ReportID, $language_cd) do Append_Report_Translation ('HR') LAST1: end-procedure begin-procedure Get-Values let $language_cd = $PRCS_LANGUAGE_CD end-procedure begin-procedure Process-Main ROW -L EV EL S E C UR ITY IN SQ R TEAM LinG - Live, Informative, Non-cost and Genuine! 519 begin-SELECT DISTINCT A.NAME (+1,1,39) on-break level=1 A.DEPTNAME (0,41,30) on-break level=1 … FROM PS_EMPLOYEES A, Join with security PS_EMERGENCY_CNTCT B, search record PS_FAST_PERSGL_VW2 SCRTY WHERE B.EMPLID = A.EMPLID Use dynamic condition [$SecurityClause] defined in hrsecty.sqc AND A.EMPLID = SCRTY.EMPLID ORDER BY [$qualifier], B.PRIMARY_CONTACT DESC, B.CONTACT_NAME ASC end-SELECT end-procedure Process-Main … #include 'hrrnctl1.sqc' !Get run control parameter values #include 'hrgetval.sqc' !Get values mask routines #include 'hrsecty.sqc' !Get SQR Security parameters … As you can see in the above excerpt from the PER004.SQR program, the program calls the Security-Param procedure. This procedure is located in the hrsecty.sqc include file. The Security-Param procedure builds the $SecurityClause variable that is used to augment the Where clause in the main select. Basically, this procedure selects the Row Security Operator Class of the operator that executes our PER004.SQR program. It also looks at the Installation table and uses the Override Row Security Class from it, in case if it is specified there to override the operator’s security class. The next listing shows how this simple procedure prepares all the necessary components that could be then used in many SQR programs. begin-procedure Security-Param Show 'This Report is using Fast Security. If no data is selected, please have your' Show 'Security Administrator verify that the Fast Security View(s) have been created' Show ' ' BEGIN-SELECT A.ROWSECCLASS LET $OverrideClass = RTRIM(&A.ROWSECCLASS,' ') FROM PS_INSTALLATION A END-SELECT BEGIN-SELECT B.ROWSECCLASS LET $OpridClass = RTRIM(&B.ROWSECCLASS,' ') FROM PSOPRDEFN B WHERE B.OPRID = $prcs_oprid 520 C HA P T E R 2 7 IM P LE M E N T I NG S E C UR IT Y I N S Q R TEAM LinG - Live, Informative, Non-cost and Genuine! END-SELECT if $OverrideClass = '' let $RowSecClass = $OpridClass else let $RowSecClass = $OverrideClass end-if Let $SecurityClause = ' AND SCRTY.ROWSECCLASS = ''' || $RowSecClass || '''' $SecurityClause can be used in the Where clause in any program that calls this routine and selects from the Security view end-procedure After the procedure is executed, the program’s main select procedure can utilize the variables that where built in the Security-Param procedure. You can see that a new view, PS_FAST_PERSGL_VW2, is added to the SQL join. This is the fast department security view developed by PeopleSoft. This view is joined with the Employees table by EMPLID. In addition, the ROWSECCLASS which is a key field in the security view, is matched with ROWSECCLASS for $prcs_oprid. In other words, the Select statement only selects records of the employees that the operator ($prcs_oprid) is authorized to access. The fast security view PS_FAST_PERSGL_VW2 is an alternate fast search record for PS_PERS_SRCH_GBL. In order to use this view, the Fast Security functionality delivered by PeopleSoft must be implemented. Fast Security uses the Application Engine to populate special security tables that were created to support search views with faster performance. Instead of the PS_FAST_PERSGL_VW2 record, the PS_PERS_SRCH_QRY1 search record can be used. Fast security is an optional feature of PeopleSoft, if it is implemented in your organization, you can take advantage of it. Similarly, if your program selects from the Job table, you can join with PS_FAST_EMPGL_VW2 or EMPLMT_SRCH_QRY. Let’s summarize now what we learned about implementing security in SQR and what practical steps have to be undertaken to support this security. • Add code to your program to make sure your SQR is executed from the Process Scheduler by verifying the $prcs_oprid variable • Include hrsecty.sqc in your SQR program • Call the Security-Param procedure to select and build the security clause • Add a Security Search record to your Main Select to restrict access to nonauthorized data Now, that we know how simple it is to implement security in SQR, can we go ahead and incorporate it in all our custom programs? The process may not always be as simple as it seems. You need to know your data and choose a correct search record for ROW -L EV EL S E C UR ITY IN SQ R TEAM LinG - Live, Informative, Non-cost and Genuine! 521 your data selection procedure. If your goal is to limit the employee selection by authorizing operators only based on the department security implemented in your system, you can use either the fast security records or the query search records. Both these views utilize the department security. If your SQR program selects neither employees nor departments, you would need to use some other alternatives based on your specific needs. KEY POINTS 1 In order to prevent an SQR program execution outside the PeopleSoft Process Scheduler, check the value of the $prcs_oprid variable. 2 Row-level security is used to control access to specific rows of data in the database. PeopleSoft delivers applications with row-level security. 3 PeopleSoft uses security search view records to provide on-line row-level security. 4 Build your own security views when you cannot use the PeopleSoft-delivered ones. 522 C HA P T E R 2 7 IM P LE M E N T I NG S E C UR IT Y I N S Q R TEAM LinG - Live, Informative, Non-cost and Genuine! C H A P T E R 2 8 Working with effective-dated tables IN THIS CHAPTER • Selecting the current rows • Selecting the top rows to include both the current and future rows • Working with both the current and prior rows • Identifying prior rows based on certain selection criteria • Finding orphan rows in the parent-child table relationship • Calculating date intervals between different events 523 TEAM LinG - Live, Informative, Non-cost and Genuine! In this chapter we will discuss effective-dated tables. Working with these tables presents certain challenges to SQR programmers, and we will consider a few sample solutions to a number of frequently used tasks. 28.1 Understanding effective-dated records effective-dated records In PeopleSoft, effective-dated understanding most applications records are built around the concept of effective dates. This approach facilitates managing data changes over time and keeping track of all changes. It also allows for creating future-dated information and making these data effective only on the appropriate date. In order to accomplish this task, some PeopleSoft tables contain a special field: Effdt—effective date. A table that contains the effective date as a part of the table key, is usually called an effective-dated table, and the data rows of such tables are called effective-dated rows. PeopleSoft relates all effective-dated rows to one of the following three categories: • historical data rows • the current data row • future data rows. The best way to better understand this concept is to relate each record to the current date. The current data row is the most recently entered row, whose effective date comes closest to today’s date without exceeding it. For a given search criteria, there can be only one current data record within a particular table. The future data rows are rows with effective dates that are greater than today’s date. All other rows are considered historical rows. The historical data rows include rows with effective dates that are less than the current row’s effective date, as well as the rows with the effective date equal to the current row effective date if these rows were entered into the system prior to the current row. As an illustration, consider the following example. A table has the following key structure: Company Code, Effdt. As time goes by, users change some of the characteristics about the companies. They would like to keep track of all the changes. Some of the table rows are entered ahead of time to take effect in the future. Table 28.1 will help you better understand historical, current, and future records: As you can see, the row with the effective date 06/01/1997 is the current row in this table even if its effective date is less than today’s date (01/01/1998). In PeopleSoft, if the effective date is present in a table record, it is always a part of the table’s key. When records in effective-dated tables are selected for processing or viewing, these tables are usually sorted by the effective date in descending order to keep the future and most recently entered rows on the top. 524 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! Table 28.1 Future, current, and historical data rows in an effective-dated table Today’s Date Effective Date Category 01/01/1998 02/01/1998 Future 01/01/1998 06/01/1997 Current 01/01/1998 04/01/1997 Historical 01/01/1998 01/01/1996 Historical 28.2 Multiple records with the same effective date multiple records & effective dates In many records multiple cases, you withneed the same to insert effective multiple date rows with the same effective date into an effective-dated table. Let’s consider an example of the effective-dated table PS_Job that belongs to the PeopleSoft HRMS system. This table contains employee job history records. An employee may have several events that can occur on the same date. Most of these events are registered in the employee’s job history. The first event, for example, may take place when an employee is transferred to another location, while a second event might happen when this employee gets a promotion. Although these two different events may occur on the same date, your HR department would like to register them as separate records. To handle situations like this, PeopleSoft provides another key field: Effective Sequence (Effseq). When piared with Effdt, this key combination allows one to enter more than one unique row with the same effective date. In table 28.2, the Job table records, in addition to the employee Id and effective date, include one more key field: effective sequence: Table 28.2 An effective-dated table with multiple records with the same effective date Today’s Date Job Effdt Job Effseq Category 10/01/1998 01/01/1999 0 Future 10/01/1998 01/01/1997 1 Current 10/01/1998 01/01/1997 0 Historical 10/01/1998 04/01/1996 0 Historical 10/01/1998 01/01/1996 0 Historical As you can see, the concept of having only one current record for a given employee remains unchanged. Two records exist with the effective date of 01/01/1997, but only one of them, the one entered last on this date, is considered current. MUL TIPL E RECOR D S & EFF ECTIVE DATES TEAM LinG - Live, Informative, Non-cost and Genuine! 525 28.3 Different techniques of selecting data from effective-dated tables selection techniques & effective-dated tables The concept Different techniques of effective of selecting date-driven datadata gives users a great deal of flexibility in tracking historical data, as well as pre-recording future events. At the same time, this approach adds a certain degree of complexity to the application development process. A clear understanding of and ability to visualize the effective-dated data is particularly important when SQR programs are developed in the PeopleSoft environment. There are many ways of selecting effective-dated records in conjunction with specific business requirements. As is always the case in programming, the same goal can be achieved by using different SQL queries. In this chapter, we will discuss a few of the most common techniques used in PeopleSoft application development. While it is impossible to foresee all tasks and challenges that await you down the road, the examples presented here may lay a good foundation for mastering the subject. It is a good habit to build programs using already-tested SQL queries, since it lowers the cost of both application development and maintenance. 28.3.1 Selecting the current data row Selecting the current data row from effective-dated tables is a fundamental part of nearly all queries. Depending on the key of the table you are working with, the task of selecting the current record may be accomplished by using a sub-query. In the following example we will show you a commonly used technique of selecting the current row from an effective-dated table using the PS_Company_Tbl table as an example. The key structure of this table includes the Company and Effdt columns. The query below selects the current row for a given company: !TEST28A.SQR !Selecting the current data rows #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Begin-Select A.Company (+1,1) A.Effdt (,+1) A.Eff_Status (,+1) From PS_COMPANY_TBL A Where A.Company = 'CCB' And A.Effdt = (Select Max(Effdt) From PS_COMPANY_TBL Where Company= A.Company And Effdt <= $AsOfToday) 526 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! End-Select End-Program #Include 'datetime.sqc' #Include 'curdttim.sqc' In the example above, the sub-query results in selecting (for a given company) the maximum effective date that is less than or equal to the today’s date stored in the $AsOfToday variable. This fits our definition of the current date: it is not just the maximum date because the future records should not be included. The main query simply selects the row with the effective date equal to the date returned from the sub-query. Please note that the today’s date is obtained by calling the PeopleSoft-delivered procedure Get-Current-DateTime located in the Curdttim.sqc file. This procedure retrieves the server system date and places this date into the $AsOfToday variable. It is a good practice to use this variable instead of, for example, Oracle’s Sysdate, since it makes your program database independent. Let’s add some complexity to our example: this time, we need to select the current records for a list of companies. We only want current records with an effective status equal to Active. Here is the modified query: !TEST28B.SQR !Limiting the current row selection to !only records with the active status #include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Begin-Select A.Company (+1,1) A.Effdt (,+2) A.Eff_Status (,+2) From PS_COMPANY_TBL A Where A.Company in ('CCB','CCA','CIA') And A.Eff_Status = 'A' And A.Effdt = (Select Max(Effdt) From PS_COMPANY_TBL Where Company= A.Company And Effdt <= $AsOfToday) End-Select End-Program #Include 'datetime.sqc' #Include 'curdttim.sqc' The above example selects only the active current rows for a given list of the company Ids. Please note that the sub query does not include the Eff_Status column in its SEL ECTION TECHNIQU ES & EFF E CTI VE- DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 527 WHERE clause. This is the right way to build the query. Placing this column into the sub query (as shown in TEST28C.SQR) will result in an incorrect selection. !TEST28C.SQR !An improperly built WHERE clause brings an incorrect result set #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Begin-Select A.Company (+1,1) A.Effdt (,+2) A.Eff_Status (,+2) From PS_COMPANY_TBL A Where A.Company in ('CCB','CCA','CIA') And A.Effdt = (Select Max(Effdt) From PS_COMPANY_TBL Where Company = A.Company And Eff_Status = 'A' ! INCORRECT And Effdt <= $AsOfToday) Moving the effective End-Select status check to the sub-query results in End-Program incorrect selection. #Include 'datetime.sqc' #Include 'curdttim.sqc' What’s wrong with the query in Test28C.sqr? This query selects the most current among all active records instead of the current active records. See the difference? Take a look at table 28.3. Table 28.3 Records with Effective Date and Effective Status Today’s Date Company Record Effective Date Eff_Status 01/01/1998 Comp1 06/01/1997 I 01/01/1998 Comp1 04/01/1997 A 01/01/1998 Comp1 01/01/1996 A 01/01/1998 Comp2 01/01/1999 I 01/01/1998 Comp2 01/01/1998 A 01/01/1998 Comp2 01/01/1997 A The correct result from Test28B.sqr is as follows: Comp2 528 01/01/1998 CHAPTER 28 A This result is correct. W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! The incorrectly written program Test28C.SQR selects the following rows: Comp1 Comp2 04/01/1997 01/01/1998 A A This result is incorrect. Now you see the difference between the two queries: the status of the current record for company Comp1 is set to “I” (Inactive). The correctly built query should not have returned this record because all we wanted was the current active records. 28.3.2 Selecting the current data row from a table which includes multiple records with the same effective dates We have already discussed tables that may include multiple records with the same effective dates. The common method of maintaining table row uniqueness in these tables is to add a sequence number to the table key that is unique for any given effective date. Conceptually, the basic query algorithm is similar to the one presented in Test28A.sqr. However, because the table key includes both the effective date and the sequence number, selecting the current row now involves using two sub-queries instead of one: one for the effective date, and another for the sequence number. The following example will show you how to select, for a given company, the current row for each employee in the PeopleSoft Job table. !TEST28D.SQR !Selecting the current record from the Job table #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Begin-Select J.Emplid (+1,1) J.Company (,+1) J.Effdt (,+1) J.Action (,+1) Show &J.Emplid From PS_JOB J Where J.Company='CCB' And J.Effdt =(Select Max(J1.Effdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsofToday) And J.Effseq =(Select Max(J2.Effseq) SEL ECTION TECHNIQU ES & EFF E CTI VE- DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 529 From PS_JOB J2 Where J2.Emplid And J2.Empl_Rcd And J2.Effdt = J.Emplid = J.Empl_Rcd = J.Effdt) End-Select End-Program #Include 'datetime.sqc' #Include 'curdttim.sqc' To understand the above example, let’s assume for a second, that the query in the example does not have the second sub-query. This will result in returning more than one row if multiple events took place on the current record, but there should not be more than one current record for a given employee. The second sub-query, for any given effective date, selects only the record with the maximum effective sequence. As a result, the entire query returns only one row for each employee: the current row. Sometimes, business requirements call for selecting the record with the lowest effective sequence, that is, the record reflecting the first event of the day. In that case, the second sub-query in Test28D.sqr can be easily modified by using the Min aggregate function in place of the Max function. 28.3.3 Using the Loops parameter in the Select paragraph to limit the number of selected rows Suppose we need to select all active employees and, for each selected record, find out whether this employee is currently enrolled in a medical plan. This task can be accomplished by employing the above described current record search technique. This time, however, when the program finds the current record for an employee, it retrieves the current health benefit record for this employee: !TEST28E.SQR !Using the Loops parameter to select the current record #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Do Main End-Program Begin-Procedure Main Begin-Select J.Emplid (+1,1,11) J.Empl_Rcd (,+2,3) P.Name (,+2,15) J.Company (,+2,10) J.Effdt (,+2,10) 530 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! Do Select_Health_Ben If $Found = 'Y' Print &B.Benefit_Plan (,+2) End-If From PS_JOB J, Ps_Personal_Data P Where J.Company ='CCB' And J.Emplid = P.Emplid And J.Empl_Status ='A' And J.Effdt=(Select Max(J1.Effdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsOfToday) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd = J.Empl_Rcd And J2.Effdt = J.Effdt) End-Select End-Procedure !******************************************* Begin-Procedure Select_Health_Ben !******************************************* Let $Found = 'N' Loops = 1 parameter tells SQR to retrieve only one record and Begin-Select Loops=1 then exit the Select loop. B.Emplid B.Plan_Type B.Benefit_Plan Let $Found='Y' Show 'Selected ' &B.Emplid ' ' &B.Plan_Type From PS_Health_Benefit B Where B.Emplid =&J.Emplid And B.Empl_Rcd =&J.Empl_Rcd And B.Effdt <=$AsOfToday And B.Plan_Type ='10' And B.Coverage_Elect = 'E' Since the records are sorted by Order By B.Emplid,B.Effdt Desc Effdt, the first retrieved row will be the current record. End-Select End-Procedure #Include 'datetime.sqc' #Include 'curdttim.sqc' Note how the main procedure selects the current data row for each active employee who belongs to the ‘CCB’ company. For each selected employee, we need to find the current record from the Health_Benefit table with Plan_Type = '10' (Medical) and Coverage_Elect flag = 'E' (Enrolled). The task is achieved by calling the Select_Health_Ben routine for each row selected in the main routine. Since the SEL ECTION TECHNIQU ES & EFF E CTI VE- DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 531 Health_Benefit table is an effective-dated table, we could have used the same “Max (Effdt)” technique but, in this case, considering that we need to select only one record for each employee, it will be much more efficient to use the Loops=1 parameter in the Begin-Select command. The Loops=1 parameter actually tells SQR to retrieve only one record that satisfies the Where clause and, then, exit the Select loop. Since we sort the selected records by the effective date in descending order, the first selected row will be the current record we are looking for. As you can see, limiting the query result set in the Select_Health_Ben routine made the program simpler and more efficient. 28.3.4 Selecting the top row from an effective-dated table As we already mentioned, the top row in an effective-dated table is not always the current row. It may be one of the future rows. In certain cases, you need to select only the top records for employees, without checking to see whether or not the records are the current records or the future records. Selecting the top records involves only a slight modification of the query presented in Test28D.sqr. Here is how it looks now: !TEST28F.SQR !Selecting the top row for each employee within a given company #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Begin-Select J.Emplid (+1,1,11) J.Company (,+2,3) J.Effdt (,+2,10) J.Action (,+2,5) If &J.Effdt > $AsofToday Print 'Future' (,+2) show 'Future ' &J.Emplid Else Print 'Current' (,+2) Show 'Current ' &J.Emplid End-If From PS_JOB J Where J.Company='CCB' And J.Effdt=(Select Max(J1.Effdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid 532 CHAPTER 28 The sub-select does not include additional restrictions on the effective dates. W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! And And J2.Empl_Rcd J2.Effdt = J.Empl_Rcd = J.Effdt) End-Select End-Program #Include 'datetime.sqc' #Include 'curdttim.sqc’ The query in the example above selects only the top record for each employee. For some employees, this may be the current record, for others, the query may return the most recent future records. An additional If-Then SQR statement in the main query prints the appropriate label (“Future” or “Current”) for each selected record. 28.3.5 Selecting the current and prior rows Now we are going to tackle a somewhat more complex task. Often a need arises to first select a record using certain criteria and, then, based on the results of the first selection, select the prior historical record. For example, when working with employee benefits, you often need to know whether the benefit plan an employee is enrolled in has changed, or whether the employee’s dependents dropped their coverage. In Human Resources, you may want to know the action code in the prior row if, for example, the current record action is termination. There may be lots of other business reasons to retrieve the prior record. Let’s take a look at some techniques which may be helpful. This task now is more complex than previous ones. Using just plain SQL might prove to be rather difficult (although we will show you how to do this), but the strength of SQR lies in its ability to combine the power of SQL with the flexibility of procedural logic. Now is the time to take advantage of this fact. In our first example, we will utilize SQR’s ability to perform logical operations on each selected row. For each row selected in the main procedure, the program will call another select procedure which will, in turn, use the data selected in the main procedure as bind variables: !TEST28G.SQR !Selecting both the current and the prior rows #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Do Main End-Program Begin-Procedure Main Begin-Select J.Emplid (+1,1) J.Empl_Rcd (,+1) SEL ECTION TECHNIQU ES & EFF E CTI VE- DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 533 J.Company (,+1) J.Effdt (,+1) J.Effseq (,+1) J.Action (,+1) Print 'Current Row' (,+1) If &J.Effseq > 0 Do Select_Prior_Seq Else Do Select_Prior_Row End-If From PS_JOB J Where J.Company='CCB' And J.Empl_Status='A' And J.Effdt=(Select Max(J1.Effdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsOfToday) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd = J.Empl_Rcd And J2.Effdt = J.Effdt) End-Select End-Procedure !****************************************** Begin-Procedure Select_Prior_Row !****************************************** Begin-Select JP.Emplid (+1,1) JP.Company (,+1) JP.Effdt (,+1) JP.Effseq (,+1) JP.Action (,+1) Print 'Prior Effdt Row' (,+1) Show &J.Emplid ' ' &J.Effseq ' ' &J.Effdt ' ' &Jp.Effdt ' ' &Jp.Effseq Add 1 To #Count_Prior From PS_JOB JP Where JP.Emplid =&J.Emplid And JP.Empl_Rcd =&J.Empl_Rcd And JP.Effdt =(Select Max(J1P.Effdt) From PS_JOB J1P Where J1P.Emplid = JP.Emplid And J1P.Empl_Rcd = JP.Empl_Rcd And J1P.Effdt < &J.Effdt) And JP.Effseq =(Select Max(JP2.Effseq) From PS_JOB JP2 Where JP2.Emplid = JP.Emplid And JP2.Empl_Rcd = JP.Empl_Rcd And JP2.Effdt = JP.Effdt) End-Select 534 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! End-Procedure !****************************************** Begin-Procedure Select_Prior_Seq !****************************************** Begin-Select JS.Emplid (+1,1) JS.Company (,+1) JS.Effdt (,+1) JS.Effseq (,+1) JS.Action (,+1) Print 'Prior Effseq Row' (,+1) From PS_JOB JS Where JS.Emplid = &J.Emplid And JS.Empl_Rcd =&J.Empl_Rcd And JS.Effdt = &J.Effdt And JS.Effseq = (Select Max(JS2.Effseq) From PS_JOB JS2 Where JS2.Emplid = JS.Emplid And JS2.Empl_Rcd = JS.Empl_Rcd And JS2.Effdt = JS.Effdt And JS2.Effseq < &J.Effseq) End-Select End-Procedure #Include 'datetime.sqc' #Include 'curdttim.sqc' To better understand the algorithm of Test28G.sqr, let’s take a look at some data samples. Suppose the current record, selected in the query of the main procedure, contains 01/01/1998 as the effective date and 03 as the effective sequence. Let’s also assume that the prior record has 01/01/1998 as its effective date and 02 as its effective sequence. In the algorithm above, after selecting the current row, we check to see if the effective sequence is greater than zero. If yes, we know that the prior row should have the same effective date, but a lower effective sequence. Therefore, we call the Select_Prior_ Seq routine. If the record selected in the main procedure query has a zero effective sequence, then we know that the prior row should have a lower effective date and call the Select_Prior_Row routine. Another method of solving the same problem is to use the just-discussed Loops parameter to limit the number of prior records to be selected: !TEST28I.SQR !Using Loops=1 to select the prior row #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Do Main SEL ECTION TECHNIQU ES & EFF E CTI VE- DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 535 Show '#Count_Prior=' #Count_Prior End-Program Begin-Procedure Main Begin-Select J.Emplid (+1,1) J.Empl_Rcd (,+1) J.Company (,+1) J.Effdt (,+1) J.Effseq (,+1) J.Action (,+1) Print 'Current Row' (,+1) Do Select_Prior_Row From PS_JOB J Where J.Company='700' And J.Empl_Status='A' And J.PayGroup='WE1' And J.Effdt=(Select Max(J1.Effdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= Sysdate) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd = J.Empl_Rcd And J2.Effdt = J.Effdt) End-Select End-Procedure !****************************************** Begin-Procedure Select_Prior_Row !****************************************** Begin-Select Loops=1 JP.Emplid (+1,1) JP.Company (,+1) JP.Effdt (,+1) JP.Effseq (,+1) JP.Action (,+1) Print 'Prior Effdt Row' (,+1) Add 1 To #Count_Prior From PS_JOB JP Where JP.Emplid =&J.Emplid And JP.Empl_Rcd =&J.Empl_Rcd And ( JP.Effdt < &J.Effdt Or (JP.Effdt = &J.Effdt And JP.Effseq < &J.Effseq) ) Order By JP.EMPLID,JP.Effdt Desc, JP.Effseq Desc End-Select End-Procedure #Include 'datetime.sqc' #Include 'curdttim.sqc' 536 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! As you can see, the Order By clause in the Select_Prior_Row routine sorts the selected rows by the effective date and the effective sequence in descending order, thereby ensuring that the query returns the prior rows with the most recent one on top. The Loops=1 parameter limits the query result set to the top selected row only. We promised to demonstrate how to select prior rows by using only SQL with no SQR procedure calls. You can do this by joining the Job table with itself. The key to this technique is to think of the second Job table as if it were a different table. Another important point of this algorithm is that it uses a concatenation of the effective date and the effective sequence. If we convert the effective date into the “YYYYMMDD” format and concatenate this string with the effective sequence, the resulting string will be a combination of both fields. Assuming that the current effective date is 01/01/1998 and the current effective sequence is 03, the string will look like this: “1998010103”. Now, we know that the combination of the effective date and effective sequence for any prior record must be less than “1998010103”. For example, if the effective date in the prior record is 01/01/1998, and the effective sequence of the prior record is 02, the combination will be “1998010102”. What if the effective sequence of the current record is zero? In that case, our string will look like “1998010100”, and the prior record will have a lower effective date. As a result, regardless of the prior record’s effective sequence, the combination of the effective date and effective sequence for this record will have a lesser value than that for the current record. For instance, the prior record’s effective date may be 12/31/1997 and the effective sequence may be 05. The This logic is valid Note only if you include string “1997123105” is lower in value than “1998010103”. the century in the year. The following code will show you how to imple- Be careful! ment the technique described above: !TEST28J.SQR !Using Self-Join to select the current !and the prior rows of data in one query #Include 'setenv.sqc' Begin-Program Do Get-Current-DateTime Show $AsOfToday Do Main Show 'Count=' #Count End-Program Begin-Procedure Main Begin-Select J.Emplid (+1,1) J.Empl_Rcd (+1,1) SEL ECTION TECHNIQU ES & EFF E CTI VE- DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 537 J.Company (,+1) J.Effdt (,+1) J.Effseq (,+1) J.Action (,+1) Print '->' (,+1) JP.Effdt (,+1) JP.Effseq (,+1) JP.Action (,+1) Add 1 To #Count Show &J.Emplid ' ' &J.Effseq ' ' &J.Effdt ' ' &Jp.Effdt ' ' &Jp.Effseq From PS_JOB J, PS_JOB JP Where J.Company='CCB' And J.Empl_Status='A' And J.Effdt=(Select Max(J1.Effdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsOfToday) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd = J.Empl_Rcd And J2.Effdt = J.Effdt) And JP. Emplid = J.Emplid And JP.Empl_Rcd = J.Empl_Rcd And JP.Effdt = (Select Max(J1P.Effdt) From PS_JOB J1P Where J1P.Emplid = JP.Emplid And J1P.Empl_Rcd = JP.Empl_Rcd And To_Char(J1P.Effdt,'YYYYMMDD') ||to_char(J1P.EFFSEQ,'99') < To_Char(J.Effdt,'YYYYMMDD')||to_char(J.EFFSEQ,'99')) And JP.effseq = (Select Max(J2P.Effseq) From PS_JOB J2P Where J2P.Emplid = JP.Emplid And J2P.Empl_Rcd = JP.Empl_Rcd And J2p.Effdt=Jp.Effdt And To_Char(J2P.Effdt,'YYYYMMDD') ||to_char(J2P.EFFSEQ,'99') < To_Char(J.Effdt,'YYYYMMDD')||to_char(J.EFFSEQ,'99')) Order By J.emplid, J.effdt End-Select End-Procedure #Include 'datetime.sqc' #Include 'curdttim.sqc' Here we use a combination of the self-join technique and a concatenation of two columns Effdt and Effseq to avoid the need to invoke extra queries. The code in this example is really efficient. This example contains one drawback, however. There are 538 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! situations in which the query will not print any rows. Can you guess when? It will happen in the case in which the prior record does not exist. The remedy to this problem (if we still want to do everything in one select procedure) is to use the outer join. You will have to make sure that your database supports outer joins. In many cases, not printing records may perfectly fit your business needs if you do not need to select any rows anyway. Regardless, you should definitely keep the above-mentioned in mind. 28.3.6 Using Exists in the Where clause to check for prior rows Often you need to select records only when the prior record with specific characteristics exists. Suppose you would like to select the current records of all active employees who were promoted prior to a specified date. Our SQR code may look like the following: !TEST28K.SQR !Using the Exists operator to check if there is at least one record !for this employee whose Action='PRO' (promotion) … Do Get-Current-Date-Time … Begin-Procedure Main Begin-Select J.Emplid (+1,1) J.Company (,+1) J.Effdt (,+1) J.Effseq (,+1) J.Action (,+1) From PS_JOB J Where J.Company=$Comp1 And J.Empl_Status='A' And J.Effdt=(Select Max(J1.Efdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsofToday) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd = J.Empl_Rcd And J2.Effdt = J.Effdt) And Exists (Select 'X' From PS_JOB JP Where JP.Emplid = J.Emplid And JP.Empl_Rcd = J.Empl_Rcd And JP.Effdt < $InputDate And JP.Action = 'PRO') If this sub-select returns at least one record, then the Exists statement will return 'True' and the current record will be selected; otherwise, the conditions in the Where clause are not satisfied, and the record is not selected. SEL ECTION TECHNIQU ES & EFF E CTI VE- DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 539 End-Select End-Procedure … #Include 'curdttim' The Exists operator checks for any records with the effective date less than the $InputDate. It does not actually care if the selected record is current or historical record. If you don’t want to include the current record in this selection, just add the following to the Exists operator in the Where clause: And To_Char(JP.Effdt,'YYYYMMDD') ||JP.EFFSEQ < To_Char(J.Effdt,'YYYYMMDD')||J.EFFSEQ 28.4 Other frequently used operations with effective-dated tables other operations & effective-dated tables Frequently Identifying used operations with effective-dated 28.4.1 orphan rowstables When dealing with parent-child relationships in database processing, it is important to make sure that all logically related tables are kept synchronized. When deleting parent records, all child records of this parent have to be deleted as well. In database design, this is called referential integrity. It can be enforced by RDBMS, but, for a number of reasons, PeopleSoft chose not to enforce referential integrity on the Human Resources Management System (HRMS) core tables. Therefore, it becomes the programmer’s responsibility. Good housekeeping should not permit “orphan” rows (rows without their respective parents), however, there are often situations in which orphan rows are created during conversions or due to some other reasons. In order to delete orphan rows, they have to be identified first. The following example demonstrates a simple technique of identifying orphan rows in the Health_Dependnt table. !TEST28L.SQR !Identifying orphan rows … Begin-Select A.Emplid (+1,1) A.Empl_Rcd (,+1) A.Cobra_Event_Id (,+1) A.Plan_Type (,+1) A.Benefit_Nbr (,+1) A.Dependent_Benef The PS_Health_Benefit table is a Do Delete-Orphan-Record parent to the PS_Health_Dependnt. From PS_HEALTH_DEPENDNT A Therefore, a parent should exist for Where each row in the child table. A.Emplid NOT IN (Select Emplid from PS_Health_Benefit 540 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! Where And And And And And End-Select End-Procedure … Emplid Empl_Rcd Cobra_Event_ID Plan_Type Benefit_NBR Effdt = = = = = = A.Emplid A.Empl_Rcd A.Cobra_Event_ID A.Plan_Type A.Benefit_NBR A.Effdt) 28.4.2 Calculating date intervals between events Working with different applications, especially with HRMS, often requires that various operations be performed on date intervals. For example, you may be asked to create a report with all employees’ anniversary dates. Another example is when you need to calculate the length of time that has passed since a specific event in an employee’s history, or to alert an HR administrator that an employee review is past due. Once the specified records are selected, there are many different ways to perform the actual date calculations. Most databases provide a number of date arithmetic and date conversion functions that could be used for date operations. In order to create database independent programs, you should avoid using database-specific functions and use the similar SQR functions instead (see chapter 4). If you use date interval calculations in the Where clause, you have to use database-specific functions: SQR commands and functions cannot be used in the Where clause. We will demonstrate different techniques using both database-specific date functions and SQR database-independent functions, leaving it up to our readers to decide which method to use, based on their own specific system requirements. 28.4.3 Calculating time difference in years since employee’s last promotion Suppose our task is to select all presently active employees for a specific company, who have stayed with the company for at least a year, and, for each selected employee, to print the employee Id, hire/rehire date, company name and length of time that has passed since the employee’s last promotion. For those employees who had not had their promotions yet, print a string of asterisks to identify a potential problem. Also, mark all employees who did not have a single promotion for more than five years. !TEST28M.SQR !Calculating the length of time that has passed since the !employee's last promotion #Include 'setenv.sqc' !************************************************* Begin-Setup OTHE R OPERA TIONS & EFF ECTIVE-DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 541 !************************************************* Declare-Variable Date $Serv_Dt Date $AsOfToday_Dt End-Declare End-Setup !************************************************* Begin-Program !************************************************* Do Get-Current-DateTime ! Get $AsOfToday Do Main End-Program !************************************************* Begin-Procedure Main !************************************************* Begin-Select J.Emplid J.Empl_Rcd J.Company J.Effdt E.Hire_Dt E.Rehire_Dt If &E.Hire_Dt > &E.Rehire_Dt Let $Serv_Dt=&E.Hire_Dt Else Let $Serv_Dt=&E.ReHire_Dt End-If Let $AsOfToday_Dt = strtodate($AsOfToday, 'YYYY-MM-DD') Let $Years_Worked=datediff($AsOfToday_Dt,$Serv_Dt,'year') If $Years_Worked >='1' Print &J.Emplid (+1,1,11) Print &J.Empl_Rcd (,+1,2) Do Select_Promotion_Row If $Found='Y' Let #Diff=datediff($AsOfToday_Dt,&JP.Effdt,'year') Move #Diff To $Diff1 99.99 Print $Diff1 (,+1,5) If #Diff > 5 Print '*' (,+1) End-If Else Print ' ' (,+7,10) Print '*********' (,+1) End-If End-If From PS_JOB J, PS_Employment E Where J.Company='CCB' 542 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! And And J.Empl_Status ='A' J.Effdt =(Select Max(J1.Effdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsOfToday) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd# = J.Empl_Rcd# And J2.Effdt = J.Effdt) And E.Emplid =J.Emplid And E.Empl_Rcd =J.Empl_Rcd End-Select End-Procedure !************************************************* Begin-Procedure Select_Promotion_Row !************************************************* Let $Found='N' Begin-Select JP.Effdt Let $Found='Y' Print &JP.Effdt (,+1,10) Note that the Action is checked in the sub-query, not in the main query. From PS_JOB JP Where JP.Emplid = &J.Emplid And JP.Empl_Rcd = &J.Empl_Rcd And JP.Company = &J.Company and JP.Effdt = (Select Max(J1P.Effdt) From PS_JOB J1P Where J1P.Emplid = JP.Emplid And J1P.Empl_Rcd = JP.Empl_Rcd And J1P.Action = 'PRO' And J1P.Effdt <= $AsOfToday) And JP.Effseq = (Select Max(J2P.Effseq) From PS_JOB J2P Where J2P.Emplid = JP.Emplid and J2P.Empl_Rcd = JP.Empl_Rcd And J2P.Effdt = JP.Effdt) End-Select End-Procedure #Include 'datetime.sqc' #Include 'curdttim.sqc' In Test28M.sqr, we accomplish our task by using a two-step process. In the first step, we select all active employees for a given company. Then, for each selected employee record, we check to see if the employee had been with the company for more than a year, and, if so, we call a separate procedure named Select_Promotion_Row. In this procedure, we only select records with Action = 'PRO' (promotion). Please note that the check for promotion is located in the Where clause of the sub-query, not in the main query because our task is to select the latest record among all the records with OTHE R OPERA TIONS & EFF ECTIVE-DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 543 Action = 'PRO'. If we had placed this check into the main query, the selection would be limited only to the promotion records that happen to be in the top row. This mistake is fairly common. In our main procedure, based on the result from the Select_Promotion_Row procedure, we either calculate the date difference using the SQR datediff() function, or print a string of asterisks if no records with Action = 'PRO' have been found. We also calculate the difference between the today’s date and the last promotion date, and, if this difference is greater than five years, print an asterisk next to this field. The code presented in Test26M.sqr uses only SQR date functions and, therefore, is database independent. The next example will demonstrate a somewhat more efficient technique, but performance in this case is achieved at the expense of database independence. In the previous example, the query in the Main procedure selects all current rows, regardless of the employee promotion history. If we wanted to limit the number of rows selected in the Main procedure, we could have included a check for the number of years in service in the Where clause. This approach will certainly increase efficiency. However, because SQR commands cannot be placed in the Where clause, we can use only database-specific functions to calculate the difference between the dates. As a result, our program will become database-specific. To demonstrate this technique, let’s rewrite the previous example using Oracle functions in date difference calculations: ! TEST28N.SQR !Using the Oracle functions to calculate date differences … Begin-Procedure Main Begin-Select J.Emplid J.Effdt Print &J.Emplid (+1,1,11) Print &J.Empl_Rcd (,+1,2) Do Select_Promotion_Row If $Found='Y' Let #Diff=DateDiff($AsOfToday_Dt,&JP.Effdt,'year') Move #Diff to $Diff1 99.99 Print $Diff1 (,+1,5) If #Diff > 5 Print '*' (,+1) End-If Else Print ' ' (,+7,10) Print '*********' (,+1) End-If From PS_JOB J, PS_Employment E Where J.Company='CCB' And J.Empl_Status ='A' And J.Effdt =(Select Max(J1.Effdt) 544 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsofToday) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd = J.Empl_Rcd And J2.Effdt = J.Effdt) And E.Emplid =J.Emplid And E.Empl_Rcd =J.Empl_Rcd And (Months_Between(Sysdate,decode (A.ReHire_Date,A.Null,A.Hire_Dt,ReHire_Dt))) >= 12 Placing the date difference calculation in the Where clause makes the program databasedependent. End-Select End-Procedure In Test26N.sqr, we use the Months_Between Oracle function to calculate the difference between the system date and the hire or rehire date. We also use another Oracle function, Decode, when deciding which date should be used in the calculation: Hire_ Date or Rehire_Date. As you can see, the program becomes smaller and more efficient since, instead of selecting all current records, we select only ones that satisfy the Where clause. But speed comes with a price tag: the program loses database independence. 28.4.4 Producing an employee fifth anniversary list In the following example, we will show you how to generate a list of employees who are eligible to receive the Fifth Anniversary Award. The program in the example runs once a year as of January 1 and produces a list of all employees who will receive the award during the run year. We will use the Oracle date calculations functions in this example: ! TEST28O.SQR !Creating an employee fifth anniversary list … Begin-Procedure Main Begin-Select A.Emplid (+1,1) A.Hire_Dt (,+1) A.Rehire_Dt (,+1) J.Empl_Status (,+1) Placing the date difference calculation in the Where From PS_Employment A, PS_Job J clause makes the program Where A.Emplid =J.Emplid database-dependent. And A.Empl_Rcd =J.Empl_Rcd And J.Empl_Status = 'A' And (Months_Between(Sysdate,decode(A.ReHire_Dt,Null, A.Hire_Dt,A.ReHire_Dt)) / 12) > 4 And (Months_Between(Sysdate,decode(A.ReHire_Dt,Null, A.Hire_Dt,A.ReHire_Dt)) / 12)<= 5 OTHE R OPERA TIONS & EFF ECTIVE-DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 545 And J.Effdt =(Select Max(J1.Efdt) From PS_JOB J1 Where J1.Emplid = J.Emplid And J1.Empl_Rcd = J.Empl_Rcd And J1.Effdt <= $AsofToday) And J.Effseq =(Select Max(J2.Effseq) From PS_JOB J2 Where J2.Emplid = J.Emplid And J2.Empl_Rcd = J.Empl_Rcd And J2.Effdt = J.Effdt) End-Select End-Procedure … As you can see, the program uses Oracle-specific functions. If we want to make our program database independent, we would have to break the above selection into two steps: select all active employees in the main procedure, and, then, for every selected employee, calculate the time difference between their hire or rehire date and today’s date, using the datediff() SQR function. KEY POINTS 1 Placing effective dates in table records allows for managing and tracking data changes over time. 2 Tables that contain the effective date columns are usually called the effectivedated tables. 3 PeopleSoft relates all effective-dated rows to one of the three categories: historical rows, current row, or future rows. 4 There can be only one current record for a particular table key. 5 In PeopleSoft, if the effective date is present in a table record, it is always a part of the table key. 6 The effective sequence fields, when paired with the effective date fields, allow for entering more than one row with the same effective date. 546 CHAPTER 28 W ORKI NG W ITH EFFECTIVE- DATED TABL ES TEAM LinG - Live, Informative, Non-cost and Genuine! KEY POINTS (CONTINUED) 7 The Max SQL aggregate function can be used to select the current rows from an effective-dated table. 8 You can improve performance when selecting the current records by sorting the table by the effective date and sequence and using Loops=1 to limit the output of sub-queries to only one record. 9 You can select both the current and prior rows from an effective-dated table. 10 You can use the Exists SQL operator to select only specific prior rows. 11 Date intervals between events can be calculated using either SQR date functions or database-specific functions. Avoiding database-specific functions makes your programs database-independent. OTHE R OPERA TIONS & EFF ECTIVE-DATED TABLES TEAM LinG - Live, Informative, Non-cost and Genuine! 547 TEAM LinG - Live, Informative, Non-cost and Genuine! appendix A Sample database In this appendix we will discuss the sample database that was used to run the SQLrelated examples throughout the book. When creating this database, we used the PeopleSoft HRMS database as a model to make our test examples as close to real life applications as possible. At the same time, our database is different from the PeopleSoft database. We included only tables that were used in our test programs and examples. This does not mean that other tables are not important, we simply did not use them in our book. When creating our tables, we included only those columns that were essential to run our examples. We assigned the columns the same names as their PeopleSoft counterparts’ names to avoid confusion and to make our examples closer to those in real business experiences. (In certain cases, we had to simplify the table structure by including only one of several related columns. For example, we included only the Address1 column into the Personal_Data table instead of the four address columns: Address1, Address2, Address3, and Address4.) 549 TEAM LinG - Live, Informative, Non-cost and Genuine! In order to emphasize the difference between the PeopleSoft database and the database used in the SQR-related chapters (parts 1 and 2), our tables do not have the PS_ prefix in their names. When running the PeopleSoft-related examples (part 3 of this book), we had to use the PeopleSoft database to take advantage of many existing PeopleSoft objects and SQC files. In these examples, the table names are prefixed with PS_. Figure A.1 displays the sample database, its tables, and the relationships between these tables. This figure is identical to the one included in chapter 2. SAMPLE DATABASE GENL_DEDUCTN PERSONAL_DATA EMPLID NAME ADDRESS1 CITY STATE POSTAL COUNTRY PHONE SEX MAR_STATUS BIRTHDATE SMOKER FT_STUDENT EMPLOYMENT EMPLID (FK) EMPL_RCD HIRE_DT REHIRE_DT TERMINATION_DT BUSINESS_TITLE SUPERVISOR_ID JOB EMPLID (FK) EMPL_RCD (FK) DEDCD (FK) EFFDT DED_CALC DEDUCTION_END_DT GOAL_AMT EMPLID (FK) EMPL_RCD (FK) EFFDT EFFSEQ DEPTID (FK) JOBCODE EMPL_STATUS ACTION COMPANY (FK) PAYGROUP ANNUAL_RT DEDUCTION_TBL PLAN_TYPE DEDCD EFFDT DESCR DEPT_TBL COMPANY_TBL DEPTID EFFDT EFF_STATUS DESCR MANAGER_ID LOCATION (FK) COMPANY (FK) DEPENDENT_BENEF EMPLID (FK) DEPENDENT_BENEF NAME SAME_ADDRESS_EMPL ADDRESS1 CITY STATE POSTAL COUNTRY RELATIONSHIP DEP_BENEF_TYPE SEX BIRTHDATE STUDENT Figure A.1 550 COMPANY EFFDT EFF_STATUS DESCR ADDRESS1 CITY STATE POSTAL COUNTRY FEDERAL_EIN LOCATION_TBL LOCATION EFFDT EFF_STATUS DESCR DESCRSHORT LOCALITY BEN_PLAN_TBL HEALTH_BENEFITS EMPLID (FK) EMPL_RCD (FK) PLAN_TYPE (FK) EFFDT DEDUCTION_END_DT COVERAGE_BEGIN_DT COVERAGE_END_DT COVERAGE_ELECT COVERAGE_ELECT_DT BENEFIT_PLAN (FK) COVERAGE_CD PLAN_TYPE BENEFIT_PLAN EFFDT DESCR DESCRSHORT GROUP_NBR HEALTH_DEPENDNT EMPLID (FK) EMPL_RCD (FK) PLAN_TYPE (FK) EFFDT (FK) DEPENDENT_BENEF HLTH_PROVIDER_ID Sample database tables A PP E N D I X A TEAM LinG - Live, Informative, Non-cost and Genuine! The Personal_Data table is the anchor table in the database. It contains personal information about each employee: name, address, phone, sex, marital status, etc. and is used to record data that do not tend to change. The table has one record per each employee. Its primary key is Emplid. Column Name Format Description Note Emplid Char Unique employee ID Primary key Name Char Employee name Address1 Char Employee address City Char Employee’s city of residence State Char Employee’s state of residence Postal Char Employee ZIP code Country Char Employee country Phone Char Employee home phone Sex Char Employee sex Mar_Status Char Employee marital status Birthdate Date Employee birth date Smoker Char Smoker char Smoker flag (Y/N) FT_Student char Full-time student flag (Y/N) The Employment table is one of the most important tables in the database. It contains each employee’s job-related information that does not tend to change. The table has one record per each employee’s employment assignment. This table is not designed to store historical information. (For example, if an employee’s business title changes, the new title replaces the old one.) Its primary key is a combination of Emplid and Empl_ Rcd. Its parent is the Personal_Data table. Column Name Format Description Note Emplid Char Employee ID Part of primary key Empl_Rcd Number Employment record number. Used to differentiate between different jobs Part of primary key Hire_Dt Date Hire date Rehire_Dt Date Rehire date Termination_Dt Date Termination date Business_Title Char Business title Supervisor_Id Char Supervisor ID SAMPLE DATABASE TEAM LinG - Live, Informative, Non-cost and Genuine! 551 The Job table is also one of the most important tables in the database. It contains each employee’s job-related information that changes over time. The table has one record per event that occurs in the course of employment. This table is designed to store historical information about different events. Its primary key is a combination of Emplid, Empl_Rcd, Effdt, and Effseq. Its parent is the Employment table. Column Name Format Purpose Note Emplid Char Employee ID Part of primary key Empl_Rcd Number Employment record number. Used Part of primary key to differentiate between different jobs Effdt Date Effective date of an event Effseq Number Effective sequence: used to conPart of primary key trol multiple events that may occur on the same effective date Deptid Char Department ID Jobcode Char Job code Empl_Status Char Employee status, e.g., “A” (active), “T” (terminated), etc. Action Char Action, e.g., “PRO” (promotion) Company Char Company Paygroup Char Paygroup Annual_Rt Number Annual rate Part of primary key Foreign key to Dept_Tbl Foreign key to Company_Tbl The Dept_Tbl table contains information about each department. This table is designed to store historical information. Its primary key is a combination of Deptid and Effdt. Column Name Format Description Deptid Char Department ID Part of primary key Effdt Date Effective date Part of primary key Eff_Status Char Effective status Descr Char Department description Manager_Id Char Manager ID Location Char Location code Foreign key to Location_Tbl Company Char Company ID Foreign key to Company_Tbl 552 Note A PP E N D I X A TEAM LinG - Live, Informative, Non-cost and Genuine! The Location_Tbl table contains information about each location. This table is designed to store historical information. Its primary key is a combination of Location and Effdt. Column Name Format Description Note Location Char Department ID Part of primary key Effdt Date Effective date Part of primary key Eff_Status Char Effective status Descr Char Location description Descrshort Char Short description Locality Char Locality The Company_Tbl table contains information about each company. This table is designed to store historical information. Its primary key is a combination of Company and Effdt. Column Name Format Description Note Company Char Company ID Part of primary key Effdt Date Effective date Part of primary key Eff_Status Char Effective status Descr Char Company description Address1 Char Company address City Char City State Char State Postal Char ZIP code Country Char Country Federal_EIN Number Federal Employer ID Number The Dependent_Benef table contains information about each employee’s dependents. The table has one record per each employee’s dependent. This table is not designed to store historical information. Its primary key is a combination of Emplid and Dependent_Benef. Its parent is the Personal_Data table. SAMPLE DATABASE TEAM LinG - Live, Informative, Non-cost and Genuine! 553 Column Name Format Description Note Emplid Char Employee ID Part of primary key Dependent_Benef Char Dependent code Part of primary key Name Char Dependent name Address1 Char Dependent address City Char Dependent’s city of residence State Char Dependent’s state of residence Postal Char Dependent’s ZIP code Country Char Dependent’s country Relationship Char Relationship to employee Dep_Benef_Type Char Dependent beneficiary type Sex Char Dependent’s sex Birthdate Date Dependent birth date Student Char Student indicator (Yes/No) The Health_Benefits table contains information about each employee health benefits. This table is designed to store historical information. Its primary key is a combination of Emplid, Empl_Rcd, Plan_Type, and Effdt. Column Name Format Description Note Emplid Char Employee ID Part of primary key Empl_Rcd Number Employment record number Part of primary key Plan_Type Char Plan type code Part of primary key Effdt Date Effective date Part of primary key Deduction_End_Dt Date Deduction end date Coverage_Begin_Dt Date Coverage begin date Coverage_End_Dt Date Coverage end date Coverage_Elect Char Coverage election code Coverage_Elect_Dt Date Coverage election date Benefit_Plan Char Benefit plan Coverage_Cd Char Coverage code Foreign key to Benef_Plan_Tbl The Health_Dependnt table contains information about each employee’s dependent health benefits. This table is designed to store historical information. Its primary key is a combination of Emplid, Empl_Rcd, Plan_Type, Effdt, and Dependent_ Benef columns. 554 A PP E N D I X A TEAM LinG - Live, Informative, Non-cost and Genuine! Column Name Format Description Note Emplid Char Employee ID Part of primary key Empl_Rcd Number Employment record number Part of primary key Plan_Type Char Plan type code Part of primary key Effdt Date Effective date Part of primary key Dependent_Benef Char Dependent code Part of primary key Hlth_Provider_Id Char Health provider ID The Ben_Plan_Tbl table contains information about available benefit plans. This table is designed to store historical information. Its primary key is a combination of Plan_Type, Benefit_Plan, and Effdt columns. Column Name Format Description Note Plan_Type Char Plan type Part of primary key Benefit_Plan Char Benefit plan Part of primary key Effdt Date Effective date Part of primary key Descr Char Plan description Descrshort Char Short plan description Provider Char Plan provider The Genl_Deductn table contains information about each employee’s payroll deductions. This table is designed to store historical information. Its primary key is a combination of Emplid, Empl_Rcd, Dedcd, and Effdt columns. Column Name Format Description Note Emplid Char Employee ID Part of primary key Empl_Rcd Number Employment record number Part of primary key Dedcd Char Deduction code Part of primary key Effdt Date Effective date Part of primary key Ded_Calc Char Deduction calculation routine Deduction_End_Dt Date Deduction end date Goal_Amt Goal amount Number SAMPLE DATABASE TEAM LinG - Live, Informative, Non-cost and Genuine! 555 The Deductn_Tbl table contains information about different types of payroll deductions for each plan type. This table is designed to store historical information. Its primary key is a combination of Plan_Type, Dedcd, and Effdt columns. Column Name Format Description Note Plan_Type Char Plan type Part of primary key Dedcd Char Deduction code Part of primary key Effdt Date Effective date Part of primary key Description Char Plan type description 556 A PP E N D I X A TEAM LinG - Live, Informative, Non-cost and Genuine! appendix B SQR command line flags SQR command line flags can be entered in the report argument portion of the SQR Dialog Box, or as a part of the SQR command line of the SQR Execute command line, or the SQR Print command line. SQR, SQR Execute, and SQR Print use different sets of flags. In the following table, all SQR command line flags are listed alphabetically. The table includes all SQR flags. Three separate table columns indicate which SQR product uses each specific flag. 557 TEAM LinG - Live, Informative, Non-cost and Genuine! Flag Dialog SQR SQR SQR Box Command Line Execute Print -A Y Y Y Y The program output will be appended to an existing output file. If the file does not exist, a new one will be created. This option is not allowed for PDS files (MVS). -Bnn Y Y Y N (Oracle, Sybase CT-Lib) The size of a buffer to hold retrieved rows each time data is retrieved from the database. The default is ten rows. You can enhance performance by increasing the number of rows in the buffer. Regardless of the setting, all rows are retrieved. When used on the command line, -B controls the setting for all BeginSelect commands. Within a program, each Begin-Select command may also have its own -Bnn flag for further optimization. -BURST:{xx} Y Y Y Y This flag is used when either the or -PRINTER:EH flag is specified (for Internet enabling). Using this flag allows you to divide your HTML output into smaller files for faster load time or to generate only certain portions of the entire report. -BURST:T generates the Table of Contents file. BURST:S[i ] generates the report output according to the symbolic Table of Contents entries. i is the level at which to burst upon (as defined in the TOC-ENTRY command) –BURST:P [ i, s [, s] . . . ] ] where i is the number of logical report pages in each .htm file and s is the page to burst upon Description –PRINTER:HT -C Y Y Y N (Windows) The Cancel dialog box will appear while the program is running so you can terminate the program execution. -CB Y Y Y N (Windows) Forces the communication box to be used (the default setting) 558 A P PE N D I X B TEAM LinG - Live, Informative, Non-cost and Genuine! Flag Dialog SQR SQR SQR Box Command Line Execute Print -Dnn N N Y Y (non-Windows) The program output report will be displayed on the terminal while it is being written to the output file. nn is the maximum number of lines to display before pausing. If no maximum number of lines is specified, the display will scroll down continuously. The printer type must be LP or the display will be ignored -DB database N Y Y N (SYBASE) The database name. This flag will override any Use command in the program. -DEBUG [xxx] Y Y N N When this flag is specified, all source code lines preceded by "#DebugY" will be compiled. y is any character among xxx or space. Without this flag, these lines are ignored. -DNT:[xx] Y Y N N Specifies the default behavior for numeric variables. The value for xx can be Integer, Float, Decimal, or V30. If V30 is specified, all numeric variables are considered float. To specify a precision for Decimal, append it with a colon delimiter (:)—for example, DNT:Decimal:20. Can be overridden by the Default parameter in the Declare-Variable command. -E[ file ] Y Y Y Y Causes SQR error messages to be directed to the named file. The default file is your program name with the .err extension. If no errors occur, no file is created. If this flag is specified, all SQR-generated errors (not the output of the Display or Show commands) will be directed to this file instead of the LOG file. You should either specify the fully qualified file name or no file name (do not specify the flag and the directory). Description SQR CO MMA ND L INE FL AGS TEAM LinG - Live, Informative, Non-cost and Genuine! 559 Dialog SQR SQR SQR Box Command Line Execute Print Flag Description -EH_APPLETS:dir Y Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. It specifies a directory where the Enhanced HTML applets reside. The default directory is where the GIF files used by the Navigation Bar reside. -EH_BQD Y Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR generates a BQD (Brio Query Format) file (with the name equal to your report name and the .BQD extension) and a BQD icon in the Navigation Bar. -EH_BQD:file_ name Y Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR associates the BQD icon with the specified file. -EH_BROWSER: Y browser_code Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. This flag specifies the target HTML browser. When browser_code is set to BASIC, SQR generates HTML suitable for all browsers. When browser_code is set to ALL, SQR generates a Java script which senses which browser is installed on the user's machine. When browser_code is set to IE, SQR generates HTML designed for Internet Explorer. When browser_code is set to NETSCAPE, SQR generates HTML designed for Netscape Navigator. -EH_CSV Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR generates a CSV (Comma Separated File) file (with the name equal to your report name and the .CSV extension) and a CSV icon in the Navigation Bar. 560 Y A P PE N D I X B TEAM LinG - Live, Informative, Non-cost and Genuine! Dialog SQR SQR SQR Box Command Line Execute Print Flag Description -EH_CSV:file_ name Y Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR associates the CSV icon with the specified file. -EH_CSVONLY Y Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR generates a CSV file and a CSV icon in the Navigation Bar without generating an HTML file. -EH_FULLHTML: Y level Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. This flag specifies the level of the Enhanced HTML code. Possible values are 30, 32, 40, TRUE (equivalent to 40), or FALSE (equivalent to 30). -EH_ICONS: dir Y Y Y Y This flag is used only when either the –PRINTER:HT or –PRINTER:EH flag is specified. This flag specifies the directory for the icons used in the HTML. -EH_IMAGES: dir Y Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. This flag specifies the directory for the GIF files used by the Navigation Bar. -EH_KEEP Y Y N N This flag is used in conjunction with the –EH_ZIP flag and only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR keeps its output files after copying them to the specified ZIP file. -EH_LANGUAGE: Y language Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. This flag specifies the language used for the HTML Navigation Bar. Possible values are ENGLISH, FRENCH, GERMAN, PORTUGUESE, SPANISH. SQR CO MMA ND L INE FL AGS TEAM LinG - Live, Informative, Non-cost and Genuine! 561 Flag Dialog SQR SQR SQR Box Command Line Execute Print -EH_PDF Y Y Y Y This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR generates an Adobe PDF file (with the name equal to your report name and the .PDF extension) and a PDF icon in the Navigation Bar. -EH_SCALE[:nnn] Y Y N N This flag is used in conjunction with the –EH_ZIP flag and only when either the –PRINTER:EP or –PRINTER:EH flag is specified. Sets the scaling factor for the report body from 50 to 200. -EH_XML: file_name Y Y N N This flag is used only when either the –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR associates the XML icon with the specified file. -EH_ZIP[: file_name] Y Y N N This flag is used only when either –PRINTER:EP or –PRINTER:EH flag is specified. When specified, SQR moves the output files to the specified file or to your_report_name.ZIP if no file name is specified. -F[ file | directory] Y Y Y Y Overrides the default output report LIS file name. If this flag is not specified, the output file will be created in the same directory in which the source program file resides. The default file name is your program name with the .lis extension. To use the current directory, specify -F without an argument. If the file name does not include a directory, the file will be created in the current directory. For multiple report programs, you can use multiple -F flags. -G file_mode N Y Y (VM) specifies the file mode to use when the report output file is created. See the VM C library manual “afopen” function, for a complete description of all the valid keywords and values. 562 N Description A P PE N D I X B TEAM LinG - Live, Informative, Non-cost and Genuine! Dialog SQR SQR SQR Box Command Line Execute Print Flag Description -G file_attributes N N Y Y (VMS, Open VMS) Specifies the file attributes to use when the report file is created. See the VAX C library manual, “create” function for a complete description of all the valid keywords and values. -GPRINT =YES|NO N N Y N (MVS) -GPRINT=YES causes SQR report file to have ANSI control characters written to the first column of each record of the file. -ID N N Y Y Causes the copyright banner to be displayed on the console -Idir_list Y Y N N Specifies the list of directories that SQR will search when processing the #Include directive if the include file does not exist in the current directory. The directory names must be separated by either commas (,) or semicolons (;). -KEEP Y Y Y N Causes SQR to create SPF output in addition to LIS output -LL{s|d} {c|i} Y Y N N The sorting method to be used in the Load-Lookup command: s = SQR sorts data, d = database sorts data, c = case sensitive sort, i = case insensitive sort. -LOCK {RR|CS |RON | RL | XX} Y Y N (SQLBase) Defines the types of locking (isolation level) for the session -Mfile Y Y N N Defines a startup file containing sizes to be assigned to various internal parameters for large or complex reports -NOLIS Y Y Y N Causes SQR to create SPF output and suppress LIS output -NR N Y Y N (SQLBase) No Recovery mode is used when connecting to the database. -O[file] Y Y Y Y Directs log messages to the specified file. If -O with no file is specified, the log file name will be your program name with the .log extension. By default, the file SQR.log is created in the current working directory SQR CO MMA ND L INE FL AGS TEAM LinG - Live, Informative, Non-cost and Genuine! 563 Flag Dialog SQR SQR SQR Box Command Line Execute Print -P N N Y Y (VM) Directs the output to the printer device -PB N Y Y N (Informix) Causes column data to retain trailing blanks -Proleid [rolepass] N Y Y N (Ingres) The role identifier used to associate permissions with SQR -PRINTER: xx Y Y Y Y Causes printer type xx to be used when creating all LIS files. Available printer types: LP (Line Printer), HP (HP LaserJet), HT (HTML), EH (Enhanced HTML), EP (Enhanced HTML/PDF), PD (PDF), PS (PostScript), WP (Windows default printer). The WP parameter sends the output to the default Windows printer. All other extensions send the output to the LIS file. -RS Y Y N N Saves the program in a pre-compiled file. The program is scanned and checked for correct syntax. Queries are validated. Then, the executable version is saved in a file with the name equal to your source program name and the .sqt extension. The Ask command variables are not prompted for after compilation. You can either use -RT flag to execute a pre-compiled program or the SQR Execute. -RT Y Y N N Executes pre-compiled files saved with the -RS flag. All syntax and query checking is skipped and processing begins immediately. The Ask command variables are not prompted for after compilation. Another way to run pre-compiled SQR programs is to use the SQR Execute. 564 Description A P PE N D I X B TEAM LinG - Live, Informative, Non-cost and Genuine! Flag Dialog SQR SQR SQR Box Command Line Execute Print -S Y Y Y N Requests that the status of all cursors be displayed at the end of the report run. Status includes the text of each SQL statement, number of times each was compiled and executed, and the total number of rows selected. This information can be used for debugging SQL statements and enhancing performance and tuning. -SORTnn N Y Y N (SQLBase) Specifies the size of the sort buffer in characters -Tnn Y Y Y N Specifies that you want to test your report for the first nn pages only. All ORDER BY clauses in SELECT statements are ignored to save time during testing. In multiple report programs this flag causes SQR to stop when the specified number of pages for the first report have been output. -T {BZ|BZ|ZB} N Y Y N (MVS) Prevents SQR from removing trailing blanks and zeros from database columns. -TB will prevent the trimming of blanks from character columns. -TZ will prevent the trimming of trailing zeros from the decimal portion of floating columns. -TBZ or -TZB will prevent both. -Uusername N Y Y N (Ingres) Specifies the user name. This overrides the user name specified in the connectivity string. Description -Vserver N Y Y N (SYBASE) Uses the named server -XB Y N Y Y (non-Windows) Suppresses the SQR banner and the “SQR ... end of Run” message XC N Y N N Suppresses the database Commit when the report has finished running. -XCB Y Y Y N Suppresses the SQR communication box. Requests for input will be made in Windows dialog boxes. SQR CO MMA ND L INE FL AGS TEAM LinG - Live, Informative, Non-cost and Genuine! 565 Flag Dialog SQR SQR SQR Box Command Line Execute Print -XI Y Y N N Prevents user interaction during a program run. If the Ask or Input command requires user input, an error occurs. -XL Y Y Y N Prevents SQR from logging on to the database. Programs run in this mode cannot contain any SQL statements. You still must supply a forward slash (/) on the command line as a placeholder for the connectivity information. Description -XLFF Y Y N N Prevents trailing form feed. -XMB Y Y Y N Disables the error message display so that a program can be run without interruption by error message boxes. Error messages will only be sent to an .err file. -XNAV Y Y Y N Prevents SQR from generating the “Navigation Bar” in generated .htm files. This will occur when only a single .htm file is produced. Multiple .htm files generated from a single report will always contain the “Navigation Bar”. -XP N Y Y N (SYBASE DBLib) Prevents SQR from creating temporary stored procedures -XTB Y Y Y Y Preserves the trailing blanks in a .lis file -XTOC Y Y Y Y Prevents SQR from generating the Table of Contents for the report. This flag is ignored when -PRINTER:HT or -PRINTER:EH is also specified. -ZIFfile Y Y Y Y Overrides the default directory for the SQR initialization file, SQR.ini -ZIV Y Y Y N Invokes the SPF Viewer after generating SPF output. In case of multiple output files, only the first report file will be passed to the SQR Viewer. 566 A P PE N D I X B TEAM LinG - Live, Informative, Non-cost and Genuine! Flag Dialog SQR SQR SQR Box Command Line Execute Print -ZMFfile Y Y Y Y Specifies the full path and name of the SQR error message file, SQRerr.dat -ZRF{file_name} Y Y Y Y Specifies the full name of an alternate registry.properties file. This file lists data sources that SQR server can access. Description SQR CO MMA ND L INE FL AGS TEAM LinG - Live, Informative, Non-cost and Genuine! 567 appendix C Built-in functions SQR built-in functions are used in expressions in the Let command and in logical expressions in the If [ Else] and While operators. SQR offers more than 50 different built-in functions. In addition, you can write your own functions in C using the supplied source file Ufunc.c (see chapter 18 for details). All SQR built-in functions can be divided into the following five categories: • numeric functions • file-related functions • date functions • string functions • miscellaneous functions. 568 TEAM LinG - Live, Informative, Non-cost and Genuine! In this appendix, we grouped all the functions by the above categories and listed the functions along with their descriptions, syntax, and examples of usage. Numeric functions abs() Description Returns the absolute value of its argument Syntax target_var = abs ( { num_value | num_expression } ) Arguments A decimal, float, or integer literal, column, or expression Returns The type of the returned value is same as the function’s argument type Example Let #Diff = abs( #Plan - #Actual) acos(), asin(), atan() Description Returns the arccosine, arcsine, or arctangent value of its argument Syntax target_var = acos ( { num_value | num_expression } ) target_var = asin ( { num_value | num_expression } ) target_var = atan ( { num_value | num_expression } ) Arguments A decimal, float, or integer literal, column, variable, or expression between -1 and 1 for acos(), asin(), and between 0 and high value for atan() Returns A float numeric in radians between 0 and π for acos(), or a float numeric in radians between -π/2 and π/2 for asin() and atan() Examples Let #Num1 = acos( 0 ) Let #Num2 = asin(#var2) Let #Num3 = atan(#tan3 / 2) cos(), sin(), tan() Description Returns the cosine, sine, or tangent value of its argument Syntax target_var = cos ( { num_value | num_expression } ) target_var = sin ( { num_value | num_expression } ) target_var = tan ( { num_value | num_expression } ) Arguments A decimal, float, or integer literal, column, variable, or expression in radians Returns A float numeric between -1 and 1 for cos() and sin(), or a float numeric between 0 and high value for tan() BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 569 Examples Let #Cos = cos( 0 ) Let #Sin = sin(#var2) Let #Tan = tan(#Var1 + #Var2) cosh(), sinh(), tanh() Description Returns the hyperbolic cosine, sine, or tangent value of its argument Syntax target_var = cosh ( { num_value | num_expression } ) target_var = sinh ( { num_value | num_expression } ) target_var = tanh ( { num_value | num_expression } ) Arguments A decimal, float, or integer literal, column, variable, or expression Returns A float numeric Examples Let #Cosh = cosh(#Var1) Let #Sinh = sinh(#Var2) Let #Tanh = tanh(#Var3) ceil(), floor() Description For ceil(), returns the smallest integer that is greater than or equal to the value of its argument. For negative arguments, returns a negative integer with an absolute value that is less than or equal to the absolute value of its argument for floor(), returns the largest integer that is less than or equal to the value of its argument. For negative arguments, returns a negative integer with an absolute value that is greater than or equal to the absolute value of its argument Syntax target_var = ceil( { num_value | num_expression } ) target_var = floor( { num_value | num_expression } ) Arguments A decimal, float, or integer literal, column, or expression Returns The type of the returned value is same as the function’s argument type Example Let #Ceil = ceil(#Var1) Let #Floor = floor(#Var2) deg(), rad() Description For deg(), converts degrees to radians For rad(), converts radians to degrees Syntax target_var = deg( { num_value | num_expression } ) target_var = rad( { num_value | num_expression } ) 570 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! Arguments A decimal, float, or integer literal, column, or expression Returns A float numeric Example Let #Degrees = deg(#Radians) Let #Radians = rad(#Degrees) e10(), exp() Description Returns the value of 10, or e raised to its argument Syntax target_var = e10( { num_value | num_expression } ) target_var = exp( { num_value | num_expression } ) Arguments A decimal, float, or integer literal, column, or expression Returns A float numeric Example Let #Dec_Value = e10(#Var1) Let #Exp_Value = exp(#Var2) log(), log10() Description Calculates the natural or base-10 logarithm of its argument Syntax target_var = log( { num_value | num_expression } ) target_var = log10( { num_value | num_expression } ) Arguments A decimal, float, or integer literal, column, or expression Returns A float numeric Example Let #Nat_Log = log(#Var1) Let #Base10_Log = log10(#Var2) mod() Description Returns the fractional remainder of a division of its first argument by its second argument Syntax target_var = mod({num_value-X | num_expression_X},{num_value-Y | num_expression_Y}) Arguments A decimal, float, or integer literal, column, or expression. The second argument must not be a zero Returns The arguments are promoted to the type of the greatest precision and the function returns a value of that type BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 571 Example Let #Rem = mod(#VarX, #VarY) power() Description Returns the value of its first argument raised to the power of its second argument Syntax target_var = power({num_value-X | num_expression_X},{num_ value-Y | num_expression_Y}) Arguments First argument: a decimal, float, or integer literal, column, or expression Second argument: a decimal, float, or integer literal, column, or expression Returns A float numeric Example Let #Power_Var =power(#VarX, #VarY) round(), trunc() Description round() rounds its first argument to the number of decimal places determined by its second argument trunc() truncates its first argument to the number of decimal places determined by its second argument Syntax target_var = round({num_value-X | num_expression_X},{num_value-Y | num_expression_Y}) target_var = trunc({num_value-X | num_expression_X},{num_value-Y | num_expression_Y}) Arguments First argument: a decimal, float, or integer literal, column, or expression Second argument: an integer literal, column, or expression Returns A value of the same type as the first argument Example Let #Round_Result =round(#VarX, #PrecisionX) Let #Trunc_Result =trunc(#VarY, #PrecisionY) sign() Description Returns -1, 0, or +1 depending on the sign of its argument Syntax target_var = sign({num_value | num_expression}) Arguments A decimal, float, or integer literal, column, or expression Returns -1, 0, or 1 572 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! Example Let #Sign = sign(#Var1) sqrt() Description Calculates the square root of its argument Syntax target_var = sqrt({num_value | num_expression}) Arguments A non-negative decimal, float, or integer literal, column, or expression Returns A float numeric Example Let #Sqrt_Val = sqrt(#Var) File-related functions delete() Description Deletes the specified file Syntax status_var = delete( file_name ) Arguments A string literal, column, or expression Returns Zero, if the file was deleted or an operating system specific return code if the file was not deleted Example Let #Result = delete($File_Name) exists() Description Determines if the specified file exists Syntax status_var = exists( file_name ) Arguments A string literal, column, or expression Returns Zero, if the file was found or an operating system specific return code if the file was not found Example Let #Exists = exists($File_Name) rename() Description Determines if the specified file exists Syntax status_var = rename( file_name ) BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 573 Arguments A string literal, column, or expression Returns Zero, if the file was renamed or an operating system specific return code if the file was not renamed Example Let #Renamed = rename($File_Name) Date functions dateadd() Description Adds the specified number of date/time units to the function’s first argument and returns the result of addition Syntax target_date = dateadd(source_date , date_unit , quantity ) Arguments First argument: a date literal, column, or expression Second argument: a string literal, column, variable, or expression. Specifies the type of the date/time unit. Valid units are: 'YEAR', 'QUARTER', 'WEEK', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND' Third argument: a numeric literal, column, variable, or expression. Specifies the number of date units to be added Returns A date Example Let $Fifth_Anniversary_Dte = dateadd(&Hire_Dte,'YEAR',5) datediff() Description Calculates the difference between the specified dates expressed in the specified number of date/time units Syntax date_diff = datediff(date_1 , date_2 , date_unit ) Arguments First argument: a date literal, column, or expression Second argument: a date literal, column, or expression Third argument: a string literal, column, variable, or expression. Specifies the type of the date/time unit. Valid units are: 'YEAR', 'QUARTER', 'WEEK', 'MONTH', 'DAY', 'HOUR', 'MINUTE', 'SECOND' Returns A float numeric Example Let #Date_Diff = datediff($Eff_Dte, $Curr_Dte, 'DAY' ) 574 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! datenow() Description Returns the current local date and time from the client machine Syntax curr_date = datenow ( ) Arguments None Returns A date Example Let $Curr_Dte = datenow() datetostr() Description Converts a date to a string with optional editing Syntax target_string = datetostr ( source_date , [ edit_mask ] ) Arguments First argument: a date variable or expression Second argument (optional): a string literal, column, variable, or expression specifying the edit mask. If not specified, the SQR_DB_ DATE_FORMAT setting will be used. If the setting is not specified, the database-dependent format will be used Returns A string Example Let $Date_String = datetostr(datenow(), 'DD-MM-YY' ) strtodate() Description Converts a string to a date with optional editing Syntax target_date = strtodate ( source_string , [ edit_mask ] ) Arguments First argument: a string literal, column, variable, or expression Second argument (optional): a string literal, column, variable, or expression specifying the edit mask. If not specified, the SQR_DB_ DATE_FORMAT setting will be used. If the setting is not specified, the database-dependent format or the database-independent format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]' will be used Returns A date Example Let $Date = strtodate(&Eff_Dte, 'MON-DD-YYYY' ) BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 575 String functions edit() Description Formats the source string using the specified edit mask Syntax target_string = edit ( source_string , edit_mask ) Arguments First argument: a string literal, column, variable, or expression Second argument: a string literal, column, variable, or expression specifying the edit mask Returns A string Example Let $SSN= edit(&SSN, 'xxx-xx-xxxx' ) instr() Description Returns the numeric position of the specified sub-string within a string. The search begins at the specified position (starting from 1) within the string Syntax position = instr ( source_string , substring , position) Arguments First argument: a date or string literal, column, variable, or expression Second argument: a string literal, column, variable, or expression specifying the searched sub-string Third argument: an integer literal, column, variable, or expression specifying the starting position Returns A float numeric Example Let #Pos= instr($Full_Name, 'Mary', 1 ) instrb() Description (Double-byte version of SQR only) Works similarly to instr() except that the starting point and returned value are expressed in bytes rather than in characters Syntax position = instrb ( source_string , substring , position) Arguments First argument: a date or string literal, column, variable, or expression Second argument: a string literal, column, variable, or expression specifying the searched sub-string Third argument: an integer literal, column, variable, or expression specifying the starting position in bytes Returns A float numeric 576 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! Example Let #Pos= instrb($Full_Name, 'Mary', 1 ) isblank() Description Returns a value of 1 if its argument is an empty string or composed of only whitespace characters; otherwise, returns a zero value Syntax target_var = isblank ( source_string) Arguments A date or string literal, column, variable, or expression Returns 0 or 1 Example Let #Blank_Flag= isblank(&Full_Name ) isnull() Description Returns a value of 1 if its argument is NULL; otherwise, returns a zero value Syntax target_var = isnull ( source_string) Arguments A date or string literal, column, variable, or expression Returns 0 or 1 Example If isnull(&Eff_Dte ) Do Print_Error Else Do Print_Detail End-If length() Description Returns the number of characters in its argument Syntax length = length ( source_string) Arguments A date or string literal, column, variable, or expression Returns A float numeric Example Let #Length= length($Input_String ) BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 577 lengthb() Description (Double-byte version of SQR only) Works similarly to length() except that the return value represents the number of bytes, rather than the number of characters Syntax length = lengthb ( source_string) Arguments A date or string literal, column, variable, or expression Returns A float numeric Example Let #Length= lengthb($Input_String ) lower(), upper() Description Converts its argument to lower (upper) case. Does not convert special characters Syntax target_string = lower ( source_string) target_string = upper ( source_string) Arguments A date or string literal, column, variable, or expression Returns A string Examples Let #Lower_Case_String= lower($Input_String ) Let #Upper_Case_String= upper($Input_String ) lpad(), rpad() Description Pads its first argument on the left (right) to the length specified in its second argument using the value specified in its third argument Syntax target_string = lpad ( source_string , length , pad_value) target_string = rpad ( source_string , length , pad_value) Arguments First argument: a date or string literal, column, variable, or expression Second argument: a decimal, float, or integer literal, column, variable, or expression specifying the length of the sub-string to be padded Third argument: a string literal, column, variable, or expression specifying the substituting sub-string Returns A string Examples Let #Left_Padded_String= lpad($Input_String, #Length, ' ' ) Let #Right_Padded_String= rpad($Input_String, 5, '/' ) 578 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! ltrim(), rtrim() Description Trims its first argument from the left (right) until a character specified in its second argument is not found Syntax target_string = ltrim ( source_string , trim_value) target_string = rtrim ( source_string , trim_value) Arguments First argument: a date or string literal, column, variable, or expression Second argument: a string literal, column, variable, or expression specifying the sub-string to be trimmed Returns A string Examples Let #Left_Trimmed_String= ltrim($Input_String, ' ' ) Let #Right_Trimmed_String= rtrim($Input_String, $Trim_Char ) replace() Description Scans the contents of source_string and replaces all occurrences of from_string with to_string. Syntax target_string = replace ( source_string , from_string , to_string ) Arguments First argument: a date or string literal, column, variable, or expression Second argument: a string literal, column, variable, or expression Third argument: a string literal, column, variable, or expression Returns A string. Example $New_Phone_Number = Replace($Old_Phone_Number,'-','/') roman() Description Converts its argument to lower case roman numerals Syntax target_string= roman ( source_numeric) Argumenst A decimal, float, or integer literal, column, variable, or expression Returns A string Example Let $Roman_Chapter_Number= roman(#Chapter_Number ) BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 579 substr() Description Extracts the specified number of characters from its first string argument. The extraction begins at the specified position (starting from 1) within the string Syntax target_string = substr ( source_string , position, length) Arguments First argument: a date or string literal, column, variable, or expression Second argument: a decimal, float, or integer literal, column, variable, or expression specifying the starting position of extraction Third argument: a decimal, float, or integer integer literal, column, variable, or expression specifying the number of extracted characters Returns A string Example Let #Substring=substr($Input_String, 1, #Length ) substrb() Description (Double-byte version of SQR only) Works similarly to substr() except that the position and length are expressed in the number of bytes, rather than in the number of characters Syntax target_string = substrb ( source_string , position, length) Arguments First argument: a date or string literal, column, variable, or expression Second argument: a decimal, float, or integer literal, column, variable, or expression specifying the starting position of extraction in bytes Third argument: a decimal, float, or integer integer literal, column, variable, or expression specifying the number of extracted bytes Returns A string Example Let #Substring=substrb($Input_String, 1, #Length_In_Bytes ) to_char() Description Converts its argument from numeric to string using maximum precision Syntax target_string = to_char( source_numeric) Arguments A decimal, float, or integer literal, column, variable, or expression Returns A string Example Let $My_String= to_char(#My_Number ) 580 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! to_multi_byte() Description (Double-byte version of SQR only) Converts its argument from single-byte format to double-byte format. Any occurrence of a doublebyte character that also has a single-byte representation (numerals, punctuation, roman characters, and katakana) will be converted Syntax target_string = to_multi_byte( source_numeric) Arguments A date or string literal, column, variable, or expression Returns A string Example Let $Multi_Byte_String= to_multi_byte($Single_Byte_String ) to_single_byte() Description (Double-byte version of SQR only) Converts its argument from double-byte format to single-byte format. Any occurrence of a singlebyte character that also has a multi-byte representation (numerals, punctuation, roman characters, and katakana) will be converted. It will also convert a sequence of kana characters followed by certain grammatical marks into a single-byte character which combines the two elements Syntax target_string = to_single_byte( source_numeric) Arguments A date or string literal, column, variable, or expression Returns A string Example Let $Single_Byte_String= to_single_byte($Multi_Byte_String ) to_number() Description Converts its argument from string to float numeric Syntax target_numeric= to_number( source_string) Arguments A string literal, column, variable, or expression Returns A float numeric Example Let #My_Number= to_number($My_String ) translate() Description Inspects the string specified in its first argument and converts the characters specified in the second argument into the corresponding characters specified in the third argument Syntax target_string = translate( source_string , from_set, to_set ) BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 581 Arguments First argument: a date or string literal, column, variable, or expression Second argument: a string literal, column, variable, or expression specifying the character set to be translated Third argument: a string literal, column, variable, or expression specifying the resulting character set Returns A string Example Let $From_Set = ',-' Let $To_Set = ' /' Let #Translated_String=translate($Input_String, $From_Set , $To_Set ) Let $New_Date = translate($Old_Date,'/','-') Miscellaneous functions array() Description Returns a pointer to the starting address of the specified array field. The returned value can only be used in user-defined functions Syntax target_string = array( array_name , field_name ) Arguments First argument: a string literal, column, variable, or expression specifying the array name Second argument: a string literal, column, variable, or expression specifying the array field name Returns A string containing the pointer value Example ! In this example, a user-defined ConvertToHex() function is used Let #Hex_String=ConvertToHex(array('Employees', 'Employee_Id'), #Length) ascii() Description Converts the first character of its argument to ASCII Syntax target_ascii= ascii( source_string) Arguments A date or string literal, column, variable, or expression Returns A float numeric Example Let #Ascii= ascii($My_String ) chr() Description Converts its argument from ASCII to character Syntax target_char= chr( source_numeric) 582 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! Arguments A decimal, float, or integer literal, column, variable, or expression Returns A string Example Let $Char= chr(#My_Ascii ) cond() Description If the first argument is non-zero, returns the second argument; otherwise returns the third argument Syntax target_variable = cond( x_value , y_value , z_value ) Arguments First argument: a decimal, float, or integer literal, column, variable, or expression Second argument: a date, string or numeric literal, column, variable, or expression Third argument: a date, string or numeric literal, column, variable, or expression If the second argument is numeric, the third argument must also be numeric If either the second or the third argument is a date, the function returns a date Returns The format depends on the function’s second or third argument (whichever is returned) Example Let #Average_Salary=#Total_Salary / cond(#Number_Of_Empl, #Number_Of_Empl, 1) getenv() Description Returns the value of the specified environmental variable Syntax target_stringr = getenv( variable_name) Arguments A string literal, column, variable, or expression Returns A string Example Let $Full_Name= getenv('PATH' ) || $File_Name nvl() Description If the first argument is NULL, returns the second argument; otherwise returns the first argument Syntax target_variable = nvl( x_value , y_value ) BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 583 Arguments First argument: a date, string, or numeric literal, column, variable, or expression Second argument: a date, string or numeric literal, column, variable, or expression If the first argument is numeric, the second argument must also be numeric; otherwise date and string arguments are compatible Returns The format depends on the format of the returned argument Example Let $Address1=nvl( &Adress1, ' N/A ' ) range() Description Returns 1 if the the first argument value is between the second and third argument values; otherwise returns zero Syntax target_variable = range( x_value , y_value, z_value ) Arguments First argument: a date, string, or numeric literal, column, variable, or expression Second argument: a date, string or numeric literal, column, variable, or expression Third argument: a date, string or numeric literal, column, variable, or expression If the first argument is numeric, the other two arguments must be numeric. If the first argument is string, the other two arguments must be string. If the first argument is a date, the other two arguments can be dates or strings Returns 0 or 1 Example Let #Flag = range(#Hourly_Rte, #Min_Rte, #Max_Rte) wrapdepth() Description Returns the number of print lines for a wrapped string Syntax number_lines = wrapdepth( source_string , width , line_height , break_chars , strip_chars ) 584 A P PE N D I X C TEAM LinG - Live, Informative, Non-cost and Genuine! Arguments First argument: a string literal, column, variable, or expression specifying the string to be wrapped Second argument: a decimal, float, or integer literal, column, variable, or expression specifying the maximum paragraph width in characters Third argument: a decimal, float, or integer literal, column, variable, or expression specifying the number of lines to skip between each line of the wrapped data Fourth argument: a string literal, column, variable, or expression specifying which characters will force a wrap to occur. Will accept regular characters plus non-display characters whose ASCII values are surrounded by angled brackets Fifth argument: a string literal, column, variable, or expression specifying which characters will be converted to spaces before the wrap occurs. Will accept regular characters plus non-display characters whose ASCII values are surrounded by angled brackets Returns A decimal, float, or integer numeric Example Let #Number_Lines= wrapdepth(&Desc, 25, 2, '<13>', '/\@-' ) BUIL T-IN F UNCTIONS TEAM LinG - Live, Informative, Non-cost and Genuine! 585 appendix D SQR command syntax In this appendix, we will provide our readers with SQR command syntax, including operands, parameters, and options. We split all SQR commands and operators into the following categories: • Program sectioning • Compiler directives • File input/output operators • Operations on string, date, and numeric variables • Logic operators • Interacting with databases • Compile-time and run-time dialog commands • Print control commands • Working with arrays 586 TEAM LinG - Live, Informative, Non-cost and Genuine! • Interacting with host operating systems • Miscellaneous commands Syntax conventions We will use the following conventions: Bold SQR commands and command arguments, e.g., Next-Column At-End=New-Page italic information to be substituted with a variable name, value, or other data, e.g., printer_type. {} indicates a set of alternative values, e.g., Border = {Yes | No } | alternative value separator X default value, e.g., 3D-Effects = {Yes | No } [] indicates optional item(s), e.g., Begin-Select [Distinct] () arguments or values must be enclosed in parentheses, e.g., ( column , line ) … preceding parameter(s) can be repeated, e.g., (report_name1[, report_namei] … ) Example Declare-Variable [Default-Numeric = {Decimal [(nn)] | Float | Integer}] [Decimal [(nn)] name1 [(nn)] [namei[(nn)] ]…] [Float name1 [ namei ] … ] [Integer name1 [ namei ] … ] [Text name1 [ namei ] … ] [Date name1 [ namei ] … ] End-Declare Program sectioning Begin-Program, End-Program Purpose To declare the beginning or the end of the Program section Syntax Begin-Program End-Program Arguments None Notes In SQR versions prior to version 4, the Program section was also called the Report section and the Begin-Report and End-Report commands were used. These commands are still valid, but may be obsolete in the future. Refer to Chapter 5 S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 587 Example Begin-Program ! SQR statements End-Program Begin-Heading, End-Heading Purpose To declare the beginning or the end of the Heading section Syntax Begin-Heading lines [ For-Reports = { (report_name1[, … report_namei] … ) | (All) } ] [ For-Tocs = ( toc_name1 [ , … toc_namei ] … )] [ Name = heading_name ] End-Heading Arguments lines The number of lines allocated for the heading. Only numeric literals can be used report_namei The name of the report(s) the heading is applied to. The report name is defined in the Declare-Report statement. If no report name or (All) is coded, the heading will be applied to all reports in the program. toc_namei The name of the table of contents (HTML only). When coded, the heading is applied to the specified table of contents. heading_name This argument cannot be used if For-Reports or For-Tocs is specified. This name is used in conjunction with the Alter-Report command. Notes SQR generates the heading and footing after the body of the page has been filled. Refer to Chapter 5 Example Begin-Heading 3 For-Reports(Detail_Rpt Total_Rpt) Print 'Summary Report' (1,1) Center End-Heading Begin-Footing, End-Footing Purpose To declare the beginning or the end of the Footing section Syntax Begin-Footing lines [For-Reports = { (report_name1 [, … report_namei] … ) | (All) } ] [ For-Tocs = ( toc_name1 [ , … toc_namei ] … )] [ Name = footing_name ] End-Footing Arguments lines The number of lines allocated for the footing. Only numeric literals can be used report_namei (optional) The name of the report(s) the footing is applied to. The report name is defined in the Declare-Report statement. If no report name or (All) is coded, the footing will be applied to all reports in the program. 588 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! toc_namei The name of the table of contents (HTML only). When coded, the footing is applied to the specified table of contents. footing_name This argument cannot be used if For-Reports or For-Tocs is specified. This name is used in conjunction with the Alter-Report command. Notes SQR generates the heading and footing after the body of the page has been filled. Refer to Chapter 5 Example Begin-Footing 2 For-Reports(Detail_Rpt Total_Rpt) Page-Number (1,50) 'Page ' End-Footing Begin-Procedure, End-Procedure Purpose To declare the beginning or the end of the Procedure section Syntax Begin-Procedure name [Local | (arg1[, … argi ] … ) ] End-Procedure Arguments name The name of the procedure Local Indicates that the procedure is local argi Input and output procedure arguments. Input arguments can be variables, SQR columns, or literals. Output arguments can only be variables. Output argument names must be preceded by a colon. Notes If a procedure has no arguments and no Local is specified, the procedure is considered global. If arguments are specified, the procedure is considered local When global variables are referenced inside a local procedure, the variable names must be preceded by an underscore. Refer to Chapter 5 Example Begin-Procedure Calc_Average (#Total_Amt, #Empl_Number, :#Average_Amt) ! SQR statements End-Procedure In this example, the procedure Calc_Average is considered local because it uses arguments: two input arguments, #Total_Amt, #Empl_Number, and one output argument, #Average_Amt. Begin-Setup, End-Setup Purpose To declare the beginning or the end of the Setup section Syntax Begin-Setup End-Setup Arguments None S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 589 Notes The Setup section must be the first section in the program. The Setup section is processed during the program compilation stage before actual program execution. Refer to Chapter 5 Example Begin-Setup Declare-Variable Date $As_Of_Date End-Declare End-Setup Begin-Select, End-Select Purpose To declare the beginning or the end of the Select paragraph Syntax Begin-Select [Distinct] [-Cnn] [-Bnn] [-XP] [NR] [-SORTnn] [-LOCK {RR | CS | RO | RL | XX} ] [ {-Dbdatabase | -Dbconnect_string } ] [Loops = nn ] [On-Error = proc_name [(arg1 [, … argi] … ) ] ] End-Select Arguments Distinct -Cnn: • • Eliminates duplicate rows from the query output (Oracle, SQLBase, Ingres) Query buffer size. If not specified, the default size is used (Sybase DB-Lib) Logical connection number. Used in the Load-Lookup and Execute commands -Bnn (Oracle, Sybase CT-Lib) Number of rows to be retrieved at one time. Used for performance purposes only. The default value is 10 or the value specified in the -B SQR command line flag. -XP (Sybase) Prevents the creation of a stored procedure for this SQL. If your program uses bind variables and dynamic query variables in the same query, this parameter may improve the program performance. -NR (SQLBase) No Recovery mode is used when connecting to the database. -SORTnn (SQLBase) The size of the sort buffer -Lock (SQLBase) Type of locking (isolation level): RR- repeatable read, CS- cursor stability, RO- read-only, RL- release locks, XX- no default isolation level. The default is RR -DBdatabase (SQLBase) Default database name -DBconnect_string (ODBC) ODBC connection string -Loops = nn If this parameter is specified, the Select loop stops after the number of retrieved rows riches nn. Can be used for debugging purposes to limit the query outputs. On-Error = proc_name The name of the SQL error handling procedure Notes You can place SQR commands in the Select paragraph anywhere between the column names and the From keyword. SQR commands in the Select paragraph must be indented. You must list all the selected columns in the Select paragraph: Select * is not allowed. 590 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! The print position parameters next to a table column in the Select paragraph cause the column value to be printed without using the Print command. Refer to Chapter 6 Example Begin-Select Loops = 100 Emplid (+1,1) Name (,+2) City (,+2) State (,+2) Do Print_Dependents From Personal_Data Where City = 'New York' End-Select Begin-SQL, End-SQL Purpose To declare the beginning or the end of the SQL paragraph. SQL paragraphs allow you to use native SQL statements (other than SQL Select) in SQR Syntax Begin-SQL [-Cnn] [-XP] [NR] [-SORTnn] [-Lock {RR | CS | RO | RL | XX} ] [ {-Dbdatabase | -Dbconnect_string } ] [On-Error = proc_name [(arg1 [, … argi] … ) ] ] (in non-Setup section) [On-Error = { Stop | Warn | Skip } ] (in Setup section) End-SQL Arguments -Cnn (Oracle, SQLBase, Ingres) Query buffer size. If not specified, the default size is used. XP (Sybase) Prevents the creation of a stored procedure for this SQL. If your program uses bind variables and dynamic query variables in the same query, this parameter may improve the program performance. -NR (SQLBase) No Recovery mode is used when connecting to the database. -SORTnn (SQLBase) The size of the sort buffer -Lock (SQLBase) Type of locking (isolation level): RR- repeatable read, CS- cursor stability, RO- read-only, RL- release locks, XX- inhibit default isolation level. The default is RR -DBdatabase (SQLBase) Default database name -DBconnect_string (ODBC) ODBC connection string On-Error = proc_name The name SQL error-handling procedure if SQL paragraph is used in other than Setup section. If no procedure name is specified, SQR displays an error message and aborts the program. On-Error If SQL paragraph is used in the Setup section, specifies an action to be taken in case of an SQL error (the default is Stop): • Stop abort the program • Warn display a warning message and continue to run • Skip ignore the error and continue to run. S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 591 Notes If there is more than one SQL statement in one SQL paragraph, each statement except the last one must be terminated by a semicolon. Oracle PL/SQL can also be used in the SQL paragraph. In this case, you have to add an extra semicolon at the end of each PL/SQL statement. Refer to Chapter 7 Example Begin-SQL On-Error=SQL_Error Update Personal_Data Set Postal = $New_Zip Where Postal = $Old_Zip; Delete From Temp_Empl End-SQL Begin-Document, End-Document Purpose To declare the beginning or the end of the Document paragraph Syntax Begin-Document ( X [ , Y ] ) End-Document Arguments X, Y The line and column positions of the document on the page. Can be absolute or relative positions Notes SQR commands are not allowed within the Document paragraph. Refer to Chapter 15 Example Begin-Document (+1, 1) … End-Document Compiler directives #Include Purpose To insert an external source file into an SQR program Syntax #Include 'name' Arguments name The name of the file to be inserted into the program. The name must include the file name extension. Notes #Include is processed during the compile stage SQR supports nested inclusions (up to four levels) Refer to Chapters 18, 19, 21 Example #Include 'my_header.dat' 592 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! #Debug Purpose To execute a specified SQR command in the debugging mode Syntax #Debug [ x … ] SQR_command Arguments x Any letter or digit Notes The #Debug directive is activated by using the -DEBUG SQR command line argument. When this command line argument is used, any SQR command preceded by #Debug will be executed. The -DEBUG flag can be suffixed by up to 10 letters or digits. These characters are then used in the #Debug command to match the suffix to the -DEBUG command line flag characters. Commands without suffixes always match. Refer to Chapter 20 Example If the value -DEBUG12 was one of the SQR command line arguments, this can be used in the following commands: #Debug Show 'Start processing' ! This command is executed ! regardless of any suffix #Debug1 Display #Empl_Total ! This command is executed ! because it matches suffix '1' #Debug2 Display #Comp_Total ! This command is executed ! because it matches suffix '2' ! But #Debug3 Display 'Nothing' will be ignored because !it does not match any suffix in the -DEBUG12 #If, #Else, #End-If / #EndIf Purpose To change the way SQR compiles the specified pieces of source code, depending on the specified substitution variable value Syntax #If subs_variable comparison_operator value SQR commands [ #Else SQR commands ] #End-If Arguments subs_variable These variables can be obtained from user input with the help of the Ask command or defined by the #Define directive. comparison_operator Any valid SQR comparison operator, e.g., “=” or “>=” value: a string or numeric literal Notes The #If [ #Else ] directives can be nested (up to 10 levels), each #If command must have a matching #End-If. #EndIf is a synonym of #End-If Refer to Chapter 20 S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 593 Example Begin-Setup Ask mode 'Debugging mode? (Yes, No)' End-Setup Begin-Program #If {mode} = 'Yes' Show 'Main section' #Else ! … #End-If ! … End-Program #IfDef, #End-If #IfNDef, #End-If Purpose #IfDef To check if the specified substitution variable was declared and, if yes, compile the SQR commands placed between #IfDef and #End-If #IfNDef To check if the specified substitution variable was declared and, if no, compile the SQR commands placed between #IfNDef and #End-If Syntax #IfDef subs_variable SQR commands #End-If #IfNDef subs_variable SQR commands #End-If Arguments subs_variable These variables can be obtained from user input with the help of the Ask command or defined by the #Define directive. They are also created automatically when the -DEBUGx command line argument is used. In this case, SQR assigns the substitution variables special names like debug[x] where 'x' is one of the suffixes used in the -DEBUGx command line argument. Notes #EndIf is a synonym of #End-If Refer to Chapter 20 Example 1 When debugging the piece of code below, one can control which Show command will be executed by entering -DEBUGa or -DEBUGb in the SQR command argument line: #IfDef debuga Show 'Get_Empl_Name started' #Else #IfDef debugb Show $Empl_Name #End-If #End-If 594 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! 2 The substitution variable debug_mode is defined with the help of the #Define command: #Define debug_mode yes #IfDef debug_mode Show 'Executing in a debug mode' Show 'Program started' #End-If #Define Purpose To declare a substitution variable and to assign the variable a value Syntax #Define subs_variable value Arguments subs_variable The substitution variable name value The substitution value Notes Each substitution variable is declared with a separate #Define directive. Substitution variables can be defined in an external source file. Refer to Chapter 11 Example #Define col_emplid #Define col_empl_name #Define col_sep ! … Print 'Emplid' Print 'Name ' 12 30 2 !Column Separator (0,1,{col_emplid}) (0,+{col_sep},{col_empl_name}) File input/output operators Open Purpose To open a sequential file for subsequent processing Syntax Open file_name As file_number {For-Reading | For-Writing | For-Append} { Record = length [ :Fixed | :Fixed_Nolf | :Vary ] } [ Status = status ] Arguments file_name The file name. Can be a string variable, SQR column, or literal file_number A file identification number which later will be used in other file processing commands. Can be any positive number less than 64,000. Can be coded as a numeric variable, SQR column, or literal For-Reading The file will be used for sequential read only. For-Writing To create a new file For-Append If the file already exists, all new records will be placed at the end of the file. If the file does not exist, a new file will be created. Fixed A fixed-length record file with no line feed characters. Can be used for both character and binary data S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 595 Fixed_Nolf A fixed-length record file with no line feed characters. Can be used for both character and binary data Vary A variable-length record file with no line feed characters. Can be used for character data only. This is the default record type. length The file record length. For variable-length record files, it is the maximum record size. Status This is a numeric variable which is set to zero if the Open command was successful and to -1 if there was a problem with Open. Notes SQR works only with sequential files. A maximum of 256 files can be opened at one time. If record type is not specified, the default is Vary. When appending to an existing file, the record length and type of the appended file must match that of the existing file. Refer to Chapter 17 Example Open 'daily_accum.dat' As 1 For-Append Record=50 Open $File_Name As #File_Handle For-Writing Record=100:Fixed Status = #Status Read Purpose To read the next record of a sequential file Syntax Read file_number Into {io_area : length } … [ Status = status ] Arguments file_number A file identification number previously specified in the Open command. Can be a numeric variable, column, or literal io_area One or more variables to hold the record data after Read is complete. The variables can be of the string, date, or numeric format. length The length of each io_area Status This is a numeric variable which is set to zero if the Read command was successful, or a system-specific error number if an error occurred. Notes If a field in the input record holds a date variable, it may be read into a date or string variable. The date variable in the record must be in one of the following formats: • SQR_DB_DATE_FORMAT setting • • database-specific format database-independent format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'. If a field in the input record is a binary number, it must be read into a numeric variable of the proper length. On end-of-file, the SQR reserved variable #end-file is set to 1; otherwise, it is set to zero. This variable must be checked after each Read operation. If the input record has variable length, you can use one io_area to read the entire record in and the Unstring command to parse the record into separate fields. In this case the length must be the maximum record length. Refer to 596 Chapter 17 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Example While Not #end-file Read #File_Handle Into $Empl_Id:4 $Empl_Name:25 $Empl_Address:50 Status=#Read_Stat If #end-file Break End-If If #Read_Stat <> 0 Do Read_Eror(#Read_Stat) Else Do Process_Input_Record End-IF End-While Close #File_Handle Write Purpose To write a record to a file from the program memory Syntax Write file_number From {io_area : [ length ] } … [ Status = status ] Arguments file_number A file identification number previously specified in the Open command. Can be a numeric variable, column, or literal io_area One or more variables to hold the data to be written to the file. The variables can be of the string, date, or numeric format. length The length of each io_area Status This is a numeric variable which is set to zero if the Write command was successful, or a system-specific error number if an error occurred. Notes If length is not specified, the current length of the variable will be used, but if the variable is numeric, the length is required. If a field program memory holds a date variable, the date will be converted to a string using the format specified by the SQR_DB_DATE_FORMAT setting or the databasespecific format. When writing binary data, the file must be open using the Fixed or Fixed_Nolf parameters. This file may not be portable between different platforms. Files opened for writing are treated as having variable-length records. To use fixedlength records, specify a length for each variable in the Write command. Refer to Chapter 17 Example Write #File_Handle From $Empl_Id:4 $Empl_Name:25 $Empl_Address:50 Close Purpose To close a sequential file Syntax Close file_number Arguments file_number A file identification number previously specified in the Open command. Can be a numeric variable, column, or literal S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 597 Notes The file must be previously opened using the Open command. Refer to Chapter 17 Example Close 1 Close #File_Handle Operations on string, date, and numeric variables Date-Time Purpose To obtain the current date/time. Depending on the syntax used, the date/time can be printed or stored in a column variable. Syntax Date-TIme ([Y, Y]) [format [column] ] Arguments X,Y The line and column positions of the date and/or time field on the page. Can be absolute or relative positions format A date format mask column Causes SQR to store the retrieved date/time in the specified SQR column instead of printing Notes The Date-TIme command may be discontinued in the future releases. It is recommended that the datenow() function or the $current-date reserved variable be used instead. If the column name is specified, the current date and time will be retrieved each time the command is executed. Otherwise, the date and time are retrieved at program start and do not change during the program execution. If no date format mask is specified, the following default formats will be used (for databases with two default formats, the first format will be used for date/time printing, the second one will be used for placing date/time into an SQR column. DB2: YYYY-MM-DD-HH:MI YYYY-MM-DD-HH:MI:SS.NNNNNN Informix: YYYY-MM-DD HH:MI YYY-MM-DD HH:MI:SS.NNN Ingres: DD-MON-YYYY HH:MI DD-MON-YYYY HH:MI:SS Oracle: DD-Mon-YYYY HH:MI:PM SQLBase: DD-Mon-YYYY HH:MI:PM Sybase: DD-Mon-YYYY HH:MI Refer to Chapter 4 Example Date-TIme (+1,4-0) MM/DD/YYYY printed. ! In this example, the current date is ! In this example, the current date and time are placed into ! &C_Date,&C_Time Date-Time ( ) MM/DD/YYYY &C_Date Date-Time ( ) HH:MI &C_Time 598 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Declare-Variable Purpose To explicitly declare a variable. In most cases, SQR variables do not have to be explicitly declared in the program; when a variable appears the first time, SQR assigns this variable its type and initial value (based on the first character of the variable's name). Syntax Declare-Variable [Default-Numeric = {Decimal [(nn)] | Float | Integer}] [Decimal [(nn)] name1 [(nn)] [namei[(nn)] ] …] [Float name1 [ namei ] … ] [Integer name1 [ namei ] … ] [Text name1 [ namei ] … ] [Date name1 [ namei ] … ] End-Declare Arguments namei The name of the variable to be declared. The name must include the proper prefix: “#” or “$”. nn Precision qualifier (for decimals only). This is not the number of decimal places! Can range between 1 and 38. The default value is 16. Default-Numeric Defines the default type for all numeric variables in the program. Overrides the default numeric type in the SQR.ini file Decimal Specifies that the numeric variables must be used as decimals. The precision qualifier can be assigned to all variables that follow the argument or to each individual variable. Float Specifies that the numeric variables must be used as double precision floating point variables Integer Specifies that the numeric variables must be used as integers with a range between -2,147,483,648 and +2,147,483,647 Text Specifies that the string variables must be used as text variables Date Specifies that the date variables may contain dates Notes Available starting from version 4 Can only be used in the Setup section or as the first statement of a local procedure. In SQR for DDO, list variables can not be declared using this command. Refer to Chapter 4 Example Begin-Setup Declare-Variable Default-Numeric = Decimal(12) Decimal(16) #Dec1 #Dec2 #Dec3 Integer #Sub1 Sub2 Date $As_Of_Date End-Declare End-Setup S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 599 Move Purpose To move the contents of one field to another field with optional editing Syntax Move source_name To target_name [[:] edit_mask | Number | Money | Date ] Arguments source_name The source field name. Can be a variable, an SQR column, or a literal target_name The target field name. Can only be a variable edit_mask The edit mask (if editing is required). The edit masks are described in chapter 4 and 9. Number Specifies that after the move, the target field will be edited using the Number-Edit-Mask Money Specifies that after the move, the target field will be edited using the Money-Edit-Mask Date Specifies that after the move, the target field will be edited using the Date-Edit-Mask Notes The source and target fields in the Move command can have different formats. In this case, the command performs the necessary conversion. Date and numeric variables are not compatible. The edit mask can be a literal or a string variable. When a string field is moved to a date variable, or when using a date format mask, or when the keyword Date is used, the source must be in one of the formats specified by: • SQR_DB_DATE_FORMAT setting • • Refer to database-specific format database-independent format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'. Chapter 4 Example Move '(xxx)bxxx-xxxx' To $Phone_Mask Move '1231234567' To $Phone :$Phone_Mask In the above example, the value of $Phone_Mask is used as the target field edit mask. After the move, the $Phone field will contain (123) 123-4567. Move 100000 To #Salary Move '$,$$$,$$$V99' To $Salary_Mask Move #Salary To $Salary :$Salary_Mask In the above example, the last Move command performs a numeric-to-string conversion and edits the target field using an edit mask. After the move, the $Salary field will contain $100,00000. Let #Sub = 5 Move #Sub To $Display_Sub In the above example, the Move command performs a numeric-to-string conversion using the default numeric edit mask. After the move, the $Display_Sub field will contain 005.000000. Move '19980615' To $Run_Date Move $Run_Date To $Display_Date Day,bMONTHbDD,YYYY 600 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! In the above example, the $Run_Date is a previously declared date format field. The first Move command converts a string literal to the date format. The second Move command converts the date format field $Run_Date back to the string format using a date edit mask. After the move, the $Display_Date field will contain Monday, JUNE 15,1998. Let Purpose To assign the value of an expression to a variable Syntax Let target_name = expression Arguments target_name The name of the variable to which the value of the result of the expression is assigned. Can be a variable or an array field expression The expression to be calculated or evaluated Notes An SQR expression may include operands, operators, and functions Operands of different types can be combined in one expression Refer to Chapter 4 Example Let #Amount = #Quantity * #Price Let $Print_Date = strtodate(&Eff_Dte, 'DD/MM/YYYY') Concat Purpose To append the contents of one field to the end of another field with optional editing Syntax Concat source_name With target_name [[:] edit_mask ] Arguments source_name The name of the source field to be appended to the end of the target field. Can be a variable, an SQR column, or a literal. The source field must be of the string or date format. target_name The name of the target field. The target field will contain the result of the concatenation. Can only be a variable edit_mask The edit mask (if editing is required). The edit masks are described in Chapters 4 and 9. Notes The edit mask can be a literal or a string variable. If the source field is a date variable or column and no edit mask is specified, SQR will convert the date to a string based on the format specified by SQR_DB_DATE_FORMAT, or if not specified, to the database-specific format. Refer to Chapter 4 Example Move $First_Name To $Full_Name Concat ' ' With $Full_Name Concat $Last_Name With $Full_Name Concat $Apt With $Disp_Apt bxx Concat $Run_Date With $Quote_Id MMDDYYYY S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 601 Extract Purpose To extract a portion of a string into another string Syntax Extract target_name From source_name start_position length Arguments target_name The name of the target field. The target field will contain the result of the extraction. Can only be a variable source_name The name of the source field from which the string is extracted. Can be a variable, an SQR column, or a literal. The source field must be of the string or date format. start_position The starting position of the extracted sub-string. The starting positions are counted from left to right starting from position zero. length The length of the extracted sub-string Notes The starting position and length can be literals or numeric variables If the source is a date variable or column, SQR will convert the date to a string before the extraction based on the format specified by SQR_DB_DATE_FORMAT, or if not specified, to the database-specific format. If the target is a date variable, the extracted portion must be in one of the formats specified by: • SQR_DB_DATE_FORMAT setting • • Refer to database-specific format database-independent format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]' Chapter 4 Example Extract $Area_Code From $Phone_Number 0 3 Extract $Reg_Date From $Input_Record #Pos #Length Find Purpose To find the starting position of a sub-string within a string Syntax Find search_argument In source_string start_position location Arguments search_argument Specifies the sub-string to be searched for. Can be a variable, an SQR column, or a literal. This field must be in a string or date format. source_string The name of the source field to be searched. Can be a variable, an SQR column, or a literal. The source string must be of the string or date format. start_position The starting position of the search. The starting positions are counted from left to right starting from position zero. location Specifies the field where SQR will place the position of the sub-string (if found) Notes If the sub-string is not found, SQR moves -1 to the location. The starting position can be a literal or a numeric variable. The location must be a numeric variable. If the source is a date variable or column, SQR will convert the date to a string before the search based on the format specified by SQR_DB_DATE_FORMAT, or if not specified, to the database-specific format. Refer to 602 Chapter 4 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Example ! Assuming $Full_Name contains last name, comma, first name, space: Find ',' In $Full_Name 1 #Location If #Location <> -1 Let #Start_Loc = #Location + 1 Find ' ' In $Full_Name #Start_Loc #End_Loc Let #Length = #End_Loc - #Start_Loc Extract $First_Name From $Full_Name #Start_Loc #Length End-If Encode Purpose To place a sequence of special characters onto a string variable. The special characters can be displayable or non-displayable characters. Can be used to send a string of special characters or escape characters to an output device. Syntax Encode source_string Into target_name Arguments source_string A string of characters to be encoded. Non-displayable characters are coded as numbers between <001> and <255>. target_name A string variable to hold the result of encoding. Refer to Chapter 4 Example Encode '<27>L11233' Into $Bold Encode '<128>' Into $Euro Print $Bold () Print $Euro (+1,20) Lowercase, Uppercase Purpose To convert a string variable to lowercase or upper case Syntax Lowercase string Uppercase string Arguments string The string to be converted. Can be only a variable Notes These two commands use the same field as source and target. Neither Uppercase nor Lowercase change the case for non-alphabetic characters. Refer to Chapter 4 Example Lowercase $Input Uppercase $Name S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 603 String, Unstring Purpose To build a string from a list of sub-strings To break a string into a list of sub-strings Syntax String substring … BY delimiter Into target_string Unstring source_string BY delimiter Into substring … Arguments For the String command: substring One or more fields to be concatenated, separated by the specified delimiters, and placed into the target string. A sub-string can be a variable, an SQR column, or a literal. Can be of the string or date format . delimiter One or more characters to be inserted as sub-string separators. Can be a variable, an SQR column, or a literal target_string The name of a string variable to hold the result of the concatenation For the Unstring command: source_string The string to be taken apart. A source string can be a variable, an SQR column, or a literal. Can be of the string or date format delimiter One or more characters to be used as sub-string delimiters. Can be a variable, an SQR column, or a literal substring One or more string variables to hold the result of the operation Notes If the source is a date variable or column, SQR will convert the date to a string before the String or Unstring operations, based on the format specified by SQR_DB_ DATE_FORMAT, or, if not specified, to the database-specific format. Use a null delimiter in the String command when no delimiter between sub-strings is needed. If the Unstring command specifies more target sub-strings than can be found in the source string, the remaining sub-strings will be set to NULL. If the Unstring command specifies fewer target sub-strings than can be found in the source string, the extra source sub-strings will not be processed. Refer to Chapter 4 Example String $First_Name $Mid_Initial $Last_Name By ':' Into $Empl_Name Unstring $Empl_Name By ':' Into $First_Name $Mid_Initial $Last_Name String &Company &Paygroup &Emplid By '' Into $Paycode Mbtosbs Purpose To convert a double-byte string to its single-byte equivalent. Any occurrence of a double-byte character that has a single-byte presentation (numerals, punctuation, roman characters, and katakana) is converted. Syntax Mbtosbs string Arguments string The string to be converted. Can be only a variable Notes This command is available for the double-byte SQR mode only. Refer to Chapter 21 604 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Example Mbtosbs $My_String Sbtombs Purpose To convert a single-byte string to its double-byte equivalent. Any occurrence of a single-byte character that has a double-byte presentation (numerals, punctuation, roman characters, and katakana) is converted. Syntax Sbtombs string Arguments string The string to be converted. Can be only a variable Notes This command is available for the double-byte SQR mode only. Refer to Chapter 21 Example Sbtombs $My_String Add, Subtract, Multiply, Divide Purpose To perform addition, subtraction, multiplication, or division operations on numeric fields with optional rounding Syntax Add source_field To target_field [Round = nn ] Subtract source_field From target_field [Round = nn ] Multiply source_field Times target_field [Round = nn ] Divide source_field Into target_field [On-Error = {High | Zero}] [Round = nn ] Arguments source_field A numeric variable, SQR column, or literal target_field A numeric variable which will hold the result of the operation Round = nn (decimal and float variables only) Rounds the result to the specified number of digits to the right of the decimal point On-Error If coded, the result of a zero-division will be set to either the highest value or to zero. If not coded, SQR stops processing when a zero-division occurs. Notes To avoid small inaccuracies when working with money fields, it is recommended to use decimals rather than float variables. Refer to Chapter 4 Example Add #Salary To #Salary_Total Subtract 1 From #Counter Multiply #Hours Times #Rate Round=2 Divide #Employee_Number Into #Average_Salary Round=2 On-Error=Zero S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 605 Logic operators Do Purpose To invoke an SQR procedure Syntax Do procedure_name [ ( arg1 [, argi ] … ) ] Arguments procedure_name The name of the procedure to be invoked argi zero, one, or more arguments to be passed to the procedure Notes The arguments must be passed in the order listed in the Begin-Procedure statement of the invoked procedure. String arguments can be passed to procedure string or date arguments. Date arguments can be passed to procedure date or string arguments. Numeric arguments can be passed to procedure numeric arguments, but the numeric subtypes (Decimal, Float, or Integer) do not have to match: SQR will automatically convert numeric values to the proper subtype. Refer to Chapter 5 Example Do Get_Company_Name Do Calc_Average (#No_Employees, &Company, #Avg_Salary) If, End-If, Else Purpose To transfer program control depending on the value of a logical expression Syntax If logical_expression SQR commands [ Else SQR commands ] End-If Arguments logical_expression Notes A valid SQR logical expression The If [ Else ] commands can be nested, each If command must have a matching End-If. The logical expression value is evaluated with a non-zero value considered TRUE. When a date variable or column is compared to a string, SQR performs a chronological comparison, not a string comparison. To support a chronological comparison, the string must be in one of the formats specified by: • SQR_DB_DATE_FORMAT setting • database-specific format database-independent format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'. • Refer to Chapter 8 Example If #Count Show 'Number of rows processed = ' #Count Else Show 'There were no rows found' 606 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! End-If If (&A.Claim_Dte >= $Policy_Eff_Dte And &A.Claim_Dte <= $Policy_Exp_Dte) Do Process_Reg_Claims Else If (&A.Claim_Status = 'S') Do Process_Special_Claims Else Do Print_Error End-If End-If Evaluate, End-Evaluate, Break Purpose To evaluate the value of a variable, column, or literal and to execute the proper sequence of SQR commands, depending on the result of the evaluation Syntax Evaluate { variable1 | column1 | literal1 } When comparison_operator { variable2 | column2 | literal2 } SQR commands … [ Break ] [ When comparison_operator { variable2 | column2 | literal2 } SQR commands … [ Break ] ] [ When-Other SQR commands … [ Break ] ] End-Evaluate Arguments variable1 | column1 | litera1l A variable, column, or literal to be used in the evaluation. Can be of the string, date, or numeric format comparison_operator Any valid comparison operator, e.g., “>”, “=”, “>=”, etc. variable2 | column2 | literal2 A variable, column, or literal to be used in the comparison. Can be of the string, date, or numeric format When Starts the comparison/action block. If the result of the comparison is TRUE, all SQR commands in the block are executed. If the expression is FALSE, the next When is evaluated. Break Is used to specify an immediate exit from the entire Evaluate command. If not coded, the next When block will be evaluated When-Other Specifies the default actions when all previous When statements yielded a FALSE result Notes The Evaluate commands can be nested, each Evaluate command must have a matching End-Evaluate. When a date variable or column is compared to a string, SQR performs a chronological comparison, not a string comparison. To support a chronological comparison, the string must be in one of the formats specified by: • SQR_DB_DATE_FORMAT setting • • Refer to database-specific format database-independent format 'SYYYYMMDD[HH24[MI[SS[NNNNNN]]]]'. Chapter 8 S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 607 Example Evaluate $Claim_Status When = 'A' Do Process_Active_Claims Break When='P' When='R' Do Process_Pending_RunOff_Claims Break When-Other Do Process_Unknown_Status Break End-Evaluate While, End-While, Break Purpose To create a logical loop which is used to process the body of the loop for a number of times until the specified condition is FALSE Syntax While logical_expression SQR commands … [ Break ] SQR commands … End-While Arguments logical_expression A valid SQR logical expression Break Is used to specify an immediate exit from the entire While loop Notes When the logical expression is evaluated, a zero result is considered FALSE; any nonzero result is considered TRUE. The While loops can be nested. Refer to Chapter 8 Example While 1 Read 1 Into $Employee_Record:80 If #end-file = 1 Break End-If Add 1 To #Record_Count Do Process_Input End-While While $Empl_Status = 'A' And $Company_Code = &Company Do Get_Employee_Name While $Emplid = $Save_Emplid Do Read_Creadit_Record If #Total_Amt > 0 Do Adjust_Total End-If End-While End-While 608 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Goto Purpose To transfer program control to the specified label Syntax Goto label Arguments label Notes An SQR program label SQR program label names must end with a colon. The Goto command can use labels only within the same section or paragraph. Example Goto Error_Routine ! … Error_Routine: Show 'Errors found' Stop Interacting with databases Connect Purpose To disconnect the current database connection and to connect under a new user Id Syntax Connect id/password [ On-Error = proc_name [ ( arg1 [ , … argi ] … ) ] ] Arguments id/password User name and password. Can be stored in a string variable or column proc_name The name of a procedure to be invoked if Connect fails Notes Connect is an SQR command, not an SQL command After each Connect, SQR sets its reserved variable $username to a new value. Connect causes all cursors and logons to be closed before its execution. Connect should not be issued within a Select or SQL paragraph. Example Let $Connect_String = 'jsmith/mypword' Connect $Connect_String On-Error = Connect_Error Commit Purpose To perform a database commit (Oracle, SQLBase, Rdb, Ingres) Syntax Commit Arguments None Notes Commit is an SQR command, not an SQL command. Should not be used within an SQL paragraph For Sybase, use Commit Transaction and, if necessary, Begin Transaction The Commit command cannot be used in SQR for Informix. Refer to Chapter 7 S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 609 Example If #SQL-count = #Insert_Count Commit End-If Rollback Purpose To perform a database rollback to the last commit point (Oracle, SQLBase, Rdb, Ingres) Syntax Rollback Arguments None Notes Rollback is an SQR command, not an SQL command. Should not be used within an SQL paragraph For Sybase, use Rollback Transaction and, if necessary, Begin Transaction. The Rollback command cannot be used in SQR for Informix. Refer to Chapter 7 Example If #SQL-error <> 0 Rollback End-If Exit-Select Purpose To exit a Select paragraph immediately Syntax Exit-Select Arguments None Notes Use Exit-Select when you need to end a query before all rows have been retrieved. Refer to Chapter 7 Example Begin-Select Emplid SSN Name City State Do Write_To_Output If #Write_Error Exit-Select End-If From Personal_Data End-Select 610 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Load-Lookup Purpose To build a table array in the program memory and populate this array with data from a database table Syntax Load-Lookup Name = lookup_table Table = database_table Key = key_column Return_Value = data_column [ Rows = initial_row_number ] [ Extent = extent ] [ Where = where_clause ] [ Sort = sort_mode ] [ Cursor = connection_number ] [ Quiet ] Arguments lookup_table The name assigned to the table array database_table The name of the database table to be used as the data source. You can use multiple tables key_column The name of the database table column to be used as a key when searching in the table array. This column must have unique values in the table. No NULL values are allowed. Can be database-supported expressions data_column The name(s) of the column(s) or expression(s) to be retrieved using the value of key_column as the key. It may be just one table column or a combination of several table columns. In case of a column combination, the columns must be concatenated in accordance to your database rule, e.g., with the help of “||” for Oracle. initial_row_number The initial number of rows in the lookup array table (default is 100) extent The number of rows to add to the array table when it becomes full (default is 25%) where_clause The Where clause to be used when the database table rows are retrieved sort_mode The sorting method to be used when ordering elements in the table array: • DC sorting by database, case-sensitive sort • DI sorting by database, case-insensitive sort • SC sorting by SQR, case-sensitive sort • SI sorting by SQR, case-insensitive sort connection_number (Sybase DB-Lib) The connection number to use. The connection number is defined in the Begin-SQL statement Quiet Is used to suppress the message “Loading lookup array…” when the Load-Lookup command is executed Notes The lookup array table built by Load-Lookup is to be used by one or more Lookup commands. The number of lookup tables and their sizes are limited only by the amount of available memory. Refer to Chapter 6 Example Begin-Setup !************** Load-Lookup Name=Client_Name_Address Rows=500 Table=Clients Key=Client_Id S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 611 Return_Value='Client_Name||''-''||Client_Address' Where=State='NY' End-Setup Lookup Purpose To search a table array created and populated by the Load-Lookup command Syntax Lookup lookup_table key_value return_value Arguments lookup_table The name of a lookup table previously defined by the LoadLookup command key_value A key value to be used for the search. Can be a variable, column, or literal. return_value A string variable to hold the search result Notes If no match is found, the return value is set to NULL Refer to Chapter 6 Example ! This example is used in conjunction with the example for the Load-Lookup ! command Lookup Client_Name_Address &Client_Id $Name_Address Unstring $Name_Address By '-' Into $Name $Address Use (Sybase and Microsoft SQL Server only) Purpose To use the specified database instead of the default database associated with your user id Syntax Use database Arguments database Notes The name of the database to use Can be used only in the Setup section prior to any query Cannot be used within an SQL paragraph Can be overridden by the -DB command line flag Example Begin-Setup Use testbase End-Setup Execute (Ingres, Sybase, Microsoft SQL Server) Purpose To execute a stored procedure Syntax Ingres Execute proc_name [ ( parm_name = parm1 [ , parmi ] … ) ] [ On-Error = proc_name [ ( arg1 [ , argi ] … ) ] ] [ status ] 612 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Sybase, MS SQL Server: Execute [-Cnn] proc_name [ [ @parm1 = ] value1 [ OUT[PUT]] [ , [@parmi = ] valuei [ OUT[PUT]]… ] [ On-Error = proc_name [ ( arg1 [ , argi ] … ) ] ] [@status = ] [@status_var = ] [ Do = do_proc_name [ ( do_arg1 [ , do_argi ] ) … ] [ Into column1 type1 [ ( length1) ] [ , columni typei [ ( lengthi) ] ] … ] [ With Recompile ] Arguments proc_name The name of the stored procedure parmi Optional arguments to be passed to the stored procedure. The arguments can be passed with or without names. If used without names, must be passed in the same sequence as defined in the stored procedure. On-Error The procedure name to be invoked if an error occurs. If not coded, SQR will halt program execution when an error occurs. status The name of a numeric variable which will hold the procedure return code. -Cnn The logical connection number (defined in the Begin-Select or Begin-SQL statement). do_proc_name The name of the procedure to be invoked for each row selected in the query. do_argi Optional arguments to be passed to the procedure invoked for each selected row. OUT[PUT] Indicates the the parameter will receive a value from the stored procedure Into Lists the names, types, and lengths of columns where the rows retrieved from the stored procedure will be stored. If the stored procedure has more than one Select query, only the first query result will be stored. Rows from subsequent queries will be ignored With Recompile Causes the query to be recompiled each time the stored procedure is invoked. Example ! Ingres: Execute On_Update(Tblname=$Department_Tbl, Key=$Deptid) #Rtn_Status= ! Sybase, MS SQL Server Execute On_Update(@Tblname=$Department_Tbl, @Key=$Deptid) @#Rtn_Status Declare-Connection Purpose Syntax To define the data source logon parameters prior to logon. Can be used to override the default connection logon parameters. Declare-Connection connection_name Dsn = datasource_name [ User = user ] [ Password = password ] [ Parameters = keyword_list ] [ No-Duplicate = {True | False } ] End-Declare S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 613 Arguments connection_name The name of the connection. Can be referred to in the AlterConnection command. datasource_name The logical datasource name as recorded in the DDO registry. Can be coded as a string variable or literal in the program body or as a string literal in the Setup section. user user id Can be coded as a string variable or literal in the program body or as a string literal in the Setup section. password password Can be coded as a string variable or literal in the program body or as a string literal in the Setup section. keyword_list A connection string as required by the data source. The string is a list of keyword-value pairs. No-Duplicates=True prevents SQR from creating additional logins to data sources that may be busy handling a previous query. Notes This command can be coded in the Setup section or in the program body. Do not wrap the lines in the parameters line. Example Declare-Connection MYCONNECTION Dsn = $Dsn User = $Userid Password = $Password No-Duplicate = True End-Declare Alter-Connection Purpose To alter the data source logon parameters prior to logon. Can be used to override the default connection logon parameters. Syntax Alter-Connection Name = connection_name Dsn = datasource_name [ User = user ] [ Password = password ] [ Parameters = keyword_list ] [ No-Duplicate = {True | False } ] Arguments connection_name The name of the connection defined in the Declare-Connection command. datasource_name The logical datasource name as recorded in the DDO registry. Can be coded as a string variable or literal. user user id Can be coded as a string variable or literal. password password Can be coded as a string variable or literal. keyword_list A connection string as required by the data source. The string is a list of keyword-value pairs. No-Duplicates=True prevents SQR from creating additional logins to data sources that may be busy handling a previous query. Notes 614 This command cannot be coded in the Setup section. Do not wrap the lines in the parameters line. A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Example Alter-Connection Name = MYCONNECTION Dsn = $Dsn User = $Userid Password = $Password No-Duplicate = True Parameters = 'logon.client=600;logon.ashost=star;logon.language=EN;' Begin-Execute, End-Execute Purpose To get a data row from various relational or non-relational datasources. The exact syntax depends on the datasource specifics. Syntax Begin-Execute [ Connection = connection_name ] [ On-Error = error_procedure[(arg1[,argi]... )] ] [ Rsv = row_number ] [ Status = status_var ] [ Schema = schema ] [ { Procedure = procedure_name [ Parameters = (parm1[{In|Inout|Null}] [,parmi[{In|Inout|Null}]]...)] | Command = command_line | Getdata = getdata_line } ] [ Begin-Select [ Before = before_select[(arg1[,argi] ...)]] [ After = after_select[(arg1[,argi] ...)]] col_name1 = {Char|Text|Number|Date [ edit_mask]} [On-Break] ,col_namei = {Char|Text|Number|Date [edit_mask]} [On-Break] ... From { Rowsets = ( {m, -n, n-m, m- | All } ) | Parameter = parm_list } End-Select ] [ Properties = keyword_list ] End-Execute Arguments connection_name The name of the connection defined in the DeclareConnection command. error_procedure The name of a procedure to be invoked if an error occurs. row_number A global numeric variable containing the number of the retrieved row set. status_var A variable to receive the status of the stored procedure. Can be coded as a list variable, numeric variable or string variable. schema Identifies the datasource, e.g., a name of an OLAP datamart. Must be registered in the DDO directory. Can be coded as a string literal or variable. procedure_name The name of the stored procedure. If the datasource is SAP/R3, the procedure must be a BAPI. Can be coded as a string literal or variable. Cannot be coded with the Command or Getdata arguments. The parameter list may include list variables, numeric variable or string variables, literals or columns. All parameters required by the stored procedure must be coded in the order the procedure will receive them. If you want to pass a null value for one of the parameters, use the Null qualifier. S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 615 command_line A text string that is passed to the datasource without any modification. Cannot be coded with the Procedure or Getdata arguments. Can be coded as a string literal or variable. getdata_line supports the DDO GetData paradigm. This argument is used to read data from various non-relational datasources, e.g., CVS, XML, OLAP. Cannot be coded with the Procedure or Command arguments. Can be coded as a string literal or variable. Begin-Select, End-Select define the names and, optionally, formats of retrieved columns, SQR procedures to be invoked before and/or after retrieval, on-break processing options, rowsets from which to retrieve the column values, and so on. The Before and After Procedures are not invoked unless at least one row is returned. From Rowsets Defines the rowsets from which to retrieve the column values. This argument can be used with all datasource types, including SAP R/3 and JDBC. If more than one rowset is specified, use identical column names and types. You can specify specific rowsets or ranges of rowsets, e.g., (1, 3-5, 8-). Rowset numbers can be coded as numeric literals or variables. From Parameter This argument can be used only with SAP R/3. Names an output parameter containing one or more rows from which the column values are retrieved. Use only in conjunction with the Procedure argument. keyword_list A list of keyword-value pairs that represent modifications to the Parameters argument in the Declare-Connection or Alter-Connection command. The difference between the Begin-Execute command and Declare-Connection and Alter-Connection commands is that this argument alters the properties of returned information as opposed to the login properties. Notes This command cannot be coded in the Setup section. The column names in the Begin-Select construct should not be indented. Examples Begin-Execute Connection = OLAPCONNECT Schema = 'FoodMart' ! Must be registered in the DDO registry Getdata = 'Sales' Begin-Select Before = Print_Header After = Print_Total Month_Year (+1,1) Type=Date Edit 'MM/YYYY'On-Break Product (,10) Type=Char Measure.Quantity (,30) Type=Number Edit '999,999' Measure.Sales (,30) Type=Number Edit '$$$,$$$.99' Measure.Profit (,30) Type=Number Edit '$$$,$$$.99' From Rowsets = (#Rowset) End-Select End-Execute Set-Members Purpose Returns the set of members in a dimension, level, or hierarchy at the specified level (OLAP DDO). Syntax Set-Members=(dimension_1,hierarchy_1 [,dimension_i,hierarchy_i,...]) Arguments dimension_i Specifies the dimension hierarchy_i Specifies the hierarchy of the dimension 616 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Example Set-Members = ('sales','gross sales.toys.toys under 5', 'time','2000.Q1.Week1', 'region','North East') Set-Generations Purpose Specifies dimension hierarchy in previously declared dimensions (OLAP DDO). Syntax Set-Generations=(dimension_1,hierarchy_1 [,dimension_i,hierarchy_i,...]) Arguments dimension_i Specifies the dimension hierarchy_i Specifies the hierarchy level Example Set-Generations = ('sales', 3,'time', 1) ! Returns Sales, Toys, Toys Under 5 ! Returns Year only Set-Levels Purpose Extends the levels in previously declared dimension hierarchies (OLAP DDO). If used only with the previous Set-Members command, returns all members under the product hierarchy and the next level_i generations. If used with the previous Set-Members and Set-Generations commands, returns all members for generation levels from the level specified in the Set-Generations command. Syntax Set-Levels=(dimension_1,level_1 [,dimension_i,level_i,...]) Arguments dimension_i Specifies the dimension level_i Specifies the level Example Set-Levels = ('sales', 1) ! Returns Sales, Toys Compile-time and run-time dialog commands Input Purpose To read user input into the program memory Syntax Input name [ Maxlen = nn ] [ prompt_text ] [ Type = { Char | Text | Number | Integer | Date } ] [ Status = status_code ] [ Noprompt ] [ Batch-Mode ] [ Format = format ] S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 617 Arguments name The name of the variable to be populated from user input. Can be of the string, date, or numeric format nn The maximum length of user input prompt_text The prompt text to be displayed. If omitted, the default prompt will be used Type If specified, SQR performs automatic type checking of user input. If a type mismatch is found, an error message is displayed and the Input command is reissued status_code: a numeric variable to hold the completion code of the Input command: • 0 Successful • 1 Type mismatch detected • 2 Input length error • 3 Nothing entered Noprompt Do not display a prompt text Batch-Mode If specified and there are no more arguments in the command line, the status code is set to 3 and the user is not prompted for input format date edit mask. Used for dates only. Notes The Input command is executed at run time. When entering a date, use one of the following formats: MM/DD/YYY [BC|AD] [HH:MI[:SS[.NNNNNN]] [AM|PM]] MM-DD-YYY [BC|AD] [HH:MI[:SS[.NNNNNN]] [AM|PM]] MM.DD.YYY [BC|AD] [HH:MI[:SS[.NNNNNN]] [AM|PM]] SYYYYMM [H24 MI[SS[NNNNNN]]] When coding the Format keyword, use any date edit mask or the keyword Date. Refer to Chapter 11 Example Let #Input_Error = 1 While #Input_Error = 1 Input $State_List Maxlen=80 Type=Char Status=#Input_Status 'Enter a list of comma separated state codes (ex. NY,CT,MI)' Let $State_List=Rtrim($State_List,' ') If $State_List = '' Or #Input_Status <> 0 Show 'Incorrect Input' Let #Input_Error = 1 Else Let #Input_Error = 0 End-If End-While Ask Purpose To obtain the value of a substitution variable from user input, command line argument, or command argument file entry Syntax Ask name [ prompt_text ] Arguments name The substitution variable name prompt_text An optional prompt text string if user input is used 618 A P P EN D I X D TEAM LinG - Live, Informative, Non-cost and Genuine! Notes Ask assigns values to its substitution variables at compile time. The Ask command can be used only in the Setup section prior to any substitution variable references. Refer to Chapter 11 Example Begin-Setup Ask Array_Size 'Enter Array Size ' Create-Array Name=My_Array Field = Emplid:char Field = Name:char Field = Zip:char End-Setup Size={Array_Size} Print control commands Declare-Report Purpose To declare a report in a multiple-report program Syntax Declare-Report report_name [ Layout = layout_name ] [ Printer-Type = {HT|HP|PS|LP|HTML|HPLASERJET |POSTSCRIPT|LINEPRINTER} ] [ Toc = toc_name ] End-Declare Arguments report_name The name of the report. This name is used in the DeclareProcedure and Use-Report commands layout_name The name of the layout to be used in the report. Layouts are defined by the Declare-Layout command. If not specified, the default layout is used Printer-Type Defines the printer type for this report. The default is LINEPRINTER toc_name The name of the table of contents (HTML only) Notes The Declare-Report command can be used only in the Setup section Multiple reports are written into different report files. When multiple reports are printed, the report file names can be specified in the -F command line flag. If not specified, the report naming will be controlled by the OUTPUT-FILE-MODE environmental variable. If OUTPUT-FILE-MODE is set to SHORT, the first report file name is program_ name.lis, additional report names are program_name.Lnn , where nn is a number between 01 and 99 in the order in which the reports are declared. If the -KEEP or –NOLIS command line arguments are used, the first report file’s name is program_ name.spf, the additional report names are program_name.Snn, where nn is a number between 01 and 99 in the order in which the reports are declared. If OUTPUT-FILE-MODE is set to LONG, the first report file is program_name.lis, additional report names are program_name_nn.lis, where nn is a number between 01 and 99 in the order in which the reports are declared. If the -KEEP or S QR C O MMA ND S Y N T A X TEAM LinG - Live, Informative, Non-cost and Genuine! 619 –NOLIS command line arguments are used, the first report file name is program_ name.spf, the additional report names are program_name_nn.spf, where nn is a number between 01 and 99 in the order in which the reports are declared. Refer to Chapter 13 Example Begin-Setup Declare-Report Employee_List_US Layout=US_Layout End-Declare Declare-Report Employee_List_EU Layout=EU_Layout Printer-Type=POSTSCRIPT End-Declare End-Setup Alter-Report Purpose To change the current report’s heading or footing sections while the report is running. Syntax Alter-Report [ Heading = heading_name ] [ Heading-Size = heading-size ] [ Footing = footing_name ] [ Footing-Size = footing-size ] Arguments heading_name The name of heading specified in the Begin-Heading command. Can be coded as a string variable, column or literal. heading_size The number of lines the heading will occupy in the page. Can be coded as a numeric variable, column or literal. If no heading_name is coded, this value applies to the current heading section. footing_name The name of footing specified in the Begin-Footing command. Can be coded as a string variable, column or literal. footing_size The number of lines the footing will occupy in the page. Can be coded as a numeric variable, column or literal. If no footing_name is coded, this value applies to the current footing section. Notes The Alter-Report command does not switch to another report: it only changes the current report’s heading or footing characteristics. If the Alter-Report command is issued within a Heading or Footing section, and the current page has not been completed, the changes to heading or footing take effect immediately; otherwise the changes take effect on the next page. If heading_name or footing_name is set to 'NONE', this heading or footing section is disabled for the current report. If heading_name or footing_name is