Write a single query that Delete the records whose grades are more than 150. Update grades and add 25 to each as internals if records exist. Insert the records if record does not exists with grade as 25.
Merge into employeegrades empg
using (select id, name from temp_emp) e
on (empg.id= e.id)
when matched then
update set empg.grades = empg.grades + 25
delete where empg.grades > 150
when not matched then insert (id,grades) values (e.id, 25);
This is working fine but when I modify the statement as below, it is not working:
merge into employeegrades eg
using (select id, name from temp_emp) emp
on (eg.id = emp.id)
when matched and eg.grades > 150 then
delete
when matched
update set eg.grades = eg.grades+25
when not matched then
insert (id,grades)
values (emp.id, 25);
what is wrong with the above query? Can't we specify two matched conditions in merge?
"Can't we specify two matched conditions in merge?"
The syntax rules in the Oracle documentation specify two clauses:
the merge_update_clause which executes updates and/or deletes against existing rows
the merge_insert_clause which executes inserts of new rows
That is, the clauses are defined in terms of the actions they implement, not whether they are MATCHED or NOT MATCHED. Because it wouldn't make sense to insert when MATCHED or update when NOT MATCHED.
So, the WHEN MATCHED THEN keyword phrase can only appear once in a MERGE statement because the syntax allows only one merge_update_clause.
Related
I have written a query where I want to just update some of the LINK_ID. But it is updating all the rows in that table.
Here is my query
UPDATE APP_FIBERINV.TBL_FIBER_INV_CMPAPPROVED_INFO
SET NE_LENGTH =
(select MAINT_ZONE_NE_SPAN_LENGTH from APP_FIBERINV.TBL_FIBER_INV_JOBS WHERE LINK_ID IN ('MORV_1020','ANND_1017','BBSR_1047','DLHI_5417','MYSR_0104'));
I still doubt that the update statement you have posted updates all rows in the table. It must throw an error
ORA-01427: single-row subquery returns more than one row
instead, because your subquery returns five rows where it must be one, as you must find one value for each row you want to update.
This means your subquery is wrong. It selects five rows, where it must select one. You don't want to find the five values for 'MORV_1020', 'ANND_1017', but the one value for the link ID of the row you are updating.
You also want to update certain rows (those with the five link IDs), so you must add a WHERE clause at the end of your update statement.
UPDATE app_fiberinv.tbl_fiber_inv_cmpapproved_info i
SET ne_length =
(
SELECT j.maint_zone_ne_span_length
FROM app_fiberinv.tbl_fiber_inv_jobs j
WHERE j.link_id = i.span_link_id
)
WHERE span_link_id IN ('MORV_1020', 'ANND_1017', 'BBSR_1047', 'DLHI_5417', 'MYSR_0104');
Assuming both tables share the LINK_ID as primary and foreign key, you could just use a MERGE:
MERGE INTO APP_FIBERINV.TBL_FIBER_INV_CMPAPPROVED_INFO APPR_NFO
USING (
SELECT LINK_ID, MAINT_ZONE_NE_SPAN_LENGTH
FROM APP_FIBERINV.TBL_FIBER_INV_JOBS
WHERE LINK_ID IN ('MORV_1020','ANND_1017','BBSR_1047','DLHI_5417','MYSR_0104')
) INV_JOBS
ON ( APPR_NFO.SPAN_LINK_ID = INV_JOBS.LINK_ID)
WHEN MATCHED THEN UPDATE SET APPR_NFO.NE_LENGTH = INV_JOBS.MAINT_ZONE_NE_SPAN_LENGTH;
Hi I am using Oracle 12c and I have executed the below query which returns 0 rows merged. Please advise if I am doing something wrong.
The table cons_temp contains two columns,
name, value.
merge into cons_temp tb
using (select 'Freez' as NAME, 'NOI' as VALUE from dual where 1=2) v
on (tb.NAME=v.NAME)
when matched then update set tb.VALUE=v.VALUE
when not matched then insert (NAME,VALUE) values(v.NAME, v.VALUE);
Why have you put this line in the USING query?
where 1=2
That will always evaluate to false (because 1 != 2). Consequently your USING query will return zero rows, so there is nothing to match or unmatch.
If you remove that WHERE clause your statement will do something.
I came across two example regarding MERGE with conditional DML
First example,
MERGE INTO bonuses D
USING (SELECT employee_id, salary, department_id FROM employees
WHERE department_id = 80) S
ON (D.employee_id = S.employee_id)
WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*.01
DELETE WHERE (S.salary > 8000)
WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus)
VALUES (S.employee_id, S.salary*.01)
WHERE (S.salary <= 8000);
I tend to understand that in MERGE, only the target table (D here) is modified. When we put a DML in WHEN, it is to act on the target table D. So in this case what do the conditions have to do with S, as in the DELETE and UPDATE clause. When do the WHERE come into action ? After the matching ? On the source/target before ON ?
Another related example with one more question
MERGE INTO destination d
USING source s
ON (s.id = d.id)
WHEN MATCHED THEN
UPDATE SET d.description = 'Updated',
d.status = 10
DELETE WHERE s.status = 10;
and
MERGE INTO destination d
USING source s
ON (s.id = d.id)
WHEN MATCHED THEN
UPDATE SET d.description = 'Updated',
d.status = 10
DELETE WHERE d.status = 10;
I don't get the difference between 2 scenarios : source versus target table in the WHERE clause.
Thanks in advance.
There are two parts to the MERGE operation: WHAT action to take (update of some sort, including inserts and deletions) - this is always ONLY on the target table; and WHEN to take the action - what condition must be met to initiate the update. The condition must refer to something in the target table, but it also refers to the source table.
In your first example: the target table only has employee id's and bounses. You want to increase each bonus by 1% of base salary, for each employee - and to add a bounus (when there was no row for that employee) for employees who weren't assigned a bonus at all. So you can't only look at the target table, you must also look somewhere else, where salaries are stored. In this case, "WHEN MATCHED" makes sure you look at the same employee id in both tables. Then you increase bonus by 1% of base salary; base salary is read from the source table. Then you delete the bonus altogether (there will be no row for the employee id in the BONUS table) if the employee has a base salary greater than 80,000 - that must be a business decision reflected in the database. So you see how you need to refer to data in places other than the target table, even though the changes themselves only affect the target.
In your second example, the effect will be the same.
In the first example,
1. Employees belonging to department 80 are identified. These employees may or may not have a bonus record against their employeeID in bonuses table.
2. If a bonus already exists in bonuses, increment bonus in bonuses for this employee by 1 percent of their salary. After that, if employee's salary is more than 8000 he must not have bonus, so remove his bonus record from bonuses.
3. if no bonus already exists and if employees salary is not more than 8000 add a new bonus record.
The sequence in this case for understanding purposes would be ON, WHEN MATCHED, THEN UPDATE, WHERE, DELETE, WHEN NOT MATCHED, WHERE, INSERT
In the second example,
query 1: If source record exists in destination,
a. update destination description and status.
b. Then if the source status is 10, then delete the record with same id from destination.
query 2: If source record exists in destination,
a. update destination description and status.
b. Then delete that record from destination.
In query2 update is redundant unless there are any triggers updating other tables.
The sequence in this case for understanding purposes would be ON, WHEN MATCHED, THEN UPDATE, WHERE, DELETE
Hope this helps.
I am using the following statement to get a result table:
EVALUATE
(
CALCULATETABLE
(
ADDCOLUMNS (
'Case',
"Casenumber", RELATED( 'CaseDetails'[nr]),
),
'Case'[Date] <= value(#dateto) )
)
However, I want to only get one record pr casenumber. In SQL I would solve this with a GROUP BY statement, but how should I do this in DAX? Case also has a dimkey, so several cases with the same casenumber can have different dimkeys.
Try this:
EVALUATE
CALCULATETABLE(
SUMMARIZE(
Case
,<comma-separated list of fields from Case you want>
,"CaseNumber"
,RELATED(CaseDetails[nr])
)
,Case[Date] <= VALUE(#dateto)
)
SUMMARIZE() takes a table as its first argument, then a comma-separated list of fields from that table and any tables that it is related to where it is on the many side (thus in a star schema, SUMMARIZE()ing the fact table will allow you to refer directly to any dimension table field), followed by a comma-separated list of , pairs where is a quoted field name and is a scalar value which is evaluated in the row context of the table in the first argument.
If you don't need to rename CaseDetails[nr], then the query would look like this (just for an illustrative example):
EVALUATE
CALCULATETABLE(
SUMMARIZE(
Case
,<comma-separated list of fields from Case you want>
,CaseDetails[nr]
)
,Case[Date] <= VALUE(#dateto)
)
In such a query, all fields will come through with column headings in the format of 'table'[field], so there is no ambiguity if you have identical field names in multiple related tables.
Edit to address new information in original:
SUMMARIZE(), just like SQL's GROUP BY clause will not eliminate distinct values from the resultset. If there is a field that is a higher cardinality than the field you want to group by, you will always see duplicates.
Is your [DimKey] necessary in the resultset? If yes, then there's no way to decrease the size of your resultset smaller than the number of distinct values of [DimKey].
If [DimKey] is unnecessary, simply omit it from the list of fields in SUMMARIZE().
If you want only a specific [DimKey], e.g. the most recent (assuming it's an IDENTITY field and the max value is the latest), then you can bring it in with another ADDCOLUMNS() wrapped around your current SUMMARIZE():
EVALUATE
ADDCOLUMNS(
SUMMARIZE(
Case
,<comma-separated list of fields from Case except for [DimKey]>
,"CaseNumber"
,RELATED(CaseDetails[nr])
)
,"MaxDimKey"
,CALCULATE(MAX(Case[DimKey]))
)
In Oracle, I use a MERGE statement like:
merge into live_table l
using (
select *
from staging_table
where merged = 'N'
) s
on (i.id = s.id)
when matched then
update l.description = s.description
when not matched then
insert (l.id, l.description)
values (s.id, s.description)
;
The staging table also contains a "merged" flag, which I want to update to 'Y' after the merge.
I thought I could combine this with an extra update (after the merge) like:
update staging_table
set merged = 'Y'
where id in (select id
from live_table)
;
but this also updates the flag on records that have already been merged in previous transactions. A workaround is to write this as a loop that handles the insert/updates one by one, but that would defeat the ease of the MERGE statement.
How can I update the merged flag in such a way that I'm sure that only those rows affected by the merge statement are updated?
In this instance I would use a PL/SQL block, and use bulk collect + bulk binds to do the needed queries and updates/inserts.