rus_el_di 1 / 1 / 0 Регистрация: 30.05.2021 Сообщений: 25 |
||||
1 |
||||
20.06.2022, 09:48. Показов 1843. Ответов 8 Метки нет (Все метки)
Всем добрый день.
__________________
0 |
250 / 149 / 78 Регистрация: 12.04.2022 Сообщений: 617 |
|
20.06.2022, 09:52 |
2 |
1. DDL таблицы product .
0 |
rus_el_di 1 / 1 / 0 Регистрация: 30.05.2021 Сообщений: 25 |
||||
20.06.2022, 09:57 [ТС] |
3 |
|||
DDL таблицы product
подозрительный! Вбивал данные в таблицу по такому формату
0 |
PaulWist 250 / 149 / 78 Регистрация: 12.04.2022 Сообщений: 617 |
||||
20.06.2022, 10:22 |
4 |
|||
Попробуйте дату загнать так:
0 |
1438 / 903 / 342 Регистрация: 31.05.2012 Сообщений: 3,169 |
|
20.06.2022, 11:01 |
5 |
а зачем параметры в процедуре коль не используются? и если они есть, то при обращении к процедуре нужно передать фактические значения параметров. у тебя же и сообщение об этом
0 |
1 / 1 / 0 Регистрация: 30.05.2021 Сообщений: 25 |
|
20.06.2022, 11:40 [ТС] |
6 |
не используются а какие параметры используются? как используются, зачем, у меня мозг ломается уже, 3 дня процедуру упрощаю, уже не понимаю что к чему соединяется
0 |
PaulWist 250 / 149 / 78 Регистрация: 12.04.2022 Сообщений: 617 |
||||
20.06.2022, 12:10 |
7 |
|||
Сообщение было отмечено rus_el_di как решение Решение
а какие параметры используются? На вход процедуре определены 10 параметров, при вызове exec их надо перечислить, либо создать процедуру без параметров, например:
0 |
1 / 1 / 0 Регистрация: 30.05.2021 Сообщений: 25 |
|
20.06.2022, 13:13 [ТС] |
8 |
процедуре хоть это и не решило мою проблему, так как мне сказали что, а если я захочу другие параметры ввести, но, меня очень удивило что можно сделать процедуру вот так без параметров и она выглядит очень просто и красиво, спасибо большое за советы
0 |
Модератор 4192 / 3031 / 577 Регистрация: 21.01.2011 Сообщений: 13,109 |
|
21.06.2022, 14:11 |
9 |
Вбивал данные в таблицу по такому формату Для сведения. Данные в поле date хранятся совсем не так, как ты вбиваешь или выводит на экран. Там хранятся числа, а на экран выводится с пом. функций преобразования (возможно по инициативе клиентской программы, например SQL Developer). Поэтому правильно использовать функции to_date и to_char, как было сказано выше. Иначе результат от NLS-установок сессии и тебе может повезти, а может нет
0 |
I know I’ve asked this online before, but is Tourette Syndrome an occupational hazard of being a programmer? I swear, it’s things like this that are going to cause me to develop it.
I’ve just been through one of the more frustrating exercises, debugging database calls that look just fine.
Problem
Contents
- Problem
- Solution
- Why This Is
- Share this:
When calling Oracle PL/SQL stored procedures from C#, it appears many people get PLS-00306 responses with the message “wrong number or types of arguments in call to ‘my_procedure’” (with the obvious substitution on procedure name). I recently spent a few days trying to track down why this was happening in my program… intermittently. Some reports worked, some didn’t. I finally tracked the cause of the problem down, and it’s a subtle one unless you know about it already.
Oracle treats null strings and empty strings more or less interchangeably in most contexts. You can’t compare the empty string to itself (” does not equal ”, but is null — in many RDBMS, the opposite is true, the empty string and the null string are distinctly different). This is itself a gotcha that people usually learn the hard way.
The way this one bit me was pretty subtle, in that it’s really not obvious looking at the code how it fails. I compared the parameter binding carefully, and all arguments were present with the correct types.
Let’s use a really simple example:
CREATE TABLE t ( s VARCHAR2(10) ); CREATE OR REPLACE PROCEDURE myproc( in_s IN VARCHAR2 ) IS BEGIN INSERT INTO t (s) VALUES (in_s); END;
Nice and simple — a table with a single varchar2 field, and a procedure that inserts a new value into the table. Believe it or not, it’s really easy for this to break when called from C#.
Pretty much all documentation I’ve seen for calling Oracle stored procedures from C# involve setting up the connection (not shown here), creating an OracleCommand, binding the parameters, and executing the command. Straightforward, but there’s a nonobvious gotcha in how it actually works.
A typical example might look something like this:
OracleConnection conn = new OracleConnection(connectString); conn.Open(); OracleCommand cmd = new OracleCommand(); cmd.Connection = conn; cmd.CommandText = "myproc"; OracleParameter op = new OracleParameter("in_s", OracleType.VarChar); op.Value = "my value"; op.Direction = ParameterDirection.Input; cmd.Parameters.Add(op); cmd.ExecuteNonQuery(); cmd.Dispose();
This can be expected to work. It would be really tedious to do every time (and more importantly, every place) you want to call myproc(), so it is likely to get wrapped in a method:
void CallMyProc(string s) { OracleConnection conn = new OracleConnection(connectString); conn.Open(); OracleCommand cmd = new OracleCommand(); cmd.Connection = conn; cmd.CommandText = "myproc"; OracleParameter op = new OracleParameter("in_s", OracleType.VarChar); op.Value = s; op.Direction = ParameterDirection.Input; cmd.Parameters.Add(op); cmd.ExecuteNonQuery(); cmd.Dispose(); }
This is easy to call and works… as long as ‘s’ is not null. That is,
string s = "foo"; CallMyProc(s);
is just fine. No problem. For that matter,
string s = ""; CallMyProc(s);
also works.
However, and this is what has been chewing on me,
does not work, producing said ‘PLS-00306’ error. Compare the stored procedure to the C# invocation — the stored procedure takes a single argument, a varchar2 (‘string’), the single C# parameter is defined as expecting a varchar and something declared as string is bound. This should work, right?
Wrong. By the time the driver sees it, a not-empty-but-null string is not a string any more. Regardless of what C# says.
Solution
I’ll skip ahead to the solution, then come back for why this is.
void CallMyProc(string s) { OracleConnection conn = new OracleConnection(connectString); conn.Open(); OracleCommand cmd = new OracleCommand(); cmd.Connection = conn; cmd.CommandText = "myproc"; OracleParameter op = new OracleParameter("in_s", OracleType.VarChar); op.Value = String.IsNullOrEmpty(s) ? "" : s; op.Direction = ParameterDirection.Input; cmd.Parameters.Add(op); cmd.ExecuteNonQuery(); cmd.Dispose(); }
It’s a simple change on line 9 and appears to have resolved the problem.
Why This Is
Figuring this out required a little bit of knowledge about how parameter binding and the execute immediate instruction work in Oracle.
Execute immediate with a bound parameter can look something like this:
EXECUTE IMMEDIATE 'begin myproc(:1); end;' using s;
This is just fine. However,
EXECUTE IMMEDIATE 'begin myproc(:1); end;' using NULL;
fails because null is typeless. Oracle cannot determine what type ‘:1’ is supposed to be and chokes (PLS-00457).
I’m not entirely sure how PLS-00457 becomes PLS-00306 here, but it seems related. When invoking an Oracle command from C#, it appears the runtime library is doing something close to
string dbcommand = "execute immediate 'begin " + command + "(" + /* build parameter list */ + "); end;' using " + /* build argument list */ + ";";
When one of the parameters has a null string value, this fails with a PLS-00306 error message. Using an empty string value instead (non-null C# string — has an address but no content) works just fine.
So, null and empty string are treated as the same thing (but not equal, oh no). Except when they aren’t.
How about that Tourette Syndrome? I’m sure I’m coming down with it.
Dealing with «PLS-306: Wrong number or types of arguments» across schemas
The PLS-306 compile error can be a real pain in the you know what. There are many possible causes, including typo in name with named notation, wrong datatype, wrong number of arguments….you know: just like the error message says.
But one of the most puzzling situations occurs when you need to execute a subprogram that is defined in another schema of your instance, or in another instance entirely, and the type of at least one parameter in the subprogram is «user-defined» (not a built-in datatype).
So let’s first state the rule that should guide you in this scenario, and then I will offer up some code so that you can verify it for yourself.
The rule:
Even if a user-defined type in one schema has the same name and same structure as a type in another schema, they are different types.
And now some code to drive the point home. Assuming you have two schemas defined, schema1 and schema2:
CONNECT schema1/schema1
CREATE OR REPLACE TYPE number_nt IS TABLE OF NUMBER
/
CREATE OR REPLACE PROCEDURE show_nt_count (n IN number_nt)
IS
BEGIN
DBMS_OUTPUT.put_line (n.COUNT);
END;
/
GRANT EXECUTE ON show_nt_count TO schema2
/
GRANT EXECUTE ON number_nt TO schema2
/
And now I can successfully invoke show_nt_count from schema2 as follows:
CONNECT schema2/schema2
DECLARE
n schema1.number_nt := schema1.number_nt (1);
BEGIN
schema1.show_nt_count (n);
END;
/
But that’s because I am defining a local variable based on the schema1 type.
If I try to use the «same» type that is defined in schema2, I get the PLS-00306 eorr:
CREATE OR REPLACE TYPE number_nt IS TABLE OF NUMBER
/
DECLARE
n number_nt := number_nt (1);
BEGIN
schema1.show_nt_count (n);
END;
/
ORA-06550: line 4, column 1:
PLS-00306: wrong number or types of arguments in call to ‘PLCH_USE_N’
ORA-06550: line 4, column 1:
PL/SQL: Statement ignored
06550. 00000 — «line %s, column %s:n%s»
*Cause: Usually a PL/SQL compilation error.
This problem does not always occur with datatypes defined in built-in packages. For example, DBMS_SQL comes with several pre-defined collection types. If I use one of those with my two schemas, I will not get the PLS-00306 error:
CONNECT schema1/schema1
CREATE OR REPLACE PROCEDURE show_nt_count (
n IN DBMS_SQL.number_table)
IS
BEGIN
DBMS_OUTPUT.put_line (n.COUNT);
END;
/
GRANT EXECUTE ON show_nt_count TO schema2
/
CONNECT schema2/schema2
DECLARE
n DBMS_SQL.number_table;
BEGIN
n(1) := 1;
schema1.show_nt_count (n);
END;
/
Ah, but why don’t I get the error? As Kim Berg Hansen points out in the comments, it is not because there is any secret and mysterious action taken by the PL/SQL compiler («Built-in packages are special.»). Instead, it is because these are two schemas in the same instance, and each instance of the Oracle RDBMS has a single copy of DBMS_SQL defined in SYS.
In other words, both schema1 and schema2 are referencing the same type in the same package. So, of course, the compiler has nothing to complain about.
If, however, you try to do the same thing via a database link, the story changes (even if the database link ends up pointing to the same instance) . If I execute that last block, with the only change being to execute the schema1.show_nt_count as a remote stored procedure:
DECLARE
n DBMS_SQL.number_table;
BEGIN
n(1) := 1;
schema1.show_nt_count@my_db_link (n);
END;
/
Then I will get a PLS-00306 error, since the DBMS_SQL package referenced in my block can no longer be resolved by the compiler to be the same DBMS_SQL package in the now-remote schema1.
To get rid of this problem, I can force the resolution of DBMS_SQL in the above block to be the same as that used by the remote procedure call:
DECLARE
n DBMS_SQL.number_table@my_db_link;
BEGIN
n(1) := 1;
schema1.show_nt_count@my_db_link (n);
END;
/
And then no more PLS-00306 error!