I have a situation in such a way that variable value has to be returned based on multiple condition. Initially it is set to passed parameter value. Below is just an algo,
create proc checkFlag (ppleid int, pflag bit, loginid int)
begin
declare vflag bit;
declare vhasRights bit;
declare vIsLocked bit;
declare vlockedUid int;
set vflag=pflag; // assign as passed param
if (vflag = 0)
then
select id, name,vflag as 'IsEditable'
from peopletable
where id=ppleid;
else
-- algo to check whether loginid has rights to edit
if (vhasrights = 1)
then
-- sql statements to check whether record is not write locked into vIsAlreadyLocked
if (vIsAlreadyLocked = 1)
then
--- sql statements to check whether locked user is login user in vlockedUid
if (vlockedUid = ploginid)
then
set vflag =1;
else
set vflag=0;
end if;
else
set vflag=0;
end if;
select id, name,vflag as 'IsEditable'
from peopletable
where id=ppleid;
end if;
end;
A. Sp. call withing sqleditor
call checkflag(values.....); returns appropriate value
B. Sp. call from c#
internal void ReadPpl()
{
bool initFlag=<flag based on client end condition>;
. code for creating connection and command of type storedproc
.
MysqlParameter prm=new parameter("pflag", initFlag);
.
.
. cmd.parameter(prm)
using (mysqldatareader rdr=cmd.ExecuteReader())
{
if (rdr.HasRows())
{
rdr.Read();
**bool IsWritable=Convert.ToBoolean(Convert.ToInt32(rdr["IsEditable"])); // This is the line that returns value as passed parameter but not based on conditions in db and params **
}
Any help will be appreciated.
Thanks in advance.
Got problem solved.
call proc(#flag...)
resulted #flag other than 0. So the else condition was executed.
But
MySqlParameter prm=new(....,Value=initValue);
considered intiValue as 0 hence, the only the first if condition in stored proc. was executed every time.
Related
Can I define the stored procedure without using the RefCursor ? (like "return refcursor")
I do not want to use OracleDbType.RefCursor because it is not sent as dbparameter in other databases.
Also DbParameter.DbType = OracleDbType.RefCursor; does not supported
I do not want to define "retval IN OUT SYS_REFCURSOR" in the code below. Is there another way?
CREATE OR REPLACE procedure SYSTEM.customer_select_row(
p_email IN CUSTOMER.Email%TYPE,
p_password IN CUSTOMER."Password"%TYPE,
retval IN OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN retval FOR
SELECT CustomerId, FirstName, LastName FROM CUSTOMER
WHERE Email = p_email AND "Password" = p_password
END customer_select_row;
You could use a pipeline Function,
It is a function that works exacltly as a table
you can call it this way
SELECT *
FROM TABLE(TEST_PIPELINE.STOCKPIVOT(10));
the TEST_PIPELINE.STOCKPIVOT(10) is a function
you can build it this way:
create or replace PACKAGE TEST_PIPELINE AS
-- here you declare a type record
type t_record is record
(
field_1 VARCHAR2(100),
field_2 VARCHAR2(100));
-- declare a table type from your previously created type
TYPE t_collection IS TABLE OF t_record;
-- declare that the function will return the collection pipelined
FUNCTION StockPivot(P_LINES NUMBER) RETURN t_collection PIPELINED;
END;
/
create or replace PACKAGE BODY TEST_PIPELINE IS
FUNCTION StockPivot(P_LINES NUMBER) RETURN t_collection PIPELINED IS
-- declare here a type of the record
T_LINE T_RECORD;
BEGIN
-- here is a loop example for insert some lines on pipeline
FOR I IN 1..P_LINES LOOP
-- inser data on your line this way
T_LINE.field_1 := 'LINE - ' || I;
T_LINE.field_2 := 'LINE - ' || I;
-- then insert insert the line for result (this kind of functions should not have a return statement)
PIPE ROW (T_LINE );
END LOOP;
END;
END;
I have a C# method which takes a SQL string statement and saves the data into xml format.
public XmlDocument GetDBRequestXml(String sql)
{
}
I have a stored procedure with output parameter as table. Is there any way to pass this stored procedure as an executable single SQL statement in the above C# method? Can somebody please help me on this!!!
create or replace PACKAGE BODY EMPLOYEE_DETAILS AS
PROCEDURE GET_EMPLOYEES(
EMP_DEPT_ID EMPLOYEES.DEPARTMENT_ID%TYPE,
EMP_SALARY employees.salary%TYPE,
TBL_EMPLOYEES OUT TABLE_EMPLOYEES)
IS
LC_SELECT SYS_REFCURSOR;
LR_DETAILS DETAILS;
TBL_EMPLOYEE EMPLOYEE_DETAILS.TABLE_EMPLOYEES := EMPLOYEE_DETAILS.TABLE_EMPLOYEES();
BEGIN
OPEN LC_SELECT FOR
SELECT EMPLOYEE_ID, FIRST_NAME, LAST_NAME
FROM EMPLOYEES
WHERE DEPARTMENT_ID = EMP_DEPT_ID AND
EMPLOYEES.SALARY > EMP_SALARY;
LOOP
FETCH LC_SELECT INTO LR_DETAILS;
EXIT WHEN LC_SELECT%NOTFOUND;
IF LR_DETAILS.EMPLOYEE_ID > 114 THEN
TBL_EMPLOYEE.extend();
TBL_EMPLOYEE(TBL_EMPLOYEE.count()) := LR_DETAILS;
END IF;
END LOOP;
CLOSE LC_SELECT;
TBL_EMPLOYEES := TBL_EMPLOYEE;
END GET_EMPLOYEES;
END EMPLOYEE_DETAILS;
I had a good run at this today. I don't know if you still need it but this is good to understand. You can output this from stored proc:
CREATE OR REPLACE PACKAGE Eidmadm.TestPkg AS
TYPE stringTbl IS TABLE OF varchar2(250) INDEX BY BINARY_INTEGER;
PROCEDURE TestProc (p_strings out stringTbl );
END;
The other table, like below, didn't work
TYPE numTbl IS TABLE OF varchar2(100);
The c# code for this is:
OracleParameter p2 = new OracleParameter(":p_strings", OracleDbType.Varchar2, ParameterDirection.Output);
p2.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
p2.Size = 100; // allocate enough extra space to retrieve expected result
// assign amount of space for each member of returning array
p2.ArrayBindSize = Enumerable.Repeat(250, 100).ToArray();
cmd.Parameters.Add(p2);
cmd.ExecuteNonQuery();
// And this is how you retrieve values
OracleString[] oraStrings = (OracleString[])p2.Value;
string[] myP2Values = new string[oraStrings.Length];
for (int i = 0; i < oraNumbers.Length; i++)
myP2Values[i] = oraStrings[i].Value;
**But most important is this: **
When you fill your pl/sql table, it needs to start from something larger than `0`, and preferably from `1`. Because and also - if you have index with skipped numbers, i.e. `2,4,6,8`, all those spaces will be part of returning `oracle array` and there will be `oracle null` in them. You would need to check for `null` in your loop
if !oraStrings[i].IsNull {....}
else {....}
Enjoy!
I have a oracle stored procedure which updates a table with the following statement.
update boxes
set location = 'some value'
where boxid = passed value
I have a page where the user selects 100+ boxes and updates them with a new location value. Currently, I have to call the stored procedure 100+ times to update each box(by passing a boxid each time).
I want to know how I can pass a list of boxids from C# into the stored procedure so that I have to call the stored procedure just one time.
I am hoping to use a where in(boxids) kind of where clause in the update statement.
Please let know how can I achieve this. Thanks in advance!
Oracle allows you to pass arrays of values as parameters. Borrowing from this SO question and this one you can define an INT_ARRAY type like this:
create or replace type CHAR_ARRAY as table of INTEGER;
Then define your stored procedure as:
CREATE OR REPLACE PROCEDURE product_search(
...
myIds IN CHAR_ARRAY,
...)
AS
SELECT ...
...
WHERE SomeIdField IN (Select column_value FROM TABLE(myIds))
...
You can then pass the list of values by setting the OracleParameter.CollectionType property like this:
OracleParameter param = new OracleParameter();
param.OracleDbType = OracleDbType.Int32;
param.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
I'd create a new procedure, designed to handle a list of values. An efficient approach would be to load the multiple values into a global temp table, using a bulk insert, and then have the procedure update using a join to the GTT.
A notional example would look like this:
OracleTransaction trans = conn.BeginTransaction(IsolationLevel.RepeatableRead);
OracleCommand cmd = new OracleCommand(insertSql, conn, trans);
cmd.Parameters.Add(new OracleParameter("BOX_ID", OracleDbType.Number));
cmd.Parameters[0].Value = listOfBoxIds; // int[] listOfBoxIds;
cmd.ExecuteArray();
OracleCommand cmd2 = new OracleCommand(storedProc, conn, trans);
cmd2.CommandType = CommandType.StoredProcedure;
cmd2.ExecuteNonQuery();
trans.Commit();
Your PL/SQL block may look like this one:
CREATE OR REPLACE PACKAGE YOUR_PACKAGE AS
TYPE TArrayOfNumber IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
PROCEDURE Update_Boxes(boxes IN TArrayOfNumber );
END YOUR_PACKAGE;
/
CREATE OR REPLACE PACKAGE BODY YOUR_PACKAGE AS
PROCEDURE Update_Boxes(boxes IN TArrayOfNumber) is
BEGIN
FORALL i IN INDICES OF boxes
update boxes
set location = boxes(i)
where boxid = ...;
END Update_Boxes;
END YOUR_PACKAGE;
The C# code you get already in answer from Panagiotis Kanavos
I understand your concern - the round trips will be taxing.
Unfortunately I don't have anything to test, but you can try
Oracle bulk updates using ODP.NET
or
-- edit1: go with Panagiotis Kanavos's answer if your provider supports it, else check below --
-- edit12 as highlighted by Wernfried, long is deprecated. Another thing consider is max length varchar2: it doesn't scale on a very big set. Use the one below as the last resort. --
changing your stored procedure to accept string
implement string_2_list in asktom.oracle.com.
create or replace type myTableType as table of varchar2 (255);
create or replace function in_list( p_string in varchar2 ) return myTableType
as
l_string long default p_string || ',';
l_data myTableType := myTableType();
n number;
begin
loop
exit when l_string is null;
n := instr( l_string, ',' );
l_data.extend;
l_data(l_data.count) :=
ltrim( rtrim( substr( l_string, 1, n-1 ) ) );
l_string := substr( l_string, n+1 );
end loop;
return l_data;
end;
Above is early variant and splice to varchar2, but if you read more (including other threads) at that site
you'll find more advanced variants (optimized, better exception handling)
ALTER PROCEDURE [dbo].[SelectCompletionNonCompletionCourseReport]
#LearnerName NVARCHAR(510) = NULL,
#ManagerId INT = NULL,
#CourseId INT = NULL,
#StartDateFrom SMALLDATETIME = NULL,
#StartDateTo SMALLDATETIME = NULL,
#TeamList XML = NULL,
#JobID NVARCHAR(max)=NULL,
#CourseStatus NVARCHAR(20)=NULL,
#ReportAdminID INT=0,
#ReportTeamList NVARCHAR(max)=NULL,
#RowsTotal int = 0,
#PageIndex int = 1,
#RowsPerPage int = 10
AS
BEGIN
DECLARE #TblCrieiria TABLE
(
id INT IDENTITY(1, 1),
areacode NVARCHAR(11),
regioncode NVARCHAR(11),
teamcode NVARCHAR(11)
)
IF #TeamList IS NULL
BEGIN
INSERT INTO #TblCrieiria VALUES(NULL,NULL,NULL)
END
BEGIN
This is the beginning of the procedure...
using (Database db = new Database(DScape.DAL.Config.ConfignPropertyName.DSCAPELMS_CONNECTION_STRING_NAME))
{
var cmd = new SqlCommand
{
CommandText = "SelectCompletionNonCompletionCourseReport",
CommandType = CommandType.StoredProcedure
};
cmd.Parameters.AddWithValue("#LearnerName", LearnerName);
cmd.Parameters.AddWithValue("#ManagerId", ManagerId);
cmd.Parameters.AddWithValue("#CourseId", CourseId);
cmd.Parameters.AddWithValue("#StartDateFrom", StartDateFrom);
cmd.Parameters.AddWithValue("#StartDateTo", StartDateTo);
cmd.Parameters.AddWithValue("#TeamList", TeamList);
cmd.Parameters.AddWithValue("#JobID", JobID);
cmd.Parameters.AddWithValue("#CourseStatus", CourseStatus);
cmd.Parameters.AddWithValue("#ReportAdminID", ReportAdminID);
cmd.Parameters.AddWithValue("#ReportTeamList", ReportTeamList);
cmd.Parameters.AddWithValue("#PageIndex", 1);
DataSet dsClient = db.GetDataSet(cmd);
if (dsClient.Tables.Count > 0)
return dsClient.Tables[0];
else
return null;
}
This is the method which communicates with the procedure, and it gaves me an error
Parameter does not exist as a stored procedure parameter/ function/procedure take too many arguments...
It's about #PageIndex parameter. Doesn't matter what is the value, we don't talk for values here but for parameter which is defined in the stored procedure but doesn't work?
And for the record, this problem did pop-up today w/o any code writing/modifying just appeared as I tried to do that report, when yesterday it was all good...I have a teammate which is next to me with absolute the same code both in sql and c# and it works just fine on his pc, but mine throws this errors, I'm trying to resolve this from 3 hours and I am completely out of answers , so please give me direction in which should I continue to resolve this .....................
and I say again, the problem is not from the connection to DB or type of the parameter or the value, the error is committed with the parameter itself - does not exist in the procedure, which is insane in my opinion.
Given that all parameters are optional, you are not required to explicitly provide any of them from your client code. Default values will be provided for you by SQL Server. The contract explictly states it in the stored procedure's signature.
An optional parameter is exactly that: optional. If you had provided the incorrect number of parameters, SQL Server would have returned a different error, indicating that the number of parameters was incorrect. This is not the case. Instead, you are seeing that you are asking for a parameter that is undefined, which indicates that the stored procedure signature you think you are calling does not match the stored procedure signature you are actually calling.
Verify that you are both connecting to the same database instance. If you are not, verify that the stored procedure is identical on both database instances.
parameter count doesnt match. check the params again.
You have to send parameters for rowstotal and rowsperpage as well because you have declared them at the top before "begin" clause.
If you do not want to send that params and they will be just constant, please declare them below as variable or constant, not a parameter.
i.e.
CREATE PROCEDURE DeleteById
#TableName sysname,
#Id int
AS
BEGIN
DECLARE #StrId AS VARCHAR(50)
SET #StrId = CONVERT(VARCHAR(50), #Id)
--any sp code here
END
Hope this helps.
I have a customer table in sqlserver which contains a rowversion field and I am incrementing it everytime I update the record,
I just have to check with
if(Customer.rowversion=#roeversion ) where customerID=#customerID
execute the update.
else RAISERROR('Update cannot be executed. There is a row version conflict.', 16, 1)
So have to now pass an out param from my c# code and return the error value. and also
- Get the Error Code for the statement just executed.
SELECT #ErrorCode=##ERROR
So how should I return the value from SQLSERVER update query into my c# code so that I can display the message.
If you're calling your sproc via ado.NET, then the SqlParameter you pass to the sproc would be set up like this:
SqlParameter P = new SqlParameter("Name of your column", SqlDbType.Int);
P.Direction = ParameterDirection.Output;
//call your sproc
int result = (int)P.Value;
EDIT
Since you're using Linq-to-SQL, adding this sproc into the methods sections should create a c# method signature for this sproc with the out parameter added for you.
If you do not already, you should have your database code in a stored procedure. The stored procedure would look something like:
CREATE PROCEDURE s_my_procedure
#RowVersion int ,
#CustomerID int ,
... additional fields here
#ErrorCode INT OUTPUT
AS
IF EXISTS(SELECT 1
FROM Customer
WHERE RowVersion = #RowVersion
AND CustomerID = #CustomerID)
BEGIN
UPDATE Customer
SET ...
WHERE RowVersion = #RowVersion
AND CustomerID = #CustomerID
SET #ErrorCode = 0
END
ELSE
BEGIN
SET #ErrorCode = 1234 -- Something meaningful to your app
END
You should try to avoid raising errors whenever possible.
Then, assuming you have a stored procedure executed by a command:
cmd.ExecuteNonQuery();
int ErrorCode = 0;
// Note that if you are not sure about your sp, you should test this for dbnull.value first
ErrorCode = Convert.ToInt32(cmd.Parameters["#ErrorCode"].Value);