getting ORA-01407:cannot update to null error - sql

i am getting ORA-01407:cannot update to null error for below query, Please help on this.
UPDATE PS_CT_IQN_ACC_STG stg
SET (CTS_WO_ID,CTS_WO_END_DATE,CTS_WO_REG_RATE,CTS_WO_OT_RATE,VENDOR_ID) = (select CT_WORK_ORDER_ID,END_DATE,CT_WO_RATEREGULAR,CT_WO_RATEOVERTIME,CT_WO_VENDOR_ID from ps_cts_iqn_empl_wo WO1
where WO1.CT_WORK_ORDER_ID= (select max(CT_WORK_ORDER_ID) from ps_cts_iqn_empl_wo WO where WO.cts_peoplesoft_id = STG.EMPLID
AND WO.ct_wo_project_id = STG.project_id and stg.report_due_date between WO.start_date and WO.end_date )
and WO1.lastupddttm = (select max(lastupddttm) from ps_cts_iqn_empl_wo WO2 where WO2.cts_peoplesoft_id = STG.EMPLID
AND WO2.ct_wo_project_id = STG.project_id and stg.report_due_date between WO2.start_date and WO2.end_date ))

Your query is updating the following 5 columns of the table PS_CT_IQN_ACC_STG:
CTS_WO_ID
CTS_WO_END_DATE
CTS_WO_REG_RATE
CTS_WO_OT_RATE
VENDOR_ID
SELECT Query to update the column values must be returning NULL for one of the columns for which NOT NULL constraint OR check of not null is applied.
You can check this from the following dictionary views:
USER_TAB_COLS -- NULLABLE column will be N for not null columns
USER_CONSTRAINTS -- Type will be C with Search_condition like <COLUMN_NAME> IS NOT NULL for not null columns.
Cheers!!

Related

Delete rows where date is not minimum

I have a table (structure below) that I need to clean up by deleting rows for each Object_ID:
WHERE Current_Step is NULL and Change = 'change'
and Date_of_Change <> MIN(Date_of_Change)
That is, I need to leave only the row with minimum date for each Object_ID.
Table sample
Object_ID
Current_Step
Change
Date_of_Change
0025307
NULL
change
16.11.2021
0025307
NULL
change
19.11.2021
0025307
NULL
change
19.11.2021
I am using MS SQL.
There are no primary keys.
All columns are VARCHAR except Date_of_Change being of type DATE.
The reason why I need to clean up this table is because it was incorrectly filled because source query was checking for IF NULL = NULL and then marked those status changes as changed even though they did not change. So I need to revert values back to original date they were changed because if they still have value NULL that means there were no actual changes happening in status.
Desired behavior
My attempt in identifying rows that I need to keep:
SELECT [Object_ID]
,MIN([Date_of_Change])
FROM table
WHERE [Current_Step] IS NULL
AND [Change] = 'change'
GROUP BY Object_ID
I just need to remove other rows with the same Object_ID whose Date_of_Change is not equal to the one identified in query above.
do join on same table like i did on 'Table1223' below.
Example:
DELETE tbl
FROM Table1223 tbl
JOIN (SELECT * FROM Table1223) objID
ON objID.Object_ID = tbl.Object_ID
WHERE tbl.Date_of_Change > objID.Date_of_Change
Schema
So you have a table with versioned objects which holds change records associated to the object with some details and a date.
Now you want to select
the first change per object
the oldest (within the GROUP of this object's changes)
using MIN function on a DATE column-type
This oldest should be retained/kept and stay. All other object change-versions should be deleted.
Solving
A. Selecting the fist/oldest changes per object in 2 steps.
Select the MIN(date) per object:
SELECT Object_ID, COUNT(Object_ID) AS Count_Changes, MIN(Date_of_Change) AS First_Change
FROM table
GROUP BY Object_ID
Resultset contains each object with the total count of changes and the date of the first change.
Select the first changes using previous result as subquery in a JOIN:
SELECT *
FROM table t
-- join with a table-subquery having only 2 columns to correlate
JOIN (
SELECT Object_ID, MIN(Date_of_Change) AS First_Change
FROM table
WHERE Current_Step is NULL and Change = 'change'
GROUP BY Object_ID
) m ON t.Object_ID = m.Object_ID AND t.Date_of_Change = m.First_Change
WHERE Current_Step is NULL and Change = 'change'
This are the rows to keep and not remove. The first change of each object should be retained and not cleaned.
B. Now we can invert the JOIN-condition to get all the rows, that we want to delete/clean:
Change the date-comparison
) m ON t.Object_ID = m.Object_ID AND t.Date_of_Change = m.First_Change
to not-equal:
) m ON t.Object_ID = m.Object_ID AND t.Date_of_Change <> m.First_Change
Run a dry-select first, to get at least the count before deleting.
SELECT COUNT(Object_ID) AS records_to_remove
FROM table t
-- join with a table-subquery having only 2 columns to correlate
JOIN (
SELECT Object_ID, MIN(Date_of_Change) AS First_Change
FROM table
WHERE Current_Step is NULL and Change = 'change'
GROUP BY Object_ID
) m ON t.Object_ID = m.Object_ID AND t.Date_of_Change <> m.First_Change
WHERE Current_Step is NULL and Change = 'change'
Prepare the DELETE statement with JOIN (if supported by DBMS):
DELETE FROM table t
JOIN (
SELECT Object_ID, MIN(Date_of_Change) AS First_Change
FROM table
WHERE Current_Step is NULL and Change = 'change'
GROUP BY Object_ID
) m ON t.Object_ID = m.Object_ID AND t.Date_of_Change <> m.First_Change
WHERE t.Current_Step is NULL AND t.Change = 'change'
Alternative to JOIN try USING on other DBMS
Some DBMS do not support JOIN in DELETE statements, but alternatives like USING:
DELETE FROM table t
USING (
SELECT Object_ID, MIN(Date_of_Change) AS First_Change
FROM table t2
WHERE t2.Current_Step is NULL AND t2.Change = 'change'
) AS m
WHERE ...
AND t.Object_ID = m.Object_ID AND t.Date_of_Change <> m.First_Change

