is possible : cast char to varchar while join table in oracle - oracle

Can I cast B1 char(2) which joins on A1 varchar2(2) :
SELECT * FROM A
LEFT JOIN B
ON CAST(B.B1 AS VARCHAR2(2)) = A.A1
It results to no errors, but there no data displayed.
Is the above query possible?

You can cast it, but it isn't doing that you think, or seem to be relying on. Assuming you have a one-character value in the field you're joining on, you don't get a match, with or without the cast:
create table a (a1 varchar2(2));
create table b (b1 char(2));
insert into a values ('X');
insert into b values ('X');
select * from a left join b on b.b1 = a.a1;
A1 B1
-- --
X
select * from a left join b on cast(b.b1 as varchar2(2)) = a.a1;
A1 B1
-- --
X
The cast is chaging the data type, but not the data; it is still blank-padded. The only difference is that it's done explicitly in the value, rather implicitly as you'd see with a char. You can verify that the value is the same with the dump() function:
select dump(b.b1) dump_char,
dump(cast(b.b1 as varchar2(2))) dump_varchar2
from b;
DUMP_CHAR DUMP_VARCHAR2
-------------------- --------------------
Typ=96 Len=2: 88,32 Typ=1 Len=2: 88,32
So the type has changed, from 96 (char) to 1 (varchar2), but the value is the same. Compare that with your value in table A and you'll see they are not the same:
select dump(a.a1) dump_varchar2 from a;
DUMP_VARCHAR2
--------------------
Typ=1 Len=1: 88
Your cast B value still has the trailing space, the A value does not, therefore they don't match. You can remove that trailing space for comparison with trim() or rtrim():
select * from a left join b on rtrim(b.b1) = a.a1;
A1 B1
-- --
X X
There is an implicit conversion from char to varchar2 within the rtrim() call, so you could still cast that explicitly for clarity.
Note that this assumes you never have a trailing space in A. It may be safer to cast the other way:
select * from a left join b on b.b1 = cast(a.a1 as char(2));
A1 B1
-- --
X X
... but which side you cast/trim will also affect which indexes can be used.

this worked for me:
SELECT * FROM A
LEFT JOIN B
ON TRIM(B.B1) = A.A1
i prefer this syntax to the cast for purely aesthetic reasons, although presumably others exist

Related

Oracle maximum number of expression issue

I have this query
#Query("SELECT b from BillInfoDetails b where b.masterAcctCode in :masterAccountList and b.msisdn in :msisdnList") List<BillInfoDetails>findAllByMsisdnAndMasterAcctList(#Param("masterAccountList")List<String> masterAccountList, #Param("msisdnList") List<String> msisdnList);
and then find this error ORA-01795: maximum number of expressions in a list is 1000.
This error has some manual solution that I have split the list manually 1 to 999 and then 1000 to 1999 and so on. But this will not the good one for me cause in this msisdnList there could be 1500 or 18000 or some other more values. Moreover I want a dynamic solution actually where any dynamic value whatever it is, it will work properly
One option is to store all those values into a table; then you'd be able to use it as a join (or a subquery). For example:
select b.*
from billinfodetails b join new_table n on n.masteracctcode = b.masteracctcode
or
select b.*
from billinfodetails b
where b.masteracctcode in (select n.masteracctcode
from new_table n)
or
select b.*
from billinfodetails b
where exists (select null
from new_table n
where n.masteracctcode = b.masteracctcode)

comparing values from two oracle table columns and getting starting value

I Have oracle table with data as shown below.
column1 column2
A1 B1
B1 C1
C1 D1
I need to get A1 value from D1. I have to implemet this in View. Need to traverse using as D1 as input and get C1 and get B1 from C1 and finally A1 using B1
Please help.
Not sure what kind of view you are looking to create; if you are thinking of passing 'A1' as an input to the view, there is no such thing in Oracle as far as I know, you would need to use a cursor for that.
The following SELECT statement can be used as an inline view (a subquery) and you can make the 'D1' value at the end of it into a bind variable if needed.
select column1
from test_data
where connect_by_isleaf = 1
connect by column2 = prior column1
start with column2 = 'D1'
;

Oracle Database Link + Inner Join + To_Number Query

