Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 4 years ago.
Improve this question
scenario:
In Oracle Database, This SQL it's much slower:
SELECT *
FROM TBL_MAIN A, TBL_CHILD_1 B, TBL_CHILD_2 C, TBL_CHILD_3 D, TBL_CHILD_4 E
WHERE A.ID_MAIN = B.ID_MAIN
AND A.ID_MAIN = C.ID_MAIN
AND A.ID_MAIN = D.ID_MAIN
AND A.ID_MAIN = E.ID_MAIN;
than that:
SELECT *
FROM TBL_MAIN X, TBL_CHILD_1 B, TBL_CHILD_2 C, TBL_CHILD_3 D, TBL_CHILD_4 E
WHERE X.ID_MAIN = B.ID_MAIN
AND X.ID_MAIN = C.ID_MAIN
AND X.ID_MAIN = D.ID_MAIN
AND X.ID_MAIN = E.ID_MAIN;
In other words, When I have 3 or more tables joined if I use "A" as an aliasing for "TBL_MAIN" table, it results in a slower query.
It becomes worst when I added more tables in an inner join with table "TBL_MAIN" AS "A".
What is happening?
Yes it is. It is part of unary collection operator "IS A SET". In Oracle it is bit complicated, in order to guarantee backward compatibility Oracle has "reserved words" and "keywords". One of them can not be used as identifiers, while others might have special meaning is some contexts - but still can be used as identifiers.
For example you can still use SQL like
select * from commit;
or
select * from join where X is a set;
words like commit, join, model can still be used as identifiers.
Of course this can not have influence on speed of SQL evaluation. Especially when cursor is reused. It might only slow down speed of parsing.
I found that X is actually faster than A, but Z is even slower:
Alias A 60.257 seconds
Alias X 57.747 seconds
Alias Y 58.383 seconds
Alias Z 62.157 seconds
To be honest, these differences are to small to prove a difference between the names of the aliases.
I tested it with 5 large tables (tbl_main 22 million, tbl_child 17 million etc). And I changed SELECT * into SELECT COUNT(*) to make sure all rows are processed. The first execution is ignored to warm the caches. The next three runs are averaged.
DECLARE
FUNCTION test1(c VARCHAR2) RETURN NUMBER IS
time1 NUMBER; time2 NUMBER; stmt VARCHAR2(3000); n NUMBER;
BEGIN
stmt := q'!
SELECT count(*)
FROM tbl_main #, tbl_child1 B, tbl_child2 C, tbl_child3 D, tbl_child4 E
WHERE #.geb_id = B.geb_id
AND #.geb_id = C.geb_id
AND #.geb_id = D.geb_id
AND #.geb_id = E.geb_id
!';
stmt := REPLACE(stmt, '#', c);
--dbms_output.put_line(stmt);
time1 := dbms_utility.get_time();
EXECUTE IMMEDIATE stmt INTO n;
time2 := dbms_utility.get_time();
return (time2-time1)/100;
END test1;
PROCEDURE test3(c VARCHAR2) IS
ignore NUMBER; seconds NUMBER;
BEGIN
ignore := test1(c);
seconds := (test1(c)+test1(c)+test1(c)) / 3;
DBMS_OUTPUT.PUT_LINE('Alias '||c||' '||round(seconds,3)||' seconds');
END test3;
BEGIN
test3('A');
test3('X');
test3('Y');
test3('Z');
END;
/
Yes. It is in 12c.
SELECT KEYWORD
FROM V$RESERVED_WORDS
WHERE KEYWORD = 'A'
;
Short answer no. A is not a reserved word. If it would be a reserved word, it would raise an error. However to analyze your performance problem, you would need to post the metrics for query execution.
The problem solved itself!
Now the two SQL has the same time, just a little faster like #wolφi said.
This scenario was going on for two days, but now its normal.
I believe after a shutdown the database became normal.
An interesting thing about this is when the problems were happening, SQL Tuning Advisor suggested an execution plan for the SQL who make work normal (99,9 % faster). unfortunately, I didn't save recommendation report in that time to show here.
but now without any SQL profile active, the SQL's are with the same time.
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 6 months ago.
The community reviewed whether to reopen this question 6 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I am using these statements in a postgres function.
Select count(*) into V_check
from employee
where employee_name like 'Raj%';
if V_check == 0
then
update exception set exception_found = 'Raj';
end if;
I get this error :
ERROR: operator does not exist: integer == integer
LINE 1: SELECT V_check == 0
You should use = instead of ==.
Here is a list of the different comparison operators that you can use:
Operator Description
= Equal
<> Not equal. Note: In some versions of SQL this operator may be written as !=
> Greater than
< Less than
>= Greater than or equal
<= Less than or equal
BETWEEN Between an inclusive range
LIKE Search for a pattern
IN To specify multiple possible values for a column
As pointed out, the comparison operator for equality is = not ==. However, you should write the condition as:
if not exists (select 1 from employee where employee_name like 'Raj%')
then
update exception
set exception_found = 'Raj';
end if;
This saves you a declaration. Also, not exists is faster than count(*) -- because not exists can stop at the first matching row.
Or dispense with the conditional entirely:
update exception
set exception_found = 'Raj'
where not exists (select 1 from employee where employee_name like 'Raj%');
I have cross applied a table valued function in a DML statement that returns two columns one of which is RiskValue (which is an integer denoting Scan Period)
Now when I print the value RiskValue along with dateadd function like this (y is function alias and am is another table)
select cast(y.RiskValue as int),dateadd(m,cast(y.RiskValue as int),#RunningDate)
from .....
it gives me proper values as
6 | 'Some Date'
but when I use it in a where clause as
where am.DateOpen >= dateadd(m,cast(y.RiskValue as int),#RunningDate)
I get the Error :
Adding a value to a 'datetime' column caused an overflow
Note that passing hard coded values as
where am.DateOpen >= dateadd(m,6,#RunningDate)
works fine. (Obviously it will)
Any suggestions what might be wrong?
Posting Aaron Bertrand's comment as answer so that people reaching this question will find it helpful :
RiskValue is 6 for that row, but you have to understand that SQL Server may not optimize the statement in the same order you wrote it. It can often error out trying to perform calculations on values that should have been excluded by a filter, but the calculations were attempted first. We can try
DATEADD(MONTH, CASE WHEN y.RiskValue < 20000 THEN y.RiskValue END, #RunningDate)
Also read this and this for better understanding
This query currently is returning no results, and it should. Can you see anything wrong with this query
field title are NEED_2_TARGET, ID, and CARD
NEED_2_TARGET = integer
CARD = string
ID = integer
value of name is 'Ash Imp'
{this will check if a second target is needed}
//**************************************************************************
function TFGame.checkIf2ndTargetIsNeeded(name: string):integer;
//**************************************************************************
var
targetType : integer; //1 is TCard , 2 is TMana , 0 is no second target needed.
begin
TargetType := 0;
Result := targetType;
with adoquery2 do
begin
close;
sql.Clear;
sql.Add('SELECT * FROM Spells WHERE CARD = '''+name+''' and NEED_2_TARGET = 1');
open;
end;
if adoquery2.RecordCount < 1 then
Result := 0
else
begin
Adoquery2.First;
TargetType := adoquery2.FieldByName(FIELD_TARGET_TYPE).AsInteger;
result := TargetType;
end;
end;
sql db looks like below
ID CARD TRIGGER_NUMBER CATEGORY_NUMBER QUANTITY TARGET_NUMBER TYPE_NUMBER PLUS_NUMBER PERCENT STAT_TARGET_NUMBER REPLACEMENT_CARD_NUMBER MAX_RANDOM LIFE_TO_ADD REPLACED_DAMAGE NEED_2_TARGET TYPE_OF_TARGET
27 Ash Imp 2 2 15 14 1 1
There are a number of things that could be going wrong.
First and most important in your trouble-shooting is to take your query and run it directly against your database. I.e. first confirm your query is correct by eliminating possibilities of other things going wrong. More things confirmed working, the less "noise" to distract you from solving the problem.
As others having pointed out if you're not clearing your SQL statement, you could be returning zero rows in your first result set.
Yes I know, you've since commented that you are clearing your previous query. The point is: if you're having trouble solving your problem, how can you be sure where the problem lies? So, don't leave out potentially relevant information!
Which bring us neatly to the second possibility. I can't see the rest of your code, so I have to ask: are you refreshing your data after changing your query? If you don't Close and Open your query, you may be looking at a previous execution's result set.
I'm unsure whether you're even allowed to change your query text while the component is Active, or even whether that depends on exactly which data access component you're using. The point is, it's worth checking.
Is your application connecting to the correct database? Since you're using Access, it's very easy to be connected to a different database file without realising it.
You can check this by changing your query to return all rows (i.e. delete the WHERE clause).
You my want to change the quotes used in your SQL query. Instead of: ...CARD = "'+name+'" ORDER... rather use ...CARD = '''+name+''' ORDER...
As far as I'm aware single quotes is the ANSI standard. Even if some databases permit double quotes, using them limits portability, and may produce unexpected results when passed through certain data access drivers.
Check the datatype of your CARD column. If it's a fixed length string, then the data values will be padded. E.g. if CARD is char(10), then you might actually need to look for 'Ash Imp '.
Similarly, the actual value may contain spaces before / after the words. Use select without WHERE and check the actual value of the column. You could also check whether SELECT * FROM Spells WHERE CARD LIKE '%Ash Imp%' works.
Finally, as others have suggested, you're better off using a parameterised query rather dynamically building the query up yourself.
Your code will be more readable and flexible.
You can make your code strongly typed; and so avoid converting things like numbers and dates into strings.
You won't need to worry about the peculiarities of date formatting.
You eliminate some security concerns.
#GordonLinoff all fields in db are all caps
If that is true then that is your problem. SQL usually performs case sensitive comparisons of character/string values unless you tell it not to do so, such as with STRCMP() (MySQL 4+), LOWER() or UPPER() (SQLServer, Firebird), etc. I would also go as far as wrapping the conditions in parenthesis as well:
sql.Text := 'SELECT * FROM Spells WHERE (NEED_2_TARGET = 1) AND (STRCMP(CARD, "'+name+'") = 0) ORDER by ID';
sql.Text := 'SELECT * FROM Spells WHERE (NEED_2_TARGET = 1) AND (LOWER(CARD) = "'+LowerCase(name)+'") ORDER by ID';
sql.Text := 'SELECT * FROM Spells WHERE (NEED_2_TARGET = 1) AND (UPPER(CARD) = "'+UpperCase(name)+'") ORDER by ID';
This is or was an issue with the
With Adoquery2 do
begin
...
end
when using name in the sql, it was really getting adoquery2.name not the var name. I fixed this by changing name to Cname had no more issues after that.
I've got an Oracle table that holds a set of ranges (RangeA and RangeB). These columns are varchar as they can hold both numeric and alphanumeric values, like the following example:
ID|RangeA|RangeB
1 | 10 | 20
2 | 21 | 30
3 | AB50 | AB70
4 | AB80 | AB90
I need to to do a query that returns only the records that have numeric values, and perform a Count on that query. So far I've tried doing this with two different queries without any luck:
Query 1:
SELECT COUNT(*) FROM (
SELECT RangeA, RangeB FROM table R
WHERE upper(R.RangeA) = lower(R.RangeA)
) A
WHERE TO_NUMBER(A.RangeA) <= 10
Query 2:
WITH A(RangeA,RangeB) AS(
SELECT RangeA, RangeB FROM table
WHERE upper(RangeA) = lower(RangeA)
)
SELECT COUNT(*) FROM A WHERE TO_NUMBER(A.RangeA) <= 10
The subquery is working fine as I'm getting the two records that have only numeric values, but the COUNT part of the query is failing. I should be getting only 1 on the count, but instead I'm getting the following error:
ORA-01722: invalid number
01722. 00000 - "invalid number"
What am I doing wrong? Any help is much appreciated.
You can test each column with a regular expression to determine if it is a valid number:
SELECT COUNT(1)
FROM table_of_ranges
WHERE CASE WHEN REGEXP_LIKE( RangeA, '^-?\d+(\.\d*)?$' )
THEN TO_NUMBER( RangeA )
ELSE NULL END
< 10
AND REGEXP_LIKE( RangeB, '^-?\d+(\.\d*)?$' );
Another alternative is to use a user-defined function:
CREATE OR REPLACE FUNCTION test_Number (
str VARCHAR2
) RETURN NUMBER DETERMINISTIC
AS
invalid_number EXCEPTION;
PRAGMA EXCEPTION_INIT(invalid_number, -6502);
BEGIN
RETURN TO_NUMBER( str );
EXCEPTION
WHEN invalid_number THEN
RETURN NULL;
END test_Number;
/
Then you can do:
SELECT COUNT(*)
FROM table_of_ranges
WHERE test_number( RangeA ) <= 10
AND test_number( RangeB ) IS NOT NULL;
Try this query:
SELECT COUNT(*)
FROM table R
WHERE translate(R.RangeA, 'x0123456789', 'x') = 'x' and
translate(R.RangeB, 'x0123456789', 'x') = 'x'
First, you don't need the subquery for this purpose. Second, using to_number() or upper()/lower() are prone to other problems. The function translate() replaces each character in the second argument with values from the third argument. In this case, it removes numbers. If nothing is left over, then the original value was an integer.
You can do more sophisticated checks for negative values and floating point numbers, but the example in the question seemed to be about positive integer values.
Coming to this question almost four years later (obviously, pointed here from a much newer thread). The other answers show how to achieve the desired output, but do not answer the OP's question, which was "what am I doing wrong?"
You are not doing anything wrong. Oracle is doing something wrong. It is "pushing" the predicate (the WHERE condition) from the outer query into the inner query. Pushing predicates is one of the most basic ways in which the Optimizer makes queries more efficient, but in some cases (and the question you ask is a PERFECT illustration) the result is not, in fact, logically equivalent to the original query.
There are ways to prevent the Optimizer from pushing predicates; or you can write the query in a better way (as shown in the other answers). But if you wanted to know why you saw what you saw, this is why.
I've a table with a varchar column (A) and another integer column(B) indicating the type of data present in A. If B is 0, then A will always contain numeric digits.
So when I form an sql like this
SELECT COUNT(*) FROM TAB WHERE B = 0 AND TO_NUMBER(A) = 123;
I get an exception invalid number.
I expect B = 0 to be evaluated first, and then TO_NUMBER(A) second, but from the above scenario I suspect TO_NUMBER(A) is evaluated first. Is my guess correct?
In contrast to programming languages like C, C#, Java etc., SQL doesn't have so called conditional logical operators. For conditional logical operators, the right operand is only evaluated if it can influence the result. So the evaluation of && stops if the left operand returns false. For || it stops if the left operand returns true.
In SQL, both operands are always evaluated. And it's up to the query optimizer to choose which one is evaluated first.
I propose you create the following function, which is useful in many cases:
FUNCTION IS_NUMBER(P_NUMBER VARCHAR2)
RETURN NUMBER DETERMINISTIC
IS
X NUMBER;
BEGIN
X := TO_NUMBER(P_NUMBER);
RETURN X;
EXCEPTION
WHEN OTHERS THEN RETURN NULL;
END IS_NUMBER;
Then you can rewrite your query as:
SELECT COUNT(*) FROM TAB WHERE B = 0 AND IS_NUMBER(A) = 123;
You can also use the function to check whether a string is a number.
Here's a simple way to force the check on B to occur first.
SELECT COUNT(*) FROM TAB
WHERE 123 = DECODE(B, 0, TO_NUMBER(A), NULL);
you can use subquery to be confident in the correctness of the result
select /*+NO_MERGE(T)*/ count(*)
from (
select *
from TAB
where B = 0
) T
where TO_NUMBER(A) = 123
in your particular example, you can compare varchars like this:
SELECT COUNT(*) FROM TAB WHERE B = 0 AND A = '123';
or if you trust oracle to do the implicit conversions, this should almost always work (i don't know in what cases it won't work, but it would be hard to debug if something went wrong)
SELECT COUNT(*) FROM TAB WHERE B = 0 AND A = 123;
It should test B = 0 first.
I not sure your guess is correct or not though without seeing sample data.
SELECT *
FROM DUAL
WHERE 1=0 AND 1/0=0
You can try add 1/0 = 0 as the last statement (like this query)
to your query to know if Oracle short circuits the logical operator.
sqlfiddle here.