Oracle Merge Into (Multiple Joined Tables) Update Set (Multiple Where Statements) Returns Invalid Column Specification - oracle

I am new to Oracle. I am trying to update the values of a table with the values from a SELECT DISTINCT statement using the MERGE INTO method. I want to update the values for a table based on what is in the USING table conditionally.
A quick diagram of what I am essentially going for is
MERGE
INTO update_table ut
USING
(SELECT DISTINCT
t1.column_1
,t2.column_2
FROM table_1 t1
INNER JOIN table_2 t2
ON t1.foreign_key = t2.primary_key) st
ON (ut.pk = st.column_1)
WHEN MATCH UPATE
SET(ut.update_column = st.column_2
WHERE st.column_1 = 1
AND st.column_2 = 1
,ut.update_column = st.column_2
WHERE st.column_1 = 2
AND st.column_2 = 2);
However, when I do so I get the INVALID COLUMN SPECIFICATION error on the line where I use SET. How can I work around this to successfully update the table, preferably in ANSI standard?

You can include the conditions that you have added in where clause in the selected column list in using clause itself. Like This. (Not tested. Your conditions in where clause were not appropriate)
MERGE
INTO update_table ut
USING (SELECT DISTINCT
t1.column_1 ,
CASE
WHEN t1.column_1 = 1
AND t2.column_2 = 1
THEN t2.column_1
WHEN t1.column_1 = 2
AND t2.column_2 = 2
THEN t2.column_2
END column_2
FROM
table_1 t1
INNER JOIN table_2 t2 ON t1.foreign_key = t2.primary_key
) st
ON (ut.pk = st.column_1)
WHEN MATCHED THEN
UPDATE SET ut.update_column = st.column_2 ;

Related

Transforming an Informix query to Oracle?

