I'm running the below code to call p_CreateRequest. All i want is to get the ID Back but i get an Field "ID" Does not have a default value error. HELP
using (var conn = Conn)
{
using (var cmd = new MySql.Data.MySqlClient.MySqlCommand("p_CreateRequest", conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new MySql.Data.MySqlClient.MySqlParameter("CREATED_DATE", cREATED_DATE));
cmd.Parameters.Add(new MySql.Data.MySqlClient.MySqlParameter("WORKFLOW_ID", wORKFLOW_ID));
cmd.Parameters.Add(new MySql.Data.MySqlClient.MySqlParameter("CREATED_BY_USER_ID", cREATED_BY_USER_ID));
cmd.Parameters.Add(new MySql.Data.MySqlClient.MySqlParameter("ID", iD));
cmd.Parameters["ID"].Direction = System.Data.ParameterDirection.Output;
cmd.ExecuteNonQuery();
iD = (int)cmd.Parameters["ID"].Value;
return iD;
}
}
the sprocs code is
CREATE DEFINER="finnsch"#"%" PROCEDURE "p_CreateRequest"(CREATED_DATE datetime,
WORKFLOW_ID int, CREATED_BY_USER_ID varchar(50),INOUT ID int)
BEGIN
DECLARE LASTCHANGEDATE Date;
SET LASTCHANGEDATE = Now();
INSERT INTO REQUEST
(CREATED_DATE,WORKFLOW_ID,CREATED_BY_USER_ID,
CURRENT_STEP_ID,LAST_ACTION_BY,LAST_EDIT_DATE)
select
CREATED_DATE,WORKFLOW_ID,CREATED_BY_USER_ID,s.ID,
CREATED_BY_USER_ID,LASTCHANGEDATE
from
STEP s where s.WORKFLOW_ID=WORKFLOW_ID and s.IS_INITIAL=1;
set ID = Last_Insert_ID();
END
Need to change the table and set ID to:
id int(20) NOT NULL AUTO_INCREMENT,
Also, check if MySql is in Strict SQL Mode. If it is, then need to run the query:
SET GLOBAL sql_mode=''.
You can also modify your my.cnf / my.ini to ensure you aren't setting STRICT_ALL_TABLES and/or STRICT_TRANS_TABLES. See this for more information:
Field 'id' doesn't have a default value?
Related
I have an insert query like the below. However, scopeIdentity does not return 42, it returns 1042.
This is the SQL Server table:
My code:
int masterId = 0;
using (SqlConnection cmd = new SqlConnection(connString))
{
using (SqlCommand conn = cmd.CreateCommand())
{
conn.CommandText = "INSERT INTO[MasterReportData]([ReportName],[CaseList],[EmployeeId],[datetime]) VALUES(#reportName, #caseList, #employeeId, #datetime) SET #ID = SCOPE_IDENTITY()";
conn.Parameters.Add("#reportName", SqlDbType.VarChar).Value = reportName;
conn.Parameters.Add("#caseList", SqlDbType.VarChar).Value = caseList;
conn.Parameters.Add("#employeeId", SqlDbType.Char).Value = employeeId;
conn.Parameters.Add("#datetime", SqlDbType.DateTime).Value = datetime;
conn.Parameters.Add("#ID", SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.Open();
conn.ExecuteNonQuery();
masterId = Convert.ToInt32(conn.Parameters["#ID"].Value);
cmd.Close();
}
}
Have a look at https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql
The description says:
Returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, if two statements are in the same stored procedure, function, or batch, they are in the same scope.
In words: it Returns the last id and not the next to use. Therefore you can't use the INSERT command like that. What you can do is:
Configure your ID as auto-increment id. Then run the INSERT command and run SELECT SCOPE_IDENTITY() afterwards to find out which ID was used.
I have a table called 'MatchType' and it contains:
ID | MatchTypeName
1 | One Day
2 | Two Day
3 | T20
I have a method to retrieve a record from that table based on a sting that will match a value in MatchTypeName:
public static int GetByName(string matchType)
{
MatchType item = new MatchType();
using (SqlConnection con = new SqlConnection(BaseDataAccessLayer.GetConnectionStringByName()))
{
using (SqlCommand cmd = new SqlCommand(STORED_PROC_GetByName, con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#matchTypeName", SqlDbType.VarChar, 20).Value = matchType;
con.Open();
using (IDataReader reader = cmd.ExecuteReader())
{
item.LoadFromReader(reader);
}
}
}
return item.Id;
}
This method calls this stored procedure:
CREATE PROCEDURE [dbo].[MatchType_GetByName]
(#matchTypeName varchar(20))
AS
BEGIN
SET NOCOUNT ON
SELECT
[Id], [MatchTypeName]
FROM
[dbo].[MatchType]
WHERE
[MatchTypeName] = #matchTypeName
RETURN ##ERROR
END
When calling the GetByName method I am passing in a string "One Day". I have confirmed this through debugging. I can't post an error message that is helpful as there isn't one being generated. Just no data is being returned in the reader...
I have a couple of other methods and stored procedures that use a similar process and they work. The only thing that I can see is the difference is that I am using the WHERE clause on a varchar/string value which is something that the other queries don't do...
Yet i can run this stored proc and it returns all the records..
BEGIN
SET NOCOUNT ON
SELECT
[Id],
[MatchTypeName]
FROM
[dbo].[MatchType]
RETURN ##ERROR
END
The default length for varchar declared without size is 1. You declared it like that in your stored proc parameter so you're only passing the first character.
Change your declaration:
(#matchTypeName varchar)
to a relevant size:
(#matchTypeName varchar(20))
Also, add the size in the code and make sure that the case in your parameter name matches your stored proc:
cmd.Parameters.Add("#matchTypeName", SqlDbType.VarChar, 20).Value = matchType;
The procedure is running the query and showing the results (which is why it works in SSMS), but doesn't actually return anything. Try this (note, I haven't actually tested it).
CREATE PROCEDURE [dbo].[MatchType_GetByName]
#matchTypeName varchar(20),
#matchTypeID INT OUTPUT
AS
BEGIN
SET NOCOUNT ON
SELECT
#matchTypeID = [Id], [MatchTypeName]
FROM
[dbo].[MatchType]
WHERE
[MatchTypeName] = #matchTypeName
RETURN ##ERROR
END
Try this:
using (SqlCommand cmd = new SqlCommand(STORED_PROC_GetByName, con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#matchTypeName", matchType);
con.Open();
using (IDataReader reader = cmd.ExecuteReader())
{
item.LoadFromReader(reader);
}
}
SqlCommand.Parameters Property
I have the below C# code to check if the record does not exist, insert and return id. But also I need if the record exists, it return the value. What change should I make to C# and SQL part for this to happen? Database is sQL server. Do I still have to use ExecuteScalar() for this?
con.Open();
// Insert ClinRefFileTypeMaster
string command1 = string.Format(
"if NOT exists (select * from [ClinRefFileTypeMaster] where [ClinRefTypeName] = '{0}') Insert into [ClinRefFileTypeMaster] ([ClinRefTypeName]) output INSERTED.[ClinRefTypeID] VALUES('{0}')",
dataToParse[i][0]
);
SqlCommand ClinRefFileTypeMaster = new SqlCommand(command1, con);
// check if there is an value
object checkValue = ClinRefFileTypeMaster.ExecuteScalar();
if (checkValue != null)
ClinRefFileTypeId = (int)checkValue;
A stored procedure to do all the stuff for you would look something like.....
CREATE PROCEDURE usp_Get_ClinRefTypeID
#ClinRefTypeName VARCHAR(100),
#ClinRefTypeID INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #NewID TABLE(ClinRefTypeID INT);
SELECT #ClinRefTypeID = [ClinRefTypeID]
FROM [ClinRefFileTypeMaster]
where [ClinRefTypeName] = #ClinRefTypeName;
IF (#ClinRefTypeID IS NULL)
BEGIN
INSERT INTO [ClinRefFileTypeMaster] ([ClinRefTypeName])
OUTPUT inserted.[ClinRefTypeID] INTO #NewID(ClinRefTypeID)
VALUES(#ClinRefTypeName)
SELECT #ClinRefTypeID = [ClinRefTypeID] FROM #NewID
END
END
And your C# code would look something like.....
con.Open();
// Insert ClinRefFileTypeMaster
SqlCommand cmd = new SqlCommand("usp_Get_ClinRefTypeID", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ClinRefTypeID", SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.Parameters.Add(new SqlParameter("#ClinRefTypeName", dataToParse));
// get the value back from the output parameter
cmd.ExecuteNonQuery();
int ClinRefTypeName = Convert.ToInt32(cmd.Parameters["#ClinRefTypeID"].Value);
There are many ways to achieve this. 1) You can do it all in inline Sql 2) you can do it all in stored proc. 3) You can do it all in code but split the code as this code is frankly doing too much. In general I would avoid insert/query in the same method.
Also try to use SqlParameters instead of building the query as string concat.
I would propose something like this which makes the code a bit more readable
public int InsertAndRetrieveClientRefId(string clientRefTypeName)
{
int id = GetIdIfRecordExists(clientRefTypeName);
if (id == 0)
{
// insert logic here
id = GetIdIfRecordExists(clientRefTypeName);
}
return id;
}
public int GetIdIfRecordExists(string clientRefTypeName)
{
int id = 0;
string command = "select id from ClinRefFileTypeMaster where ClinRefTypeName = #ClinRefTypeName";
SqlParameter nameParameter = new SqlParameter("#ClinRefTypeName", System.Data.SqlDbType.NVarChar, 10) { Value = clientRefTypeName };
using (SqlConnection connection = new SqlConnection("ConnectionString"))
{
using (SqlCommand cmd = new SqlCommand(command))
{
cmd.Parameters.Add(newParameter);
connection.Open();
cmd.Connection = connection;
int.TryParse(cmd.ExecuteScalar().ToString(), out id);
}
}
return id;
}
do all this in database i.e in store procedure
if not exists (select 1 from [ClinRefFileTypeMaster] where [ClinRefTypeName] =#name)
begin
Insert into [ClinRefFileTypeMaster] ([ClinRefTypeName]) values (#name)
end
else
begin
select (as desired) from ClinRefFileTypeMaster where where [ClinRefTypeName] =#name
end
this will either insert new record or it will select already inserted information
Youll need to add an IF EXISTS clause to the SQL statement as well, checking for the same conditions, and providing logic to return a value.
It seems using ExecuteReader would be better if you need it to return the value from the database.
2¢
I personally would split the logic into two queries and run the If statement within c# checking if the value is in the database, then updating the database else returning a value from the database
conn.open()
int CheckDb;
String Command1 = "select * from [ClinRefFileTypeMaster] where [ClinRefTypeName] = #ClinRefFileTypeId";
using (SqlCommand ClinRefFileTypeMaster = new SqlCommand(command1, con);
{
cmd.Parameters.AddWithValue("#ClinRefFileTypeId", {0});
CheckDb = (int)ClinRefFileTypeMaster.ExecuteScalar();
}
If (CheckDb != 0)
//Logic for returning the value from the database
Else
//Here you can request user check data or insert the value into the database.
if you want to perform Instert operation, I think its better you call a stored procedure and write your query in the procedure. It will be safer.
SqlCommand command = new SqlCommand("procedureName",con);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue(“#value1”, txtValue1.Text);
command.Parameters.AddWithValue(“#value2”, Value2);
int value = command.ExecuteScalar();
IF EXISTS (SELECT 1 FROM Table WHERE FieldValue='')
BEGIN
SELECT TableID FROM Table WHERE FieldValue=''
END
ELSE
BEGIN
INSERT INTO TABLE(FieldValue) VALUES('')
SELECT SCOPE_IDENTITY() AS TableID
END
If you want to pass the querystring, you can call select query and if it returns null perform a insert opeartion and use scope_Identity() to get the ID
INSERT INTO YourTable(val1, val2, val3 ...)
VALUES(#val1, #val2, #val3...);
SELECT SCOPE_IDENTITY();
I am inserting into a database using a stored procedure and i am getting the error:
Procedure or function 'sp_Addrecord' expects parameter '#RecordNumber', which was not supplied.
RecordNumber is an auto incrementing ID so i understand id have to omit it from my insert command and specify which columns and where i have to insert to avoid this but i am calling the procedure which is handled by another class so where would i be able to specify this as you would normally say something like this:
SqlCommand cmd = new SqlCommand("INSERT INTO CARS (carDate, carTime) Values (#Date, #Time)", conDatabase);
Here is my code, i avoided the using statement for simplicity of this example:
List<CarRecord> carRecords;
private void Save_Record_Click(object sender, RoutedEventArgs e)
{
SqlConnection conDatabase = new SqlConnection(String.Format(#"Data Source={0};Initial Catalog={1};Persist Security Info=True;User ID={2};Password={3}", SQLFunctions.connectSQL.SQLSERVER_ID, SQLFunctions.connectSQL.SQLDatabaseName, SQLFunctions.connectSQL.SQLServerLoginName, SQLFunctions.connectSQL.SQLServerPassword));
conDatabase.Open();
SqlCommand cmd = new SqlCommand("sp_Addrecord", conDatabase);
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
conDatabase.Close();
}
public bool Addrecord(CarRecord DataRecord)
{
return ExecuteNonQuery("sp_Addrecord", null,
CreateParameter("#Date", SqlDbType.NVarChar, DataRecord.carDate),
CreateParameter("#Time", SqlDbType.NVarChar, DataRecord.carTime),
);
}
EDIT - Stored Procedure:
USE [SDC Logging]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_Addrecord]
#RecordNumber int,
#Date nvarchar(50),
#Time nvarchar(50),
AS
BEGIN
SET NOCOUNT ON;
WITH [source](RecordNumber, Date, Time)
AS
(
SELECT #RecordNumber, #Date, #Time,
)
MERGE dbo.Bags AS [target] USING [source]
ON [target].Date = [source].Date
WHEN MATCHED THEN UPDATE SET
[target].Date = #Date,
[target].Time = #Time,
WHEN NOT MATCHED THEN
INSERT ( Date, Time, )
VALUES( #Date, #Time, );
SELECT SCOPE_IDENTITY()
END
The error says it all. Your sp_Addrecord has a parameter specified that you are supplying. Basically, the parameters you specify here...
return ExecuteNonQuery("sp_Addrecord", null,
CreateParameter("#Date", SqlDbType.NVarChar, DataRecord.carDate),
CreateParameter("#Time", SqlDbType.NVarChar, DataRecord.carTime),
);
must match the name and datatype of the parameters defined by sp_Addrecord stored procedure. In addition, make sure your stored procedure's query matches this query...
INSERT INTO CARS (carDate, carTime) Values (#Date, #Time)
Edit based on your Edit
You need to specified the #RecordNumber parameter here...
return ExecuteNonQuery("sp_Addrecord", null,
CreateParameter("#RecordNumber", SqlDbType.Int, DataRecord.recordNumber),
CreateParameter("#Date", SqlDbType.NVarChar, DataRecord.carDate),
CreateParameter("#Time", SqlDbType.NVarChar, DataRecord.carTime),
);
Don't worry about the insert just make sure that when inserting you pass a "invalid record number" such as -1, if the MERGE statement doesn't find the record with id of -1 it will successfully insert the record with an auto-generated Id with the help of your identity column
Try This.
You don't need to call separate method Addrecord.
However, you still want to use a separate method. Add code below in the AddRecord method and remove existing code:
SqlParameter []parms = new SqlParameter[1];
parms[0] = new SqlParameter("#Date",DataRecord.carDate) ;
parms[1] = new SqlParameter("#Time",DataRecord.carTime) ;
cmd.Parameters.AddRange(parms);
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
conDatabase.Close();
Hi all I have the following stored procedure
#UserName varchar(150),
#UserEmail varchar(300),
#UserPassword varchar(150),
#ContactNumber varchar(150),
#ContactMobile varchar(150),
#AreaOfCountry varchar(150),
#UserId int OUTPUT,
#AllreadyReg int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--DECLARE #UserId int, #AllreadyReg int
IF (SELECT COUNT(UserId) FROM Users WHERE (UserName = #UserName) OR (UserEmail = #UserEmail)) > 0
BEGIN
SET #UserId = 0
SET #AllreadyReg = 1
END
ELSE
BEGIN
INSERT INTO Users (UserName,UserEmail,UserPassword,ContactNumber,ContactMobile,AreaOfCountry) VALUES (#UserName,#UserEmail,#UserPassword,#ContactNumber,#ContactMobile,#AreaOfCountry)
SELECT #UserId = SCOPE_IDENTITY()
SET #AllreadyReg = 0
END
however when I use it using c# and asp.net its not returning anything, however when I just execute it it does have a results #UserId and #AllreadyReg but the return value is 0 and a single field.
my c# code is below but it never has any rows
using (SqlConnection con = new SqlConnection(connectionString))
{
Response.Write("Line 61");
using (SqlCommand cmd = new SqlCommand("spR_Register", con))
{
Response.Write("line 64");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#UserName", TxtUsername.Text.Trim());
cmd.Parameters.AddWithValue("#UserEmail", TxtEmail.Text.Trim());
cmd.Parameters.AddWithValue("#UserPassword", TxtPassword.Text.Trim());
cmd.Parameters.AddWithValue("#ContactNumber", TxtPhone.Text);
cmd.Parameters.AddWithValue("#ContactMobile", TxtMobile.Text);
cmd.Parameters.AddWithValue("#AreaOfCountry", TxtAreaOfCountry.SelectedValue);
cmd.Parameters.AddWithValue("#UserId", ParameterDirection.Output);
cmd.Parameters.AddWithValue("#AllreadyReg", ParameterDirection.Output);
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows)
{
Response.Write("line 78");
etc etc
can anyone help
thanks
With the edit: the mistake is using ExecuteReader on a command that doesn't select a data grid - that should be done with ExecuteNonQuery.
The important thing here is how the parameter is added. For example:
var alreadyReg = cmd.CreateParameter();
alreadyReg.Direction = System.Data.ParameterDirection.Output;
alreadyReg.ParameterName = "AllreadyReg";
alreadyReg.DbType = DbType.Int32;
cmd.Parameters.Add(alreadyReg);
//...
cmd.ExecuteNonQuery();
//...
int val = (int)alreadyReg.Value;
Depending on the type of DbCommand object, there may be an overload that accepts all of these in one line - the above assumes just DbCommand. With SqlCommand, you can simplify a bit:
var alreadyReg = cmd.Parameters.Add("AllreadyReg", SqlDbType.Int);
alreadyReg.Direction = ParameterDirection.Output;
//...
cmd.ExecuteNonQuery();
//...
int val = (int)alreadyReg.Value
You might have already handled it, but just in case, make sure to specify direction of parameter in c# code.
Here
cmd.Parameters.AddWithValue("#UserId", ParameterDirection.Output);
wrong usage of AddWithValue. The second parameter is parsed as parameter value, not direction type. Use proper parameter contructor and the add the result to collection.