ORA-32795: cannot insert into a generated always identity column - sql

Guys I am trying to execute below insert statement and I keep getting the error:
cannot insert into a generated always identity column
the statement is :
INSERT INTO leaves_approval
SELECT *
FROM requests_temp r
WHERE r.civil_number = 33322
AND r.request_id = (SELECT Max(s.request_id)
FROM requests_temp s)

One of the columns in your target table (leaves_approval) contains an identity column that was defined as Generated always.
Identity columns can be created in 2 modes - Generated always, that cannot be assigned and Generated by default that can be assigned.
If you wish you can change the column mode and then do your insert "as is".
Take in consideration that this might create duplicates in the identity column or failed due to constraints.
ALTER TABLE leaves_approval MODIFY **my_identity_column** GENERATED BY DEFAULT AS IDENTITY;
Or you can exclude the identity column from the INSERT list (but you'll have to indicate the full column list, except for the identity column), e.g. -
INSERT INTO leaves_approval (c1,c2,c3,c4,...)
SELECT c1,c2,c3,c4 ...
FROM requests_temp r
WHERE r.civil_number = 33322
AND r.request_id = (SELECT Max(s.request_id)
FROM requests_temp s)
Database SQL Language Reference - CREATE TABLE
ALWAYS If you specify ALWAYS, then Oracle Database always uses the
sequence generator to assign a value to the column. If you attempt to
explicitly assign a value to the column using INSERT or UPDATE, then
an error will be returned. This is the default.
BY DEFAULT If you specify BY DEFAULT, then Oracle Database uses the
sequence generator to assign a value to the column by default, but you
can also explicitly assign a specified value to the column. If you
specify ON NULL, then Oracle Database uses the sequence generator to
assign a value to the column when a subsequent INSERT statement
attempts to assign a value that evaluates to NULL.

What don't you understand about the error? You have an "identity" column, where the value is generated as a sequence. You cannot insert into it. So, list all the other columns:
INSERT INTO LEAVES_APPROVAL(col1, col2, col3, . . .)
SELECT col1, col2, col3, . . .
FROM REQUESTS_TEMP r
WHERE r.CIVIL_NUMBER = 33322 AND
r.REQUEST_ID = (SELECT MAX(s.REQUEST_ID) FROM REQUESTS_TEMP s);
In general, it is a good idea to list all the columns in an INSERT anyway. This prevents unexpected errors, because the columns are in the wrong order or the tables have different numbers of columns.

Example: my_table_column NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY - if you have the column defined as, then it will get the value when it is NULL and will not interfere if you are to insert/update with values for that column. It worked for me.

Related

Does Oracle allow an SQL INSERT INTO using a SELECT statement for VALUES if the destination table has an GENERATE ALWAYS AS IDENTITY COLUMN

I am trying to insert rows into an Oracle 19c table that we recently added a GENERATED ALWAYS AS IDENTITY column (column name is "ID"). The column should auto-increment and not need to be specified explicitly in an INSERT statement. Typical INSERT statements work - i.e. INSERT INTO table_name (field1,field2) VALUES ('f1', 'f2'). (merely an example). The ID field increments when typical INSERT is executed. But the query below, that was working before the addition of the IDENTITY COLUMN, is now not working and returning the error: ORA-00947: not enough values.
The field counts are identical with the exception of not including the new ID IDENTITY field, which I am expecting to auto-increment. Is this statement not allowed with an IDENTITY column?
Is the INSERT INTO statement, using a SELECT from another table, not allowing this and producing the error?
INSERT INTO T.AUDIT
(SELECT r.IDENTIFIER, r.SERIAL, r.NODE, r.NODEALIAS, r.MANAGER, r.AGENT, r.ALERTGROUP,
r.ALERTKEY, r.SEVERITY, r.SUMMARY, r.LASTMODIFIED, r.FIRSTOCCURRENCE, r.LASTOCCURRENCE,
r.POLL, r.TYPE, r.TALLY, r.CLASS, r.LOCATION, r.OWNERUID, r.OWNERGID, r.ACKNOWLEDGED,
r.EVENTID, r.DELETEDAT, r.ORIGINALSEVERITY, r.CATEGORY, r.SITEID, r.SITENAME, r.DURATION,
r.ACTIVECLEARCHANGE, r.NETWORK, r.EXTENDEDATTR, r.SERVERNAME, r.SERVERSERIAL, r.PROBESUBSECONDID
FROM R.STATUS r
JOIN
(SELECT SERVERSERIAL, MAX(LASTOCCURRENCE) as maxlast
FROM T.AUDIT
GROUP BY SERVERSERIAL) gla
ON r.SERVERSERIAL = gla.SERVERSERIAL
WHERE (r.LASTOCCURRENCE > SYSDATE - (1/1440)*5 AND gla.maxlast < r.LASTOCCURRENCE)
) )
Thanks for any help.
Yes, it does; your example insert
INSERT INTO table_name (field1,field2) VALUES ('f1', 'f2')
would also work as
INSERT INTO table_name (field1,field2) SELECT 'f1', 'f2' FROM DUAL
db<>fiddle demo
Your problematic real insert statement is not specifying the target column list, so when it used to work it was relying on the columns in the table (and their data types) matching the results of the query. (This is similar to relying on select *, and potentially problematic for some of the same reasons.)
Your query selects 34 values, so your table had 34 columns. You have now added a 35th column to the table, your new ID column. You know that you don't want to insert directly into that column, but in general Oracle doesn't, at least at the point it's comparing the query with the table columns. The table has 35 columns, so as you haven't said otherwise as part of the statement, it is expecting 35 values in the select list.
There's no way for Oracle to know which of the 35 columns you're skipping. Arguably it could guess based on the identity column, but that would be more work and inconsistent, and it's not unreasonable for it to insist you do the work to make sure it's right. It's expecting 35 values, it sees 34, so it throws an error saying there are not enough values - which is true.
Your question sort of implies you think Oracle might be doing something special to prevent the insert ... select ... syntax if there is an identity column, but in facts it's the opposite - it isn't doing anything special, and it's reporting the column/value count mismatch as it usually would.
So, you have to list the columns you are populating - you can't automatically skip one. So you statement needs to be:
INSERT INTO T.AUDIT (IDENTIFIER, SERIAL, NODE, ..., PROBESUBSECONDID)
SELECT r.IDENTIFIER, r.SERIAL, r.NODE, ..., r.PROBESUBSECONDID
FROM ...
using the actual column names of course if they differ from the query column names.
If you can't change that insert statement then you could make the ID column invisible; but then you would have to specify it explicitly in queries, as select * won't see it - but then you shouldn't rely on * anyway.
db<>fiddle