I have a query that doesn't work; can you help me with the transformation?
The original Informix query that I want to transform to Oracle.
SELECT DISTINCT table3.no_cev,
table1.literal,
table1.colid,
table2.repid,
table2.valor,
table2.indicador,
'',
'',
table2.origen,
table2.codi,
table2.no_cia,
table2.num_dcca,
table2.no_aprof,
table2.no_compta
FROM table1,
OUTER table2,
table3
WHERE ( table1.colid = table2.colid) and
( table1.grupid = table2.grupid) and
( table3.no_cev = table2.no_cev) and
( ( table1.grupid = 2) AND
( table2.cod_exp = 99609 ) AND
( table2.indicador = 'S' ) ) AND
( table3.num_dcca = 1);
( table3.codest = 76695);
My transformation of the query from Informix to Oracle — but it looks like it doesn't work:
SELECT DISTINCT table3.no_cev,
table1.literal,
table1.colid,
table2.repid,
table2.valor,
table2.indicador,
'',
'',
table2.origen,
table2.codi,
table2.no_cia,
table2.num_dcca,
table2.no_aprof,
table2.no_compta
FROM table1
LEFT OUTER JOIN (table2
RIGHT OUTER JOIN table3
ON table3.no_cev = table2.no_cev)
ON (( table1.colid = table2.colid)
AND ( table1.grupid = table2.grupid))
WHERE ( ( table1.grupid = '2' )
AND ( table2.cod_exp = '99609' )
AND ( table2.indicador = 'S' ) )
AND ( table3.num_dcca = '1')
AND ( table3.codest = '76695');
You have joined the table with ON clause at wrong place in the code.
Corrected your code now as following:
SELECT DISTINCT
TABLE3.NO_CEV,
TABLE1.LITERAL,
TABLE1.COLID,
TABLE2.REPID,
TABLE2.VALOR,
TABLE2.INDICADOR,
'',
'',
TABLE2.ORIGEN,
TABLE2.CODI,
TABLE2.NO_CIA,
TABLE2.NUM_DCCA,
TABLE2.NO_APROF,
TABLE2.NO_COMPTA
FROM
TABLE1
LEFT OUTER JOIN
-- ( -- removed this bracket
TABLE2 ON ( ( TABLE1.COLID = TABLE2.COLID )
AND ( TABLE1.GRUPID = TABLE2.GRUPID ) ) -- added this ON here
RIGHT OUTER JOIN TABLE3 ON TABLE3.NO_CEV = TABLE2.NO_CEV
-- ) -- removed this bracket
WHERE
TABLE1.GRUPID = '2'
AND TABLE2.COD_EXP = '99609'
AND TABLE2.INDICADOR = 'S'
AND TABLE3.NUM_DCCA = '1'
AND TABLE3.CODEST = '76695' ; -- no need of extra brackets
Cheers!!
It makes life unnecessarily difficult for people who would like to help you when you don't include a more or less minimal outline schema for the tables used in your query, and some sample data, and the expected results. Further, you seem to have converted numbers (integers) in the original Informix query into strings in the Oracle query. It is not clear why. Again, the schema would help explain what's going on.
As I noted in the comments, you should omit the two empty/null fields in the select-list; you could also drop a number of the columns from table2 — candidates for being dropped include all the columns not otherwise named in the query, such as repid, valor, origen, codi, no_cia, no_aprof, no_compta. Keep one or two of them; you don't really need more. However, I've preserved all the named columns in the sample data.
Schema and data
Here is some Informix SQL that appears to match the tables and columns in the query shown in the question. In case of doubt, the column was made into an INTEGER column. All the columns are qualified with NOT NULL.
DROP TABLE IF EXISTS table1;
DROP TABLE IF EXISTS table2;
DROP TABLE IF EXISTS table3;
CREATE TABLE table1
(
grupid INTEGER NOT NULL, -- 2
literal VARCHAR(32) NOT NULL,
colid INTEGER NOT NULL
);
CREATE TABLE table2
(
grupid INTEGER NOT NULL,
no_cev INTEGER NOT NULL,
colid INTEGER NOT NULL,
repid INTEGER NOT NULL,
valor INTEGER NOT NULL,
indicador CHAR(1) NOT NULL, -- 'S'
origen INTEGER NOT NULL,
codi INTEGER NOT NULL,
no_cia INTEGER NOT NULL,
num_dcca INTEGER NOT NULL,
no_aprof INTEGER NOT NULL,
no_compta INTEGER NOT NULL,
cod_exp INTEGER NOT NULL -- 99609
);
CREATE TABLE table3
(
no_cev INTEGER NOT NULL,
num_dcca INTEGER NOT NULL, -- 1
codest INTEGER NOT NULL -- 76695
);
LOAD FROM "table1.unl" INSERT INTO table1;
LOAD FROM "table2.unl" INSERT INTO table2;
LOAD FROM "table3.unl" INSERT INTO table3;
The annotations indicate the value specified in the query for that column; they helped guide the construction of the sample data.
Three sample data files in the Informix (pipe-separated values) UNLOAD format are:
table1.unl
2|Literal value 1|100
2|Literal value 2|123
2|Literal value 3|134
2|Literal value 4|145
table2.unl
2|2345|100|222|333|S|444|555|666|777|888|999|99609
2|2346|123|223|333|S|444|555|666|776|888|999|99609
2|2347|134|224|333|S|444|555|666|775|888|999|99609
2|2348|145|225|333|S|444|555|666|774|888|999|99609
1|2345|100|225|333|S|444|555|666|773|888|999|99609
2|2340|123|226|333|S|444|555|666|772|888|999|99609
3|2347|134|227|333|S|444|555|666|771|888|999|99609
2|2350|145|228|333|S|444|555|666|770|888|999|99609
table3.unl
2345|1|76695
2346|1|88776
2347|2|76695
2348|1|76695
Result of query using Informix-style OUTER join
Assuming that the stray early semicolon in the original query should be an AND (that matches what is written in the proposed Oracle query), removing the two empty string result columns, and removing the superfluous level of parentheses, then the original query looks like:
SELECT DISTINCT
table3.no_cev,
table1.literal,
table1.colid,
table2.repid,
table2.valor,
table2.indicador,
table2.origen,
table2.codi,
table2.no_cia,
table2.num_dcca,
table2.no_aprof,
table2.no_compta
FROM table1,
OUTER table2,
table3
WHERE (table1.colid = table2.colid) AND
(table1.grupid = table2.grupid) AND
(table3.no_cev = table2.no_cev) AND
(table1.grupid = 2) AND
(table2.cod_exp = 99609) AND
(table2.indicador = 'S') AND
(table3.num_dcca = 1) AND
(table3.codest = 76695);
On the sample data shown, using Informix 12.10.FC6 running on a MacBook Pro with macOS 10.14.6 Mojave (not that the o/s is likely to be a factor in the results), this produces:
2345|Literal value 1|100|222|333|S|444|555|666|777|888|999
2345|Literal value 2|123|||||||||
2345|Literal value 3|134|||||||||
2345|Literal value 4|145|||||||||
2348|Literal value 1|100|||||||||
2348|Literal value 2|123|||||||||
2348|Literal value 3|134|||||||||
2348|Literal value 4|145|225|333|S|444|555|666|774|888|999
Why, you ask? Good question! The Informix old-style OUTER join is a complex critter, and doesn't necessarily have a simple translation to modern standard SQL (and hence to Oracle, etc). You can find some description of the way it works at Complex Outer Joins.
There are two groups of tables — table1 and table3 are the dominant tables, and table2 is the only OUTER table here. This means that Informix processes table1 and table3 using inner join, and then outer joins the result with table2. Since there is no direct join between table1 and table3, the result is a cartesian product of the two tables — each of the 4 rows in table1 is joined with each of the 4 rows in table3, yielding 16 rows. However, the filter conditions eliminate the rows from table3 where no_cev is 2346 and 2347. All the remaining 8 rows will be preserved, regardless of the results of the outer join operation. Now the rows are outer joined with table2. The rows with (no_cev, colid) of (2345, 100) and (2348, 145) have matching rows in table3 where the data satisfies the conditions in the WHERE clause. The other rows don't have such matching rows so the columns from table2 for those rows are 'all NULL'. As I said, it is weird — contorted. And explaining is hard work!
A first approximation using standard SQL
This query is a moderate approximation to a direct translation of the Informix query:
SELECT DISTINCT
t3.no_cev,
t1.literal,
t1.colid,
t2.repid,
t2.valor,
t2.indicador,
t2.origen,
t2.codi,
t2.no_cia,
t2.num_dcca,
t2.no_aprof,
t2.no_compta
FROM table1 AS t1
INNER JOIN table3 AS t3 ON 1 = 1
LEFT JOIN table2 AS t2 ON t3.no_cev = t2.no_cev
AND t1.colid = t2.colid
AND t1.grupid = t2.grupid
WHERE t1.grupid = 2
AND t2.cod_exp = 99609
AND t2.indicador = 'S'
AND t3.num_dcca = 1
AND t3.codest = 76695;
The output is:
2345|Literal value 1|100|222|333|S|444|555|666|777|888|999
2348|Literal value 4|145|225|333|S|444|555|666|774|888|999
This is missing the rows with 'null values'.
Achieving the same result using standard INNER and OUTER joins
We can collect those rows by looking for rows where one of the columns in table2 is null (because they're either all null or none null — because the columns are qualified NOT NULL):
SELECT DISTINCT
t3.no_cev,
t1.literal,
t1.colid,
t2.repid,
t2.valor,
t2.indicador,
t2.origen,
t2.codi,
t2.no_cia,
t2.num_dcca,
t2.no_aprof,
t2.no_compta
FROM table1 AS t1
INNER JOIN table3 AS t3 ON 1 = 1
LEFT JOIN table2 AS t2 ON t3.no_cev = t2.no_cev
AND t1.colid = t2.colid
AND t1.grupid = t2.grupid
WHERE t1.grupid = 2
AND ((t2.cod_exp = 99609 AND t2.indicador = 'S') OR t2.cod_exp IS NULL)
AND t3.num_dcca = 1
AND t3.codest = 76695;
This yields the output:
2345|Literal value 1|100|222|333|S|444|555|666|777|888|999
2345|Literal value 2|123|||||||||
2345|Literal value 3|134|||||||||
2345|Literal value 4|145|||||||||
2348|Literal value 1|100|||||||||
2348|Literal value 2|123|||||||||
2348|Literal value 3|134|||||||||
2348|Literal value 4|145|225|333|S|444|555|666|774|888|999
This is the same as the original old-style Informix OUTER join query.
Tejash's proposed solution
The SQL in Tejash's answer (revision 1) yields, on the same data:
2345|Literal value 1|100|222|333|S|\ |\ |444|555|666|777|888|999
2348|Literal value 4|145|225|333|S|\ |\ |444|555|666|774|888|999
The backslash-space values correspond to the empty strings — it's Informix's slightly peculiar way of encoding a zero-length non-null string. It's an area where Oracle may well behave slightly differently, but it is tangential to the problem with the query.
Clearly, this is not the same result as the Informix query. It's probably more reasonable; it works out of the box (I simply did copy'n'paste, quoted numbers and all, and it worked with no editing needed).
I don't know about Informix OUTER syntax, so my answer may be wrong. The WHERE clause, however, lacking any relation between table1 and table3 suggests that this is just a cross join of table1 and table3 and then an outer join of table2.
One way to write this:
select t3.no_cev, t1.literal, t1.colid, t2.*
from table1 t1
cross join table3 t3
left join table2 t2 on t2.colid = t1.colid
and t2.grupid = t1.grupid
and t2.no_cev = t3.no_cev
and t2.cod_exp = 2
and t2.indicador = 'S'
where t1.grupid = 2
and t3.num_dcca = 1
and t3.codest = 76695;
Another is:
with t1 as (select * from table1 where grupid = 2)
, t2 as (select * from table1 where grupid = 2 and cod_exp = 2 and indicador = 'S')
, t3 as (select * from table3 where num_dcca = 1 and codest = 76695)
select t3.no_cev, t1.literal, t1.colid, t2.*
from t1
cross join t3
left join t2 on t2.colid = t1.colid and t2.no_cev = t3.no_cev;
Above queries are standard SQL and supported by Oracle as of version 9i I think.

UPDATE query is slow in combination with RETURNING INTO clause

I have update query which returns updated rows ID. Execution time of query is about 90 seconds. When i remove Returning clause, then execution time is 1ms.
Table update_table has 39000 rows.
Query updates 0 rows in this case. When updates 3 rows- execution time is same.
DECLARE
type intTable IS TABLE OF INTEGER;
idCol intTable;
BEGIN
UPDATE
update_table
SET
prop1 = 3, prop2 = NULL
WHERE EXISTS (
SELECT null FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75)
AND f.ID = update_table.ID
)
ReTURNING ID BULK COLLECT INTO idCol;
.
.
.
END;
Why returning clause slows down query?
A good part of using Oracle is knowing what is "supposed" to happen and what isn't.
Adding a RETURNING INTO clause is not "supposed" to make your update run more slowly. When something happens that isn't supposed to happen, check Oracle's support site to see whether it is a known bug.
In your case, it looks like you are encountering:
Bug 27131648 - SUB OPTIMAL PLAN ON UPDATE STATEMENT WITH RETURNING INTO
I am not sure if there is a patch, but there is a simple workaround: use the UNNEST hint. In your case, that would be:
UPDATE
update_table
SET
prop1 = 3, prop2 = NULL
WHERE EXISTS (
SELECT /*+ UNNEST */ null FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75)
AND f.ID = update_table.ID
)
ReTURNING ID BULK COLLECT INTO idCol;
I would recommend splitting it into two parts, first BULK COLLECT and next FORALL collected ID's, both extremely fast and you'll keep being able to further reference updated ID's from idCol.
DECLARE
type intTable IS TABLE OF INTEGER;
idCol intTable;
BEGIN
SELECT f.id
BULK COLLECT INTO idCol
FROM update_table f
INNER JOIN rel_table1 u ON f.ID= u.ID
INNER JOIN rel_table2 VP ON f.another_ID = VP.another_ID
WHERE (u.prop1 = 3)
AND VP.prop1 = 1
AND (u.prop2 = 75);
FORALL indx IN 1..idCol.COUNT
UPDATE update_table
SET prop1 = 3, prop2 = NULL
WHERE id = idCol(indx);
.
.
.
END;
I hope I helped!

