Oracle bulk update table from where clause - oracle

I have a master (MD_TS_MAST) which contains
MAST_ID
ENG_ID
MS_DATE
Detail (MD_TS_DETAIL) table
ID
MD_ID (rel with MAST_ID)
JOB_FOR
JOB_TYPE_ID
ACCOUNT_ID
Below is SQL where it locates the "MD_TS_DETAIL IDs" by eng_id and date range.
this gets me the details of.
102038
102134
101970
102244
Then I just need to update the MD_TS_DETAIL table.
UPDATE MD_TS_DETAIL
SET JOB_FOR ='25',
JOB_TYPE_ID ='344',
ACCOUNT_ID ='8'
WHERE MD_TS_DETAIL.ID IN (
SELECT D.ID
FROM MD_TS_MAST M
LEFT JOIN MD_TS_DETAIL D ON M.MAST_ID = D.MD_ID WHERE ENG_ID = '621'
AND MS_DATE BETWEEN '02-OCT-14' AND '05-OCT-14';
)
This would be converted into a form. But my mind has gone blank on how to update this.

Looks like ive had too much coffee.
I added a semi_colon just before the closing bracket (oops). removed and worked fine

Related

Update with count and group by expressions

First, I know there is a common issue in Stack Overflow, but the following solutions are not working well here. So I still need some help.
Oracle - Update COUNT of rows with specific value
Oracle - Update rows with a min value in the group of a column from another table
Oracle update statement with group function
Oracle - Update COUNT of rows with specific value
The problem is: I have a +700k lines table:
REVIEWS (PRODUCT_ID, REVIEW, REVIEW_DATE, RELEASE_DATE, ..., REVIEW_COUNT)
I'm trying to update REVIEW_COUNT by counting the lines with the same PRODUCT_ID (I want just reviews before product release). So the code below works very well for my purpose:
SELECT COUNT(PRODUCT_ID) FROM REVIEWS
WHERE REVIEW_DATE < RELEASE_DATE
GROUP BY PRODUCT_ID
But I'm having a hard time to do the update. First I tried this:
UPDATE REVIEWS R
SET R.REVIEWS_COUNT =
(SELECT COUNT(RR.PRODUCT_ID) FROM REVIEWS RR
WHERE RR.DATA < RR.REL_DATE
GROUP BY RR.PRODUCT_ID)
The error is "more than one row", which is not surprising, but since I'm using the group by statement, it shouldn't occur. So I tried a self-join:
UPDATE REVIEWS R
SET R.REVIEWS_COUNT =
(SELECT COUNT(RR.PRODUCT_ID) FROM REVIEWS RR
WHERE RR.PRODUCT_ID = R.PRODUCT_ID AND RR.DATA < RR.REL_DATE)
But the query is taking forever and I don't think that should take so long, the simple select is pretty normal-fast.
I've also tested some more fancy and more simple stuff, but the outcome remains the same: long time waiting and it seems just wrong.
Please, what I'm missing in such easy update?
Maybe instead of updating you could define view:
select product_id, review_date, release_date,
count(case when review_date < release_date then 1 end)
over (partition by product_id) review_count
from reviews;
You could also try merge instead update:
merge into reviews a
using (select product_id, count(product_id) cnt from reviews
where review_date < release_date
group by product_id ) b
on (a.product_id = b.product_id)
when matched then update set reviews_count = b.cnt
dbfiddle
I think your second update is correct:
UPDATE REVIEWS R
SET R.REVIEWS_COUNT =
(SELECT COUNT(RR.PRODUCT_ID) FROM REVIEWS RR
WHERE RR.PRODUCT_ID = R.PRODUCT_ID AND RR.DATA < RR.REL_DATE)
;
This will update every record in the reviews table. Is that what you wanted?
An index on product_id will make the inner query run faster, but it will still update all 700K or so records.

Oracle trying to update a table by joining a non indexed table

