How do I do a case/switch query with Progress 4GL 9.1D? - progress-4gl

I'm trying to write a query for a Progress 4GL database where there are around 8 different cases for it to account for. Is there an easier way to do this than a massive if/elseif block?

9.1D allows you to change the query filter conditions at run time, like so:
DEFINE QUERY q-query
FOR TableName.
QUERY q-query:QUERY-PREPARE("FOR EACH TableName " +
"WHERE TableName.fieldname = ""something""").
You can build your query string in a variable and substitute that in the QUERY-PREPARE.
QUERY q-query:QUERY-PREPARE(chFilterCondition).
You can also grab the query's handle like so:
hQuery = QUERY q-query:HANDLE.
and then use that to set the filter condition:
hQuery:QUERY-PREPARE("FOR EACH TableName " +
"WHERE TableName.fieldname = ""something""").

The short answer is - yes, you can do a CASE.
DEFINE VARIABLE iVar AS INTEGER NO-UNDO.
ASSIGN iVar = 4.
CASE iVar:
WHEN 1 THEN DO:
MESSAGE "case no 1" VIEW-AS ALERT-BOX.
END.
WHEN 2 THEN DO:
MESSAGE "case no 2" VIEW-AS ALERT-BOX.
END.
WHEN 3 OR WHEN 6 THEN DO:
MESSAGE "case no 3" VIEW-AS ALERT-BOX.
END.
WHEN 5 THEN DO:
MESSAGE "case no 4" VIEW-AS ALERT-BOX.
END.
OTHERWISE DO:
MESSAGE "case no 5" VIEW-AS ALERT-BOX.
END.
END CASE.
The long answer might depend on your query. Perhaps an IF/CASE statement isn't what you need but a dynamic query or something else? To answer that you really need to post more information and best of all: code.

Related

Writing a nested 4gl query after 'WHERE'