How to update and set values based on multiple joins in select statment?

I want to update a column prtnum and revlvl in table invdtl based on value from select statment, here is the code
update invdtl set invdtl.prtnum = usr_prtmst_xref.prtnum,invdtl.revlvl =
usr_prtmst_xref.colnam ([select
invdtl.prtnum,usr_prtmst_xref.prtnum AS
crossref,invdtl.revlvl,aremst.arecod,aremst.fwiflg from invdtl
join usr_prtmst_xref
on usr_prtmst_xref.prtnum = usr_prtmst_xref.prtnum
join invsub
join invlod
join locmst
join aremst
on aremst.arecod = locmst.arecod
and aremst.wh_id = locmst.wh_id
on locmst.stoloc = invlod.stoloc
and locmst.wh_id = invlod.wh_id
on invlod.lodnum = invsub.lodnum
on invsub.subnum = invdtl.subnum where aremst.arecod = 'EXPR' or
aremst.fwiflg = '1' and rownum <2])
I want to copy two values prtnum and revlvl that are returned by select statement but there is some syntax issue.
There are a bunch of errors here:
The syntax for a multi-column update is basically
update blah
set ( col1, col2 ) = ( select x, y
from
...
)
The syntax for multiple joins is basically
from table1 t1
join table2 t2
on t1.col = t2.col
join table3 t2 on
t2.col = ...
Get ride of "[" and "]"
The predicate rownum<2 is probably to get around the message you received, something like "single row sub-query returns more than 1
row" Which this predicate "fixes" that problem, you are just getting
the first random row; probably not what you want. You probably need to
correlate the sub-query with the update
I would fix these basic syntax errors and try again.