I tried looking for a similar example to my problem but could not reproduce the solution to my success.
I have 2 tables, Controller and Actions.
The Actions table has the columns Step, Script, Description, Wait_Until and Ref_Code.
The Controller table can only be joined on the Action table by the Ref_Code.
The Action table cannot have a PK because for each Ref_Code there is a Step to be taken.
Im getting an error when trying to update the Controller table using a merge statement:
ORA-30926: unable to get a stable set of rows in the source tables
My merge statement is as follows:
MERGE INTO DSTETL.SHB_FTPS_CONTROLLER ftpsc
USING (SELECT DISTINCT FTPSC.SESSION_ID,
FTPSC.ORDER_DATE,
sa.step,
sa.next_step,
LAST_ACTION_TMSTMP,
SA.ACTION_SCRIPT,
sa.ref_code,
SA.WAIT_UNTIL
FROM DSTETL.SHB_FTPS_CONTROLLER ftpsc, DSTETL.SHB_ACTIONS sa
WHERE SA.REF_CODE = FTPSC.REF_CODE
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step) v1
ON (v1.REF_CODE = FTPSC.REF_CODE)
WHEN MATCHED
THEN
UPDATE SET FTPSC.LAST_ACTION_TMSTMP = CURRENT_TIMESTAMP,
ftpsc.next_step = v1.next_step,
ftpsc.curr_step = v1.STEP,
ftpsc.action_script = v1.action_script
WHERE CURRENT_TIMESTAMP >= v1.LAST_ACTION_TMSTMP + v1.WAIT_UNTIL;
COMMIT;
I tried doing this using a normal update as well but Im getting ORA-01732: data manipulation operation not legal on this view.
UPDATE (SELECT FTPSC.SESSION_ID,
FTPSC.ORDER_DATE,
FTPSC.CURR_STEP,
FTPSC.NEXT_STEP,
FTPSC.ACTION_SCRIPT,
sa.step, --New Step
sa.next_step AS "NNS", --New Next Step
FTPSC.LAST_ACTION_TMSTMP,
SA.ACTION_SCRIPT AS "NAS", --New action script
sa.ref_code,
SA.WAIT_UNTIL
FROM DSTETL.SHB_FTPS_CONTROLLER ftpsc
LEFT JOIN
DSTETL.SHB_ACTIONS sa
ON SA.REF_CODE = FTPSC.REF_CODE
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step) t
SET t.curr_step = t.step,
t.LAST_ACTION_TMSTMP = CURRENT_TIMESTAMP,
t.next_step = t."NNS",
t.action_script = t."NAS";
COMMIT;
Any advice would be appreciated, I already understand this is because the Action table has multiple Ref_Codes but REF_CODE||STEP is unique. And the output of:
SELECT DISTINCT FTPSC.SESSION_ID,
FTPSC.ORDER_DATE,
sa.step,
sa.next_step,
LAST_ACTION_TMSTMP,
SA.ACTION_SCRIPT,
sa.ref_code,
SA.WAIT_UNTIL
FROM DSTETL.SHB_FTPS_CONTROLLER ftpsc, DSTETL.SHB_ACTIONS sa
WHERE SA.REF_CODE = FTPSC.REF_CODE
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step;
Is how I want the Controller table to be updated like.
Thanks in advance.
It sounds like what you want to do is: update each row in the Controller table with the matching "next step" details from the Actions table. But your Merge statement is querying the Controller table twice, which confuses things.
Is this what you're trying to do?
MERGE INTO DSTETL.SHB_FTPS_CONTROLLER ftpsc
USING (SELECT
step,
next_step,
ACTION_SCRIPT,
ref_code,
WAIT_UNTIL
FROM DSTETL.SHB_ACTIONS
) sa
ON (sa.REF_CODE = FTPSC.REF_CODE)
WHEN MATCHED
THEN
UPDATE SET FTPSC.LAST_ACTION_TMSTMP = CURRENT_TIMESTAMP,
ftpsc.next_step = sa.next_step,
ftpsc.curr_step = sa.STEP,
ftpsc.action_script = sa.action_script
WHERE CURRENT_TIMESTAMP >= ftpsc.LAST_ACTION_TMSTMP + sa.WAIT_UNTIL
AND SA.STEP > ftpsc.curr_step
AND sa.step = ftpsc.next_step;
EDIT: updated query
EDIT2: So, in your original query, in the USING section you were selecting the rows in the Controller table that you wanted to update... but you never joined those rows to the Controller table from the MERGE INTO section to match them up. Having the same alias "ftpsc" just made it less clear that they're two separate objects in the query, and which one you wanted to update.
Honestly I don't really understand why Oracle won't let you update columns that appear in the USING..ON clause. It apparently works fine in SQL Server.