How to fix Error 213 in SQL Columns that ALL allow null

I am having an issue with an sql query used in job automation
The procedure inserts data from a source table(48 columns) to destination table(49 columns where the 49th/last column is NOT in the source table). But all columns in the destination and source table accept null, so that shouldn't be an issue copying from 48 columns to 49 columns.
It throws this error :
Column name or number of supplied values does not match table definition. [SQLSTATE 21S01] (Error 213). The step failed.
It should just insert null into the 49th column and I have checked the column names and they correspond.
Let's treat this like I can't delete the 49th column.
Please what can I do here?
Accepting NULL doesn't mean you can specify 49 cols and 48 values in the sql INSERT statement. The number of columns and number of values must match exactly. Either drop extra column from INSERT list or add 49th value (NULL I guess) to the values list. In both cases if column is NULLable, it will be set to NULL.
First, if you have code that's not working, you should post it so we can tell for sure what's happening. But I'd be pretty willing to bet you're trying to short cut the process and use something like this:
INSERT tableB
SELECT *
FROM tableA
But the tables don't have the same number of columns, so the SQL Engine doesn't know which source column goes into which destination column. You need to provide an explicit list so it knows which one you intend to ignore:
INSERT tableB
(
col1,
col2,
...
col48
)
SELECT
col1,
col2,
...
col48
FROM tableA;

Copying values / columns from one table to another existing tabler in SQL Server Management Studio

