update set value based on another value of another column and/or same column in another row:-ORA 1427 - oracle

I'm trying to set a column to reset to zero or increment by +1 based on a pass or fail in another column, and/or the value of that same column in the previous weeks row.
There are two other variable columns which must match those in the previous weeks row.
Table is something like:
WEEK | ID1 | ID2 | FLAG | INCREMENT_COUNT |
--------------------------------------------------------
--------------------------------------------------------
I have been trying to get this part of the procedure to work, and the best I've got so far is:
ID_IN and ID_IN3 are passed in the procedure call
OLD_DATE and NEW_DATE are set as the previous week and current week
----------------------------------------------------------------------
update table1
set table1.INCREMENT_COUNT = CASE
WHEN table1.FLAG is null then null
WHEN table1.FLAG = 1 then 0
WHEN table1.FLAG = 0 then (NVL(INCREMENT_COUNT,0)+ 1)
END
where (select INCREMENT_COUNT
from table1
where WEEK=NEW_DATE
and ID1=ID_IN
and exists (select (1)
from table2
where table1.ID2=table2.ID2
and table2.ID3=ID_IN3))
=
(select INCREMENT_COUNT
from table1
where WEEK=OLD_DATE
and ID1=ID_IN
and exists (select (1)
from table2
where table1.ID2=table2.ID2
and table2.ID3=ID_IN3));
When this procedure is called I get the error
ORA-01427: single-row subquery returns more than one row
Additionally, in MySQL I could do it something like this and get it working...
update table1 as t01
left join(select ID3, ID2, INCREMENT_COUNT as prev_count from table1 as t10 inner join table2 as t2 on t10.ID2=t2.ID2 where ID1=ID_IN and ID3=ID_IN3 and t10.WEEK=OLD_DATE) as prev_date on t01.WEEK=NEW_DATE and prev_date.ID2=t01.ID2 and t01.ID1=ID_IN
set t01.INCREMENT_COUNT = if(t1.FLAG is null, null, if(t1.FLAG,0, IFNULL(prev_date.prev_count,0)+1))
where t01.ID1=ID_IN
and t1.WEEK=NEW_DATE
and prev_date.ID3=ID_IN3;

