I used a query to create a table, which has a SET in one of its columns.
T1:
serial _c3
1 193748 ["special","normal","normal"]
2 263565 ["normal","normal"]
Then I have another Table with serials only.
T2:
serial
1 193748
2 263565
3 636474
4 928396
I want a query that produces serials from T2 IF they are NOT in T1 or if T1's _c3 data has the word "special" in it. I also want a boolean value that indicates if T1 is in T2.
So using above example, I want:
T3:
serial in_t1
1 193748 1
3 636474 0
4 928396 0
Here is my query so far:
SELECT
T2.serial,
array_contains(T1._c3, 'special') as in_t1
FROM T2 LEFT OUTER JOIN T1 ON T1.serial = T2.serial
WHERE T1.serial is NULL OR array_contains(T1._c3, 'special')
LIMIT 50;
So for array_contains in select line I get this error message:
Error while compiling statement: FAILED: cannot recognize input near 'T1' '.' '_c3' in select expression.
When I remove that line from select and just run:
SELECT
T2.serial
FROM T2 LEFT OUTER JOIN T1 ON T1.serial = T2.serial
WHERE T1.serial is NULL OR array_contains(T1._c3, 'special')
LIMIT 50;
I get the same error but in the WHERE clause line now: cannot recognize input near 'T1' '.' '_c3' in select expression
Could you point me in the right direction? Thank you!
_c3 is illegal alias/column name, due to the underscore as its first character.
If you want to use it, wrap it with ticks signs (`).
Anther option would be to rename to column.
The cleanest solution would have been to alias the expression in the first place.
Related
A super simple example of my script looks as follows:
-- Report Name: "Report_1"
col letters new_value p_letters
SELECT letters
FROM param_table
WHERE report_name = 'Report_1';
CREATE TABLE temp_table_1
(letter varchar2(1));
INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE '&&p_letters' = '' OR letter IN (&&p_letters);
For some reason, our system has a table called param_table: users enter parameters through the UI, the parameters entered are written to param_table, and then my script pulls the user's parameters from param_table.
As far as I understand, the first SELECT statement selects the letters column from param_table and makes its values accessible in '&&p_letters'. In my INSERT INTO statement, when my WHERE clause looks like this...
WHERE letter IN (&&p_letters);
...and the user enters letters separated by single quotes, eg ('A', B', C'), the script runs fine. I want to make the parameter optional, so I adjusted the WHERE clause like this:
WHERE '&&p_letters' = '' OR letter IN (&&p_letters);
In my output file, I get the following error:
WHERE (('' = '') OR letter IN ()) *
ERROR at line ...:
ORA-00936: missing expression
The compiler has evaluated the substitution variable correctly as '', but I'm getting an error.
Any idea what I could be doing wrong here?
The ORA-00936 is because IN () is not valid - you're missing something inside that. It is that it is complaining about, not the '' = '' part, though the result of that is undefined. You can check both conditions:
SQL> select * from dual where '' = '';
no rows selected
SQL> select * from dual where dummy in ();
select * from dual where dummy in ()
*
ERROR at line 1:
ORA-00936: missing expression
If you set verify on you can see how the substitution is handled. For your original query you'd see:
old:INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE letter IN (&&p_letters)
new:INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE letter IN ('A','B','C')
3 rows inserted.
You can see that the post-substitution statement looks, and is, valid.
With your modified query you'd see:
old:INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE '&&p_letters' = '' OR letter IN (&&p_letters)
new:INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE ''A','B','C'' = '' OR letter IN ('A','B','C')
which generates an ORA-00920 because of the messed-up single quotes in the first expression. With no value from letters you'd instead see:
old:INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE '&&p_letters' = '' OR letter IN (&&p_letters)
new:INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE '' = '' OR letter IN ()
which is the error you saw, ORA-00936.
I'd be tempted to do this with a collection type, either your own, or if you're comfortable with it then a built-in one:
INSERT INTO temp_table_1(letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE SYS.DBMS_DEBUG_VC2COLL(&&p_letters) IS EMPTY
OR letter MEMBER OF SYS.DBMS_DEBUG_VC2COLL(&&p_letters);
That works with your three comma-separated values, or null, since an empty collection is allowed. Read more about is empty and member of.
It would be better, of course, to not store comma-separated lists in a single column value anyway, and to change your data model so this kind of manipulation and reliance on client behaviour isn't necessary.
Assuming you're stuck with the data model, you could at least avoid the client reliance buy tokenizing the string (I'm using one common approach below) and looking for matches. However, you also need to account for either the report name not being in the table at all or the report existing with no letters value, both of which are handled by the max(letters) .. is null check - which makes it a bit ugly.
It's all in one statement though, with no need for a separate query to get the parameters and no need for substitution variables. (And there may be better ways to do it!)
INSERT INTO temp_table_1 (letter)
SELECT DISTINCT letter
FROM table_alphabet
WHERE (
SELECT MAX(letters)
FROM param_table
WHERE report_name = 'Report_2'
) IS NULL
OR letter IN (
SELECT TRIM(q'[']' FROM REGEXP_SUBSTR(letters, '[^,]', 1, LEVEL))
FROM param_table
WHERE report_name = 'Report_2'
CONNECT BY REGEXP_SUBSTR(letters, '[^,]', 1, level) IS NOT NULL
);
I am not an oracle expert. I faced a very strange problem but do not know why this occur.
My query is
SELECT hc.id, hc.owner_name, hc.national_id, hc.phone_no, hc.location, hc.status, hc.expiry_status, od.office_title AS issuer, hc.create_date, hc.email, hc.LATTITUDE, hc.LONGITUDE, hc.HASAD_NO, hc.NUMBERATION, hc.BREEDING_TYPE, hc.PROGENY, hc.office_id, hc.issuer_id, hc.expiry_status, hc.status FROM health_cards hc
LEFT JOIN office_details od ON od.office_id = hc.issuer_id AND od.lang = :lang
WHERE hc.id = :search_data_num OR hc.national_id = :search_data_num or hc.phone_no = :search_data_num OR hc.owner_name LIKE :search_data ORDER BY hc.create_date DESC, hc.id desc OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY
When i m running this query i m getting following error
ORA-00918: column ambiguously defined
00918. 00000 - "column ambiguously defined"
But if i remove OFFSET 0 ROWS FETCH NEXT 50 ROWS ONLY from my query it work perfectly.
I want to know the reason why this query not working with offset statement.
You have repeated column hc.status.
`select 1 as "A" as "A" from dual` - execute OK;
`select * from (select 1 as "A"
, 2 as "A"
from dual);` - ORA-00918: column ambiguously defined
If you and offset, oracle probably does something similar.
You have the column hc.expiry_status twice in your select list.
The problem is that we allow this in a select list, but not within an inline view. When you add the row limiting clause, Oracle transforms the query and the transformation uses an inline view. There is a bug, 13687511 which is marked as fixed.
Meanwhile, the workaround is to either not select it twice, or alias the column(s).
I have an application which automatically add brackets after WHERE condition and send it to JDBC Oracle driver, Oracle doesn't like it and thrown: ORA-00907: missing right parenthesis
I'm not sure how to work with it in the scope of Oracle syntax, but any suggestion to fix it having this brackets or it's not supported by the syntax?
Original query works just fine:
SELECT count(*) as ErrorCount, Engine_name, to_char(log_time,'hh24') as Hour FROM eailog_data.err_log WHERE err_timestamp > sysdate-1/24 GROUP BY engine_name, to_char(log_time,'hh24') HAVING count(*) > 100 AND count(*) > 0 ORDER BY count(*)
and 3rd party application modify it like this (note brackets added after WHERE condition):
SELECT count(*) as ErrorCount, Engine_name, to_char(log_time,'hh24') as Hour
FROM eailog_data.err_log
WHERE
(
err_timestamp > sysdate-1/24
GROUP BY engine_name,
to_char(log_time,'hh24')
HAVING count(*) > 100
)
AND count(*) > 0
ORDER BY count(*)
Any ideas how to fix SQL with added brackets?
The WHERE clause parenthetical expression needs to end at the end of the WHERE clause and the condition in the HAVING clause ends with a parenthesis, but never begins.
In terms of adding parenthesis, certainly you could add a parenthesis at the end of the WHERE clause and add a parenthesis at the beginning of the HAVING clause as follows:
SELECT count(*) AS errorcount,
engine_name,
to_char(log_time,'hh24') AS HOUR
FROM eailog_data.err_log
WHERE ( err_timestamp > SYSDATE-1/24 )
GROUP BY engine_name,
to_char(log_time,'hh24')
HAVING ( count(*) > 100 )
AND count( *) > 0
ORDER BY count(*)
Since this is an application, it sounds like you need to work with the author of the application to fix their parenthesis usage.
Here is an example using the DUAL table
Before, malformed parenthetical expression in the WHERE and HAVING clause.
SCOTT#dev> SELECT dummy,
2 COUNT(*)
3 FROM dual
4 WHERE (dummy != 'Y'
5 GROUP BY dummy
6 HAVING COUNT( *) = 1)
7 AND COUNT( *) > 0
8 ORDER BY COUNT(*)
9 /
WHERE (dummy != 'Y'
*
ERROR at line 4:
ORA-00907: missing right parenthesis
After, corrected parenthetical expression in the 'WHERE' and 'HAVING' clause.
SCOTT#dev> --corrected
SCOTT#dev> SELECT dummy,
2 COUNT(*)
3 FROM dual
4 WHERE (dummy != 'Y')
5 GROUP BY dummy
6 HAVING (COUNT( *) = 1)
7 AND COUNT( *) > 0
8 ORDER BY COUNT(*)
9 /
D COUNT(*)
= ==========
X 1
A SQL statement consists of several clauses (some of which are optional):
the column list
the table list (FROM clause)
filter conditions (WHERE clause)
aggregate columns (GROUP BY clause)
aggregate conditions (HAVING clause)
etc.
The key concept that seems to be missing is that you can't open a parenthesis in one clause and close it in another. The reason the error you're getting is "missing right parenthesis" is that the SQL engine thinks you're done with the WHERE clause as soon as it sees GROUP BY. Since there was a un-closed parenthetical at that point, it can't parse any further.
To use an analogy, the SQL you provided is like having the opening and closing parenthesis in different methods in Java. It simply can't work.
There are at least two ways to get around some tool mangling your SQL syntax: Creative SQL to subvert the parser, and convert the SQL.
Creative SQL
Parsing Oracle SQL is virtually impossible to do 100% correctly since the syntax is much more complicated than other languages. This leads to problems with beautifiers and code generators. I've seen tools fail in ways very similar to your example.
But this complexity also offers many opportunities to subvert tools that try to rewrite SQL. Try different features until you find something that is immune to their parser. Here are some ideas:
Inline view. select * from (select ... from ... where ... ) where 1=1;
Common table expression. with some_query as (select ... from ... where ... ) select * from some_query;
Alternative quoting mechanism.
select dummy
from dual
where '''' = q'<'>' --The parser probably thinks this is still a string.
and 1 = 1
group by dummy
--' --And it probably thinks this is the end.
Convert SQL
In extreme cases there are ways to make Oracle accept completely broken SQL. Check out
SQL Translation Framework
and DBMS_ADVANCED_REWRITE.
Those tools are definitely a last resort. It would be great if we could wave a magic wand and fix all 3rd party programs but we have to live in the real world.
DB: Oracle 11g
Query:
SELECT CASE
WHEN rs.OPTION = '3'
THEN
(SELECT COUNT(DISTINCT ex.EXTS) AS TMPCOL0
FROM CRSIDM.SUB_OPTS ex
INNER JOIN CRSIDM.SUB_OPTS_GRP cg
ON cg.GROUP_ID = ex.GRP_ID
)
ELSE
(SELECT COUNT(DISTINCT ex.EXTS) AS TMPCOL0
FROM CRSIDM.SUB_OPTS ex
INNER JOIN CRSIDM.SUB_OPTS_POL cg
ON cg.GROUP_ID = ex.GRP_ID
)
END AS PROPTS
FROM PR_OPTS
I am getting error 'expected CHAR got NUMBER', here EXTS,GROUP_ID & GRP_ID are numeric. Then how there is a chance of expecting CHAR?
Generally when Oracle compares different datatypes such as a NUMBER with a CHARACTER, implicit conversion kicks in and all is well (provided the data can be converted.) For example, if you have a function that expects a CHARACTER value but you pass it a NUMBER, all is well - Oracle simply converts the NUMBER to character.
E.g. a function like this:
create or replace function get_something(p_id VARCHAR2) return number ...
works if you call it with this:
get_dno(10);
or this:
get_dno('10');
and in SQL:
select * from some_table where numeric_column = '10' -- no problem.
A popular place where you see this kind of error is with the return values in CASE statements. For instance, you'll get that error if you have something like this:
SQL> SELECT CASE WHEN 1 = 1 THEN '1' ELSE 2 END
2 FROM dual
3 ;
SELECT CASE WHEN 1 = 1 THEN '1' ELSE 2 END
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected CHAR got NUMBER
(The datatype from the first WHEN clause is what it expects in the other WHEN/ELSE clauses that follow.)
But in your case the WHEN and THEN both return counts - the datatypes are consistent. So, I think you have a red-herring in there.
As Alex mentioned above, OPTION is a keyword and if you try and create a table with that as a column name, Oracle disagrees:
SQL> create table dummy
2 (option varchar2(10)
3 );
(option varchar2(10)
*
ERROR at line 2:
ORA-00904: : invalid identifier
This works:
SQL> create table dummy
2 (option_col varchar2(10)
3 );
Table created.
or you could do it with quotes:
SQL> create table dummy
2 ("option" varchar2(10));
Table created.
But now you're in a world of hurt - you need quotes from now on:
SQL> select option from dummy;
select option from dummy
*
ERROR at line 1:
ORA-00936: missing expression
SQL> select d.option from dummy d;
select d.option from dummy d
*
ERROR at line 1:
ORA-01747: invalid user.table.column, table.column, or column specification
With quotes:
SQL> select d."option" from dummy d;
no rows selected
So, if your query is really giving you "expected CHAR, got NUMBER", it looks to me like something is off.
Essentially, it means some of the fields you are using aren't compatible with each other. It's basically a "type mismatch". Just check to see if any types of CHAR are being used with types of NUMBER. Then you can either switch the type of one, or simply use a conversion as part of the query.
The issue is OPTION = '3', the quotation marks indicate that you're looking for a string containing the solitary character 3.
Try this instead:
SELECT CASE
WHEN rs.OPTION = 3
THEN
(SELECT COUNT(DISTINCT ex.EXTS) AS TMPCOL0
FROM CRSIDM.SUB_OPTS ex
INNER JOIN CRSIDM.SUB_OPTS_GRP cg
ON cg.GROUP_ID = ex.GRP_ID)
ELSE
(SELECT COUNT(DISTINCT ex.EXTS) AS TMPCOL0
FROM CRSIDM.SUB_OPTS ex
INNER JOIN CRSIDM.SUB_OPTS_POL cg
ON cg.GROUP_ID = ex.GRP_ID)
END AS PROPTS
FROM PR_OPTS
Suppose this is my table:
ID STRING
1 'ABC'
2 'DAE'
3 'BYYYYYY'
4 'H'
I want to select all rows that have at least one of the characters in the STRING column somewhere in another row's STRING variable.
For example, 1 and 2 have an A in common and 1 ad 3 have a B in common, but 4 does not have any characters in common with any of the other rows. So my query should return only the first three lines.
I don't need to know with which line it matched.
Thanks!
#A.B.Cade : Good solution but could be done without any distinct nor join.
SELECT * FROM test t1
WHERE EXISTS
(
SELECT * FROM test t2
WHERE t1.id<>t2.id AND
regexp_like(t1.string, '['|| replace(t2.string, '.[]', '\.\[\]')||']')
)
The query won't compare the string with extra rows since it'll stop the comparison as soon as 1 match is found for the current row...
See fiddle.
#GolezTrol's answer is a good one, but here is another approach:
select distinct t1."ID", t1."STRING"
from table1 t1, table1 t2
where t1."ID" <> t2."ID"
and regexp_like(t1."STRING", '['|| t2."STRING"||']')
First take a cartessian product of the table
Then make sure your not comparing the same string to itself
then create a regexp from one string for comparing to the other - [<string1>] means that the string must contain one of the letters in the [ ] which are all from string1
Here is a fiddle
Like this:
select distinct
id, name
from
(select distinct
x.id,
x.NAME,
length(x.NAME) as leng,
substr(x.name, level, 1) as namechar
from
YourTable x
start with
level = 0
connect by
level <= length(x.name)) y
where
exists
(select
'x'
from
YourTable z
where
instr(z.name, y.namechar) > 0 and
z.id <> y.id)
order by
id
What it does:
First, (inner select) use the table with a number generator that returns a number for each letter in the name. Now each record in YourTable is returned Length(Name) times, each with another number. That generated number is used to isolate that letter (substr).
Then (subselect in top level where clause) check if records exist that contain that isolated letter. Distinct is needed, because records are returned more than once if more than one letter matches. You could add namechar to the outer select field list to see the letter that match.