I am getting a index out of range error when trying to get a string value from a datareader. The column USER_ROLE which is the only column from a INNER JOIN condition. It was working and for some reason has now started throwing this index out of range error. I've verified the actual stored procedure works via SSMS and the column is being returned.
Below is the code for the stored procedure
ALTER PROCEDURE [dbo].[usp_GetUsersLogonInformation]
(
-- inactive = 0, active = 1, all = 2
#active int = 2
)
AS
BEGIN
DECLARE #whereClauseNeeded bit = 1
DECLARE #whereClause nvarchar(100) = concat(' WHERE usr.ACTIVE = ', #active)
DECLARE #sqlCmd nvarchar(max)= 'SELECT
usr.USER_PK,
usr.PRINCIPAL_ID,
usr.AA_USER_FK,
usr.FIRST_NAME,
usr.LAST_NAME,
usr.[USER_NAME],
usr.EMAIL_ADDRESS,
usr.ACTIVE,
usr.LV_USER_ROLE_FK,
lvur.USER_ROLE,
usr.CREATED_BY,
usr.CREATED_SYSDATE
FROM dbo.USERS usr
INNER JOIN dbo.LV_USER_ROLES lvur ON lvur.LV_USER_ROLE_PK = usr.LV_USER_ROLE_FK'
IF #active = 0 OR #active = 1
BEGIN
set #sqlCmd = concat(#sqlCmd, #whereClause)
END
EXEC sp_executesql #sqlCmd
END
the c# code retrieving the data
using (SqlConnection dbConn = theVoiceSqlHelpers.GetDbConnection())
{
using (SqlCommand sqlCmd = new SqlCommand(USP_GET_USER_INFO, dbConn))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Parameters.AddWithValue("#whereClause",string.Format("USER_NAME = \'{0}\'", txbxUserName.Text));
SqlDataReader dr = sqlCmd.ExecuteReader();
while (dr.Read())
{
user = new Tbl_Users();
user.USER_PK = dr.GetInt32(dr.GetOrdinal("USER_PK"));
user.PRINCIPAL_ID = dr.GetInt32(dr.GetOrdinal("PRINCIPAL_ID"));
user.AA_USER_FK = dr.GetInt32(dr.GetOrdinal("AA_USER_FK"));
user.FIRST_NAME = dr.GetString(dr.GetOrdinal("FIRST_NAME"));
user.LAST_NAME = dr.GetString(dr.GetOrdinal("LAST_NAME"));
user.USER_NAME = dr.GetString(dr.GetOrdinal("USER_NAME"));
user.EMAIL_ADDRESS = dr.GetString(dr.GetOrdinal("EMAIL_ADDRESS"));
user.ACTIVE = dr.GetBoolean(dr.GetOrdinal("ACTIVE"));
user.LV_USER_ROLE_FK = dr.GetInt32(dr.GetOrdinal("LV_USER_ROLE_FK"));
user.USER_ROLE = dr.GetString(dr.GetOrdinal("USER_ROLE"));
user.CREATED_BY = dr.GetString(dr.GetOrdinal("CREATED_BY"));
user.CREATED_SYSDATE = dr.GetDateTime(dr.GetOrdinal("CREATED_SYSDATE"));
}
dr.Close();
}
}
I have ensure the column name is correct however I am now stuck at this new found exception.
Has anyone seen this behavior before. My apologies if I am overlooking and obvious but could use an extra set of eyes on this.
LV_USER_ROLES Table
USERS Table
Charlieface's comment resolved the exception. In this database there is 2 similiar named usp's and I was calling the wrong one.
Related
I've been developing a stored procedure in order to get a register from a table so I built the following query to achieve this:
ALTER procedure [dbo].[procedure_odd]
#codSeccion int,
#NomDoce varchar(500),
#codMate varchar(500)
as
begin
declare #hora varchar(50);
set #hora = (
select b.man_nomhor
from ra_hpl_horarios_planificacion a
inner join ra_man_grp_hor b on a.hpl_codman = b.man_codigo
where a.hpl_codcil = 100
and a.hpl_codemp = (select emp_codigo from pla_emp_empleado
where emp_nombres_apellidos = #NomDoce)
and a.hpl_codmat = #codMate
and a.hpl_descripcion = #codSeccion)
return #hora
end
I've tested this query (ONLY the query not the stored procedure with the query) in my SQL Server console and it works just fine. The problem is when I call it from C# it doesn't work no matter what I try! Also I tried to develop a stored procedure with output parameter but with no result.
Also I've tried this other way(which works so good and fine!):
select b.man_nomhor
from ra_hpl_horarios_planificacion a
inner join ra_man_grp_hor b on a.hpl_codman = b.man_codigo
where a.hpl_codcil = 100
and a.hpl_codemp =
(select emp_codigo from pla_emp_empleado
where emp_nombres_apellidos = 'julio escobar')
and a.hpl_codmat = 'FONO-I'
and a.hpl_descripcion = 1;
Here is my code on C# (My 11000 solution):
public String horarios(int Seccion, string NomDocent, string CodMate)
{
cn.Open();
cmd.Connection = cn;
cmd.CommandText = "select b.man_nomhor from ra_hpl_horarios_planificacion
a inner join ra_man_grp_hor b on a.hpl_codman = b.man_codigo where
a.hpl_codcil = 100 and a.hpl_codemp =(select emp_codigo from
pla_emp_empleado where emp_nombres_apellidos = '" + NomDocent +
"') and a.hpl_codmat = '" + CodMate + "' and a.hpl_descripcion = '" + Seccion + "'";
dr = cmd.ExecuteReader();
if (dr.HasRows)
{
if (dr.Read())
{
msj = dr[0].ToString();
}
}
cn.Close();
return msj;
}
When I run my Visual Studio it doesn't show any error at all but in the variable MSJ it set an empty STRING like this MSJ = "";
This must be really easy but it just that (believe) I've tried so hard to get to the solution with bno results, please help!
It looks like Seccion (and thus a.hpl_descripcion) are integers, but your query is placing apostrophes around it like a literal.
Try removing the apostrophes:
... + "' and a.hpl_descripcion = " + Seccion;
If that's indeed the issue, parameterizing your query can eliminate these kinds of mistakes:
cmd.CommandText = "... and a.hpl_codemp =(select emp_codigo from pla_emp_empleado where emp_nombres_apellidos = #NomDocent) and a.hpl_codmat = #CodMate and a.hpl_descripcion = #Seccion";
cmd.AddParameterWithValue("#NomDocent", NomDocent);
cmd.AddParameterWithValue("#CodMate", CodMate);
cmd.AddParameterWithValue("#Seccion, Seccion);
dr = cmd.ExecuteReader();
Few things:
if you want this to be a stored procedure, be sure to set command.CommandType = CommandType.StoredProcedure.
you do not need to call return or set a variable to return the text you have. instead, just have a select statement return the one field:
ALTER procedure [dbo].[procedure_odd]
#codSeccion int,
#NomDoce varchar(500),
#codMate varchar(500)
as
begin
select top 1 b.man_nomhor from ra_hpl_horarios_planificacion a inner join
ra_man_grp_hor b on a.hpl_codman = b.man_codigo where a.hpl_codcil = 100
and a.hpl_codemp = (select emp_codigo from pla_emp_empleado
where emp_nombres_apellidos = #NomDoce) and a.hpl_codmat = #codMate and
a.hpl_descripcion = #codSeccion)
END
once this is done, just execute the stored procedure with a dataReader, that will return your value. if you need more information, I can edit my answer to clarify.
I created a SQL Server stored procedure in which I use a cursor and I created an ASP.NET method to execute that procedure. I'm using a SqlDataReader and while(reader.Read()) to read the values. The problem is that the cursor in the stored procedure returns many rows, but the method reads the first record only. Anyone can help?
stored procedure:
create procedure [dbo].[GetMenusUserGroupCanView]
(
#UserGroupID int
,#LanguageID int
) as begin
declare #MenuID int
declare #Title varchar(255)
declare db_cursor cursor for
(
select MenuID
from TrioGate.dbo.Sys_UserGroupMenus
where UserGroupID=#UserGroupID and ViewFlag='true'
)
open db_cursor
fetch next from db_cursor into #MenuID
while ##fetch_status = 0 begin
select
Sys_Menus.MenuID
,Sys_Menus.ParentMenuID
,Sys_Menus.DescriptionLabelID
,Sys_Menus.TitleLabelID
,Sys_Menus.TooltipLabelID
,Sys_Menus.[Icon]
,Sys_Menus.[MenuName]
,Sys_Menus.[MenuTypeID] as MenuType
,[dbo].[Get_ParentMenu_Name](Sys_Menus.ParentMenuID) as ParentMenuName
,[dbo].[Get_Label_Description](Sys_Menus.TitleLabelID,1) as Title
,[dbo].[Get_Label_Description](Sys_Menus.TooltipLabelID,1) as Tooltip
,[dbo].[Get_Label_Description](Sys_Menus.DescriptionLabelID,1) as [Description]
,Sys_Menus.[MainTableName] as [Table]
,Sys_Menus.[Seq],Sys_Menus.[MenuPath],Sys_Menus.ActivateLog,Sys_Menus.MenuID
from Sys_Menus
left join Sys_LabelDetails
on Sys_Menus[TitleLabelID] = Sys_LabelDetails[LabelID]
where Sys_LabelDetails.LanguageID = #LanguageID
and MenuID = #MenuID
fetch next from db_cursor into #MenuID
end
close db_cursor
deallocate db_cursor
end
Method:
public List<Menu> GetMenusUserGroupCanView(int UserGroupID, int LanguageID)
{
List<Menu> list = new List<Menu>();
SqlCommand cmd = new SqlCommand("GetMenusUserGroupCanView ", Connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#UserGroupID", UserGroupID);
cmd.Parameters.AddWithValue("#LanguageID", LanguageID);
try
{
Connection.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Menu entry = new Menu();
entry.MenuID = (int)reader["MenuID"];
entry.ParentMenuID = (int)reader["ParentMenuID"];
entry.ActivateLog = (bool)reader["ActivateLog"];
entry.Description = reader["Description"].ToString();
entry.DescriptionLabelID = (int)reader["DescriptionLabelID"];
entry.Icon = (byte[])reader["Icon"];
entry.MainTableName = reader["Table"].ToString();
entry.MenuName = reader["MenuName"].ToString();
entry.MenuPath = reader["MenuPath"].ToString();
entry.MenuTypeID = reader["MenuType"].ToString();
entry.ParentMenuName = reader["ParentMenuName"].ToString();
entry.Seq = (int)reader["Seq"];
entry.Title = reader["Title"].ToString();
entry.TitleLabelID = (int)reader["TitleLabelID"];
entry.Tooltip = reader["Tooltip"].ToString();
entry.ToolTipLabelID = (int)reader["ToolTipLabelID"];
list.Add(entry);
}
}
catch { }
finally
{
Connection.Close();
}
return list;
}
Each iteration of your loop performs a select. So, instead of one set of multiple records, you're getting multiple sets.
Choose your destiny:
A. (treat the symptom): advance your reader between resultsets with IDataReader.NextResult()
or B. (fix the problem): There's no need for that cursor; use a join instead:
select
* /* dump your columns back in here */
from Sys_UserGroupMenus as g
join Sys_Menus as m
join Sys_LabelDetails as d
on d.LabelID = m.TitleLabelID
and d.LanguageID = #LanguageID
on m.MenuID = g.MenuID
where g.UserGroupID = #UserGroupID
and g.ViewFlag = 'true'
When I try to run this, it gives me the following error message:
Conversion failed when converting the varchar value 'category_id' to data type int.
Here's my SQL and parameter code, I supposed it should work, but it doesn't.
mycmd.CommandText="SELECT * FROM categories WHERE #db_property = #property_id";
// This contains a string "category_id", which is correct.
mycmd.Parameters.Add("#db_property", SqlDbType.VarChar).Value=db_property_field;
// This contains an Int, referring to the category_id in database. As of now, this is 1
mycmd.Parameters.Add("#property_id", SqlDbType.Int).Value=property_id;
After I'm going through this code, I run it through a Reader, and that's where I get the error message above. Been asking teacher, and excellent students in my class, no one can find a clue on, where the problem is.
You shouldn't add field name as parameter. Try to change your script to include actual field id:
mycmd.CommandText = "SELECT * FROM categories WHERE category_id = #property_id";
mycmd.Parameters.Add("#property_id", SqlDbType.Int).Value = property_id;
I'm not sure about your structure, but try the following:
mycmd.CommandText = "SELECT * FROM categories WHERE Cast(#db_property as Int) = #property_id";
Your query is matching the two variables you are passing in so it will either return all the data or none of it! On top of that you are matching a char variable with an int. SQL will try to cast the char variable to an int.
#db_property = #property_id
should your query look like this?
SELECT * FROM categories WHERE db_property = #db_property AND property_id = #property_id
If you look at your statement you are comparing the two parameters. The WHERE clause is not on a table column ("categories") and the two parameters you are passing are different data types. VarChar and Int. When that command is executed the SQL engine is trying to compare two variables of different data types.
If you run the following SQL statements straight against SQL you will receive the same error.
DECLARE #Var1 VARCHAR(100)
DECLARE #Var2 INT
SELECT #Var1 = 'Test', #Var2 = 1
SELECT * FROM dbo.categories WHERE #Var1 = #Var2
You can get solution from the following address:
http://net-informations.com/csprj/data-providers/cs-procedure-parameter.htm
For your information I Just reshape the code and use it to my needs.
Code of Stored Procedure is as follow:
Create PROCEDURE [dbo].[PmSPValidate]
#a varchar(10)
AS
BEGIN
(SELECT AcctDsc,AcctAge
FROM dbo.tblCoa
WHERE AcctNo >= #a)
END
Code of C# :
private void btnThirdTrial_Click(object sender, EventArgs e)
{
string connetionString = null;
SqlConnection connection;
SqlDataAdapter adapter;
SqlCommand command = new SqlCommand();
SqlParameter param;
DataSet ds = new DataSet();
int i = 0;
connetionString = "Data Source=FIN03;Initial Catalog=CmsTest;Integrated Security=True";
connection = new SqlConnection(connetionString);
connection.Open();
command.Connection = connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.PmSPValidate";
param = new SqlParameter("#a",Account.Text.ToString ());
param.Direction = ParameterDirection.Input;
param.DbType = DbType.String;
command.Parameters.Add(param);
adapter = new SqlDataAdapter(command);
adapter.Fill(ds);
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
MessageBox.Show(" Name " + ds.Tables[0].Rows[i][0].ToString() + " Age " + ds.Tables[0].Rows[i][1].ToString());
}
connection.Close();
}
Question : Is it possible to return using OUT :
Both : A variable & A cursor, from my code below ??
I saw a similar question for SqlDB but after a really long search found no solution for OracleDB.
In PLSQL :
CREATE OR REPLACE
PROCEDURE SPGETRESULTANDSETFLAG
(
pFilter VARCHAR2,
pMaxRowCount VARCHAR2,
pTableID RAW,
myFlag OUT NUMBER,
myCursor OUT types.cursorType
)
AS
BEGIN
Declare
CountQuery VARCHAR(20000) := '';
DataQuery VARCHAR(20000) := '';
ResultingRows NUMBER := -1;
Begin
myFlag := -1;
CountQuery := 'SELECT COUNT(*) FROM '
|| F_GET_TABLENAME_FROM_ID(PTABLEID => pTableID)
|| ' WHERE ' || pFilter;
EXECUTE IMMEDIATE CountQuery INTO ResultingRows;
--Get the Return Value
if( pMaxRowCount > ResultingRows ) then myFlag := 1; end if;
DataQuery := 'SELECT * FROM '
|| F_GET_TABLENAME_FROM_ID(PTABLEID => pTableID)
|| ' WHERE ' || pFilter;
--Get the Return Cursor
Open myCursor for DataQuery;
End;
END SPGETRESULTANDSETFLAG;
In Code Behind..
Database db = DBSingleton.GetInstance();
using (DbCommand command = db.GetStoredProcCommand(spName))
{
//The three Add In Parameters... & then the Add out Parameter as below
db.AddOutParameter(command, "myFlag", System.Data.DbType.Int32, LocVariable );
using ( IDataReader reader = db.ExecuteReader(command))
{
//Loop through cursor values & store them in code behind class-obj(s)
}
}
I Thought this was not possible as how do I read both the value & the cursor, because..
if only flag param out then i would use db.ExecuteNonQuery(..)
& if only cursor out then i would use db.ExecuteReader(..)
Yes, it is possible to have more than one out parameter. Here's an example that I use to call an Oracle stored procedure in c#:
OracleParameter op = null;
OracleDataReader dr = null;
/* custom code here. Yours would look a little different */
OracleCommand cmd = (OracleCommand) this.FactoryCache.Connection.CreateCommand();
cmd.CommandText = "pkg_prov_index.getNextPanel";
cmd.CommandType = CommandType.StoredProcedure;
op = new OracleParameter("pCurrentPanelId", OracleType.VarChar);
op.Direction = ParameterDirection.Input;
op.Value = masterProviderIndex.CurrentPanelId;
cmd.Parameters.Add(op);
op = new OracleParameter("pRefCursor", OracleType.Cursor);
op.Direction = ParameterDirection.Output;
cmd.Parameters.Add(op);
op = new OracleParameter("pReturnCode", OracleType.Number);
op.Direction = ParameterDirection.Output;
op.Size = 5;
cmd.Parameters.Add(op);
op = new OracleParameter("pReturnMessage", OracleType.VarChar);
op.Direction = ParameterDirection.Output;
op.Size = 4000;
cmd.Parameters.Add(op);
cmd.ExecuteNonQuery();
returnCode = Convert.ToInt16(cmd.Parameters[2].Value);
returnMessage = cmd.Parameters[3].Value.ToString();
dr = (OracleDataReader) cmd.Parameters[1].Value;
while (dr.Read()) {
}
Thank you for the answers
I was really desperate to get a working result & somehow came across a solution & after reading a bit found out why it worked :
Oracle Stored Procedure as is with no change.
Code Behind - Changed as follows :
Database db = DBSingleton.GetInstance();
using (DbCommand command = db.GetStoredProcCommand(spName))
{
//The three Add In Parameters... & then the Add out Parameter as below
db.AddOutParameter(command, "myFlag", System.Data.DbType.Int32, LocVariable );
using ( IDataReader reader = db.ExecuteReader(command))
{
//Loop through cursor values & store them in code behind class-obj(s)
//The reader must be closed before trying to get the "OUT parameter"
reader.Close();
//Only after reader is closed will any parameter result be assigned
//So now we can get the parameter value.
//if reader was not closed then OUT parameter value will remain null
//Getting the parameter must be done within this code block
//I could not get it to work outside this code block
<Type> result = (typecast)command.Parameters["OUT_parameter_name"];
}
}
//I USED THIS APPROACH TO RETURN MULTIPLE PARAMETERS ALONG WITH THE CURSOR READ
using (myCmd)
{
myCmd.Parameters.AddWithValue("p_session_id", sessionId);
myCmd.Parameters.AddWithValue("p_user", SessionHelper.UserEmailID);
OracleParameter retval = new OracleParameter("p_status", OracleType.NVarChar, 35);
retval.Direction = ParameterDirection.Output;
myCmd.Parameters.Add(retval);
OracleParameter retval2 = new OracleParameter("p_status_dtl", OracleType.NVarChar, 300);
retval2.Direction = ParameterDirection.Output;
myCmd.Parameters.Add(retval2);
OracleParameter retval3 = new OracleParameter("p_output", OracleType.Cursor);
retval3.Direction = ParameterDirection.Output;
myCmd.Parameters.Add(retval3);
myCmd.ExecuteNonQuery();
status = myCmd.Parameters["p_status"].Value.ToString();
statusDetail = myCmd.Parameters["p_status_dtl"].Value.ToString();
using (OracleDataReader reader = (OracleDataReader)myCmd.Parameters["p_output"].Value)
{
outPutDt.Load(reader);
}
}
}
I don't know which library you use for Oracle access... but usually it is possible to declare the cursor out and the param out both as Parameters and use ExecuteNonQuery with an anoynmous PL/SQL-block (in which you call the Stored Procedure)... for example with the Devart dotconnect components this is possible... (not affilliated, just a happy customer)
One could consider an alternative to the repeated query in your procedure. For example:
CREATE OR REPLACE
PROCEDURE SPGETRESULTANDSETFLAG
(
pFilter VARCHAR2,
pTableID RAW,
myCursor OUT types.cursorType
)
AS
DataQuery VARCHAR(20000) := '';
BEGIN
DataQuery := 'SELECT COUNT(*) OVER () AS TheCount, T.* FROM '
|| F_GET_TABLENAME_FROM_ID(PTABLEID => pTableID)
|| ' AS T WHERE ' || pFilter;
--Get the Return Cursor
Open myCursor for DataQuery;
END SPGETRESULTANDSETFLAG;
In this way you don't have to query the table twice, you have the count in each row of your resultset. You can get rid of your parameters dealing with the max rowcount as well, and check the count value in your calling routine by fetching one row.
Just an alternative thought...
I have searched and tried different things for the past week or so and my problem is possibly to specific to find an answer through google.
If I execute this query in SQL Server Management Studio and replace the parameter #zoekterm with '%something%', it works fine and returns the result that I want. But when I call the same procedure from C# it returns nothing.
Is this a bug or am I just that stupid?
Here's code of stored procedure and function in C#, (I know I should have used switch case...)
Stored procedure:
-- =============================================
-- Author: Daan
-- Create date:
-- Description:
-- =============================================
ALTER PROCEDURE [dbo].[quick_bedrijf]
-- Add the parameters for the stored procedure here
#zoekterm varchar(100) = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT bedrijf.bedrijf_nr, bedrijf.zoeknaam, bedrijf.plaats
FROM bedrijf
WHERE zoeknaam LIKE #zoekterm AND NIETactief = 0
ORDER BY bedrijf.zoeknaam, bedrijf.plaats
END
C#:
private void snel_zoek2()
{
listView1.Items.Clear();
con.Open();
if (type == 1)
{
command1 = new SqlCommand("quick_project", con);
colnum = 5;
}
else if (type == 2)
{
command1 = new SqlCommand("quick_bedrijf", con);
colnum = 3;
}
else if (type == 3)
{
command1 = new SqlCommand("quick_persoon", con);
colnum = 4;
}
command1.CommandType = CommandType.StoredProcedure;
SqlParameter zoekterm = command1.Parameters.Add("#zoekterm", SqlDbType.VarChar, 100);
zoekterm.Direction = ParameterDirection.Input;
//command1.Parameters.Add(new SqlParameter("#zoekterm", SqlDbType.VarChar)).Value = " '%zee%'";// + textBox2.Text.ToString()+
zoekterm.Value = "'%"+textBox2.Text.ToString()+"%'";
// MessageBox.Show(zoekterm.Value.ToString());
SqlDataAdapter adapt = new SqlDataAdapter();
DataTable dt = new DataTable();
adapt.SelectCommand = command1;
adapt.Fill(dt);
dataGridView1.BindingContext = new BindingContext();
dataGridView1.DataSource = dt;
con.Close();
}
You don't put the quotes into the parameter (those are to signify a literal); it should be:
zoekterm.Value = "%"+textBox2.Text+"%";
It is failing currently because if the text is "abc", it is looking for a string that starts and ends with a single quote and includes "abc". In SQL terms, you were asking it for:
LIKE '''%abc%'''