Batch insert/update using stored procedure - sql

Can anyone give me a sample script for batch insert/update of records in table using stored procedure in SQL Server?

I've done something like this in the past:
CREATE PROCEDURE InsertProductIds(#ProductIds xml) AS
INSERT INTO Product (ID)
SELECT ParamValues.ID.value('.', 'VARCHAR(20)')
FROM #ProductIds.nodes('/Products/id') as ParamValues(ID)
END
Obviously this is just a single-column table, but the XML approach applies for multi-column tables as well. You then do this:
EXEC InsertProductIds #ProductIds='<Products><id>3</id><id>6</id></Products>'

Sending a table value parameter is another option.
SQL
CREATE TYPE TestTableType AS TABLE
(
ID INT,
Name NVARCHAR(100),
Description NVARCHAR(2000)
);
GO
CREATE proc [dbo].[Test_Table_Parameter]
#Tbl TestTableType READONLY
as
SELECT 'Return'
GO
Code
var param = new SqlParameter();
param.ParameterName = "#Tbl";
param.SqlDbType = SqlDbType.Structured;
var dt = new DataTable();
var str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + DateTime.Now;
//map the fields to datatypes here
dt.Columns.Add("ID", typeof (Int32));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Description", typeof(string));
for (var i = 0; i < rows; i++)
{
dt.Rows.Add(new object[] {i + 1, (i + 1).ToString(), str });
}
param.Value = dt;
These were taken from here which also looks at performance of this and the xml approach on the SQL query end.
This looks at performance on the data transmission end. Keep both in mind and the size of the data you are passing back and forth and how it is going to be used in a query to choose the best approach.

Related

Returning a table from stored procedure as a full result set (then passing it to script task as object)

I have a procedure that returns a table (name "ValidationResultTbl"):
CREATE PROCEDURE SP_CM_ValidateInput #FileName VARCHAR(250)AS
DECLARE #ValidationResultTbl TABLE (ValidationDescription VARCHAR(100), ErrCnt INT)
DECLARE #ErrCounter INT
BEGIN
--some irrelevant code
IF #ErrCounter > 0
INSERT INTO #ValidationResultTbl
VALUES ('Errors were found in the file''s mapping', #ErrCounter)
SELECT * FROM #ValidationResultTbl
END
GO
I'm executing the stored procedure in SSIS using an sql-task:
In Result Set tab I set the variable that receives the result as ValidationResult (type object):
I then add a script task that is supposed to concatenate the values of the first column of ValidationResult table:
using System.Data;
using System.Data.OleDb;
public void Main()
{
OleDbDataAdapter adapt = new OleDbDataAdapter();
DataTable dt = new DataTable();
adapt.Fill(dt, Dts.Variables["User::ValidationResult"].Value);
String msg = "";
String msg1 = "";
foreach (DataRow row in dt.Rows)
{
Object[] array = row.ItemArray;
msg = array[0].ToString();
msg1 += msg + "\n";
}
Dts.TaskResult = (int)ScriptResults.Success;
}
(The task script editor:)
It doesn't work as expected, and when I debug I see that the object that is supposed to hold the table is actually empty.
I've tried changing the procedure to return a single string row, then used it in the script task and didn't have a problem so I'm assuming the problem has something to do with the SQL task not retrieving the table properly or not passing it to the Script task properly.
Not sure if this is your issue but for SSIS (depending on version of SQL server) has issue sgrabbing the result set from temp tables or table variables, in your script task you need to do something like this to identify the columns and types of data returned:
EXEC SP_CM_ValidateInput
WITH RESULT SETS
(
(
ValidationDescription VARCHAR(100),
ErrCnt INT
)
)
GO
Details on WITH RESULT SET:
https://www.mssqltips.com/sqlservertip/2356/overview-of-with-result-sets-feature-of-sql-server-2012/

SQL lock row,table on insert and select

I am trying to achieve some sort of lock on sql.
To explain what am i doing simple:
One table with Id int autoincrement as PK, and one field Data varchar(max) non-clustered IX
Now i have some C# code that simlpy checks if the item isn't in the db, makes an insert
The sql code that i am using behind is like:
INSERT INTO {0}.{1} WITH (TABLOCKX) VALUES(#data...)
and the select one is:
SELECT Id FROM {0}.{1} WITH (TABLOCKX) WHERE(Data = #data)
But i can see that there are items with the same value inserted multiple times
TABLOCK creates deadlocks, and i dont want to use unique index because its very slow.
Is there a way to achieve this with locking?
I'm not sure it is what you want, I hope that this reply is helpful.
private void test(string aConnectionString, string aData)
{
using (SqlConnection sqlConnection = new SqlConnection(aConnectionString))
{
sqlConnection.Open();
SqlCommand sqlCommand = sqlConnection.CreateCommand();
SqlTransaction sqlTransaction = sqlConnection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted);
sqlCommand.Connection = sqlConnection;
sqlCommand.Transaction = sqlTransaction;
try
{
sqlCommand.CommandText = #"IF NOT EXISTS(SELECT Id FROM {0}.{1} WHERE Data = #Data)
BEGIN
INSERT INTO {0}.{1}
SELECT #Data
END";
sqlCommand.Parameters.Add("#Data", System.Data.SqlDbType.VarChar).Value = aData;
sqlTransaction.Commit();
}
catch (Exception ex)
{
sqlTransaction.Rollback();
}
}
}

Retrieve value of a table and using the value in stored procedure

I'm beginner in SQL Server 2012; I need to generate a product ID in a stored procedure, I generated part of the ID in C#, that part of ID includes Industrialist ID and I pass this to my stored procedure. In the stored procedure I need the last product of my Industrialist number and save in to as SQL variable on my stored procedure. How can I do this?
There are many ways to pass items between SQL and C#, you could use an output parameter where you will populate the parameter within the stored procedure.
string variableName;
using (var conn = new SqlConnection("**connection string**"))
using (var cmd = new SqlCommand("storedProcedureName", conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("inputParameter", inputParameter);
var outputParameter = new SqlParameter(){
ParameterName="ParameterName"
,Direction = ParameterDirection.Output
,SqlDbType = SqlDbType.VarChar
,DbType = DbType.VarChar
};
conn.Open();
try
{
cmd.ExecuteNonQuery();
variableName = string.Format("{0}", outputParameter.Value);
}
catch{}
finally
{
conn.Close();
}
}
You could return the value using something along the lines of RETURN #returnValue in your procedure, or you could return it within a table.
string variableName;
using (var conn = new SqlConnection("**connection string**"))
using (var cmd = new SqlCommand("storedProcedureName", conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("inputParameter", inputParameter);
conn.Open();
try
{
using (var dbReader = cmd.ExecuteReader())
{
if (dbReader.Read())
{
variableName = string.Format("{0}", dbReader["ColumnName"]);
}
}
}
catch{}
finally
{
conn.Close();
}
}
A generic example of a stored procedure which might work:
CREATE PROC [dbo].[storedProcedureName]
#InputParameter VARCHAR
,#OutputParameter VARCHAR OUTPUT
AS BEGIN
DECLARE #insertedId INT;
BEGIN TRANSACTION
INSERT INTO TableName (...Column Names...)
VALUES (... Values...)
SET #insertedId = SCOPE_IDENTITY()
COMMIT
SELECT #OutputParameter = ColumnName
FROM TableName
WHERE IdColumnName = #insertedId
END
EDIT: Possibly more relevant:
CREATE PROC [dbo].[storedProcedureName]
#IndustrialistId INT
,#OutputParameter VARCHAR OUTPUT -- This might be an int, but it's unclear what you want
AS BEGIN
DECLARE #productId INT;
SELECT #productId = MAX(ProductId)
FROM Products
WHERE IndustrialistId = #IndustrialistId;
SET #OutputParameter = CONVERT(VARCHAR,#IndustrialistId) + '-' + CONVERT(VARCHAR,#productId)
END
If you were to provide some code it might be easier for someone to give you a more tailored response. None of the above code has been syntax checked etc. so should be considered more pseudo code but hopefully it gives you something to work with.

Implicit conversion from data type nvarchar to varbinary(max) is not allowed

I get this exception when I try to insert a DBNull.Value into a nullable varbinary(max) field:
Implicit conversion from data type nvarchar to varbinary(max) is not allowed. Use the CONVERT function to run this query.
This is my code:
insertCMD.Parameters.AddWithValue("#ErrorScreenshot", SqlDbType.VarBinary).Value = DBNull.Value;
I know there exist duplicate questions on SO, but I do NOT use any String like the others do.
What do I wrong?
UPDATE:
using (var insertCMD = new SqlCommand("INSERT INTO TestplanTeststep (TeststepId,TestplanId,CreatedAt,ErrorText,ErrorScreenshot,TestState) VALUES (#TeststepId, #TestplanId,#CreatedAt,#ErrorText,#ErrorScreenshot,#TestState)", con))
{
var p1 = insertCMD.Parameters.Add("#TeststepId", SqlDbType.Int);
var p2 = insertCMD.Parameters.Add("#CreatedAt", SqlDbType.DateTime);
insertCMD.Parameters.AddWithValue("#TestplanId", testplan.Id);
insertCMD.Parameters.AddWithValue("#ErrorText", (object) DBNull.Value);
insertCMD.Parameters.AddWithValue("#ErrorScreenshot", (object) DBNull.Value);
insertCMD.Parameters.AddWithValue("#TestState", (int)Teststep.TeststepTestState.Untested);
foreach (Teststep step in teststeps)
{
p1.Value = step.Id;
p2.Value = step.CreatedAt;
insertCMD.ExecuteNonQuery();
}
}
I had the same problem while insertion DBNull.Value for a Varbinary(Max) column. After Googling I found a solution that may help you as well:
You need to set size -1 which means Max length for varbinary column when adding your sql parameter:
this.cmd.Parameters.Add("#Photo", SqlDbType.VarBinary, -1).Value = DBNull.Value;
So in your case:
insertCMD.Parameters.Add("#ErrorScreenshot", SqlDbType.VarBinary,-1).Value = DBNull.Value;
Why not change your SQL to:
INSERT INTO TestplanTeststep
(TeststepId,TestplanId,CreatedAt,ErrorText,ErrorScreenshot,TestState)
VALUES
(#TeststepId, #TestplanId,#CreatedAt,NULL,NULL,#TestState)
or just
INSERT INTO TestplanTeststep
(TeststepId,TestplanId,CreatedAt,TestState)
VALUES
(#TeststepId, #TestplanId,#CreatedAt,#TestState)
...and omit the two parameters?
If it's always NULL, that will have the same effect.
Otherwise, try it in two lines:
var binary1 = insertCMD.Parameters.Add("#ErrorScreenshot", SqlDbType.VarBinary, -1);
binary1.Value = DBNull.Value;
Otherwise, in your original SQL insert statement, you're not defining the parameter type but passing in varbinary, hence the error.

Insert Into temp table from a stored procedure that returns multiple result sets

Consider the following sql
A stored proc called myProc which returns two result sets. Result set 1 returns column1, column2. Result set 2 returns column 3, column 4, column 5.
The following sql will fail since the temp table only has defined 2 int columns.
Create Table #temp1(
Column1 int,
Column2 int)
insert into #temp1 exec myProc
My question is is it possible to just insert the first result set into #temp1?
Old post, but I faced the same problem and although the answers mentioned above are a bit related, the OP's question is about SP that returns multiple sets. The only solution I could find, apart from rewriting the SP to split it into smaller SPs, was to write a SQL CLR procedure that executes the SP and returns only the required result set. The procedure gets the index of the required result set, executes a SqlCommand to run the intial T-SQL SP, then loops through a SqlDataReader results until it finds the desired result set and returns the corresponding records. The following code is part of the SQL CLR procedure:
SqlDataReader rdr = command.ExecuteReader();
int index = 0;
bool bContinue = true;
while (index < resultSetIndex.Value)
{
if (!rdr.NextResult())
{
bContinue = false;
break;
}
index++;
}
if (!bContinue)
throw new Exception("Unable to read result sets.");
.......
List<SqlMetaData> metadataList = new List<SqlMetaData>();
for (int i = 0; i < rdr.FieldCount; i++)
{
string dbTypeName = rdr.GetDataTypeName(i);
SqlMetaData metadata;
if (dbTypeName.ToLower().Contains("char"))
metadata = new SqlMetaData(rdr.GetName(i), (SqlDbType)Enum.Parse(typeof(SqlDbType), dbTypeName, true), 50);
else
metadata = new SqlMetaData(rdr.GetName(i), (SqlDbType)Enum.Parse(typeof(SqlDbType), dbTypeName, true));
metadataList.Add(metadata);
}
SqlDataRecord record = new SqlDataRecord(metadataList.ToArray());
object[] values = new object[rdr.FieldCount];
if (rdr.HasRows)
{
SqlContext.Pipe.SendResultsStart(record);
while (rdr.Read())
{
rdr.GetValues(values);
record.SetValues(values);
SqlContext.Pipe.SendResultsRow(record);
}
SqlContext.Pipe.SendResultsEnd();
}
There's another way
SELECT * into #temp
from OPENROWSET('SQLNCLI', 'Server=(local)\\(instance);Trusted_Connection=yes;',
'EXEC (database).(schema).(sproc)')
This'll insert the first resultset into #temp