Oracle merge sql no rows inserted - sql

I'm using merge sql to find if the rows exist and then update or insert the data.
My problem is that if rows exist then update block works. But if not, there is no data inserted.
Is it because I use fixed data instead of datasource from q2?
Database version : oracle 9i
SQL:
merge into TBL_QTY q1
using (
select PROD_NO, PART_BATCH_NO, COND1_TYPE, BEG_DT, END_DT
from TBL_QTY
WHERE PROD_NO = 'A111'
AND PART_BATCH_NO = 'BAT0000171624'
AND COND1_TYPE = '172'
AND COND2_TYPE = 'XXX'
AND COND3_TYPE = 'XXX'
AND BEG_DT = '20201225'
AND END_DT = '20201225' ) q2
on 
( q1.PROD_NO = q2.PROD_NO
and q1.PART_BATCH_NO = q2.PART_BATCH_NO
and q1.COND1_TYPE = q2.COND1_TYPE
and q1.BEG_DT = q2.BEG_DT
and q1.END_DT= q2.END_DT)
WHEN MATCHED THEN
update SET q1.OBTAIN_QTY = 15 ,
q1.SALE_QTY = 15
WHEN NOT MATCHED THEN
INSERT (PROD_NO, PART_BATCH_NO, COND1_TYPE, BEG_DT
,END_DT , OBTAIN_QTY , SALE_QTY )
VALUES('A111' ,'BAT0000171624' ,'172','20201225'
,'20201225', 17, 17)

The USING subquery selects values from the same table you want to insert into. This means it returns no rows when the values in your WHERE clause don't exist in that table.
Why doesn't that trigger the WHEN NOT MATCHED branch as you expect? Because you have an empty set: there is nothing for Oracle to match against, it's not even evaluated.
The way MERGE works is, we get a set of rows from the USING query and compare them with the contents of the target: the match is evaluated and the appropriate branch is triggered for each row in the USING set.
To make your code work you must have rows in the USING subsubquery. The normal way to do that is to select literals from DUAL. (Caution: the following code is free-styled and untested, so may contain syntax errors which are left as an exercise for the reader).
merge into TBL_QTY q1
using (
select 'A111' as prod_no
,'BAT0000171624' as part_batch_no
,'172' as cond1_type
,'20201225' as beg_dt
,'20201225' as end_dt
, 17 as obtain_qty
, 17 as sale_qty ) q2
on 
( q1.PROD_NO = q2.PROD_NO
and q1.PART_BATCH_NO = q2.PART_BATCH_NO
and q1.COND1_TYPE = q2.COND1_TYPE
and q1.BEG_DT = q2.BEG_DT
and q1.END_DT= q2.END_DT)
WHEN MATCHED THEN
update SET q1.OBTAIN_QTY = 15
,q1.SALE_QTY = 15
WHEN NOT MATCHED THEN
INSERT (PROD_NO, PART_BATCH_NO, COND1_TYPE, BEG_DT
,END_DT , OBTAIN_QTY , SALE_QTY )
VALUES (q2.PROD_NO, q2.PART_BATCH_NO, q2.COND1_TYPE, q2.BEG_DT
,q2.END_DT , q2.OBTAIN_QTY , q2.SALE_QTY )
If you need to do this for multiple rows, you can use UNION ALL to weld together selects on DUAL in the USING subquery.

Related

Complex INSERT INTO SELECT statement in SQL

I have two tables in SQL. I need to add rows from one table to another. The table to which I add rows looks like:
timestamp, deviceID, value
2020-10-04, 1, 0
2020-10-04, 2, 0
2020-10-07, 1, 1
2020-10-08, 2, 1
But I have to add a row to this table if a state for a particular deviceID was changed in comparison to the last timestamp.
For example this record "2020-10-09, 2, 1" won't be added because the value wasn't changed for deviceID = 2 and last timestamp = "2020-10-08". In the same time record "2020-10-09, 1, 0" will be added, because the value for deviceID = 1 was changed to 0.
I have a problem with writing a query for this logic. I have written something like this:
insert into output
select *
from values
where value != (
select value
from output
where timestamp = (select max(timestamp) from output) and output.deviceID = values.deviceID)
Of course it doesn't work because of the last part of the query "and output.deviceID = values.deviceID".
Actually the problem is that I don't know how to take the value from "output" table where deviceID is the same as in the row that I try to insert.
I would use order by and something to limit to one row:
insert into output
select *
from values
where value <> (select o2.value
from output o2
where o2.deviceId = v.deviceId
order by o2.timestamp desc
fetch first 1 row only
);
The above is standard SQL. Specific databases may have other ways to express this, such as limit or top (1).