Similarl to your mySQL example, you can do something like this in oracle. This may not work for you depending on your data model. I've put together a crude basic version based on your information, but you've not provided enough information about your data model and your tables/aliases/column names are poor for readability...
(more on update with a subquery here -> https://docs.oracle.com/database/121/SQLRF/statements_10008.htm#i2067871)
update
(select t01.increment_count, t01.flag, prev_date.prev_count
from table1 t01
left join(select ID3, ID2, INCREMENT_COUNT as prev_count
from table1 t10
inner join table2 t2 on t10.ID2=t2.ID2
where ID1=ID_IN
and ID3=ID_IN3
and t10.WEEK=OLD_DATE) prev_date on t01.WEEK=NEW_DATE and prev_date.ID2=t01.ID2 and t01.ID1=ID_IN
where t01.ID1=ID_IN
and t1.WEEK=NEW_DATE
and prev_date.ID3=ID_IN3)
set INCREMENT_COUNT = if(FLAG is null, null, if(FLAG,0, IFNULL(prev_count,0)+1));

One of the queries in where condition returns more than 1 record

This seems to have done the job.
Thanks for the help, it got me thinking in a different way.
UPDATE TABLE1 T01
SET INCREMENT_COUNT = CASE
WHEN T01.FLAG IS NULL THEN NULL
WHEN T01.FLAG = 1 THEN 0
WHEN T01.FLAG = 0 THEN (NVL((SELECT INCREMENT_COUNT
FROM TABLE1 T10
WHERE T10.WEEK=OLD_DATE
AND T01.WEEK=NEW_DATE
AND T01.ID2=T10.ID2
AND ID1=ID_IN),0)+ 1)
END
WHERE EXISTS (SELECT (1)
FROM TABLE2
WHERE TABLE1.ID2=TABLE2.ID2
AND TABLE2.ID3=ID_IN3);

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.

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

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 ;

INSERT ALL statement incredibly slow, even when no records to insert

This is on 11g. I have an INSERT ALL statement which uses a SELECT to build up the values to insert. The select has some subqueries that check that the record doesn't already exist. The problem is that the insert is taking over 30 minutes, even when there are zero rows to insert. The select statement on its own runs instantly, so the problem seems to be when it is used in conjunction with the INSERT ALL. I rewrote the statement to use MERGE but it was just as bad.
The table has no triggers. There is a primary key index, and a unique constraint on two of the columns, but nothing else that looks like it might be causing an issue. It currently has about 15000 rows, so definitely not big.
Has anyone a suggestion for what might be causing this, or how to go about debugging it?
Here's the INSERT ALL statement.
insert all
into template_letter_merge_fields (merge_field_id, letter_type_id,table_name,field_name,pretty_name, tcl_proc)
values (template_let_mrg_fld_sequence.nextval,letter_type_id,table_name,field_name, pretty_name, tcl_proc)
select lt.letter_type_id,
i.object_type as table_name,
i.interface_key as field_name,
i.pretty_name as pretty_name,
case
when w.widget = 'dynamic_select' then
'dbi::'||i.interface_key||'::get_name'
when w.widget = 'category_tree' and
i.interface_key not like '%_name' and
i.interface_key not like '%_desc' then
'dbi::'||i.interface_key||'::get_name'
else
'dbi::'||i.interface_key||'::get_value'
end as tcl_proc
from template_letter_types lt,
dbi_interfaces i
left outer join acs_attributes aa on (aa.object_type||'_'||aa.attribute_name = i.interface_key
and decode(aa.object_type,'person','party','aims_organisation','party',aa.object_type) = i.object_type)
left outer join flexbase_attributes fa on fa.acs_attribute_id = aa.attribute_id
left outer join flexbase_widgets w on w.widget_name = fa.widget_name
where i.object_type IN (select linked_object_type
from template_letter_object_map lom
where lom.interface_object_type = lt.interface_object_type
union select lt.interface_object_type from dual
union select 'template_letter' from dual)
and lt.interface_object_type = lt.interface_object_type
and not exists (select 1
from template_letter_merge_fields m
where m.sql_code is null
and m.field_name = i.interface_key
and m.letter_type_id = lt.letter_type_id)
and not exists (select 1
from template_letter_merge_fields m2
where m2.pretty_name = i.pretty_name
and m2.letter_type_id = lt.letter_type_id)

How to update table1 field by using other table and function

I have two table and one function,
Table1 contains shop_code,batch_id,registry_id
shop_code| batch_id|registry_id
123 | 100 |12
124 | 100 |13
125 | 100 |12
Table2 contains shop_code,shop_name
shop_code| shop_name
123 | need to populate
124 | need to populate
125 | need to populate
Function1 take parameter registry_id from table1 and returns shop_name
Table2 shop_name is empty I want to populate against the shop_code.
I have tried my best but all effort is gone in vain.
It will be great if someone can help I am using Oracle.
I tried below code but giving error on from keyword
update TABLE2 set T2.SHOP_NAME = T.SHOP_NAME
from(
select GET_shop_name(t1.registry_id) as shop_name ,
t1.shop_code shop_code
from TABLE1 T1
) t where t.shop_code = t1.shop_code;
I am not entirely 100% sure if I got your question right, but I believe you want something like
update
table2 u
set
shop_name = (
select
get_shop_name(t1.batch_id)
from
table1 t1
where
t1.chop_code = u.shop_code
);
can you try this approach try to put inner query to get shop name value; I have not tested it but I think approach will work for you.
update TABLE2 T2
set T2.SHOP_NAME =
(select GET_shop_name(t1.batch_id, t1.shop_code) from table1 t1 wehre t1.shop_code = t2.shop_code)
where T2.shop_name is null
You want the MERGE statement.
Something like this might work:
MERGE INTO TABLE2 t2
USING (
SELECT GET_shop_name(t1.batch_id) AS shop_name ,
t1.shop_code shop_code
FROM TABLE1 T1 ) t1
ON (t2.shop_code = t1.shop_code)
WHEN MATCHED THEN
UPDATE SET t2.shop_name = t1.shop_name
;
You'll have to excuse if the exact code above doesn't work I don't have SQL Dev where I am right now for syntax details. :)

Copy records from one table to another with pl-sql

I want to copy records from one table to another.
The only records from table 1 that will be copied to table 2 are the ones that still dont exist in table 2.
If duplicate records exists in Table 1 then only be copied to table 2 the record with the larger size name.
I could already implement a query that almost does what I want.
The problem I have is when there are names with the same maximum size of characters.
In these cases, my query returns more than one record and I just want to insert one new record in table 2.
Does anyone know how I can fix this?
Here is my code:
For x in (Select distinct xdd.id_t, xdd.name_t
From table1 xdd
Where xdd.id_t not in (Select distinct det.id_t2
From table2 det)
And LENGTH(xdd.name_t) in (Select Max(LENGTH(xdd2.name_t))
From table1 xdd2
Where xdd2.id_t = xdd.id_t)
) Loop
Insert into id_t2 (id_t2, name_t2)
Values (x.id_t, x.name_t);
End loop;
Can you give me an example to solve this?
Sure. If I understood requirements correctly, then the merge statement will look similar to this one:
We use row_number() analytic function to choose a duplicate record with longer name_t
merge into table_two t2
using(
select id_t
, name_t
from (select id_t
, name_t
, row_number() over(partition by id_t
order by length(name_t) desc) as rn
from table_one) q
where q.rn = 1
) t1
on (t2.id_t = t1.id_t)
when not matched then
insert(id_t, name_t)
values(t1.id_t, t1.name_t)
SQLFiddle demo
This is a merge statement that should "upsert" data from table 1 into table 2. Matching keys should update only when the name field in table1 is greater than that of table 2. And inserts should occur when keys from table one are not matched to table 2.
MERGE INTO table2 D
USING (SELECT table1.id_t, table1.name_t FROM table1) S
ON (D.id_t2 = S.id_t)
WHEN MATCHED THEN UPDATE SET D.name_t2 = S.name_t
WHERE (LENGTH(S.name_t) > LENGTH(D.name_t2))
WHEN NOT MATCHED THEN INSERT (D.id_t, D.name_t)
VALUES (S.id_t2, S.name_t2);

Resources