How to retrieve data from 3 tables using sub query oracle SQL

I want to retrieve users name and there responsibility_key where there end_date is null and i want to convert it to (sysdate+1) using nvl but i am only able to retrieve the responsibility_key not the name please help.
The error in the image says "column ambiguously defined". Take a close look. Your last END_DATE could refer to either the u alias or the table from the subquery. Change it to match the rest of your subquery (FIND_USER_GROUPS_DIRECT.END_DATE)
EDIT
Your query is
select u.USER_NAME, d.responsibility_key from FND_USER u,FND_RESPONSIBILITY_VL d
where responsibility_id in(
select responsibility_id from
FND_USER_RESP_GROUPS_DIRECT WHERE END_USER_RESP_GROUPS_DIRECT.END_DATE=nvl(END_DATE,sysdate+1)) and
u.END_DATE=nvl(END_DATE,SYSDATE + 1)
;
The query isn't formatted, which makes it hard to read.
Not all columns are qualified with table name (or aliases), as mentioned in the comments.
The query currently uses an implicit join.
The query is impossible to understand without seeing the table definitions (desc [table_name]).
For points 1 and 2, a properly formatted query will look something like
select u.user_name, d.responsibility_key
from
fnd_user u,
fnd_responsibility_vl d
where
d.responsibility_id in (
select urgd.responsibility_id
from
fnd_user_resp_groups_direct urgd
where
urgd.end_date = nvl(u.end_date, sysdate+1)
) and
u.end_date = nvl(urgd.end_date, sysdate + 1)
;
This makes it easier to read and in addition to this, you can see that without table definitions I guessed (see point 4) as to which tables the end_date column belongs in your query. If I had to guess, so does Oracle. That means you have an ambiguity problem. To fix it, take a close look at the end_date column as it appears in your original query and where you do not prefix it with anything, you need to prefix it with the appropriate alias (after you have aliased all your tables).
For point 3, you can write your query more clearly with an explicit join and by using aliases for all columns. As for the explicit join I have no idea what your tables look like but one possibility is something like
select u.user_name, d.responsibility_key
from fnd_user u
join fnd_responsibility_vl d
on u.id = d.user_id
where
d.responsibility_id in (
select responsibility_id
from fnd_user_resp_groups_direct urgd
where
urgd.end_date = nvl(u.end_date, sysdate+1)
) and
u.end_date = nvl(urgd.end_date, sysdate+1)
;
If you follow these points you will get to the root of the error.

Update statement with joins in Oracle