I want to copy all columns from dbo.die to dbo.technology.
Both tables exist! In dbo.technology, the primary key is idTechnology
In dbo.die, the primary key is idDie and we have a foreign key, which is Technology_idTechnology in it, which connects the die table with the technology table.
How could I do that, so that the values got copied to the right rows, which match the same idTechnology?
I tried this:
INSERT INTO dbo.die
(Technology_idTechnology, Technology_D, Technology_Type, Technology_Manufacturer, Technology_SOI, Technology_Node, Technology_Name, Technology_Number_Metal, Technology_Number_Poly, Technology_Power_Cu, Technology_FEComplexity, Technology_FEComplexity_Sec, Technology_Trench, Technology_IMID, Technology_Remarks)
SELECT *
FROM dbo.technology tech
WHERE tech.idTechnology = idTechnology;
but I'm always getting an error!
Cannot insert duplicate key row in object 'dbo.die' with unique index 'ui_dieIdsample'. The duplicate key value is ().
Don't know what I should do.. I thought it's easy & simple
If a column is declared as NOT NULL (and has no default value), a value for the column must be specified in the INSERT statement.
In this specific case you should add Table2_Feld to the insert column list, and specify a value in the SELECT for it!
You will need to change your column list (lets say that its acceptable to insert a default value of 0 into column Table2_Feld)
INSERT INTO dbo.table2
(Table1_idTech, Tech_D, Techn_Type, Tech_Man,
Techn_Node, Tech_Name, Technology_Numb, Tech_Po,
Tech_FEC, Techn_Comp_Sec,
Tech_R,Table2_Feld)
select *,0 from table1 tech

How to Return the ID Value When Inserting a Record Using ElevateDB

With SQL server you can simply return ##Identity to get the ID value of the last insert.
Is there a way to do this using elevatedb?
ElevateDB provides a LASTIDENTITY function which returns the last identity value assigned to the specified column in the specified table.
SELECT LASTIDENTITY('Customer', 'CustNo') AS LastCustNo
You can also obtain the same information using the INSERT statement.
EDBQuery1.SQL.Clear;
EDBQuery1.SQL.Add('INSERT INTO Table1 (ID, Text1)');
EDBQuery1.SQL.Add('VALUES(:ID, :Text1)');
EDBQuery1.Prepare;
EDBQuery1.ParamByName('Text1').AsString:='Some text';
EDBQuery1.ExecSQL;
ShowMessage(EDBQuery1.ParamByName('ID').AsString);
INSERT statements automatically set all parameters to IN/OUT so that
any generated/computed column values (including IDENTITY columns) can
be retrieved via the same parameters after the INSERT is executed.
The above example is from the Support Forum.
ElevateDB does not support identity or auto-incrementing column types. So, there is no syntax like ##Identity.

How do I update a key field on insert for SQL Server (Siebel table S_CAMP_CON)

I need help on writing a trigger in SQL Server to be used in Siebel:
The system field ROW_ID has to be unique (key)
When the field INSERT_CD and CAMP_WAVE_ID is null then ROW_ID must be generated (see below).
If not, leave ROW_ID as is.
ROW_ID is a key field of varchar(15).
The following statement generates the perfect key/row id:
select substring(replace (CAST (newid() as varchar(36)),'-',''),1,15)
I need help on writing a SQL Server 2005 trigger to generate this key.
I would strongly suggest not to modify ROW_ID. Use a different column, maybe add your own extension column, but don't modify (or try to set yourself) ROW_ID. Leave it at the value that Siebel puts in there.
You must not modify any of the system columns (Type "System", check in Tools, in Table > Columns)
Here is one way we did it. We setup OBIEE to generate a ROW_ID that is unique for the current load and hence why the WHERE clause can get the record to be updated.
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'S_CAMP_CON_Direct_Load_ROW_ID' AND type = 'TR')
DROP TRIGGER S_CAMP_CON_Direct_Load_ROW_ID
GO
CREATE TRIGGER S_CAMP_CON_Direct_Load_ROW_ID
ON S_CAMP_CON FOR INSERT
AS
UPDATE S_CAMP_CON
SET ROW_ID = (select substring(replace (CAST (newid() as varchar(36)),'-',''),1,15))
WHERE S_CAMP_CON.ROW_ID IN
(SELECT ROW_ID FROM inserted WHERE INSERT_CD = 'Direct Load')
But we are concerned by the uniqueness of ROW_ID since we are using a substring.