“Oops! Something went wrong” seems to be the fashionable thing to do when throwing errors these days. Not surprisingly, when issuing a query from PowerShell against Oracle, it kept producing this error at random
ORA-24381: error(s) in array DML
The error did not always happen. It only happened once in a while and it was always on this statement:
SELECT NVL(MAX(USERNAME),'NOT_THERE') as usr1 FROM DBA_USERS WHERE USERNAME = 'AN_USER_ID'
The error gave me no clue about what the underlying issue is and more importantly I had no idea why it was so random. The complete error is below:
System.Management.Automation.MethodInvocationException: Exception calling “Fill” with “1” argument(s): “ORA-24381: error(s) in array DML”
—> Oracle.DataAccess.Client.OracleException: ORA-24381: error(s) in array DML
at Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck)
at Oracle.DataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior)
at Oracle.DataAccess.Client.OracleDataAdapter.Fill(DataTable[] dataTables, Int32 startRecord, Int32 maxRecords, IDbCommand command, CommandBehavior behavior)
at System.Data.Common.DbDataAdapter.Fill(DataTable dataTable)
at CallSite.Target(Closure , CallSite , Object , Object )
— End of inner exception stack trace —
at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
As one can see from the error message, I do use the Oracle-provided ODP.NET OLEDB drivers and not the Microsoft provider for Oracle. No amount of frantic Googling helped although the issue seemed to have a theme. The theme was that this error happened to others when the data would not fit into the target usually when doing BULK INSERT. In my case, I was just doing a simple SELECT.
Solution: CAST to length
In any case, without beating around the bush, I figured that my problem had to do Oracle’s bad estimation of the string length (that led to the issue). So, I forced a CAST to limit the string to 30 characters and the issue went away!
SELECT NVL(MAX(USERNAME), CAST('NOT_THERE' AS VARCHAR2(30)) ) as usr1 FROM DBA_USERS WHERE USERNAME = 'AN_USER_ID'
In your case, the problem might be a similar data length issue and you might be able to resolve it too with a simple cast. I hope that this post helps you. Good luck!
Краткие итоги
Входной массив
Может быть и PL/SQL массивом и Nested Table.
Три метода перебора элементов
1.Указание интервала индексов
forall i in l_col.first .. l_col.last
forall i in 1 .. 10
-- В этом случае индексы округлятся
forall i in 1.3 .. 2.4
В этом случае нельзя работать с коллекциями с дырками (удаленными элементами)
2. INDICES OF
FORALL i IN INDICES OF l_t
FORALL i IN INDICES OF l_t BETWEEN 1 AND 1
Позволяет работать с коллекцияями с дырками. Можно ограничить интервал
3. VALUES OF
FORALL i IN VALUES OF l_tsubscripts
В одной коллекции храним индексы (PLS_INTEGER или BINARY_INTEGER) от рабочей коллекции. Если указанного элемента в рабочей коллекции нет — будет ошибка. Индексы могут храниться как в PL/SQL так и в Nested массиве.
Обработка ошибок
Если на одном из элементов коллекции проиисходит иисключение, то транзакцияя сохранит все предыдущие успешные операции.
Можно использовать конструкцию SAVE EXCEPTION. В этом случае операция дойдет до конца и, если при выполнении встретились ошибки, будет сгенерировано исключение ORA-24381: error(s) in array DML. Для обработки ошибок можно использовать массив SQL%BULK_EXCEPTIONS
SQL%BULK_EXCEPTIONS
1. Нумерация всегда с 1. Чаще всего для обработки будет использоваться
FOR err IN 1 .. SQL%BULK_EXCEPTIONS.COUNT LOOP
2. SQL%BULK_EXCEPTIONS(err).ERROR_INDEX — номер итерации. Если массив неразряженный и начинается с 1 — то получить входные данные, вызвавшие ошибку очень просто. В остальных случаях придется яотсчитывать в цикле
3. SQL%BULK_EXCEPTIONS(err).ERROR_CODE — код ошибки
4. SQLERRM(-SQL%BULK_EXCEPTIONS(err).ERROR_CODE) — сообщение об ошибке
SQL%BULK_ROWCOUNT
Массив, который хранит в себе сколько строк было обработано на каждой итерации. Массив индексируется индексами от рабочего массива, т.е. если в рабочем массиве элементы со 2-го по 5-й, то и в SQL%BULK_ROWCOUNT будут 2, 3, 4, 5
Пример кода
DROP TABLE a;
CREATE TABLE a (a NUMBER CHECK (a > 0));
DECLARE
l_cnt NUMBER;
TYPE t IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
l_t t;
TYPE t_nest IS TABLE OF NUMBER;
l_tn t_nest := t_nest();
-- suscript -- только PLS_INTEGER and BINARY_INTEGER
TYPE t_subscripts IS TABLE OF PLS_INTEGER;
l_tsubscripts t_subscripts := t_subscripts();
TYPE t_subscripts2 IS TABLE OF PLS_INTEGER index by pls_integer;
l_tsubscripts2 t_subscripts2;
BEGIN
l_t(1) := 1;
l_t(3) := 3;
-- ora-22160 -- коллекция разряженная
BEGIN
FORALL i IN l_t.first .. l_t.last
INSERT INTO a VALUES (l_t(i));
dbms_output.put_line('Should be 0');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('Error: ' || SQLCODE || ' - ' || SQLERRM);
END;
-- можно использовать indices of
FORALL i IN INDICES OF l_t
INSERT INTO a VALUES (l_t(i));
dbms_output.put_line('Row count: ' || SQL%ROWCOUNT);
-- можно использовать indices of
FORALL i IN INDICES OF l_t BETWEEN 1 AND 1
INSERT INTO a VALUES (l_t(i));
dbms_output.put_line('Indices of with lower and upper bound Row count: ' || SQL%ROWCOUNT);
-- Аналогично работает с nested tables
l_tn.EXTEND(3);
l_tn(1) := 1;
l_tn(2) := 2;
l_tn(3) := 3;
l_tn.delete(2);
BEGIN
FORALL i IN l_tn.first .. l_tn.last
INSERT INTO a VALUES (l_tn(i));
dbms_output.put_line('Should be 0');
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('Nested tables Error: ' || SQLCODE || ' - ' || SQLERRM);
END;
FORALL i IN INDICES OF l_tn
INSERT INTO a VALUES (l_tn(i));
dbms_output.put_line('Nested Row count: ' || SQL%ROWCOUNT);
-- values of
l_tsubscripts.EXTEND(1);
l_tsubscripts(1) := 3;
-- При использованиии values of + nested table коллекция должна начинаться с 1
BEGIN
FORALL i IN VALUES OF l_tsubscripts
INSERT INTO a VALUES (l_tn(i));
dbms_output.put_line('values of + nested table Row count: ' || SQL%ROWCOUNT);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('values of + nested table Error: ' || SQLCODE || ' - ' || SQLERRM);
END;
-- При использованиии values of + pl/sql массивы коллекция может быть какая угодно
l_tsubscripts2(1000) := 3;
BEGIN
FORALL i IN VALUES OF l_tsubscripts2
INSERT INTO a VALUES (l_tn(i));
dbms_output.put_line('Values of Row count: ' || SQL%ROWCOUNT);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('Error: ' || SQLCODE || ' - ' || SQLERRM);
END;
-- Нет элемента -- это ошибка
l_tsubscripts2(1000) := 3;
l_tsubscripts2(1001) := 10;
BEGIN
FORALL i IN VALUES OF l_tsubscripts2
INSERT INTO a VALUES (l_tn(i));
dbms_output.put_line('Values of Row count: ' || SQL%ROWCOUNT);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('No element Error: ' || SQLCODE || ' - ' || SQLERRM);
END;
l_t.delete;
l_t(1) := 1;
l_t(2) := 2;
l_t(3) := 3;
-- Границы округляются до ближайшего целого
FORALL i IN 1.3 .. 2.4
INSERT INTO a VALUES (l_t(i));
dbms_output.put_line('bounds rounded Row count: ' || SQL%ROWCOUNT);
-- SAVE EXCEPTION
-- Если не все гладко, то рейсит ошибку ORA-24381
-- Далее разбираемся с SQL%BULK_EXCEPTIONS
l_t.delete;
l_t(1) := 1;
l_t(2) := 2;
l_t(3) := 3;
l_t(4) := -4;
l_t.delete(2);
BEGIN
FORALL i IN 1 .. 4 SAVE EXCEPTIONS
INSERT INTO a VALUES (l_t(i));
dbms_output.put_line('With save exception Row count: ' || SQL%ROWCOUNT);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('Error: ' || SQLCODE || ' - ' || SQLERRM);
FOR err IN 1 .. SQL%BULK_EXCEPTIONS.COUNT LOOP
dbms_output.put_line('Row ' || err || ' iteration ' || SQL%BULK_EXCEPTIONS(err).ERROR_INDEX
|| ' code ' || SQL%BULK_EXCEPTIONS(err).ERROR_CODE
|| ' message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(err).ERROR_CODE));
END LOOP;
END;
-- SAVE EXCEPTION для разряженных коллекций с INDICES OF
l_t.delete;
l_t(10) := 1;
l_t(20) := 2;
l_t(30) := -3;
BEGIN
FORALL i IN INDICES OF l_t SAVE EXCEPTIONS
INSERT INTO a VALUES (l_t(i));
dbms_output.put_line('With save exception indices of Row count: ' || SQL%ROWCOUNT);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('indices of Error: ' || SQLCODE || ' - ' || SQLERRM);
FOR err IN 1 .. SQL%BULK_EXCEPTIONS.COUNT LOOP
dbms_output.put_line('indices of Row ' || err || ' iteration ' || SQL%BULK_EXCEPTIONS(err).ERROR_INDEX
|| ' code ' || SQL%BULK_EXCEPTIONS(err).ERROR_CODE
|| ' message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(err).ERROR_CODE));
END LOOP;
END;
ROLLBACK;
-- при ошибке все предыдущие операции остаются
l_t.delete;
l_t(1) := 1;
l_t(2) := -1;
BEGIN
FORALL i IN 1 .. 2
INSERT INTO a VALUES (l_t(i));
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('insert with error Row count: ' || SQL%ROWCOUNT);
SELECT COUNT(*) INTO l_cnt FROM a;
dbms_output.put_line('insert with error rows in table ' || l_cnt);
END;
-- использование SQL%BULK_ROWCOUNT
ROLLBACK;
l_t.delete;
l_t(1) := 1;
l_t(2) := 1;
FORALL i IN 1 .. 2
INSERT INTO a VALUES (l_t(i));
-- Индекс в BULK_ROWCOUNT совпадает с тем, что мы обрабатывали
FORALL i IN 2 .. 2
UPDATE a SET a = l_t(i) WHERE a = l_t(i);
dbms_output.put_line('Updated SQL%BULK_ROWCOUNT=' || SQL%BULK_ROWCOUNT(2));
END;
/
1、table create sql:
CREATE TABLE «COMM_APG_TEST».»GOORA_TEMP_VISIT»
( «VISIT_ID» NUMBER(10,0) NOT NULL ENABLE,
«NAME» VARCHAR2(200),
«VAL» NUMBER(10,2),
«VISIT_DATE» DATE,
«MAJOR» VARCHAR2(100)
)
2、code:
sqlText := INSERT INTO GOORA_TEMP_VISIT(VISIT_ID, NAME, VAL, VISIT_DATE, MAJOR) VALUES(:1, :2, :3, :4, :5)
rowNum := 2
visitID := make([]driver.Value, rowNum)
nameText := make([]driver.Value, rowNum)
val := make([]driver.Value, rowNum)
date := make([]driver.Value, rowNum)
major := make([]driver.Value, rowNum)
initalVal := 1.1
for index := 0; index < rowNum; index++ {
visitID[index] = (int64)(index + 1)
if index == 0 {
nameText[index] = «M130201_01»
} else {
nameText[index] = «S170101_01»
}
val[index] = initalVal
date[index] = time.Now().AddDate(0, index, 0)
initalVal += 1.1
if index == 0 {
major[index] = «M-13» <==========
} else {
major[index] = «SP-17» <==========
}
//if index%5 == 0 {
// _, err = stmt.Exec(index, nameText, val, nil)
//} else {
// _, err = stmt.Exec(index, nameText, val, time.Now())
//}
//if err != nil {
// return err
//}
//val += 1.1
}
result, err := conn.BulkInsert(sqlText, rowNum, visitID, nameText, val, date, major)
if err != nil {
return err
}
3、err returned by code snippet witch in «func (session *Session) readPacket() (PacketInterface, error) {«
for loop {
msg, err := session.GetByte()
if err != nil {
return nil, err
}
switch msg {
case 4:
loop = false
session.Summary, err = NewSummary(session)
if err != nil {
return nil, err
}
if session.HasError() {
return nil, session.GetError() <=======
}
4、bulk insert success when i replace the value of field to …
if index == 0 {
major[index] = «M-13» <==========
} else {
major[index] = «S-17» <==========
}
I faced this error Error message: ORA-24381: error(s) in array DML in doing insert operation. There is no exception handling involved in the sql. Can I know now the root cause for this through any view ?
My database is oracle 12c
asked Feb 16, 2020 at 2:43
1
Check if this is similar to this article
I figured that my problem had to do Oracle’s bad estimation of the string length (that led to the issue).
So, I forced aCAST
to limit the string to 30 characters and the issue went away!SELECT NVL(MAX(USERNAME), CAST('NOT_THERE' AS VARCHAR2(30)) ) as usr1 FROM DBA_USERS WHERE USERNAME = 'AN_USER_ID'
In your case, the problem might be a similar data length issue and you might be able to resolve it too with a simple cast.
answered Feb 16, 2020 at 2:58
VonCVonC
1.2m519 gold badges4346 silver badges5164 bronze badges
2
Hi,
While running the job, I’ve the below error msg. Can anyone help in resolving this..
Employees,0: The OCI function OCIStmtExecute returned status 1. Error code: 1400, Error message: ORA-01400: cannot insert NULL into (""SCH"".""EMPLOYEES"".""EMP_ID"").
Employees,0: The following SQL statement failed: INSERT INTO atmosphere.projects(EMP_ID,EMP_NAME,EMP_STATUS,EMP_PHASE,FUN,COST,EMP_TYPE,EMP_TYPE1,NEW_FLAG,CHANGE_FLAG,NO_FLAG,EMP_TRACKING_FLAG,EXCEPTION_STATUS_ID,LOG,HAMATE,WFT,FACK_FLAG,CREATE_USER_ID,EMP_USER_ID,WORK,LAST_UPDATE_DATE,LAST_UPDATE_USER_ID,EXC,REGN_NAME,EXC_REA,GRP_NAME,DIST_NAME,DIV_NAME,CHANGE_ID,NOYPE,PHASE_NAME,CREATE_DATE,STATUS,PARENT_ID,MASTER_ID,REQ_ID,PR,SUP,MS_ID,MS_STATUS,LOG,HAR,COMM,EXP,GOAL,TRANSACTION,NI) VALUES(:EMP_ID,:EMP_NAME,:EMP_STATUS,DEFAULT,:DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,:LOG,:HAR,:COST,:FAS_FLAG,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,:MASTER_ID,:USER_ID,:PR_ID,:SUP_ID,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT,DEFAULT).
Employees,0: The OCI function OCIStmtExecute returned status -1. Error code: 24,381, Error message: ORA-24381: error(s) in array DML. (CC_OraStatement::executeInsert, file CC_OraStatement.cpp, line 1,925)
Employees,0: Number of rows inserted on the current node: 0.
Employees,0: The runLocally() of the operator failed.
Employees,0: Input 0 consumed 500 records.
Employees,0: Operator terminated abnormally: runLocally() did not return APT_StatusOk
node_node1: Player 10 terminated unexpectedly.
main_program: APT_PMsectionLeader(1, node1), player 10 - Unexpected termination by Unix signal 15(SIGTERM).
main_program: Step execution finished with status = FAILED.
main_program: Startup time, 0:13; production run time, 0:08.
Job Info aborted.