I need to update one column in table A with the result of a multiplication of one field from table A with one field from table B.
It would be pretty simple to do this in T-SQL, but I can't write the correct syntax in Oracle.
What I've tried:
UPDATE TABLE_A
SET TABLE_A.COLUMN_TO_UPDATE =
(select TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID)
WHERE TABLE_A.MONTH_ID IN (201601, 201602, 201603);
But I keep getting errors. Could anybody help me, please?
I generally prefer to use the below format for such cases since this will ensure there's no update performed if there's no data in the table(query extracted temp table) whereas in the above solution provided by Brian Leach will update the new value as null if there's no record present in the 2nd table but exists in the first table.
UPDATE
(
select TABLE_A.COLUMN_TO_UPDATE
, TABLE_A.PRODUCT_ID
, TABLE_A.COLUMN_WITH_SOME_VALUE * TABLE_B.COLUMN_WITH_PERCENTAGE as value
from TABLE_A
INNER JOIN TABLE_B
ON TABLE_A.PRODUCT_ID = TABLE_B.PRODUCT_ID
AND TABLE_A.SALES_CHANNEL_ID = TABLE_B.SALES_CHANNEL_ID
AND TABLE_A.MONTH_ID IN (201601, 201602, 201603)
) DATA
SET DATA.COLUMN_TO_UPDATE = DATA.value;
This solution can cause key preserved value issues which shouldn't be an issue here since i expect a single row in both the tables for one product(ID).
More on Key Preserved table concept in inner join can be found here
https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:548422757486
#Jayesh Mulwani raiesed a valid point, this will set the value to null if there is no matching record. This may or may not be the desired result. If it isn't, and no change is desirect, you can change the select statement to:
coalesce((SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id AND table_a.sales_channel_id = table_b.sales_channel_id),1)
If this is the desired outcome, Jayesh's solution will be more efficient as it will only update matching records.
UPDATE table_a
SET table_a.column_to_update = table_a.column_with_some_value
* (SELECT table_b.column_with_percentage
FROM table_b
WHERE table_a.product_id = table_b.product_id
AND table_a.sales_channel_id = table_b.sales_channel_id)
WHERE table_a.month_id IN (201601, 201602, 201603);

Better solution than left join subqueries?

TablePatient.Patient_ID(PK)
TableProviders.Encounter (joins to PK)
TableProviders.Provider_Type
TableProviders.Provider_ID
TableNames.Full_Name
TableNames.Provider_ID (joins to Table Names)
I want a query that will give, for all the Patient_IDs, the Full_Name of the provider for every Provider ID.
There are about 30 provider_types.
I have made this already using a left join a ton of left joins. It takes a long time to run and I am thinking there is a trick I am missing.
Any help?
Ok, my previous answer didn't match at all what you meant. You want to pivot the table to have on each line one Patient_ID with every Full_name for every provider_type. I assume that each patient has only one provider for one type and not more ; if more, you will have more than one row for each patient, and anyway I don't think it's really possible.
Here is my solution with pivot. The first part is to make it more understandable, so I create a table named TABLE_PATIENT in a subquery.
WITH TABLE_PATIENT AS
(
SELECT TablePatient.Patient_ID,
TableProviders.Provider_Type,
TableNames.Full_Name
FROM TablePatient LEFT JOIN
TableProviders on TablePatient.Patient_ID = TableProviders.Encounter
LEFT JOIN
TableNames on TableNames.Provider_ID = TableProviders.Provider_ID
group by TablePatient.Patient_ID,
TableProviders.Provider_Type,
TableNames.Full_Name
)
SELECT *
FROM TABLE_PATIENT
PIVOT
(
min(Full_name)
for Provider_type in ([type1], [type2],[type3])
) AS PVT
So TABLE_PATIENT just has many rows for each patient, with one provider each row, and the pivot puts everything on a single row. Tell me if something doesn't work.
You need to write every type you want in the [type1],[type2] etc. Just put them inside [], no other character needed as ' or anything else.
If you put only some types, then the query will not show providers of other types.
Tell me if something doesn't work.
If I understand what you mean, you just want to group the answer by Patient Id and then Provider ID. A full name is unique on a provider id right ?
This should be something like
SELECT TablePatient.Patient_ID,
TableProviders.Provider_ID,
TableNames.Full_Name
FROM TablePatient LEFT JOIN
TableProviders on TablePatient.Patient_ID = TableProviders.Encounter
LEFT JOIN
TableNames on TableNames.Provider_ID = TablerProviders.Provider_ID
group by TablePatient.Patient_ID,
TableProviders.Provider_ID,
TableNames.Full_Name
You can either group by TableNames.Full_Name or select First(TableNames.Full_Name) for example if indeed a full name is unique to a provider ID.
Note : I used the SQL server Syntax, there can be différences with Oracle ..

Resources