Need to Create calculated columns from input date -18 months in Spotfire for the actual columns

I need to create a report where for each columns there will be a previous column which will give the value from date 18 months back from the date that is given as input. Basically I am getting data from few columns into Spotfire for a particular date and want few of the columns to show the output that was 18 months back.
Code Summary-
This is the code i have to implement into Spotfire. This report takes input of a particular single day's date and gets column values for it. Sub-query gets for few of the values and they are sent to the main query. Typically this report has
few common columns and few other columns which have the value from current date and previous dates for the same columns. I can implement all the columns from the main query, but need suggestions to get values for the previous columns calculated in Spotfire or anyway to implement as an Oracle view since we will be getting only one input for the main query and sub-query will be deducting static no of days/month[in this case its 18 months]
Code Sample:-
select st.x1,
cs.x2 ,
sp.x3, sp.x4,
el.x5 current_zxc, --New data 2
el.xxxx current_zvvxx, --New data 3
por.x6 current_zczxc, --New data 4
el.x7 current_sdcvzv, --New data 5
prev_yr_data.prev_1 previous_czzxczxc,
prev_yr_data.prev_2 previous_xcvv,
prev_yr_data.prev_3 previous_zcvzxz,
prev_yr_data.prev_4 PREVIOUS_czxcvzxv,
prev_yr_data.prev_5 previous_vvvxcvxc,
prev_yr_data.prev_6 previous_zxvxvv,
from table1 cs
inner join table2 usr on cs.xxx = usr.zzzzz
inner join table3 emp on emp.xxx = usr.zzzzz
inner join table4 gbst on cs.xxxs = gbst.zzzzz
inner join table5 sp on cs.xxx = sp.zzzzz
inner join table6 st on sp.xxx = st.zzzzz
inner join table7 ol on ol.xxx = cs.zzzzz
inner join table8 el on el.xxx = ol.zzzzz
inner join table9 spt on trim(upper(el.xxxx)) = trim(upper(spt.xxx))
inner join table10 por on
por.xxx = el.xxxx and
por.xxxx = el.xxxx and
por.xxxx = cs.zzzzz
inner join
(select st.x1,
cs.zzzzz case_zzzzz,
cs.x2 prev_4,
sp.zzzzz ,
sp.x3, sp.x4,
spt.zzzzz ,
spt.xxx prev_1, --Old data 1
el.x5 prev_2, --Old data 2
el.x6 prev_3, --Old data 3
por.xxxx prev_5, --Old data 4
el.x7 prev_6 --Old data 5
from table1 cs
inner join table5 sp on cs.xxxx = sp.zzzzz
inner join table6 st on sp.xxxx = st.zzzzz
inner join table7 ol on ol.xxxx = cs.zzzzz
inner join table8 el on el.xxxxx = ol.zzzzz
inner join table9 spt on trim(upper(el.x_part_name)) = trim(upper(spt.x_part_number))
inner join table10 por on
por.xxx = el.xxxx and
por.xxxx = el.xxxx and
por.xxxx = cs.zzzzz
where ol.date_time between add_months(to_date('date_input','mm/dd/yyyy'), -18) and to_date('date_input','mm/dd/yyyy')
) prev_yr_data on
sp.zzzzz = prev_yr_data.zzzzz and
spt.zzzzz = prev_yr_data.zzzzzz
where ol.date_time >= to_date('date_input','mm/dd/yyyy') and ol.date_time < ( to_date('date_input','mm/dd/yyyy') + 1 )
I would suggest adding a transformation when you bring in the data set to calculate your date 18 months in the future (or past whichever you prefer). Then you can do a self join within Spotfire where [Date] = [18MonthsForward] and bring in the same value column(s) that you desire.
The steps to achieve this look like this when viewed under source information:
2. Select Insert > Transformations...
Add the transformations:
a. Calculate new column
Name: 18MonthsForward
Expression: DateAdd("month",18,[Date])
3. Select Insert > Columns...
Select 'From Current Analysis'
Source location: Data Table
Automatic update.
Match columns:
Date => 18MonthsForward
Ignore columns:
Date
Select join method: LeftOuterJoin
Treat empty values as equal: False
As a step by step instruction you would:
(1) Select Insert >> Transformation
(2) Select Calculation new column from the drop down
(3) Type the expression: DateAdd("month",18,[Date]) and name this whatever you prefer
(4) Select Insert >> Columns
(5) Select from current data and select your current data table
(6) Join on Date and your newly calculated 18MonthsForward date column
(7) Select your value column as the new column
Attached in an image of the data resultant data table. 18 months prior value

