I saw this kind of code in vb6.
Private Enum enmMain
STEP_INIT = 1
STEP_RUN = 2
STEP_SLEEP = 3
STEP_SUSPEND = 4
STEP_ERROR = 5
End Enum
Private mStep As enmMain
Select Case mStep
Case Is <= enmMain.STEP_RUN
'Do something
Case enmMain.STEP_RUN To enmMain.STEP_ERROR
'Do something
I don't understand this:
Case enmMain.STEP_RUN To enmMain.STEP_ERROR
If it goes into that case when it meets this condition:
the latest value is STEP_RUN
current value is STEP_ERROR
How does it work?
I am posting on mobile can't write clean.
It means that the case statement will be satisfied by all values of mStep that are between 2 and 5, inclusive.
So there is an imprecision in code. Because the value STEP_RUN appears in an inclusive test twice (see the <= operator). Which behaviour is intended for STEP_RUN, the first or the second? You need to figure it out by understanding the program's logic.
Well, let's read the manual:
If testexpression matches any Case expressionlist expression, the statements following that Case clause are executed up to the next Case clause, or, for the last clause, up to End Select. Control then passes to the statement following End Select. If testexpression matches an expressionlist expression in more than one Case clause, only the statements following the first match are executed.
Select Case will run the first block that matches, and the criteria you can use to match are much more flexible than those allowed in many other languages. Case Is <= enmMain.STEP_RUN Will run for any value of mStep that is less than or equal to 2, and Case enmMain.STEP_RUN To enmMain.STEP_ERROR would run for any value between 2 and 5 inclusive.
Now it seems like somebody didn't quite understand what that meant, though, or at least wrote it in a confusing way, because for a value of 2 only the first Case would run, since as the section I quoted says only the first match is executed.
So the end result is that first 'Do something will run on values of 2 or less, and the second 'Do something will run on values of 3, 4, or 5.
Related
this is my first post here, I'd like to ask for help on a theoretically simple query.
The current query I'm creating is to try and generate a random number, which will be identified as even or odd. Depending on that value, it should print either "M" as even, or "W" as odd. Though when I try using a case inside the select, I'm getting the "missing keyword" error. This is my following code:
select
case
when mod(round(dbms_random.value(1,100)),2) = 0 then dbms_output.put_line('M');
when mod(round(dbms_random.value(1,100)),2) = 1 then dbms_output.put_line('W');
end
from dual
I've tried checking the website for similar problems, and while I've found similar problems and people getting the solution correctly, I don't quite know what I'm missing here. When I run the select alone, I get the even or odd number I want to get, but not when I try to print out a result.
SELECT statement cannot PRINT, it can only RETURN something.
You may use query like
select
v,
case when mod(v, 2) = 0 then 'M' else 'W' end l
from
(select round(dbms_random.value(1, 100)) v from dual)
and process it's result as you wish (print, for example).
Why are you trying to embed dbms_output in a query? Just write:
select
case
when mod(round(dbms_random.value(1,100)),2) = 0 then 'M'
when mod(round(dbms_random.value(1,100)),2) = 1 then 'W'
end
from dual
Also you know, since you are calling dbms_random two different times, you get two different values in the same query. If you want one value to be compared, then use a WITH clause
with rand_value as
( select round(dbms_random.value(1,100)) val from dual
)
select
case
when mod(val,2) = 0 then 'M'
when mod(val,2) = 1 then 'W'
end
from rand_value
You get the error because of the semicolons.
Also you are calling the random function twice and thus get two different random values. I would use IF-THEN-ELSE instead of a SELECT statement, since the latter one cannot print.
IF mod(round(dbms_random.value(1,100)),2) = 0 THEN
dbms_output.put_line('M');
ELSE
dbms_output.put_line('W');
END IF;
I have an SSRS report where there is a parameter that asks the user to include records where revenue is greater than zero, or records with revenue values that are just zero.
Since the query is not a stored procedure and it is not an option to put it into a procedure, I need to use some case logic for the embedded query. I need to do this in the where clause in the end.
I am trying to do something like this:
SELECT * FROM TABLE
WHERE MY_DATE BETWEEN D_START AND D_END
AND
CASE
WHEN :REVENUE = 1 THEN REV != 0
WHEN :REVENUE = 2 THEN REV = 0
END
However, when I run this query I get the following error:
ORA-00905: missing keyword
Is what I am doing not possible? Or is there an error that someone can see and help me with?
Please help. Thanks!
UPDATE: Just to clarify, the user is passing a value of 1 or 2. And the query should filter the data according to what value is passed to it. If 1 is passed in the parameter, then filter out all revenue not equal to zero. Else if two is passed, then filter so that only records where revenue is zero is returned.
You can write it better with a bit of boolean logic:
SELECT * FROM TABLE
WHERE MY_DATE BETWEEN D_START AND D_END
AND (
(:REVENUE = 1 AND REV != 0)
OR
(:REVENUE = 2 AND REV = 0 )
)
CASE is meant to extract different values based on conditions, so you can use it to check conditions, but you need to use it as a value to check against a condition
It's not necessary to use a CASE expression to get this particular result.
But it is possible to make use of one.
The problem in the original query is that Oracle is more strict than other databases (like MySQL) in that Oracle doesn't implicitly convert a boolean expression to a value, or convert a value into boolean.
I suspect that Oracle is choking in a couple of places. The error message is only showing us one of those.
The CASE expression returns a value, and Oracle is balking that he won't evaluate the value as a boolean.
To get that value evaluated as a boolean, we could do a comparison of the value to some other value.
If we fix that, I think Oracle is still going to choke on the expression following THEN. Oracle is expecting to return a value, and it's finding a comparison, which evaluates to a boolean.
Okay, so we know the CASE expression needs to return a value, and we need to use that in a boolean expression. If we move that conditional test into the WHEN part, and specify a value to be returned in the THEN, we can compare the return from the CASE expression to another value.
(As an aside... I strongly recommend that you qualify the column references in the SQL statement. That makes the intent more clear. Looking at the statement, it looks like MY_DATE, D_START and D_END are all column references. That's perfectly valid, it just seems a bit odd to me.)
As an example, we could do something like this with the CASE expression:
SELECT t.*
FROM TABLE t
WHERE t.MY_DATE BETWEEN t.D_START AND t.D_END
AND CASE
WHEN ( :REVENUE = 1 AND t.REV != 0 ) THEN 1
WHEN ( :REVENUE = 2 AND t.REV = 0 ) THEN 1
ELSE NULL
END = 1
The parens inside the CASE aren't necessary; I just included them to highlight the part that Oracle is evaluating in a boolean context.
So, does that work? If the value passed in for :REVENUE is 2, the condition in the first WHEN won't evaluate to TRUE (the result of first comparison is guaranteed to be FALSE). The condition in the second WHEN may evaluate to TRUE (first comparison will yield TRUE, the result from second comparison will depend on the value in the REV column.)
That CASE expression is either going to return a value of 1 or NULL. (We could just as easily use a 0 or a -1, or 999 in place of NULL if we wanted.)
Once the CASE expression is evaluated, the value returned will be compared to a literal value, as if we wrote e.g. val = 1. That comparison is evaluated as boolean. If it evaluates to TRUE, the row will be returned...
To get Oracle to behave similarly to other databases (like MySQL), we would need to make the conversion from boolean to value and value to boolean explicit. We would still need the return from the CASE compared to 1, like we did above. In place of REV != 0 we could use another CASE expression. I'm not recommending this, just shown here for illustration, converting a boolean to a value.
WHERE CASE
WHEN ( :REVENUE = 1 )
THEN CASE WHEN ( t.REV != 0 ) THEN 1 ELSE NULL END
WHEN ( :REVENUE = 2 )
THEN CASE WHEN ( t.REV = 0 ) THEN 1 ELSE NULL END
ELSE
NULL
END = 1
Note that the return from the outermost CASE expression is being compared to a value, so we get a boolean (where Oracle expects a boolean.)
All of the ELSE NULL in the statements above can be omitted for an equivalent result, since that's the default when ELSE is omitted.)
Again, it's not necessary to use a CASE expression. You can get equivalent results without it. For example:
SELECT t.*
FROM TABLE t
WHERE t.MY_DATE BETWEEN t.D_START AND t.D_END
AND ( ( :REVENUE = 1 AND t.REV != 0 )
OR ( :REVENUE = 2 AND t.REV = 0 )
)
In these queries that all return an equivalent result, the CASE expression doesn't buy us anything. But in some circumstances, it can have some advantages over a regular OR, because the CASE expression stops evaluation when a condition in a WHEN clause evaluates to TRUE.
The problem is that Oracle SQL does not have the boolean data type, so you cannot have columns of type boolean, pass boolean parameters to a query, have boolean expressions etc. So they have the somewhat unnatural concept of "condition" which is something that goes into logical conditions (like in the WHERE clause). Unfortunately, when they introduced the case EXPRESSION, which can be used wherever any other expression can be used (but this excludes boolean), they DID NOT introduce a "case CONDITION" - which could be used where other conditions can be used. This omission is odd, since the code for a case condition would probably use 95% of the code for the case expression. All the more weird since PL/SQL does have the boolean type, and the case expression there works seamlessly for Booleans.
I realize this is very basic, but I could not work this out from Prolog tutorials, so I hope someone here could help me solve my problem.
I have a term which is true if one of several conditions applies:
answer -->
Var,
a([Att1],[Var1]),
a([Att2],[Var2]),
a([Att3],[Var3]),
{
[one, two, three] = [Att1, Att2, Att3] -> Var1 = Var; % first condition
[one, three, two] = [Att1, Att2, Att3] -> Var1 = Var; % second condition
[two, one, three] = [Att1, Att2, Att3] -> Var2 = Var; % third condition
[three, one, two] = [Att1, Att2, Att3] -> Var2 = Var; % fourth condition
}
All Attributes and Variables come with a fixed value, and I want to offer "answer", if any of the conditions in the "{}" section are fulfilled - but for some reason, it doesn't work. The thing is, if I just check for one of the conditions, say, the first, it works as expected. But I don't want to copy/paste the rule 4 times because I didn't get a logical "or" to work properly.
Just to put it into words, in case I coded something completely different, the first condition is meant to say: check if Att1 is equal to one and Att2 is equal to two and Att3 is equal to three. If that is the case, also confirm that Var1 is equal to the value in Var. If not, check if any of the other conditions can be resolved.
edit: Turns out I just had a ';' too much in the code.
I'm working on a project. Currently I have a fairly large conditional statement, that assigns a value to a variable based on some input parameters. So, I have something like this.
if some condition
x = some value
elsif another condition
x = a different value
...
What's the best way to refactor this? I'm hoping that I might end up with something like
x = some value if some condition || another value if another condition
Is there a pattern for this sort of thing?
Just put the assignment outside the if.
x = if some condition
some value
elsif another condition
a different value
Or you could use a Hash.
x = dict[some condition]
It's not a pattern, but an operator. The one you're referring to is the ternary operator:
If Condition is true ? Then value X : Otherwise value Y
Here is an example:
speed = 90
speed > 55 ? puts("I can't drive 55!") : puts("I'm a careful driver")
Using the ternary statement is short, sweet, and does the job.
x = some condition ? some value :
another condition ? a different value : ...
A conditional statement is also an expression, so one of the first things you can do, if the variable is the same in each condition, is:
x = if cond1
expr1
elsif cond2
expr2
....
end
If the conditions are all states of a single expression, you can make this even neater, using a case statement.
However, the next most obvious re-factoring exercise is to get the big conditional isolated into a method, which should be fed the bare minimum data required to evaluate all the conditions and expressions.
E.g.
# Where conditional is currently, and x assigned, assuming the conditionals
# need a couple of variables . . .
x = foo param1, param2
# Elsewhere
private
def foo p1, p2
if cond1
expr1
elsif cond2
expr2
....
end
end
If you want to refactor for code clarity and flexibility, consider the replacing conditional with polymorphism refactor.
There's not enough detail in your question to go much further with recommendations, but this refactor will make your code base much more resistant to change. If you receive a new requirement, it's bad form to break open the conditional and modify it (more prone to introducing bugs, more difficult to do); it's preferable to create a new object that you can plug into the existing codebase. This flexibility what the Open/Closed Principle (the "O" in the SOLID acronym) describes.
Is it better to use NOT or to use <> when comparing values in VBScript?
is this:
If NOT value1 = value2 Then
or this:
If value1 <> value2 Then
better?
EDIT:
Here is my counterargument.
When looking to logically negate a Boolean value you would use the NOT operator, so this is correct:
If NOT boolValue1 Then
and when a comparison is made in the case of the first example a Boolean value is returned. either the values are equal True, or they are not False. So using the NOT operator would be appropriate, because you are logically negating a Boolean value.
For readability placing the comparison in parenthesis would probably help.
The latter (<>), because the meaning of the former isn't clear unless you have a perfect understanding of the order of operations as it applies to the Not and = operators: a subtlety which is easy to miss.
Because "not ... =" is two operations and "<>" is only one, it is faster to use "<>".Here is a quick experiment to prove it:
StartTime = Timer
For x = 1 to 100000000
If 4 <> 3 Then
End if
Next
WScript.echo Timer-StartTime
StartTime = Timer
For x = 1 to 100000000
If Not (4 = 3) Then
End if
Next
WScript.echo Timer-StartTime
The results I get on my machine:
4.783203
5.552734
Agreed, code readability is very important for others, but more importantly yourself. Imagine how difficult it would be to understand the first example in comparison to the second.
If code takes more than a few seconds to read (understand), perhaps there is a better way to write it. In this case, the second way.
The second example would be the one to go with, not just for readability, but because of the fact that in the first example, If NOT value1 would return a boolean value to be compared against value2. IOW, you need to rewrite that example as
If NOT (value1 = value2)
which just makes the use of the NOT keyword pointless.