How to add the IF THEN ELSE in the BY/BREAK BY keyword of FOR EACH? - progress-4gl

here is the sample that i want to ask:
FOR EACH table-name.... NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that + sort-that2).
END.
This would result in an error.
if it is just
FOR EACH .. NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that).
END.
then there is no error. Progress would accept the code
What is need is if condition is true then sort by one field else sort by two or more fields

If you are in a modern enough version of Progress, then you could construct a dynamic query. This will be more efficient in terms of run time as well as getting you round your problem as having IF statements in your query predicate will make index selection hard.
QueryString = "for each table no-lock...".
if true then
QueryString = QueryString + " by sort-this".
else
QueryString = QueryString + " by sort-that by sort-other".
create query QueryHandle.
QueryHandle:add-buffer(buffer table:handle).
QueryHandle:query-prepare(QueryString).
QueryHandle:query-open.
do while QueryHandle:get-next():
/*some stuff*/
end.
QueryHandle:query-close.
delete object QueryHandle.

As per the previous reply, this is not supported.
Typically, you'd have to prepare the result into a temp-table first, using a logical field in the temp-table for the result of your IF THEN ELSE expression.

Unfortunately, this syntax is not supported. As per the documentation, BREAK/BY expects an expression following it but not a statement.

Related

Call function only for unsuppressed records