I only have access to write a 4gl query after Where(. Is it possible to write a nested query to search in a completely different table?
For example
**FOR EACH WORK_ORDER WHERE(**
//my query starts on this line
1=1 and
( for each purchase_order where key <> '123' end)
This obviously does not work, but is it possible to make it work?
This query will give me ERROR: PREPARE syntax is {FOR|PRESELECT} EACH OF..WHERE" (7324)
No, it's not possible. The query needs to be aware of all it's buffers. If the only thing you can manipulate is the actual WHERE-clause it won't be enough.
This is how it can be done, but you need to manipulate more than just the where:
DEFINE QUERY q1 FOR WORK_ORDER, PURCHASE_ORDER.
QUERY q1:QUERY-PREPARE("FOR EACH WORK_ORDER, EACH PURCHASE_ORDER WHERE PURCHASE_ORDER.id = WORK_ORDER.id" ).
QUERY q1:QUERY-OPEN().
QUERY q1:GET-FIRST.
IF AVAILABLE work_order THEN DO:
DISP work_order.
END.
ELSE DO:
DISP "no work_order".
END.
IF AVAILABLE purchase_order THEN DO:
DISP purchase_order.
END.
ELSE DO:
DISP "no purchase_order".
END.
QUERY q1:QUERY-CLOSE.
You could try using a join.
FOR EACH WORK_ORDER NO-LOCK WHERE... , EACH PURCHASE_ORDER WHERE PURCHASE_ORDER.key <> '123'

line continuation in intersystems cache objectscript

I am writing in intersystems cache object script.
I have a statement which has become very long.
Is there any way to continue a statement in the next line?
Thanks.
Can you post a sample?
If you have a long string you can concatenate it like this:
Set SQL = "SELECT * "_
"FROM Sample.Person "_
"WHERE Name [ 'a'"
This is equal to:
Set SQL = "SELECT * FROM Sample.Person WHERE Name [ 'a'"
Other types of statements can also be placed on several lines.

Returning to a text field block again from the query

I have a form that prompt for customer name and pass that value to a query,
FORM compname
customer.cusname
WITH FRAME f1.
UPDATE compname WITH FRAME f1.
This form wil pass the compname value to the following query,
FOR EACH customer WHERE customer.name = compname NO-LOCK :
if available(company) then
do:
CREATE temptt.
assign temptt.num = customer.kco
temptt.no = string(customer.kco)
temptt.name = customer.name
temptt.status = false.
END.
else
message "not matched " view-as alert-box.
end.
What i want to do is, if search does not receive any rows, it should again prompt for customer name. what should i do for this ??
how do i call that form again in the "else block" and also, currently I am giving the complete name in the field, but i want to give part of the name, for eg., customer name is "John Smith Doe" and if i input "Smith" it should retrieve the related rows. How should i alter the "Where" clause for this ?? Please help me.
Repeating the search
This can be done in several ways. Here's one example:
DEFINE TEMP-TABLE customer NO-UNDO
FIELD cusname AS CHARACTER
FIELD num AS INTEGER.
DEFINE VARIABLE compnum AS INTEGER NO-UNDO.
DEFINE VARIABLE compname AS CHARACTER NO-UNDO.
DEFINE QUERY qSearch FOR customer.
FORM compname compnum WITH FRAME f1.
/* Create some bogus data */
CREATE customer.
ASSIGN customer.cusname = "john doe"
customer.num = 1.
CREATE customer.
ASSIGN customer.cusname = "jane doe"
customer.num = 2.
CREATE customer.
ASSIGN customer.cusname = "name name"
customer.num = 3.
loop:
REPEAT:
CLEAR FRAME f2 ALL.
UPDATE compname compnum WITH FRAME f1.
/* Quit if neither name or number is entered */
IF compname = "" AND compnum = 0 THEN
LEAVE loop.
/* If num is entered - search by it, otherwise by name */
IF compnum <> 0 THEN DO:
OPEN QUERY qSearch FOR EACH customer NO-LOCK WHERE customer.num = compnum.
END.
ELSE DO:
OPEN QUERY qSearch FOR EACH customer NO-LOCK WHERE customer.cusname MATCHES "*" + compname + "*".
END.
GET NEXT qSearch.
DO WHILE AVAILABLE customer:
IF AVAILABLE customer THEN DO:
DISPLAY customer WITH FRAME f2 10 DOWN.
DOWN WITH FRAME f2.
END.
GET NEXT qSearch.
END.
/* If we have results - leave the loop otherwise try again */
IF QUERY qSearch:NUM-RESULTS = 0 THEN
LEAVE loop.
END.
MESSAGE "Quitting" VIEW-AS ALERT-BOX.
Searching for part of the name
There are a couple of operators for matching strings:
BEGINS
Tests a character expression to see if that expression begins with a second character expression.
Syntax:
expression1 BEGINS expression2
Example:
FOR EACH customer WHERE NO-LOCK customer.cusname BEGINS "john":
MATCHES
Compares a character expression to a pattern and evaluates to a TRUE value if the expression satisfies the pattern criteria.
The pattern can contain wildcard characters: a period (.) in a particular position indicates that any single character is acceptable in that position; an asterisk (*) indicates that any group of characters is acceptable, including a null group of characters.
Syntax:
expression1 MATCHES expression2
Example:
FOR EACH customer NO-LOCK WHERE customer.cusname MATCHES "*doe*":
MATCHES sounds like what you're after but be adviced: MATCHES will not utilize indices in the database so whole tables will be scanned. This can/will effect performance and possibly make your queries take long time.
The WHERE clause above replaced with MATCHES would look something like this:
FOR EACH customer NO-LOCK WHERE customer.cusname MATCHES "*" + compname + "*":
CONTAINS
There's also a third operator called CONTAINS that uses something called a WORD index. That will require you or your DBA to create these kind of indices in the database first. Read more about word indices and CONTAINS in the online help or in the PDF found here: Progress ABL Reference (page 1004).
CONTAINS is probably a better idea than MATCHES but will require you to make changes to your database as well.

Nested if else Condition in Crystal report

I have a .rpt file , with a view datasource . I have four parameter which i use in filtering the selection. I have written my selection formula like below.
if ({?actype} <> "All") OR ({?actype} <> "All") OR ({?collectorname} <> "All") OR ({?batchno}<> "All") Then
(
if {?actype} <> "All" Then
{CollectorPerformance.accountType} = {?actype};
if {?collectorname} <> "All" Then
{CollectorPerformance.realname} = {?collectorname};
if {?batchno} <> "All" Then
{CollectorPerformance.batchno} = {?batchno}
and
{CollectorPerformance.clientid} = {?clientid}
and
Date({CollectorPerformance.paymentdate}) >= Date({?from})
and
Date({CollectorPerformance.paymentdate}) <= Date({?to})
)
My issue with the formula, above is that it does not filter by realname and actType. I understand the reason is because the key word "and" is missing . however, it filters the batchno correctly> please how do i make it filter by the remaining two if's ? any help would appreciated.
A selection formula has to be one long valid boolean statement, which is, I think, what you were already suggesting when you say the "and is missing". So in order to fix the first half, you just need to translate those statements into one simplified boolean statement instead of individual statements (those that end in a ';').
({?actype}="All" or {?actype}={CollectorPerformance.accountType})
and
({?collectorname}="All" or {?collectorname}={CollectorPerformance.realname})
and
({?batchno}="All" or {?batchno}={CollectorPerformance.batchno})
...
For each parameter, a user can either select "All" or enter a specific value to filter by. If "All" is selected, that particular portion of the statement (The part that looks like {?Parameter}="All") will evaluate to True and no filtering will be done. Otherwise, only records matching the entered parameter value will return True.

Firebird 2.5.x. Extract column names and column datatypes of a result from stored procedure

I have a Firebird 2.5 database .As an example I have stored procedure with a name QRESULT which expected return is:
Parameter - DATATYPE
a - date
b - numeric(18,0)
c - integer
d - varchar(50)
and so on....
I use PHP - PDO to query the firebird database using the procedure QRESULT like this:
SELECT a,b,d from QRESULT() where a = "some value"
I need to run some query before QRESULT procedure and i need it to return the datatype of all the columns that QRESULT would return if it was ran. So i can help user to type proper value for my "where" clause.I know i can set that manually in the user interface, but in the real project there are lots of procedures and if there is a way i can make my filter interface generate dynamically i would be happy about that.If this is not possible for a stored procedure i can make it with select statements.I just need some lead.
The information you want is in the RDB$PROCEDURE_PARAMETERS table, basically what you need is query
SELECT r.RDB$PARAMETER_NAME ParName, F.RDB$FIELD_TYPE ParType
FROM RDB$PROCEDURE_PARAMETERS r
JOIN RDB$FIELDS F ON(F.RDB$FIELD_NAME = R.RDB$FIELD_SOURCE)
WHERE r.RDB$PROCEDURE_NAME = 'QRESULT'
AND r.RDB$PARAMETER_TYPE = 1
ORDER BY r.RDB$PARAMETER_TYPE, r.RDB$PARAMETER_NUMBER
Note that the SP name should be in upper case as this is how it is stored into system tables (unless you use quoted identifiers). If you want to get both input and output parameters the delete the r.RDB$PARAMETER_TYPE = 1 predicate from the WHERE (type 0 is input parameters and 1 is output).
The type returned by this query is integer id for the type, quick googling found this:
14,"TEXT "
7,"SHORT "
8,"LONG "
9,"QUAD "
10,"FLOAT "
27,"DOUBLE "
35,"TIMESTAMP "
37,"VARYING "
261,"BLOB "
40,"CSTRING "
45,"BLOB_ID "
12,"DATE "
13,"TIME "
16,"INT64 "
but if you want to have more precise type then see this SO post.