Oracle SQL Update statement with value generated in subquery

I am trying to write an update statement to insert a value that's calculated in a subquery, and having limited success.
The statement I've tried so far is:
update intuit.men_doc doc1
set doc1.doc_udf5 = (select
substr(doc.doc_dtyc, instr(doc.doc_dtyc, 'GAPP-', 2)+5 )||'_'||row_number() over(partition by
doc.doc_dtyc order by doc.doc_cret) docDeleteId
from
intuit.men_doc doc
where
doc.doc_dtyc != 'DM-GAPP-SFUL'
and doc.doc_dtyc like 'DM-GAPP%'
and doc.doc_cred >= '01/Oct/2017' and doc.doc_cred < '01/Oct/2018'
and doc1.doc_code = doc.doc_code
)
Which gives mes the following error message
ERROR: Error 1427 was encountered whilst running the SQL command. (-3)
Error -3 running SQL : ORA-01427: single-row subquery returns more than one row
I don't have much experience with UPDATE statements, so any advice on how I can rewrite this so that I can update a few thousand records at once would be appreciated.
EDIT: Adding example data
Example data:
MEN_DOC
DOC_CODE DOC_DTYC DOC_UDF5 DOC_CRED
123456A CV 08/Nov/2017
456789B CV 11/Jan/2018
789123C CV 15/Feb/2018
123987B TRAN 01/Dec/2017
How I want the data to look once the script is run
MEN_DOC
DOC_CODE DOC_DTYC DOC_UDF5 DOC_CRED
123456A CV CV_1 08/Nov/2017
456789B CV CV_2 11/Jan/2018
789123C CV CV_3 15/Feb/2018
123987B TRAN TRAN_1 01/Dec/2017
Thanks
You are using row_number(), which suggests that you expect the subquery to return more than one row. The inequality on doc_code supports this interpretation.
Just change the row_number() to count(*), so you have an aggregation which will always return one row and get the sequential count you want:
update intuit.men_doc doc1
set doc1.doc_udf5 = (select substr(doc.doc_dtyc, instr(doc.doc_dtyc, 'GAPP-', 2)+5 ) ||'_'|| count(*) docDeleteId
from intuit.men_doc doc
where doc.doc_dtyc <> 'DM-GAPP-SFUL' and
doc.doc_dtyc like 'DM-GAPP%' and
doc.doc_cred >= date '2017-10-01' and
doc.doc_cred < date '2018-10-01' and
doc1.doc_code = doc.doc_code
);
You can use your select as source table in merge, like here:
merge into men_doc tgt
using (select doc_code,
doc_dtyc||'_'||row_number() over (partition by doc_dtyc order by doc_cred) as calc
from men_doc) src
on (tgt.doc_code = src.doc_code)
when matched then update set tgt.doc_udf5 = src.calc;
dbfiddle
I assumed that doc_code is unique.

Oracle SQL UPDATE SELECT with JOIN got error

Here is my SQL Query
UPDATE
(SELECT
*
FROM
web_fe_ipo_ipo_application apps
INNER JOIN web_fe_ipo_ipo_entry ipo ON apps.ipo_link_id = ipo.ipo_ref_number
WHERE
( ( apps.status = 0
OR ( apps.status = 2
AND apps.sub_status = 0
AND ipo.enable_omnibus_account = 1 ) )
AND ( apps.applied_qty BETWEEN 1 AND 10 )
AND apps.ipo_link_id = '984'
AND apps.latest = 1
AND ipo.latest = 1
AND apps.tranche_name = 'Public-1' )
ORDER BY
ipo_random(50)
FETCH FIRST 5 ROWS ONLY) selection
SET selection.initial_allot_qty = 5;
but it was not working well
I need to select random data from table then update after execute this query I got bellow error
Error at Command Line : 20 Column : 5
Error report -
SQL Error: ORA-01733: virtual column not allowed here
01733. 00000 - "virtual column not allowed here"
*Cause:
*Action:
Use a MERGE statement.
Here is an example:
MERGE INTO workers e
USING data_works h -- You can use the SELECT statement as well.
ON (e.f_id= h.f_id)
WHEN MATCHED THEN
UPDATE SET e.address= h.address
WHEN NOT MATCHED THEN
INSERT (f_id, address)
VALUES (h.f_id, h.address);
Here is your code. Hope it will work.
MERGE INTO web_fe_ipo_ipo_application apps
USING (SELECT * FROM web_fe_ipo_ipo_entry ipo ORDER BY
ipo_random)
ON(apps.ipo_link_id = ipo.ipo_ref_number)
WHEN MATCHED THEN
UPDATE SET apps.initial_allot_qty = 5
WHERE apps.status = 0
OR(apps.status = 2
AND apps.sub_status = 0
AND ipo.enable_omnibus_account = 1)
AND apps.applied_qty BETWEEN 1 AND 10
AND apps.ipo_link_id = '984'
AND apps.latest = 1
AND ipo.latest = 1
AND apps.tranche_name = 'Public-1'
AND rownum <=5;
Use the MERGE statement to select rows from one or more sources for INSERT or UPDATE operations in a table or view.
You can specify conditions to determine whether the target table or view is updated.
Merge operation is a convenient way to combine multiple operations. It allows you to avoid multiple INSERT, UPDATE, and DELETE DML statements as well.