DatabaseA - TableA - FieldA VARCHAR2
DatabaseB - TableB - FieldB NUMBER [dblink created]
SELECT *
FROM TableB#dblink b
INNER JOIN TableA a
ON b.FieldB = a.FieldA
There are 2 complications.
1. FieldA is VARCHAR2 but FieldB is NUMBER.
2. FieldA contains - and FieldB contains 0.
More info about the fields
FieldA: VARCHAR2(15), NOT NULL
Sample values
-
123
No non-numeric values, except for -
FieldB: NUMBER(5,0)
Sample values
0
123
No non-numeric values
What I'm trying to do is to ignore the rows if FieldA='-' OR FieldB=0, otherwise compare FieldA to FieldB.
SELECT *
FROM TableB#dblink b
JOIN TableA a
ON to_char(b.FieldB) = a.FieldA
I get the following error:
SQL Error: 17410, SQLState: 08000
No more data to read from socket.
NULLs will never match with equals, so your join already takes care of that.
You would get an implicit type conversion of (probably) the NUMBER to VARCHAR, so that should also be taken care of.
Having said that, I am a big proponent of not relying on implicit datatype conversions. So I would write my query as
SELECT *
FROM TableB#dblink b
JOIN TableA a
ON to_char(b.FieldB) = a.FieldA
If that is not giving the results you want, perhaps posting examples of the data in each table and the results you desire would be helpful.

join with exact match value otherwise join with default value

I have a table a
c1 c2 c3 c4 value
all all all all 5
all david all Y 6
all all cd all 7
and table b
c1 c2 c3 c4
a peter cd N
b david all Y
c all cd N
I want to have get the value from table a into table b, the desired results is like this:
c1 c2 c3 c4 Value
a david cd N 5
b david ab Y 6
c all cd N 7
That is use the default "all" value if there is no close match find.
Thanks a lot!
I see a couple of possibilities assuming you have some errors in your example result data. (C2 for value 5 and C3 for value 6 are suspect to me)
Use a CTE to union the results replace all with null and use aggregation to get max value
Use a Join on value and evaluate table a's value for each c column if it's not all use it, otherwise use B's value. (IF a and b are both all it doesn't matter which we use.) this may be problematic if both values are potentially different like if Value 6 had a Y in A, and a N in B. but no such example exits in your data so I'm trusting it doesn't happen. (or if it did, picking A's value is more appropriate if it's not all)
AS a cte: (Common Table expression)
WITH cte as (
SELECT replace(c1,'all',null)
, replace(c2,'all',null)
, replace(c3,'all',null)
, replace(c4,'all',null)
, value
FROM A
UNION ALL
SELECT replace(c1,'all',null)
, replace(c2,'all',null)
, replace(c3,'all',null)
, replace(c4,'all',null)
, value
FROM b)
/* We have to eval the max as if it's null we need to replace it with all
Might be able to avoid the replacing all provided all values of c1-c4 are
greater than all... replacing just seemed safer. at a hit to performance.*/
SELECT coalesce(max(c1),'all') as c1
,coalesce(max(c2),'all') as c2
,coalesce(max(c3),'all') as c3
,coalesce(max(c4),'all') as c4
,value
FROM cte
GROUP BY value
Using a join (simpler from a maintenance and perhaps performance standpoint)
SELECT case when A.C1 <> 'all' then A.C1 else B.c1 end as C1,
case when A.C2 <> 'all' then A.C2 else B.c2 end as C2,
case when A.C3 <> 'all' then A.C3 else B.c3 end as C3,
case when A.C4 <> 'all' then A.C4 else B.C4 end as C4,
A.value --A.val = b.val so it doesn't matter which se use.
FROM A
INNER JOIN B
on A.value = B.Value
Depending on existing indexes and data volume the first approach might be better than the second.

substr function for substract the column value basis of another column in oracle

How to use substr function in oracle to substract the column value
based on another column vale in same table.
For example:suppose table abc having some column value like a=01-CEDAPR while B=AB_52MM_01-CEDAPR
Now i want to populate the column c on the basis of value AB_52MM. can any one suggest me
what is right way to achieve this .
This should be relatively straightforward. All you want to do is replace the value of a, if found in b, with nothing. Right?
WITH abc AS (
SELECT '01-CEDAPR' AS a, 'AB_52MM_01-CEDAPR' AS b
FROM dual
)
SELECT a, b, REPLACE(b, a)
FROM abc
See SQL Fiddle Demo here.
If you need to replace the _ preceding the value of a, then you might want to use REGEXP_REPLACE() (in case the _ may or may not exist):
WITH abc AS (
SELECT '01-CEDAPR' AS a, 'AB_52MM_01-CEDAPR' AS b
FROM dual
)
SELECT a, b, REGEXP_REPLACE(b, '_?' || a || '$')
FROM abc
The $ sign ensures that the value of a is anchored to the end; the ? makes _ optional.
SQL Fiddle Demo here.
Here's a couple of solutions that may or may not help you, based on the sketchy information you have provided. If they don't, then you will need to edit your question to provide more a more detailed explanation of what you're after!
with sd as (select '01-CEDAPR' a, 'AB_52MM_01-CEDAPR' b from dual) -- assumed O1 in column b was a typo
select a,
b,
regexp_replace(b, '_'||a),
substr(b, 1, instr(b, '_', -1) -1)
from sd;

Resources