Copy DataRow from One SQL Table to another Identical SQL Table C# - c#

I have two identical SQL tables (one is work table and the other is history table). I want to be able to write a record from the work table to the history table whenever a user selects a record to be deleted. The two tables are a clone of each other just to save deleted records to a history file.
Both tables contain an Identity Column (first column) and an image datatype column.
What i want to know is what is the best way to do this. Here is what I have, but I keep getting an error saying:
"Unable to cast object of type 'System.String' to type 'System.Byte[]'."
string sql = "SELECT * FROM EDI10000 WHERE File_Id = " + fid;
DataTable dt1 = Vit.GetDataRecords(sql);
if (dt1.Rows.Count > 0)
{
DataRow dr = dt1.Rows[0];
int fileId = int.Parse(fid);
string tpID = Vit.GetFieldValue(dr, "Tp_Id");
string fName = Vit.GetFieldValue(dr, "File_Name");
int fileSz = int.Parse(Vit.GetFieldValue(dr, "File_Size"));
string conType = Vit.GetFieldValue(dr, "Content_Type");
object obj = Vit.GetFieldValue(dr, "File_Data");
Byte[] byteData = (Byte[])obj;
MemoryStream ms = new MemoryStream(byteData);
object[] Insert = new object[] { fileId, tpID, fName, fileSz, ms, conType };
string sql2 = string.Format(#"INSERT INTO EDI10500 VALUES ({0}, '{1}', '{2}', {3}, '{4}', '{5}')", Insert);
Vit.UpdateDB(sql2);
}
Any help would be appreciated.
Thank you

Here is a ROUGH example of a trigger:
CREATE TRIGGER [dbo].[tr_SourceTableName_Audit] ON [dbo].[SourceTableName] FOR DELETE AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
INSERT INTO AuditTable (cols)
Select * from deleted
END TRY
BEGIN CATCH
-- In this case, I just want to eat the error.
END CATCH
END

Related

Retrieving the ID of the last row inserted I am using SQL Server 2008. Wrong value return in the textbox [duplicate]

I have this code:
string insertSql =
"INSERT INTO aspnet_GameProfiles(UserId,GameId) VALUES(#UserId, #GameId)";
using (SqlConnection myConnection = new SqlConnection(myConnectionString))
{
myConnection.Open();
SqlCommand myCommand = new SqlCommand(insertSql, myConnection);
myCommand.Parameters.AddWithValue("#UserId", newUserId);
myCommand.Parameters.AddWithValue("#GameId", newGameId);
myCommand.ExecuteNonQuery();
myConnection.Close();
}
When I insert into this table, I have an auto_increment int primary key column called GamesProfileId, how can i get the last inserted one after this so I can use that id to insert into another table?
For SQL Server 2005+, if there is no insert trigger, then change the insert statement (all one line, split for clarity here) to this
INSERT INTO aspnet_GameProfiles(UserId,GameId)
OUTPUT INSERTED.ID
VALUES(#UserId, #GameId)
For SQL Server 2000, or if there is an insert trigger:
INSERT INTO aspnet_GameProfiles(UserId,GameId)
VALUES(#UserId, #GameId);
SELECT SCOPE_IDENTITY()
And then
Int32 newId = (Int32) myCommand.ExecuteScalar();
You can create a SqlCommand with CommandText equal to
INSERT INTO aspnet_GameProfiles(UserId, GameId) OUTPUT INSERTED.ID VALUES(#UserId, #GameId)
and execute int id = (int)command.ExecuteScalar.
This MSDN article will give you some additional techniques.
string insertSql =
"INSERT INTO aspnet_GameProfiles(UserId,GameId) VALUES(#UserId, #GameId)SELECT SCOPE_IDENTITY()";
int primaryKey;
using (SqlConnection myConnection = new SqlConnection(myConnectionString))
{
myConnection.Open();
SqlCommand myCommand = new SqlCommand(insertSql, myConnection);
myCommand.Parameters.AddWithValue("#UserId", newUserId);
myCommand.Parameters.AddWithValue("#GameId", newGameId);
primaryKey = Convert.ToInt32(myCommand.ExecuteScalar());
myConnection.Close();
}
This will work.
I had the same need and found this answer ..
This creates a record in the company table (comp), it the grabs the auto ID created on the company table and drops that into a Staff table (staff) so the 2 tables can be linked, MANY staff to ONE company. It works on my SQL 2008 DB, should work on SQL 2005 and above.
===========================
CREATE PROCEDURE [dbo].[InsertNewCompanyAndStaffDetails]
#comp_name varchar(55) = 'Big Company',
#comp_regno nchar(8) = '12345678',
#comp_email nvarchar(50) = 'no1#home.com',
#recID INT OUTPUT
-- The '#recID' is used to hold the Company auto generated ID number that we are about to grab
AS
Begin
SET NOCOUNT ON
DECLARE #tableVar TABLE (tempID INT)
-- The line above is used to create a tempory table to hold the auto generated ID number for later use. It has only one field 'tempID' and its type INT is the same as the '#recID'.
INSERT INTO comp(comp_name, comp_regno, comp_email)
OUTPUT inserted.comp_id INTO #tableVar
-- The 'OUTPUT inserted.' line above is used to grab data out of any field in the record it is creating right now. This data we want is the ID autonumber. So make sure it says the correct field name for your table, mine is 'comp_id'. This is then dropped into the tempory table we created earlier.
VALUES (#comp_name, #comp_regno, #comp_email)
SET #recID = (SELECT tempID FROM #tableVar)
-- The line above is used to search the tempory table we created earlier where the ID we need is saved. Since there is only one record in this tempory table, and only one field, it will only select the ID number you need and drop it into '#recID'. '#recID' now has the ID number you want and you can use it how you want like i have used it below.
INSERT INTO staff(Staff_comp_id)
VALUES (#recID)
End
-- So there you go. You can actually grab what ever you want in the 'OUTPUT inserted.WhatEverFieldNameYouWant' line and create what fields you want in your tempory table and access it to use how ever you want.
I was looking for something like this for ages, with this detailed break down, I hope this helps.
In pure SQL the main statement kools like:
INSERT INTO [simbs] ([En]) OUTPUT INSERTED.[ID] VALUES ('en')
Square brackets defines the table simbs and then the columns En and ID, round brackets defines the enumeration of columns to be initiated and then the values for the columns, in my case one column and one value. The apostrophes enclose a string
I will explain you my approach:
It might be not easy to understand but i hope useful to get the big picture around using the last inserted id. Of course there are alternative easier approaches. But I have reasons to keep mine. Associated functions are not included, just their names and parameter names.
I use this method for medical artificial intelligence
The method check if the wanted string exist in the central table (1). If the wanted string is not in the central table "simbs", or if duplicates are allowed, the wanted string is added to the central table "simbs" (2). The last inseerted id is used to create associated table (3).
public List<int[]> CreateSymbolByName(string SymbolName, bool AcceptDuplicates)
{
if (! AcceptDuplicates) // check if "AcceptDuplicates" flag is set
{
List<int[]> ExistentSymbols = GetSymbolsByName(SymbolName, 0, 10); // create a list of int arrays with existent records
if (ExistentSymbols.Count > 0) return ExistentSymbols; //(1) return existent records because creation of duplicates is not allowed
}
List<int[]> ResultedSymbols = new List<int[]>(); // prepare a empty list
int[] symbolPosition = { 0, 0, 0, 0 }; // prepare a neutral position for the new symbol
try // If SQL will fail, the code will continue with catch statement
{
//DEFAULT und NULL sind nicht als explizite Identitätswerte zulässig
string commandString = "INSERT INTO [simbs] ([En]) OUTPUT INSERTED.ID VALUES ('" + SymbolName + "') "; // Insert in table "simbs" on column "En" the value stored by variable "SymbolName"
SqlCommand mySqlCommand = new SqlCommand(commandString, SqlServerConnection); // initialize the query environment
SqlDataReader myReader = mySqlCommand.ExecuteReader(); // last inserted ID is recieved as any resultset on the first column of the first row
int LastInsertedId = 0; // this value will be changed if insertion suceede
while (myReader.Read()) // read from resultset
{
if (myReader.GetInt32(0) > -1)
{
int[] symbolID = new int[] { 0, 0, 0, 0 };
LastInsertedId = myReader.GetInt32(0); // (2) GET LAST INSERTED ID
symbolID[0] = LastInsertedId ; // Use of last inserted id
if (symbolID[0] != 0 || symbolID[1] != 0) // if last inserted id succeded
{
ResultedSymbols.Add(symbolID);
}
}
}
myReader.Close();
if (SqlTrace) SQLView.Log(mySqlCommand.CommandText); // Log the text of the command
if (LastInsertedId > 0) // if insertion of the new row in the table was successful
{
string commandString2 = "UPDATE [simbs] SET [IR] = [ID] WHERE [ID] = " + LastInsertedId + " ;"; // update the table by giving to another row the value of the last inserted id
SqlCommand mySqlCommand2 = new SqlCommand(commandString2, SqlServerConnection);
mySqlCommand2.ExecuteNonQuery();
symbolPosition[0] = LastInsertedId; // mark the position of the new inserted symbol
ResultedSymbols.Add(symbolPosition); // add the new record to the results collection
}
}
catch (SqlException retrieveSymbolIndexException) // this is executed only if there were errors in the try block
{
Console.WriteLine("Error: {0}", retrieveSymbolIndexException.ToString()); // user is informed about the error
}
CreateSymbolTable(LastInsertedId); //(3) // Create new table based on the last inserted id
if (MyResultsTrace) SQLView.LogResult(LastInsertedId); // log the action
return ResultedSymbols; // return the list containing this new record
}
I tried the above but they didn't work, i found this thought, that works a just fine for me.
var ContactID = db.GetLastInsertId();
Its less code and i easy to put in.
Hope this helps someone.
You can also use a call to SCOPE_IDENTITY in SQL Server.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace DBDemo2
{
public partial class Form1 : Form
{
string connectionString = "Database=company;Uid=sa;Pwd=mypassword";
System.Data.SqlClient.SqlConnection connection;
System.Data.SqlClient.SqlCommand command;
SqlParameter idparam = new SqlParameter("#eid", SqlDbType.Int, 0);
SqlParameter nameparam = new SqlParameter("#name", SqlDbType.NChar, 20);
SqlParameter addrparam = new SqlParameter("#addr", SqlDbType.NChar, 10);
public Form1()
{
InitializeComponent();
connection = new System.Data.SqlClient.SqlConnection(connectionString);
connection.Open();
command = new System.Data.SqlClient.SqlCommand(null, connection);
command.CommandText = "insert into employee(ename, city) values(#name, #addr);select SCOPE_IDENTITY();";
command.Parameters.Add(nameparam);
command.Parameters.Add(addrparam);
command.Prepare();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void buttonSave_Click(object sender, EventArgs e)
{
try
{
int id = Int32.Parse(textBoxID.Text);
String name = textBoxName.Text;
String address = textBoxAddress.Text;
command.Parameters[0].Value = name;
command.Parameters[1].Value = address;
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
int nid = Convert.ToInt32(reader[0]);
MessageBox.Show("ID : " + nid);
}
/*int af = command.ExecuteNonQuery();
MessageBox.Show(command.Parameters["ID"].Value.ToString());
*/
}
catch (NullReferenceException ne)
{
MessageBox.Show("Error is : " + ne.StackTrace);
}
catch (Exception ee)
{
MessageBox.Show("Error is : " + ee.StackTrace);
}
}
private void buttonSave_Leave(object sender, EventArgs e)
{
}
private void Form1_Leave(object sender, EventArgs e)
{
connection.Close();
}
}
}
There are all sorts of ways to get the Last Inserted ID but the easiest way I have found is by simply retrieving it from the TableAdapter in the DataSet like so:
<Your DataTable Class> tblData = new <Your DataTable Class>();
<Your Table Adapter Class> tblAdpt = new <Your Table Adapter Class>();
/*** Initialize and update Table Data Here ***/
/*** Make sure to call the EndEdit() method ***/
/*** of any Binding Sources before update ***/
<YourBindingSource>.EndEdit();
//Update the Dataset
tblAdpt.Update(tblData);
//Get the New ID from the Table Adapter
long newID = tblAdpt.Adapter.InsertCommand.LastInsertedId;
Hope this Helps ...
After inserting any row you can get last inserted id by below line of query.
INSERT INTO aspnet_GameProfiles(UserId,GameId)
VALUES(#UserId, #GameId);
SELECT ##IDENTITY
If you're using executeScalar:
cmd.ExecuteScalar();
result_id=cmd.LastInsertedId.ToString();
Maybe this answer helps as well as my database seems to have no column specified as "IDENTITY" (which is needed for "SELECT SCOPE_IDENTITY()" or "##IDENTITY" calls). Also my "ID" column was of type "binary(16)" so I needed to convert the output like stated below:
string returnId = BitConverter.ToString((byte[])cmd.ExecuteScalar()).Replace("-", "");
// skip the replace if you handle the hyphen otherwise
Use SELECT SCOPE_IDENTITY() in query
After this:
INSERT INTO aspnet_GameProfiles(UserId, GameId) OUTPUT INSERTED.ID VALUES(#UserId, #GameId)
Execute this
int id = (int)command.ExecuteScalar;
It will work
INSERT INTO aspnet_GameProfiles(UserId,GameId) VALUES(#UserId, #GameId)";
then you can just access to the last id by ordering the table in desc way.
SELECT TOP 1 UserId FROM aspnet_GameProfiles ORDER BY UserId DESC.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[spCountNewLastIDAnyTableRows]
(
#PassedTableName as NVarchar(255),
#PassedColumnName as NVarchar(225)
)
AS
BEGIN
DECLARE #ActualTableName AS NVarchar(255)
DECLARE #ActualColumnName as NVarchar(225)
SELECT #ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #PassedTableName
SELECT #ActualColumnName = QUOTENAME( COLUMN_NAME )
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = #PassedColumnName
DECLARE #sql AS NVARCHAR(MAX)
SELECT #sql = 'select MAX('+ #ActualColumnName + ') + 1 as LASTID' + ' FROM ' + #ActualTableName
EXEC(#SQL)
END

Truncating an Oracle Temp Table in a Transaction, Truncates *ALL* Temp Tables

I have some C# code that creates several Oracle Temporary Tables with the "ON COMMIT DELETE ROWS" option inside of a transaction.
Inside of the transaction, I will insert a bunch of rows in to various temp tables. In some scenarios I need to Truncate a particular temp table so that I can start fresh with that table, but leave the other temp tables alone.
I found that Oracle must be doing an implicit COMMIT when you perform the Truncate, as not only was the particular temp table being Truncated, but all of my temp tables are being truncated.
Ok, I've read elsewhere that the Truncate command is considered a DDL command and that is why the commit is being processed which results in my "ON COMMIT DELETE ROWS" temp tables being cleared.
If that is true, wouldn't the act of creating a new temp table also be a DDL command and it would also trip the same commit clearing all of the other temp tables? If so, I haven't seen that behavior. I have created new temp tables in my code and found that the previously created temp tables still have their rows intact after the new temp table has been created.
Here's some C# code that demonstrates the problem (helper routines not included here):
private void RunTest()
{
if (_oc == null)
_oc = new OracleConnection("data source=myserver;user id=myid;password=mypassword");
_oc.Open();
_tran = _oc.BeginTransaction();
string tt1 = "DMTEST1";
AddTempTable(tt1, false);
int TempTableRowCount0 = GetTempTableRowCount(tt1);
AddRows(tt1, 5);
int TempTableRowCount10 = GetTempTableRowCount(tt1);
string tt2 = "DMTEST2";
AddTempTable(tt2, false);
int TempTableRowCount12 = GetTempTableRowCount(tt1); // This will have the same value as TempTableRowCount10
AddRows(tt2, 6);
int TempTableRowCount13 = GetTempTableRowCount(tt2); // This will have the same value as TempTableRowCount10
string tt3 = "DMTEST3";
AddTempTable(tt3, true); // The TRUE argument which does a TRUNCATE against the DMTEST3 table is the problem
int TempTableRowCount14 = GetTempTableRowCount(tt1); // This will return 0, it should be = TempTableRowCount10
int TempTableRowCount15 = GetTempTableRowCount(tt2); // This will return 0, it should be = TempTableRowCount13
_tran.Commit();
_tran = null;
int TempTableRowCount20 = GetTempTableRowCount(tt1); // This should be 0 because the transaction was committed
int TempTableRowCount21 = GetTempTableRowCount(tt2); // and the temp tables are defined as "ON COMMIT DELETE ROWS"
}
private void AddTempTable(string TableName, bool Truncate)
{
IDbCommand ocmd = new OracleCommand();
ocmd.Connection = _oc;
if (!TableExists(TableName))
{
ocmd.CommandText = string.Format("CREATE GLOBAL TEMPORARY TABLE {0} ({1}) ON COMMIT DELETE ROWS", TableName, "FIELD1 Float");
int rc = ocmd.ExecuteNonQuery();
}
if (Truncate)
{
ocmd.CommandText = "TRUNCATE TABLE " + TableName;
int rc = ocmd.ExecuteNonQuery();
}
}
In Oracle, you don't create global temporary tables at runtime. You create them once when you deploy the system. Each session gets its own "copy" of the temp table automatically.
Also, if you can avoid the TRUNCATE I'd recommend it - i.e. if you can rely on the ON COMMIT DELETE ROWS which causes the data to disappear when you commit, then that's the most efficient way.
To answer your other question ("CREATE GLOBAL TEMPORARY doesn't seem to commit") - I tried it myself, and it seems to me that CREATE GLOBAL TEMPORARY does indeed commit. My test case:
create global temporary table test1 (n number) on commit delete rows;
insert into test1 values (1);
--Expected: 1
select count(*) from test1;
commit;
--Expected: 0
select count(*) from test1;
insert into test1 values (2);
--Expected: 1
select count(*) from test1;
create global temporary table test2 (n number) on commit delete rows;
--Expected: 0
select count(*) from test1;
commit;
--Expected: 0
select count(*) from test1;

Inserting a List<> into SQL Server table

I have an entity Report whose values I want to insert into a database table. The following attributes of Report have to be inserted:
reportID - int
RoleID - int
Created_BY = SYSTEM(default)
CURRENT_TIMESTAMP
Now the problem is with the 2nd attribute. I have a report with the LIST<ROLES> attributes. ROLES is a well defined entity which has an ID and a NAME. From this list I have to extract every role and insert each role's ID into the table.
So my query presently looks as below :
INSERT INTO REPORT_MARJORIE_ROLE(REPORT_ID, ROLE_ID, CREATED_BY, CREATED)
VALUES({0}, {1}, 'SYSTEM', CURRENT_TIMESTAMP)
The C# code from where I am parsing these values is as follows :
try
{
StringBuilder _objSQL = new StringBuilder();
_objSQL.AppendFormat(Queries.Report.ReportQueries.ADD_NEW_ROLES, report.ID, "report.MarjorieRoles.Add(MarjorieRole"));
_objDBWriteConnection.ExecuteQuery(_objSQL.ToString());
_objDBWriteConnection.Commit();
_IsRolesAdded = true;
}
So please guide me how to add roles from C# function
I'm assuming you say SQL (structured query language) and you really mean Microsoft SQL Server (the actual database product) instead - right?
You cannot insert a whole list as a whole into SQL Server - you need to insert one row for each entry. This means, you need to call the INSERT statement multiple times.
Do it like this:
// define the INSERT statement using **PARAMETERS**
string insertStmt = "INSERT INTO dbo.REPORT_MARJORIE_ROLE(REPORT_ID, ROLE_ID, CREATED_BY, CREATED) " +
"VALUES(#ReportID, #RoleID, 'SYSTEM', CURRENT_TIMESTAMP)";
// set up connection and command objects in ADO.NET
using(SqlConnection conn = new SqlConnection(-your-connection-string-here))
using(SqlCommand cmd = new SqlCommand(insertStmt, conn)
{
// define parameters - ReportID is the same for each execution, so set value here
cmd.Parameters.Add("#ReportID", SqlDbType.Int).Value = YourReportID;
cmd.Parameters.Add("#RoleID", SqlDbType.Int);
conn.Open();
// iterate over all RoleID's and execute the INSERT statement for each of them
foreach(int roleID in ListOfRoleIDs)
{
cmd.Parameters["#RoleID"].Value = roleID;
cmd.ExecuteNonQuery();
}
conn.Close();
}
let say lstroles is your LIST<ROLES>.
lstroles.ForEach(Role =>
{
/* Your Insert Query like
INSERT INTO REPORT_MARJORIE_ROLE(REPORT_ID, ROLE_ID, CREATED_BY, CREATED)
VALUES(REPORT_ID, Role.ID, {0}, {1}, 'SYSTEM', CURRENT_TIMESTAMP);
Commit you query*\
});
On a personal note: Beware of SQL Injection.

How do I perform an insert and return inserted identity with Dapper?

How do I perform an insert to database and return inserted identity with Dapper?
I've tried something like this:
string sql = "DECLARE #ID int; " +
"INSERT INTO [MyTable] ([Stuff]) VALUES (#Stuff); " +
"SELECT #ID = SCOPE_IDENTITY()";
var id = connection.Query<int>(sql, new { Stuff = mystuff}).First();
But it did't work.
#Marc Gravell thanks, for reply.
I've tried your solution but, still same exception trace is below
System.InvalidCastException: Specified cast is not valid
at Dapper.SqlMapper.<QueryInternal>d__a`1.MoveNext() in (snip)\Dapper\SqlMapper.cs:line 610
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Boolean buffered, Nullable`1 commandTimeout, Nullable`1 commandType) in (snip)\Dapper\SqlMapper.cs:line 538
at Dapper.SqlMapper.Query[T](IDbConnection cnn, String sql, Object param) in (snip)\Dapper\SqlMapper.cs:line 456
It does support input/output parameters (including RETURN value) if you use DynamicParameters, but in this case the simpler option is simply:
var id = connection.QuerySingle<int>( #"
INSERT INTO [MyTable] ([Stuff]) VALUES (#Stuff);
SELECT CAST(SCOPE_IDENTITY() as int)", new { Stuff = mystuff});
Note that on more recent versions of SQL Server (2005+) you can use the OUTPUT clause:
var id = connection.QuerySingle<int>( #"
INSERT INTO [MyTable] ([Stuff])
OUTPUT INSERTED.Id
VALUES (#Stuff);", new { Stuff = mystuff});
A late answer, but here is an alternative to the SCOPE_IDENTITY() answers that we ended up using: OUTPUT INSERTED
Return only ID of inserted object:
It allows you to get all or some attributes of the inserted row:
string insertUserSql = #"INSERT INTO dbo.[User](Username, Phone, Email)
OUTPUT INSERTED.[Id]
VALUES(#Username, #Phone, #Email);";
int newUserId = conn.QuerySingle<int>(
insertUserSql,
new
{
Username = "lorem ipsum",
Phone = "555-123",
Email = "lorem ipsum"
},
tran);
Return inserted object with ID:
If you wanted you could get Phone and Email or even the whole inserted row:
string insertUserSql = #"INSERT INTO dbo.[User](Username, Phone, Email)
OUTPUT INSERTED.*
VALUES(#Username, #Phone, #Email);";
User newUser = conn.QuerySingle<User>(
insertUserSql,
new
{
Username = "lorem ipsum",
Phone = "555-123",
Email = "lorem ipsum"
},
tran);
Also, with this you can return data of deleted or updated rows. Just be careful if you are using triggers because (from link mentioned before):
Columns returned from OUTPUT reflect the data as it is after the
INSERT, UPDATE, or DELETE statement has completed but before triggers
are executed.
For INSTEAD OF triggers, the returned results are generated as if the
INSERT, UPDATE, or DELETE had actually occurred, even if no
modifications take place as the result of the trigger operation. If a
statement that includes an OUTPUT clause is used inside the body of a
trigger, table aliases must be used to reference the trigger inserted
and deleted tables to avoid duplicating column references with the
INSERTED and DELETED tables associated with OUTPUT.
More on it in the docs: link
KB:2019779,"You may receive incorrect values when using SCOPE_IDENTITY() and ##IDENTITY",
The OUTPUT clause is the safest mechanism:
string sql = #"
DECLARE #InsertedRows AS TABLE (Id int);
INSERT INTO [MyTable] ([Stuff]) OUTPUT Inserted.Id INTO #InsertedRows
VALUES (#Stuff);
SELECT Id FROM #InsertedRows";
var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
The InvalidCastException you are getting is due to SCOPE_IDENTITY being a Decimal(38,0).
You can return it as an int by casting it as follows:
string sql = #"
INSERT INTO [MyTable] ([Stuff]) VALUES (#Stuff);
SELECT CAST(SCOPE_IDENTITY() AS INT)";
int id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
Not sure if it was because I'm working against SQL 2000 or not but I had to do this to get it to work.
string sql = "DECLARE #ID int; " +
"INSERT INTO [MyTable] ([Stuff]) VALUES (#Stuff); " +
"SET #ID = SCOPE_IDENTITY(); " +
"SELECT #ID";
var id = connection.Query<int>(sql, new { Stuff = mystuff}).Single();
There is a great library to make your life easier Dapper.Contrib.Extensions. After including this you can just write:
public int Add(Transaction transaction)
{
using (IDbConnection db = Connection)
{
return (int)db.Insert(transaction);
}
}
I was using .net core 3.1 with postgres 12.3. Building on the answer from Tadija Bagarić I ended up with:
using (var connection = new NpgsqlConnection(AppConfig.CommentFilesConnection))
{
string insertUserSql = #"INSERT INTO mytable(comment_id,filename,content)
VALUES( #commentId, #filename, #content) returning id;";
int newUserId = connection.QuerySingle<int>(
insertUserSql,
new
{
commentId = 1,
filename = "foobar!",
content = "content"
}
);
}
where AppConfig is my own class which simply gets a string set for my connection details. This is set within the Startup.cs ConfigureServices method.
I see answer for sql server, well here it is for MySql using a transaction
Dim sql As String = "INSERT INTO Empleado (nombres, apepaterno, apematerno, direccion, colonia, cp, municipio, estado, tel, cel, correo, idrol, relojchecadorid, relojchecadorid2, `activo`,`extras`,`rfc`,`nss`,`curp`,`imagen`,sueldoXHra, IMSSCotiza, thumb) VALUES (#nombres, #apepaterno, #apematerno, #direccion, #colonia, #cp, #municipio, #estado, #tel, #cel, #correo, #idrol, #relojchecadorid, #relojchecadorid2, #activo, #extras, #rfc, #nss, #curp, #imagen,#sueldoXHra,#IMSSCotiza, #thumb)"
Using connection As IDbConnection = New MySqlConnection(getConnectionString())
connection.Open()
Using transaction = connection.BeginTransaction
Dim res = connection.Execute(sql, New With {reg.nombres, reg.apepaterno, reg.apematerno, reg.direccion, reg.colonia, reg.cp, reg.municipio, reg.estado, reg.tel, reg.cel, reg.correo, reg.idrol, reg.relojchecadorid, reg.relojchecadorid2, reg.activo, reg.extras, reg.rfc, reg.nss, reg.curp, reg.imagen, reg.thumb, reg.sueldoXHra, reg.IMSSCotiza}, commandTimeout:=180, transaction:=transaction)
lastInsertedId = connection.ExecuteScalar("SELECT LAST_INSERT_ID();", transaction:=transaction)
If res > 0 Then
transaction.Commit()
return true
end if
End Using
End Using
If you're using Dapper.SimpleSave:
//no safety checks
public static int Create<T>(object param)
{
using (SqlConnection conn = new SqlConnection(GetConnectionString()))
{
conn.Open();
conn.Create<T>((T)param);
return (int) (((T)param).GetType().GetProperties().Where(
x => x.CustomAttributes.Where(
y=>y.AttributeType.GetType() == typeof(Dapper.SimpleSave.PrimaryKeyAttribute).GetType()).Count()==1).First().GetValue(param));
}
}

How to get last inserted id?

I have this code:
string insertSql =
"INSERT INTO aspnet_GameProfiles(UserId,GameId) VALUES(#UserId, #GameId)";
using (SqlConnection myConnection = new SqlConnection(myConnectionString))
{
myConnection.Open();
SqlCommand myCommand = new SqlCommand(insertSql, myConnection);
myCommand.Parameters.AddWithValue("#UserId", newUserId);
myCommand.Parameters.AddWithValue("#GameId", newGameId);
myCommand.ExecuteNonQuery();
myConnection.Close();
}
When I insert into this table, I have an auto_increment int primary key column called GamesProfileId, how can i get the last inserted one after this so I can use that id to insert into another table?
For SQL Server 2005+, if there is no insert trigger, then change the insert statement (all one line, split for clarity here) to this
INSERT INTO aspnet_GameProfiles(UserId,GameId)
OUTPUT INSERTED.ID
VALUES(#UserId, #GameId)
For SQL Server 2000, or if there is an insert trigger:
INSERT INTO aspnet_GameProfiles(UserId,GameId)
VALUES(#UserId, #GameId);
SELECT SCOPE_IDENTITY()
And then
Int32 newId = (Int32) myCommand.ExecuteScalar();
You can create a SqlCommand with CommandText equal to
INSERT INTO aspnet_GameProfiles(UserId, GameId) OUTPUT INSERTED.ID VALUES(#UserId, #GameId)
and execute int id = (int)command.ExecuteScalar.
This MSDN article will give you some additional techniques.
string insertSql =
"INSERT INTO aspnet_GameProfiles(UserId,GameId) VALUES(#UserId, #GameId)SELECT SCOPE_IDENTITY()";
int primaryKey;
using (SqlConnection myConnection = new SqlConnection(myConnectionString))
{
myConnection.Open();
SqlCommand myCommand = new SqlCommand(insertSql, myConnection);
myCommand.Parameters.AddWithValue("#UserId", newUserId);
myCommand.Parameters.AddWithValue("#GameId", newGameId);
primaryKey = Convert.ToInt32(myCommand.ExecuteScalar());
myConnection.Close();
}
This will work.
I had the same need and found this answer ..
This creates a record in the company table (comp), it the grabs the auto ID created on the company table and drops that into a Staff table (staff) so the 2 tables can be linked, MANY staff to ONE company. It works on my SQL 2008 DB, should work on SQL 2005 and above.
===========================
CREATE PROCEDURE [dbo].[InsertNewCompanyAndStaffDetails]
#comp_name varchar(55) = 'Big Company',
#comp_regno nchar(8) = '12345678',
#comp_email nvarchar(50) = 'no1#home.com',
#recID INT OUTPUT
-- The '#recID' is used to hold the Company auto generated ID number that we are about to grab
AS
Begin
SET NOCOUNT ON
DECLARE #tableVar TABLE (tempID INT)
-- The line above is used to create a tempory table to hold the auto generated ID number for later use. It has only one field 'tempID' and its type INT is the same as the '#recID'.
INSERT INTO comp(comp_name, comp_regno, comp_email)
OUTPUT inserted.comp_id INTO #tableVar
-- The 'OUTPUT inserted.' line above is used to grab data out of any field in the record it is creating right now. This data we want is the ID autonumber. So make sure it says the correct field name for your table, mine is 'comp_id'. This is then dropped into the tempory table we created earlier.
VALUES (#comp_name, #comp_regno, #comp_email)
SET #recID = (SELECT tempID FROM #tableVar)
-- The line above is used to search the tempory table we created earlier where the ID we need is saved. Since there is only one record in this tempory table, and only one field, it will only select the ID number you need and drop it into '#recID'. '#recID' now has the ID number you want and you can use it how you want like i have used it below.
INSERT INTO staff(Staff_comp_id)
VALUES (#recID)
End
-- So there you go. You can actually grab what ever you want in the 'OUTPUT inserted.WhatEverFieldNameYouWant' line and create what fields you want in your tempory table and access it to use how ever you want.
I was looking for something like this for ages, with this detailed break down, I hope this helps.
In pure SQL the main statement kools like:
INSERT INTO [simbs] ([En]) OUTPUT INSERTED.[ID] VALUES ('en')
Square brackets defines the table simbs and then the columns En and ID, round brackets defines the enumeration of columns to be initiated and then the values for the columns, in my case one column and one value. The apostrophes enclose a string
I will explain you my approach:
It might be not easy to understand but i hope useful to get the big picture around using the last inserted id. Of course there are alternative easier approaches. But I have reasons to keep mine. Associated functions are not included, just their names and parameter names.
I use this method for medical artificial intelligence
The method check if the wanted string exist in the central table (1). If the wanted string is not in the central table "simbs", or if duplicates are allowed, the wanted string is added to the central table "simbs" (2). The last inseerted id is used to create associated table (3).
public List<int[]> CreateSymbolByName(string SymbolName, bool AcceptDuplicates)
{
if (! AcceptDuplicates) // check if "AcceptDuplicates" flag is set
{
List<int[]> ExistentSymbols = GetSymbolsByName(SymbolName, 0, 10); // create a list of int arrays with existent records
if (ExistentSymbols.Count > 0) return ExistentSymbols; //(1) return existent records because creation of duplicates is not allowed
}
List<int[]> ResultedSymbols = new List<int[]>(); // prepare a empty list
int[] symbolPosition = { 0, 0, 0, 0 }; // prepare a neutral position for the new symbol
try // If SQL will fail, the code will continue with catch statement
{
//DEFAULT und NULL sind nicht als explizite Identitätswerte zulässig
string commandString = "INSERT INTO [simbs] ([En]) OUTPUT INSERTED.ID VALUES ('" + SymbolName + "') "; // Insert in table "simbs" on column "En" the value stored by variable "SymbolName"
SqlCommand mySqlCommand = new SqlCommand(commandString, SqlServerConnection); // initialize the query environment
SqlDataReader myReader = mySqlCommand.ExecuteReader(); // last inserted ID is recieved as any resultset on the first column of the first row
int LastInsertedId = 0; // this value will be changed if insertion suceede
while (myReader.Read()) // read from resultset
{
if (myReader.GetInt32(0) > -1)
{
int[] symbolID = new int[] { 0, 0, 0, 0 };
LastInsertedId = myReader.GetInt32(0); // (2) GET LAST INSERTED ID
symbolID[0] = LastInsertedId ; // Use of last inserted id
if (symbolID[0] != 0 || symbolID[1] != 0) // if last inserted id succeded
{
ResultedSymbols.Add(symbolID);
}
}
}
myReader.Close();
if (SqlTrace) SQLView.Log(mySqlCommand.CommandText); // Log the text of the command
if (LastInsertedId > 0) // if insertion of the new row in the table was successful
{
string commandString2 = "UPDATE [simbs] SET [IR] = [ID] WHERE [ID] = " + LastInsertedId + " ;"; // update the table by giving to another row the value of the last inserted id
SqlCommand mySqlCommand2 = new SqlCommand(commandString2, SqlServerConnection);
mySqlCommand2.ExecuteNonQuery();
symbolPosition[0] = LastInsertedId; // mark the position of the new inserted symbol
ResultedSymbols.Add(symbolPosition); // add the new record to the results collection
}
}
catch (SqlException retrieveSymbolIndexException) // this is executed only if there were errors in the try block
{
Console.WriteLine("Error: {0}", retrieveSymbolIndexException.ToString()); // user is informed about the error
}
CreateSymbolTable(LastInsertedId); //(3) // Create new table based on the last inserted id
if (MyResultsTrace) SQLView.LogResult(LastInsertedId); // log the action
return ResultedSymbols; // return the list containing this new record
}
I tried the above but they didn't work, i found this thought, that works a just fine for me.
var ContactID = db.GetLastInsertId();
Its less code and i easy to put in.
Hope this helps someone.
You can also use a call to SCOPE_IDENTITY in SQL Server.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace DBDemo2
{
public partial class Form1 : Form
{
string connectionString = "Database=company;Uid=sa;Pwd=mypassword";
System.Data.SqlClient.SqlConnection connection;
System.Data.SqlClient.SqlCommand command;
SqlParameter idparam = new SqlParameter("#eid", SqlDbType.Int, 0);
SqlParameter nameparam = new SqlParameter("#name", SqlDbType.NChar, 20);
SqlParameter addrparam = new SqlParameter("#addr", SqlDbType.NChar, 10);
public Form1()
{
InitializeComponent();
connection = new System.Data.SqlClient.SqlConnection(connectionString);
connection.Open();
command = new System.Data.SqlClient.SqlCommand(null, connection);
command.CommandText = "insert into employee(ename, city) values(#name, #addr);select SCOPE_IDENTITY();";
command.Parameters.Add(nameparam);
command.Parameters.Add(addrparam);
command.Prepare();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void buttonSave_Click(object sender, EventArgs e)
{
try
{
int id = Int32.Parse(textBoxID.Text);
String name = textBoxName.Text;
String address = textBoxAddress.Text;
command.Parameters[0].Value = name;
command.Parameters[1].Value = address;
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
int nid = Convert.ToInt32(reader[0]);
MessageBox.Show("ID : " + nid);
}
/*int af = command.ExecuteNonQuery();
MessageBox.Show(command.Parameters["ID"].Value.ToString());
*/
}
catch (NullReferenceException ne)
{
MessageBox.Show("Error is : " + ne.StackTrace);
}
catch (Exception ee)
{
MessageBox.Show("Error is : " + ee.StackTrace);
}
}
private void buttonSave_Leave(object sender, EventArgs e)
{
}
private void Form1_Leave(object sender, EventArgs e)
{
connection.Close();
}
}
}
There are all sorts of ways to get the Last Inserted ID but the easiest way I have found is by simply retrieving it from the TableAdapter in the DataSet like so:
<Your DataTable Class> tblData = new <Your DataTable Class>();
<Your Table Adapter Class> tblAdpt = new <Your Table Adapter Class>();
/*** Initialize and update Table Data Here ***/
/*** Make sure to call the EndEdit() method ***/
/*** of any Binding Sources before update ***/
<YourBindingSource>.EndEdit();
//Update the Dataset
tblAdpt.Update(tblData);
//Get the New ID from the Table Adapter
long newID = tblAdpt.Adapter.InsertCommand.LastInsertedId;
Hope this Helps ...
After inserting any row you can get last inserted id by below line of query.
INSERT INTO aspnet_GameProfiles(UserId,GameId)
VALUES(#UserId, #GameId);
SELECT ##IDENTITY
If you're using executeScalar:
cmd.ExecuteScalar();
result_id=cmd.LastInsertedId.ToString();
Maybe this answer helps as well as my database seems to have no column specified as "IDENTITY" (which is needed for "SELECT SCOPE_IDENTITY()" or "##IDENTITY" calls). Also my "ID" column was of type "binary(16)" so I needed to convert the output like stated below:
string returnId = BitConverter.ToString((byte[])cmd.ExecuteScalar()).Replace("-", "");
// skip the replace if you handle the hyphen otherwise
Use SELECT SCOPE_IDENTITY() in query
After this:
INSERT INTO aspnet_GameProfiles(UserId, GameId) OUTPUT INSERTED.ID VALUES(#UserId, #GameId)
Execute this
int id = (int)command.ExecuteScalar;
It will work
INSERT INTO aspnet_GameProfiles(UserId,GameId) VALUES(#UserId, #GameId)";
then you can just access to the last id by ordering the table in desc way.
SELECT TOP 1 UserId FROM aspnet_GameProfiles ORDER BY UserId DESC.
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[spCountNewLastIDAnyTableRows]
(
#PassedTableName as NVarchar(255),
#PassedColumnName as NVarchar(225)
)
AS
BEGIN
DECLARE #ActualTableName AS NVarchar(255)
DECLARE #ActualColumnName as NVarchar(225)
SELECT #ActualTableName = QUOTENAME( TABLE_NAME )
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = #PassedTableName
SELECT #ActualColumnName = QUOTENAME( COLUMN_NAME )
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME = #PassedColumnName
DECLARE #sql AS NVARCHAR(MAX)
SELECT #sql = 'select MAX('+ #ActualColumnName + ') + 1 as LASTID' + ' FROM ' + #ActualTableName
EXEC(#SQL)
END

Categories