sql query is not working properly

i am trying to get non matching records from two table by comparing some columns which are common in both tables.i am using sql query to get the result. my first table is snd_marketvisits this table have properties like id ,pjpCode , section code, popCode .pop_name and landmark similary my 2nd table have pjpcode , section code, popcode popname are common and there are some other fields.i want to get the names of the pop which are not in second table but present in snd_marketvisit table by comparing popcode, sectioncode and pjpcode in both tables.
SELECT *
FROM snd_marketvisits sm
LEFT JOIN snd_marketvisit_pops sp ON
sm.distributorCode = sp.distributor AND
sm.pjpCode = sp.pjp AND
sm.sectionCode = sp.sectionCode AND
sm.popCode = sp.popCode
WHERE
sm.sectionCode = '00016' AND
sm.pjpCode = '0001' AND
sm.distributorCode = '00190A'
It depends on the database, as far as I know, but if you ask for NULL inside your yoined fields you should get only the rows without a match.
SELECT *
FROM snd_marketvisits sm
LEFT JOIN snd_marketvisit_pops sp ON
sm.distributorCode = sp.distributor AND
sm.pjpCode = sp.pjp AND
sm.sectionCode = sp.sectionCode AND
sm.popCode = sp.popCode
WHERE
sm.sectionCode = '00016' AND
sm.pjpCode = '0001' AND
sm.distributorCode = '00190A'
AND sp.distributor IS NULL

Get Only One Row For Each ID With the Highest Value

I have a query
SELECT
*
FROM
mgr.MF_AGREEMENT_LGR TABLE1
INNER JOIN
(SELECT
MAX(VALUE_DATE) AS VALUE_DATE,
REGISTRATION_NO AS REGISTRATION_NO
FROM
mgr.MF_AGREEMENT_LGR
GROUP BY
REGISTRATION_NO) AS TABLE2 ON TABLE1.REGISTRATION_NO = TABLE2.REGISTRATION_NO
WHERE
TABLE1.VALUE_DATE = TABLE2.VALUE_DATE
AND TABLE1.TRX_CODE = 'LCLR'
ORDER BY
TABLE1.REGISTRATION_NO
This returns the rows with the latest date for each REGISTRATION_CODE. Some have like three or more results for each REGISTRATION_CODE because it has more than one transaction on the same date.
Also, each row has its DOC_NO field.
My question is, how am I going to get only one row from each REGISTRATION_CODE with the highest DOC_NO.
By the way, DOC_NO is a varchar.
Example value for this field is: Amort 1, Amort 12, Amort 5
If those examples are in one REGISTRATION_CODE, I only need the row with the highest amort which is Amort 12.
I am using a SQL Server 2000.
SQL Server 2000 has not been supported in years. You really should upgrade to supported software.
You can get what you want with not exists:
SELECT al.*
FROM mgr.MF_AGREEMENT_LGR al
WHERE NOT EXISTS (SELECT 1
FROM mgr.MF_AGREEMENT_LGR al2
WHERE al2.registration_no = al.registration_no and
(al2.date > al2.date or
al2.date = al.date and al2.DOC_NO > al.DOC_NO
)
) AND
al.TRX_CODE = 'LCLR';
You probably want the condition on 'LCLR' in the subquery as well. However, that is not in your original query, so I'm leaving it out.