The sql
statement in Uniface 10.4 empowers developers to execute direct database queries, offering unparalleled flexibility for complex data operations. This deep dive explores how to leverage this powerful feature, understand its nuances, and integrate it effectively into your Uniface applications, regardless of the underlying database system like Oracle, SQL Server, or MySQL.
What Exactly is Uniface?
Before delving into the specifics of the sql
statement, it’s crucial to understand its environment. Uniface, a Fourth Generation Language (4GL) development platform from Rocket Software, is engineered for building robust enterprise applications across web, desktop, and mobile platforms. Its model-driven architecture and ProcScript, Uniface’s proprietary scripting language, facilitate the creation of sophisticated application logic.
The sql
Statement: Core Syntax
The fundamental structure for employing the sql
statement is as follows:
sql{/data {/fieldname}} {/print} DMLStatement, "PathString"
Let’s break down each element:
* DMLStatement
: This is your actual SQL query, which can be up to 32KB in length.
* PathString
: Specifies the name of your database connection, excluding the $
prefix.
* Qualifiers: Optional flags that dictate how the query results are formatted and returned.
Decoding the Qualifiers
The sql
statement’s power is significantly enhanced by its three key qualifiers:
/data
: Structured Data Retrieval 📊
When you need to process query results programmatically, the /data
qualifier is invaluable. It returns the complete query output as a nested Uniface list, where each row is a list item and each column within that row is a sub-item.
sql/data "SELECT product_id, product_name FROM products", "my_db_conn"
; The $result variable now holds a structured Uniface list of product data.
/fieldname
: Including Column Headers 📋
Used in conjunction with /data
, the /fieldname
qualifier enriches your structured results by including column names as the very first item in the returned Uniface list. This provides clear context for each data point.
sql/data/fieldname "SELECT customer_id, customer_name FROM customers", "ora_conn"
; $result will start with: customer_id, customer_name, followed by data rows.
/print
: Human-Readable Output 🖨️
For scenarios where you need to display results directly to users or for debugging, the /print
qualifier is ideal. It formats the query output with distinct columns and appropriate whitespace, mirroring the readability of a standard SQL client.
sql/print "SELECT * FROM orders WHERE order_date >= '2023-01-01'", "mssql_conn"
putmess $result ; Displays the neatly formatted query results.
Practical Demonstrations
Let’s explore some common use cases for the sql
statement.
Basic Data Validation ✅
A frequent requirement is to check for the existence of a value in the database.
trigger loseFocus
if ($fieldendmod != 0)
sql "SELECT emp_id FROM employees WHERE emp_id = '%%employee_number%%'", "oracle_hr"
if ($status > 0)
message "Employee ID already registered!"
return (-1)
endif
endif
end
Populating Dynamic Value Lists 📝
The /data
qualifier is perfect for dynamically filling dropdowns or list boxes.
function getRegionList
sql/data "SELECT region_code, region_name FROM regions ORDER BY region_name", "default_db"
forlist vRecord in $result
getitem vCode, vRecord, 1
getitem vName, vRecord, 2
putitem/id pRegionList, vCode, vName
endfor
end
Understanding System Return Values
After every sql
statement execution, Uniface provides critical feedback through system variables, $status
and $result
.
$status
Values 📊
This variable indicates the outcome of the SQL operation:
* ≥ 0
: Denotes the number of records retrieved or affected by the query.
* -1
: Signifies an invalid or missing database connection path.
* -3
: Indicates an I/O error or an empty DML statement.
* -9
: Implies that the maximum number of database connections has been reached.
* -11
: Signals that a record is already locked.
* -31
: Reports that the SQL statement exceeds the 32KB size limit.
$result
Variable 💾
This variable stores the actual query results. For basic queries without qualifiers, it typically holds the value of the first column from the last selected row. When qualifiers like /data
or /print
are used, $result
contains the formatted or structured data as described previously.
Best Practices and Essential Tips
To maximize efficiency and maintainability, adhere to these guidelines when using the sql
statement:
Prioritize Portability 🌐
While powerful, raw SQL can reduce application portability. Whenever feasible, opt for standard Uniface I/O operations, which abstract database specifics and enhance maintainability across diverse database systems.
Avoid Manual Transaction Control ❌
Uniface expertly manages database transactions internally. Refrain from embedding COMMIT
or ROLLBACK
statements within your sql
commands, as this can disrupt Uniface’s transaction integrity.
Handling Long Data Types 📏
Be cautious with Oracle’s LONG
data types; the sql
statement truncates them at 42 bytes. For full retrieval of LONG
fields, it’s best to use standard Uniface entity retrieval mechanisms.
Robust Error Handling 🛡️
Always implement error checking by examining the $status
variable after every sql
operation. This proactive approach allows for graceful error management and improves application stability.
sql "UPDATE accounts SET balance = balance - %%vAmount%% WHERE account_id = %%vAccountId", "fin_db"
if ($status < 0)
message "Transaction failed: %%$procerror"
return (-1)
endif
When to Choose sql
vs. Standard Uniface I/O
Understanding when to use the sql
statement versus standard Uniface I/O operations is critical for optimal application design.
Opt for sql
when you need:
- Complex Joins: Involving multiple tables for intricate data retrieval.
- Aggregate Functions: Such as
COUNT
,SUM
,AVG
,MAX
,MIN
for data analysis. - Database-Specific Functions: Accessing proprietary features not supported by Uniface’s abstraction layer.
- Bulk Operations: Performing large-scale updates, insertions, or deletions efficiently.
Stick to Standard Uniface I/O for:
- Simple CRUD Operations: Basic Create, Read, Update, Delete tasks.
- Form-Based Data Entry: Seamless integration with Uniface forms and entities.
- Database Portability: Applications designed to run across various database systems without code changes.
Conclusion
The sql
statement in Uniface 10.4 serves as a vital bridge, connecting Uniface’s high-level data handling capabilities with the raw power of direct database interaction. While it’s a potent tool for specialized, complex database operations that extend beyond routine form-driven data manipulation, its judicious use is paramount. Always prioritize proper error handling and consider the long-term portability implications of your SQL code. By doing so, you can harness its full potential to build more powerful and responsive Uniface applications. Happy coding!