Select rows having value combination listed in another table

I have tables:
Result containing 5 columns: result_id, num_1, num_2, num_3, num_4
Ref containing 4 columns: num_1, num_2, num_3, num_4
Columns num contain random int in range of 1-9
Aim of exercise is to display all result_id from Result table which have num values combination present in Ref table and to display result_id which have not met combination criteria.
I've been trying left joining ref to result, but unfortunately no success. Could you please share some light how to deal with it?
If you want the result_id for which combination exists in the ref table then use following JOIN query:
select distinct r.result_id
from results r
join ref on r.num_1 = ref.num_1 and r.num_2 = ref.num_2
and r.num_3 = ref.num_3 and r.num_4 = ref.num_4
If you want the result_id for which combination do not exists in REF table then use the LEFT JOIN as follows:
select r.result_id
from results r
left join ref on r.num_1 = ref.num_1 and r.num_2 = ref.num_2
and r.num_3 = ref.num_3 and r.num_4 = ref.num_4
where ref.num_1 is null -- or use PK / Not nullable column of REF table here
Assuming you want the columns to "line up" and you want to add a flag to the result_id in the first table, then use exists:
select t1.*,
(case when exists (select 1
from table2 t2
where t2.n1 = t1.n1 and t2.n2 = t1.n2 and t2.n3 = t1.n3 and t2.n4
)
then 'present' else 'not present'
end) as flag
from t2;

Change Select statement to an update statement

SELECT A.GRPNO, A.EMPNO, A.DEPNO, A.PENDCD FROM EMPDEP A, EEDPELIG B
WHERE A.GRPNO=B.GRPNO
AND A.EMPNO=B.EMPNO
AND A.DEPNO=B.DEPNO
AND A.GRPNO = 6606 AND A.SPOUSE = 'T'
AND B.ELIGFLAG01 = 'T' AND SNAPTHRUDT ='DEC312999'
Our selection statement has been successful at pulling the information we need however we're new with SQL and are struggling to create an update statement that is replacing the "a.pendcd=0" to "a.pendcd=20" from the information in the select statement. Any help is appreciated, thank you.
update a
a.pendcd=20
FROM EMPDEP A inner join EEDPELIG B
on A.GRPNO=B.GRPNO
AND A.EMPNO=B.EMPNO
AND A.DEPNO=B.DEPNO
AND A.GRPNO = 6606 AND A.SPOUSE = 'T'
AND B.ELIGFLAG01 = 'T' AND SNAPTHRUDT ='DEC312999'
where a.pendcd=0
Oracle does not support FROM or JOIN in UPDATE (under most circumstances).
Just use EXISTS:
UPDATE EMPDEP ed
SET . . .
WHERE EXISTS (SELECT 1
FROM EEDPELIG p
WHERE ed.GRPNO = p.GRPNO AND
ed.EMPNO= p.EMPNO AND
ed.DEPNO= p.DEPNO AND
p.ELIGFLAG01 = 'T'
)
ed.GRPNO = 6606 AND
ed.SPOUSE = 'T' AND
ed.SNAPTHRUDT ='DEC312999';
It is unclear if the condition on SNAPTHRUDT is on the outer table or inner table. If it is on p, then move it to the subquery.
You can use MERGE statement as following:
Lets assume EMPDEP table has primary key which is EMPDEP_UID.
MERGE INTO EMPDEP TRG
USING
(SELECT A.EMPDEP_UID, A.PENDCD
FROM EMPDEP A, EEDPELIG B
WHERE A.GRPNO=B.GRPNO
AND A.EMPNO=B.EMPNO
AND A.DEPNO=B.DEPNO
AND A.GRPNO = 6606
AND A.SPOUSE = 'T'
AND B.ELIGFLAG01 = 'T'
AND SNAPTHRUDT ='DEC312999') SRC
ON (TRG.EMPDEP_UID = SRC.EMPDEP_UID)
WHEN MATCHED THEN
UPDATE SET TRG.PENDCD = 0
WHERE TRG.PENCD = 20;
You can use unique keys instead of primary key to identify the records to be updated. But it is safe to use primary key as unique key can contain nulls which can change the behaviour of our query.
Cheers!!