Does Oracle implicit conversion depend on joined tables or views

I've faced with a weird problem now. The query itself is huge so I'm not going to post it here (I could post however in case someone needs to see). Now I have a table ,TABLE1, with a CHAR(1) column, COL1. This table column is queried as part of my query. When I filter the recordset for this column I say:
WHERE TAB1.COL1=1
This way the query runs and returns a very big resultset. I've recently updated one of the subqueries to speed up the query. But after this when I write WHERE TAB1.COL1=1 it does not return anything, but if I change it to WHERE TAB1.COL1='1' it gives me the records I need. Notice the WHERE clause with quotes and w/o them. So to make it more clear, before updating one of the sub-queries I did not have to put quotes to check against COL1 value, but after updating I have to. What feature of Oracle is it that I'm not aware of?
EDIT: I'm posting the tw versions of the query in case someone might find it useful
Version 1:
SELECT p.ssn,
pss.pin,
pd.doc_number,
p.surname,
p.name,
p.patronymic,
to_number(p.sex, '9') as sex,
citiz_c.short_name citizenship,
p.birth_place,
p.birth_day as birth_date,
coun_c.short_name as country,
di.name as leg_city,
trim( pa.settlement
|| ' '
|| pa.street) AS leg_street,
pd.issue_date,
pd.issuing_body,
irs.irn,
irs.tpn,
irs.reg_office,
to_number(irs.insurer_type, '9') as insurer_type,
TO_CHAR(sa.REG_CODE)
||CONVERT_INT_TO_DOUBLE_LETTER(TO_NUMBER(SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 2, 3)))
||SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 5, 4) CONVERTED_SSN_DOSSIER_NR,
fa.snr
FROM
(SELECT pss_t.pin,
pss_t.ssn
FROM EHDIS_INSURANCE.pin_ssn_status pss_t
WHERE pss_t.difference_status < 5
) pss
INNER JOIN SSPF_CENTRE.file_archive fa
ON fa.ssn = pss.ssn
INNER JOIN SSPF_CENTRE.persons p
ON p.ssn = fa.ssn
INNER JOIN
(SELECT pd_2.ssn,
pd_2.type,
pd_2.series,
pd_2.doc_number,
pd_2.issue_date,
pd_2.issuing_body
FROM
--The changed subquery starts here
(SELECT ssn,
MIN(type) AS type
FROM SSPF_CENTRE.person_documents
GROUP BY ssn
) pd_1
INNER JOIN SSPF_CENTRE.person_documents pd_2
ON pd_2.type = pd_1.type
AND pd_2.ssn = pd_1.ssn
) pd
--The changed subquery ends here
ON pd.ssn = p.ssn
INNER JOIN SSPF_CENTRE.ssn_archive sa
ON p.ssn = sa.ssn
INNER JOIN SSPF_CENTRE.person_addresses pa
ON p.ssn = pa.ssn
INNER JOIN
(SELECT i_t.irn,
irs_t.ssn,
i_t.tpn,
i_t.reg_office,
(
CASE i_t.insurer_type
WHEN '4'
THEN '1'
ELSE i_t.insurer_type
END) AS insurer_type
FROM sspf_centre.irn_registered_ssn irs_t
INNER JOIN SSPF_CENTRE.insurers i_t
ON i_t.irn = irs_t.new_irn
OR i_t.old_irn = irs_t.old_irn
WHERE irs_t.is_registration IS NOT NULL
AND i_t.is_real IS NOT NULL
) irs ON irs.ssn = p.ssn
LEFT OUTER JOIN SSPF_CENTRE.districts di
ON di.code = pa.city
LEFT OUTER JOIN SSPF_CENTRE.countries citiz_c
ON p.citizenship = citiz_c.numeric_code
LEFT OUTER JOIN SSPF_CENTRE.countries coun_c
ON pa.country_code = coun_c.numeric_code
WHERE pa.address_flag = '1'--Here's the column value with quotes
AND fa.form_type = 'Q3';
And Version 2:
SELECT p.ssn,
pss.pin,
pd.doc_number,
p.surname,
p.name,
p.patronymic,
to_number(p.sex, '9') as sex,
citiz_c.short_name citizenship,
p.birth_place,
p.birth_day as birth_date,
coun_c.short_name as country,
di.name as leg_city,
trim( pa.settlement
|| ' '
|| pa.street) AS leg_street,
pd.issue_date,
pd.issuing_body,
irs.irn,
irs.tpn,
irs.reg_office,
to_number(irs.insurer_type, '9') as insurer_type,
TO_CHAR(sa.REG_CODE)
||CONVERT_INT_TO_DOUBLE_LETTER(TO_NUMBER(SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 2, 3)))
||SUBSTR(TO_CHAR(sa.DOSSIER_NR, '0999999'), 5, 4) CONVERTED_SSN_DOSSIER_NR,
fa.snr
FROM
(SELECT pss_t.pin,
pss_t.ssn
FROM EHDIS_INSURANCE.pin_ssn_status pss_t
WHERE pss_t.difference_status < 5
) pss
INNER JOIN SSPF_CENTRE.file_archive fa
ON fa.ssn = pss.ssn
INNER JOIN SSPF_CENTRE.persons p
ON p.ssn = fa.ssn
INNER JOIN
--The changed subquery starts here
(SELECT ssn,
type,
series,
doc_number,
issue_date,
issuing_body
FROM
(SELECT ssn,
type,
series,
doc_number,
issue_date,
issuing_body,
ROW_NUMBER() OVER (partition BY ssn order by type) rn
FROM SSPF_CENTRE.person_documents
)
WHERE rn = 1
) pd --
--The changed subquery ends here
ON pd.ssn = p.ssn
INNER JOIN SSPF_CENTRE.ssn_archive sa
ON p.ssn = sa.ssn
INNER JOIN SSPF_CENTRE.person_addresses pa
ON p.ssn = pa.ssn
INNER JOIN
(SELECT i_t.irn,
irs_t.ssn,
i_t.tpn,
i_t.reg_office,
(
CASE i_t.insurer_type
WHEN '4'
THEN '1'
ELSE i_t.insurer_type
END) AS insurer_type
FROM sspf_centre.irn_registered_ssn irs_t
INNER JOIN SSPF_CENTRE.insurers i_t
ON i_t.irn = irs_t.new_irn
OR i_t.old_irn = irs_t.old_irn
WHERE irs_t.is_registration IS NOT NULL
AND i_t.is_real IS NOT NULL
) irs ON irs.ssn = p.ssn
LEFT OUTER JOIN SSPF_CENTRE.districts di
ON di.code = pa.city
LEFT OUTER JOIN SSPF_CENTRE.countries citiz_c
ON p.citizenship = citiz_c.numeric_code
LEFT OUTER JOIN SSPF_CENTRE.countries coun_c
ON pa.country_code = coun_c.numeric_code
WHERE pa.address_flag = 1--Here's the column value without quotes
AND fa.form_type = 'Q3';
I've put separating comments for the changed subqueries and the WHERE clause in both queries. Both versions of the subqueries return the same result, one of them is just slower, which is why I decided to update it.
With the most simplistic example I can't reproduce your problem on 11.2.0.3.0 or 11.2.0.1.0.
SQL> create table tmp_test ( a char(1) );
Table created.
SQL> insert into tmp_test values ('1');
1 row created.
SQL> select *
2 from tmp_test
3 where a = 1;
A
-
1
If I then insert a non-numeric value into the table I can confirm Chris' comment "that Oracle will rewrite tab1.col1 = 1 to to_number(tab1.col1) = 1", which implies that you only have numeric characters in the column.
SQL> insert into tmp_test values ('a');
1 row created.
SQL> select *
2 from tmp_test
3 where a = 1;
ERROR:
ORA-01722: invalid number
no rows selected
If you're interested in tracking this down you should gradually reduce the complexity of the query until you have found a minimal, reproducible, example. Oracle can pre-compute a conversion to be used in a JOIN, which as your query is complex seems like a possible explanation of what's happening.
Oracle explicitly recommends against using implicit conversion so it's wiser not to use it at all; as you're finding out. For a start there's no guarantees that your indexes will be used correctly.
Oracle recommends that you specify explicit conversions, rather than rely on implicit or automatic conversions, for these reasons:
SQL statements are easier to understand when you use explicit data type conversion functions.
Implicit data type conversion can have a negative impact on performance, especially if the data type of a column value is converted to that of a constant rather than the other way around.
Implicit conversion depends on the context in which it occurs and may not work the same way in every case. For example, implicit conversion from a datetime value to a VARCHAR2 value may return an unexpected year depending on the value of the NLS_DATE_FORMAT
parameter.
Algorithms for implicit conversion are subject to change across software releases and among Oracle products. Behavior of explicit conversions is more predictable.
If you do only have numeric characters in the column I would highly recommend changing this to a NUMBER(1) column and I would always recommend explicit conversion to avoid a lot of pain in the longer run.
It's hard to tell without the actual query. What I would expect is that TAB1.COL1 is in some way different before and after the refactoring.
Candidates differences are Number vs. CHAR(1) vs. CHAR(x>1) vs VARCHAR2
It is easy to introduce differences like this with subqueries where you join two tables which have different types in the join column and you return different columns in your subquery.
To hunt that issue down you might want to check the exact datatypes of your query. Not sure how to do that right now .. but an idea would be to put it in a view and use sqlplus desc on it.

Resources