I use two string formulas in my details section to determine which countries to suppress. Formula1 refers to the Company field, and I suppress certain values:
{Countries}
In Formula2 i call a function with a parameter like #Formula1:
function(#formula1)
But the problem is that the function still takes the suppressed countries into account. Is this normal behavior, or do I need something extra like whileprintingdata to narrow my results.
When i debug the function the first time the function is called the incoming string is country1 even though I specified it to suppress that one. How can I ensure only unsuppressed values are passed to the function?
You could write additional logic into Formula2 to perform the same check that Formula1 does. Something like:
IF ({Countries} = 10) THEN
function("Company A")
ELSE IF ({Countries} = 14) THEN
function("Company B")
// ELSE IF....
ok got it your edit makes it clear...
what you can do is use the same supress condition in your formula 2 and pass the value to function.
some thing like below:
if your supress condition is
if {Countries} in (10,11,12)
then true
else false
then use the same in function 2 as
if {Countries} <> (10,11,12)
then function(formula1)
else any message as you wish

Returning query results within rules (when clause of drl file) in drools

I need help in retrieving query results within when clause of drl file in drools.
Example rule file having query:
query getUsersForCard (Long ipCardNumber)
$listOfUsers : UsersList()
$listOfUserCards : User(cardNumber == ipCardNumber, $cardNum : cardNumber) from $listOfUsers.user_list
end
rule "matchUser"
when
getUsersForCard("4444333322221112L";)
then
System.out.println( "$$$card number in VisaMessage matched with card number in UsersList$$$" );
end
How to obtain $cardNum aftergetUsersForCard query call so that then clause gets printed? I don't want to retrieve $cardNum in Java code from Working Memory but rather should be able to do it within the drl file itself.
Help is much appreciated!
Going back to the original question: "Can query results be accessed from the LHS of a rule?", the answer is: "Yes, using unification (Sections 8.8.3.3.6 and 8.9)".
A query in Drools can have input and output parameters. Input parameters are those that have a value (are bound) when the query is invoked. Those parameters that don't have a value (unbound) are considered output parameters.
The first thing to do is to rewrite your query using unification:
query getUsersForCard (Long ipCardNumber, Long $cardNum)
$listOfUsers : UsersList()
$listOfUserCards : User(
cardNumber == ipCardNumber,
$cardNum := cardNumber
) from $listOfUsers.user_list
end
The important thing to notice is the := (unification) sign being used. This operator is basically saying: "If the variable has a value, then I'll act as an == operator. Otherwise I'll act as a variable binding.
When invoking your query from a rule, you need to make sure you don't provide any value for the second parameter in the query. Given that you are already using positional arguments, that's easy to do:
rule "matchUser"
when
getUsersForCard("4444333322221112L", $cardNum;)
then
System.out.println( "$$$card number in VisaMessage matched with card number in UsersList$$$: "+$cardNum );
end
When the query is invoked, $cardNum will not have a value and it will set by the query because of the unification mechanism.
Hope it helps,
You cannot retrieve query results within the when clause the way I think you think it can be done. But there is no need to do so, simply write the rule as
rule "match card number"
when
$listOfUsers: UsersList()
$user: User(cardNumber == "4444333322221112L") from $listOfUsers
then ... end
Assuming the text of the println indicates what you really want to do, you might use
rule "match card number"
when
VisaMessage( $cardNo: cardNumber )
$listOfUsers: UsersList()
$user: User(cardNumber == $cardNo) from $listOfUsers
then ... end

Progress if statement

I'm a progress noob, actually having problem with basic blocks.
Below the issue is in my if else statement. It works fine when its just if, then, else then, but when I want to put in more than one statement into the if portion, I have to put it in a block, so I'm using if, then do: else, then do: but these aren't working for me. Any obvious errors you can see? My error message is **Colon followed by white space terminates a statement. (199)
INPUT FROM "r:\_content\stephen\4gl apps\dpl\output.csv".
REPEAT:
ASSIGN i_cntr = (i_cntr + 1).
myRow = "".
IMPORT DELIMITER ',' myRow.
IF myRow[5] <> "" THEN DO:
/*change this to assign 2 rows - 2 creates - 2 sets of four*/
c_fname = myRow[1].
MESSAGE
c_fname SKIP
myRow[2] SKIP
myRow[3] skip
myRow[4] skip
myRow[5] SKIP
i_cntr
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END./*end of if, then do:*/
ELSE IF myRow[5] = "" THEN DO:
MESSAGE
myRow[1] SKIP
myRow[2] skip
myRow[3] skip
myRow[4] skip
i_cntr
VIEW-AS ALERT-BOX INFO BUTTONS OK.
END./*end of else if, then do:*/
END./*end of repeat*/
A very simple syntax error: you need at least one space after the END-statement.
END. /*end of if, then do:*/
/* ^ Make sure there's space above here! */
And if you don't want to follow the excellent advice in Tims answer (use CASE). This is the "complete" syntax of the IF statement.
IF expression1 THEN DO:
/* Code goes here */
END.
ELSE IF expression2 THEN DO:
/* Code goes here */
END.
ELSE DO:
/* Code goes here */
END.
expressions:
A constant, field name, variable name, or expression whose value is logical (TRUE or FALSE). The expression can include comparisons, logical operators, and parentheses.
You can also leave out the DO: END. When the IF code to be executed only consists of a single statement:
IF TRUE THEN DISPLAY "TRUE".
ELSE DISPLAY "NOT TRUE".
You could also use other block-statements (such as FOR or REPEAT) but that will most likely only create code that is hard to read.
Rather than using nested IF/ELSE, you'd be better off using a CASE statement like so:
CASE varname:
WHEN "" THEN DO: /*something */ END.
WHEN "value" THEN DO: /*something */ END.
OTHERWISE DO: /*something */ END.
END CASE.
Check the docs on this statement for more details.
I figured out the issue. This wasn't caused by a coding error. Apparently Progress doesn't like comments too close to the code, which caused it to throw an error.
END. /*end of if, then do:*/ => This is ok.
END./*end of if, then do:*/ => This caused the issue comments too close to statement.
Thanks To Tim Kuehn for his response.

Neo4j 1.9 Cypher Issue with "Has" Clause

I'm migrating to Neo4j 1.9 and updating the cypher queries according to the deprecations migration guide, specifically:
The ! property operator in Cypher Expressions like node.property! =
"value" have been deprecated, please use has(node.property) AND
node.property = "value" instead.
The problem is that when using the HAS clause in combination with NOT I can't get the expected result. For example:
When the status property exists and is set to something other than "DONE":
AND not(n.status! = "DONE")
evaluates true (as expected)
AND not(has(n.status) AND n.status = "DONE")
evaluates false???
When the status property doesn't exist
AND not(n.status! = "DONE")
evaluates false
AND not(has(n.status) AND n.status = "DONE")
throws exception because the node doesn't exist, surely HAS should have prevented this? It's as though wrapping the check in NOT prevents the HAS check from being executed.
This can be reproduced via the live query examples on the neo4j docs website using the following queries:
MATCH n
WHERE NOT (n.name! = 'Peter')
RETURN n
This returns all (3) nodes who either have no name or whose name is not "Peter". This is the result I want to reproduce but without using the now deprecated "!" operator.
MATCH n
WHERE NOT (HAS (n.name) AND n.name = 'Peter')
RETURN n
Throws a node not found exception because one node doesn't have a name property. :/
MATCH n
WHERE (HAS (n.name) AND n.name = 'Peter')
RETURN n
Correctly returns the node whose name is "Peter".
I've tried rewriting the query in a few alternative ways but can't seem to reliably get the result I want that matches the deprecated ! operator. Maybe it's just getting late and I'm missing something obvious? :)
Any Help is appreciated!
Thanks,
Mark.
I think the second query raising an exception is a bug.
What you can do is use de Morgan's law to transform the predicate:
MATCH n
WHERE NOT (HAS (n.name)) OR (n.name <> 'Peter')
RETURN n

oledb/ado.net: Get the command's text, with all parameters replaced

Is it possible to get the text of an OleDbCommand with all parameters replaced with their values? E.g. in the code below I'm looking for a way to get the query text
SELECT * FROM my_table WHERE c1 = 'hello' and c2 = 'world'
after I finished assigning the parameters.
var query = "SELECT * FROM my_table WHERE c1 = ? and c2 = ?";
var cmd = new OleDbCommand(query, connection);
cmd.Parameters.Add("#p1", OleDbType.WChar).Value = "hello";
cmd.Parameters.Add("#p2", OleDbType.WChar).Value = "world";
No: you have to iterate through the parameters collection yourself, doing a string.Replace() to get the equivalent. It's particularly painful when you have to use the ? syntax rather than the #parametername syntax.
The reason for this is that the full string is never assembled. The parameters and sent to the server and treated as data, and are never included in the string.
All the same, I for one understand your pain. It would have been nice if they included some kind of .ComposeSQL() method you could call for debugging purposes, that perhaps also produces a compiler warning to help avoid use in production.
If you just need to see what query was executed and dont need to work with it programmatically, you can use SQL Profiler.