merge sql condition is null issue

I have two very similar SQL statements. On of them work and on not. The SQL error message seems to be misleading. Can you figure it out?
SQL 1 -- this works just fine
Merge into t1
Using (
Select art_ref_nr, channel, some_value From s1 ) t2
On ( t1.art_ref_nr = t2.art_ref_nr and t1.channel = t2.channel
and ( t1.datasource is null
or (t1.datasource = 'no such value' ) -- only null values will be found
))
WHEN MATCHED THEN UPDATE SET
t1.some_value = t2.some_value
, t1.datasource = 'value 1'
;
SQL 2 -- this fails
Merge into t1
Using (
Select art_ref_nr, channel, some_value From s1 ) t2
On ( t1.art_ref_nr = t2.art_ref_nr and t1.channel = t2.channel
and ( t1.datasource is null
))
WHEN MATCHED THEN UPDATE SET
t1.some_value = t2.some_value
, t1.datasource = 'value 2'
;
SQL1 runs fine.
SQL2 messages:
Columns referenced in the ON Clause cannot be updated: string Cause:
LHS of UPDATE SET contains the columns referenced in the ON Clause
On the other side I reference the on-clause "datasource", in both SQLs, so the error message cannot be full truth.
It seems like the problem is that one time I only check for null value entries. But why does this affect the SQL logic?
Many greetings,
Peter
My guess is that your first query doesn't produce an error because a matching row is never found.
For the second query, it has to do an UPDATE, but can't because you are refering the column to UPDATE into the ON clause.
To overcome this,try to move into a WHERE clause, the part of the ON clause refering the column(s) you are trying to UPDATE:
Merge into t1
Using (
Select art_ref_nr, channel, some_value From s1 ) t2
On (t1.art_ref_nr = t2.art_ref_nr and t1.channel = t2.channel)
WHEN MATCHED THEN UPDATE SET
t1.some_value = t2.some_value
, t1.datasource = 'value 2'
WHERE t1.datasource is null
;

UPDATE row when matching row exists in another table

I need to update a field on a table to be true only if a matching row exists in another table, for all the rows where the column is currently null in the main table.
This is a description of what I want to achieve:
UPDATE [LenqReloaded].[dbo].[Enquiry] A
SET [ResponseLetterSent] = 1
WHERE [ResponseLetterSent] IS NULL
AND EXISTS
(
SELECT * FROM [LenqReloaded].[dbo].[Attachment] B
WHERE A.[EnquiryID] = B.[EnquiryID]
)
This isn't syntactically correct.
I can't code it via an IF EXISTS... statement because I don't have the [EnquiryID] without reading the data from the table.
How should I format my UPDATE statement?
You weren't far off...
UPDATE A
SET A.[ResponseLetterSent] = 1
FROM [LenqReloaded].[dbo].[Enquiry] A
WHERE A.[ResponseLetterSent] IS NULL
AND EXISTS ( SELECT * FROM [LenqReloaded].[dbo].[Attachment] B WHERE A.[EnquiryID] = B.[EnquiryID] )
You need to use a join in your update:
UPDATE [LenqReloaded].[dbo].[Enquiry] SET [ResponseLetterSent] = 1
FROM [LenqReloaded].[dbo].[Enquiry] A
join [LenqReloaded].[dbo].[Attachment] B on A.[EnquiryID] = B.[EnquiryID]
WHERE A.[ResponseLetterSent] IS NULL
This seems counterintuitive, but you need to establish a table alias in a From clause but use that alias in the Update Clause...
Update E Set
ResponseLetterSent = 1
From LenqReloaded.dbo.Enquiry E
Where ResponseLetterSent Is Null
And Exists (Select * From LenqReloaded.dbo.Attachment
Where EnquiryID = E.EnquiryID)
The thing you are missing is the 'from' clause, which is a t-sql extension - it is the only way to assign an alias to the updated table
update [lenqreloaded].[dbo].[enquiry]
set [responselettersent] = 1
from [lenqreloaded].[dbo].[enquiry] a
where [responselettersent] is null
and exists (
select *
from [lenqreloaded].[dbo].[attachment] b
where a.[enquiryid] = b.[enquiryid]
)