How to simplify batched SQL Inserts with too many parameters - c#

So I am trying to insert data that looks like this:
INSERT INTO RELATIONSHIP_CONFIG (USERID, WORKGROUPID, PRIORITY) VALUES
(#userId, #WorkgroupId10, #SmartFeedPriority10),
(#userId, #WorkgroupId11, #SmartFeedPriority11),
(#userId, #WorkgroupId12, #SmartFeedPriority12),
(#userId, #WorkgroupId13, #SmartFeedPriority13);
Which generally is very simple and linear as all inserts happen one after the other and performs fine (I think).
The issue is that there is a hard limit with the number of SQL Parameters I am allowed to use- 2100.
The upper limit edge case accounts for an insert that is quite a bit above that.
I was thinking about passing the data for WorkgroupId and SmartFeedPriority as a csvs and using a split function to create tables or something like that...
What is the best approach for dealing with data like this?
Maybe creating a stored procedure, passing the #UserId, #WorkgroupId (CSV), and #SmartFeedPriority (CSV) and having linear, one by one inserts done this way, but I am not too sure how the logic for this will look...

Looking at your question it's a bit difficult to suggest a good approach. I'm unable to see how and where the source of the data is.
I see you mentioned a CSV file. You can import data from a CSV file using the below script. Once the data is in a table, you can try one of the below examples.
IF OBJECT_ID('tempdb.dbo.#TempTable', 'U') IS NOT NULL
DROP TABLE #TempTable ;
CREATE TABLE #TempTable
(
[UserID] NVARCHAR(MAX) NULL,
[WorkGroup] NVARCHAR(MAX) NULL,
[SmartFeedPriority] NVARCHAR(MAX) NULL,
)
BULK INSERT #TempTable
FROM ' put your csv file path here '
WITH
(
FIELDTERMINATOR = ',', -- comma delimited
ROWTERMINATOR = '\n',
CODEPAGE = 65001 --'65001'
)
If the data you're trying to insert is from a table, you could try selecting the data into the table you need it to be.
Example :
SELECT [UserID], [WorkgroupID], [SmartFeedPriority]
INTO [dbo].[RELATIONSHIP_CONFIG]
FROM [dbo].[SorceTable]
If you would like to take the procedural route you can try the below. The below sample would work if the source of your data is in a table and you would like to individually insert each record.
Example :
procedure for insert
CREATE PROCEDURE [dbo].[CREATE_RELATIONSHIP_CONFIG](#UserId INT, #WorkgroupId INT, #SmartFeedPriority INT)
AS
BEGIN
INSERT INTO RELATIONSHIP_CONFIG (UserId, WorkgroupId, Priority) VALUES
(#userId, #WorkgroupId, #SmartFeedPriority)
END
You can wrap the above procedure in a while loop.
I've added a example for it below.
declare #UserId int;
declare #WorkgroupId int;
declare #Priority int;
WHILE EXISTS (SELECT [UserID] FROM #TempTable)
BEGIN
SELECT TOP 1 #UserId=[UserID], #WorkgroupId=[WorkGroup] , #Priority=[SmartFeedPriority] FROM #TempTable
EXEC [dbo].[CREATE_RELATIONSHIP_CONFIG] #UserId, #WorkgroupId, #Priority
DELETE TOP (1)FROM #TempTable
END

Related

How can I call a stored procedure with INSERT using LINQ?

Is there any possible way to execute a non query without having to assign it to a LINQ to SQL class?
Let's say I have this procedure and I want to INSERT only if the Town_Name (UNIQUE CONSTRAINT) is non existent. Otherwise, the UPDATE is executed. This procedure works well and does the job when executed through SQL Server. However, I can't manage to execute it through C# code.
I am trying to avoid using SQL client, as part of my coursework, my little application has to be capable of selecting, inserting / updating and deleting data using LINQ.
The approach I'm taking always results in 0 rows affected.
// Call Procedure.
db.P_SaveClient("000000001M", "Test", "Dummy", null, "+35699999999");
-- Procedure to be executed.
CREATE PROCEDURE Appointment.SaveClient
(
#id NVARCHAR(10),
#firstName NVARCHAR(35),
#lastName NVARCHAR(35),
#tel NVARCHAR(12),
#mob NVARCHAR(12)
)
AS
BEGIN
DECLARE #clientCount TINYINT
SELECT #clientCount = COUNT(Client_ID)
FROM Appointment.Client
WHERE Client_ID = #id
IF #clientCount = 0
BEGIN
INSERT INTO Appointment.Client
VALUES (
#id
, #firstName
, #lastName
, #tel
, #mob
)
END
ELSE
BEGIN
UPDATE Appointment.Client
SET Client_ID = #id
, First_Name = #firstName
, Last_Name = #lastName
, Telephone = #tel
, Mobile = #mob
END
END
Some tutorials I found:
https://www.youtube.com/watch?v=dlXT-vE46sc
https://www.youtube.com/watch?v=-PAMtSwplu8
You're looking for a Merge statement to execute in SQL, which you could call via the stored proc. This will allow you to insert or update depending on whether it was found. It can even return the ID of the record inserted which can save you another query.
Merge Town t
using ( select #Town_Name as 'Town_Name')
src on (src.Town_Name = t.Town_Name )
when NOT MATCHED then
insert (Town_Name) values (src.Town_Name)
output INSERTED.Town_ID
See here for syntax and more examples:
https://msdn.microsoft.com/en-us/library/bb510625.aspx

C# TableAdapter Fill/Get by Multiple IDs

I'm faced with a little problem. The situation is:
I have a MSSQL table which contains IDs (int, identity, primarykey), and names (string).
This table is huge, so i don't want to fill the entire dataset just for one LINQ-query.
I have a search algorithm, which fills a List<int> with more than one ID.
I want to load the matching rows in a datagridview with the following code:
dataGridView1.DataSource = tbl_WorklistTableAdapter.GetDataByID(ids_here);
But i can't handle multiple IDs, just a single. The GetDataByID() code needs to be this (i think):
SELECT [ID]
,[NAME]
FROM [DataBase].[dbo].[tbl_Namelist]
WHERE ID IN (#searchterm)
However WHERE ID IN accepts comma-separated ints, like 1,2,3. But the #variable is just one int.
How should i convert string to ints?
Thank you, and sorry for bad eng. :)
In that case you need to change in sql so you need to generate one temp table for comma separated id and apply condition in the your query.
Like:-
DECLARE #variables VARCHAR(200) = #searchterm -- Your Variable
CREATE TABLE #temp(ID NUMERIC) DECLARE #temp VARCHAR(200)
SET #temp = #variables WHILE LEN(#temp) > 0 BEGIN DECLARE #t1 VARCHAR(200)
IF CHARINDEX(',',#temp) > 0
BEGIN
SET #t1 = SUBSTRING(#temp,0,CHARINDEX(',',#temp))
INSERT INTO #TEMP SELECT #t1
SET #temp = SUBSTRING(#temp,CHARINDEX(',',#temp)+1,len(#temp))
END
ELSE
BEGIN
INSERT INTO #TEMP SELECT #temp
SET #temp = ''
END
END
SELECT [ID]
,[NAME]
FROM [DataBase].[dbo].[tbl_Namelist]
WHERE ID IN (SELECT ID FROM #temp)
You can use the built-in function SPLIT_STRING function:
SELECT [ID], [NAME]
FROM [DataBase].[dbo].[tbl_Namelist]
WHERE ID IN (SELECT VALUE FROM SPLIT_STRING(#searchterm,','))
This only works on Compatibility level 130 or greater.
If you are on an older version of SQL Server you can follow this answer, which defines an equivalent function:
SELECT [ID], [NAME]
FROM [DataBase].[dbo].[tbl_Namelist]
WHERE ID IN (SELECT VALUE FROM fn_split_string(#searchterm,','))

Multiple inserts in a single stored procedure?

TableName: Information
Stored procedure that inserts data into the above table.
CREATE PROCEDURE sp_insert_information
(
#profileID as int,
#profileName as varchar(8)
#profileDescription as varchar(100)
)
AS
BEGIN
INSERT INTO information(profileid, profilename, profiledescription)
VALUES (#profileID, #profileName, #profileDescription);
END
I call this procedure from .NET, is there a way to do multiple inserts if I pass profileID's as a comma separated parameter? (can I use split function?)
I can either loop through the profileID's and send 1 by 1 to procedure, however my data is going to be the same except different profileID.
Table data (with 3 columns):
1 profileUnavailable User Error
2 profileUnavailable User Error
3 profileUnavailable User Error
4 profileUnavailable User Error
5 profileUnavailable User Error
Any other approaches that I can try to do this in a single shot?
You have a couple options:
SqlBulkInsert - You can create a dataset that you can dump to the table. This is useful for many inserts. This will bypass the procedure altogether.
Table Valued Parameters - You can use a table value parameter as a parameter of the stored procedure, again manipulating data using a dataset.
The CSV Parameter with string split IS an option, but I would recommend one of the above over it.
Nope. That sproc does one insert at a time as it is written presently. You have to invoke it separately.
You might also consider wrapping that up into a transaction so if one fails, all of them won't be committed.
My favourite technique up to some years ago was to have an arsenal of splitting functions, that could split a delimited list of homogeneous values (e.g. all integers, all booleans, all datetimes, etc.) into a table variable. Here is an example of such a function.
CREATE FUNCTION [dbo].[fn_SplitInt](#text varchar(8000),
#delimiter varchar(20) = '|')
RETURNS #Values TABLE
(
pos int IDENTITY PRIMARY KEY,
val INT
)
AS
BEGIN
DECLARE #index int
SET #index = -1
-- while the list is not over...
WHILE (LEN(#text) > 0)
BEGIN
-- search the next delimiter
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) -- if no more delimiters (hence this field is the last one)
BEGIN
IF (LEN(#text) > 0) -- and if this last field is not empty
INSERT INTO #Values VALUES (CAST (#text AS INT)) -- then insert it
ELSE -- otherwise, if this last field is empty
INSERT INTO #Values VALUES (NULL) -- then insert NULL
BREAK -- in both cases exit, since it was the last field
END
ELSE -- otherwise, if there is another delimiter
BEGIN
IF #index>1 -- and this field is not empty
INSERT INTO #Values VALUES (CAST(LEFT(#text, #index - 1) AS INT)) -- then insert it
ELSE -- otherwise, if this last field is empty
INSERT INTO #Values VALUES (NULL) -- then insert NULL
SET #text = RIGHT(#text, (LEN(#text) - #index)) -- in both cases move forward the read pointer,
-- since the list was not over
END
END
RETURN
END
When you have a set of functions like these, then your problem has a solution as simple as this one:
CREATE PROCEDURE sp_insert_information
(
#profileID as varchar(2000),
#profileName as varchar(8)
#profileDescription as varchar(100)
)
AS
BEGIN
DECLARE #T TABLE (Id int)
INSERT INTO #T (Id)
SELECT val FROM dbo.fn_SplitInt(#profileID)
INSERT INTO information(profileid, profilename,profiledescription)
SELECT Id, #profileName, #profileDescription
FROM #T
END
But today it might be quicker to execute, and even require less coding, to generate an XML representation of the data to insert, then pass the XML to the stored procedure and have it INSERT INTO table SELECT FROM xml, if you know what I mean.
WHILE len(#ProfileId) > 0
BEGIN
DECLARE #comm int= charindex(',',#ProfileId)
IF #comm = 0 set #comm = len(#ProfileId)+1
DECLARE #Profile varchar(1000) = substring(#ProfileId, 1, #comm-1)
INSERT INTO Information(ProfileId,ProfileName,ProfileDescription)
VALUES (#ProfileId,#ProfileName,#ProfileDescription)
SET #ProfileId= substring(#ProfileId, #comm+1, len(#ProfileId))
END

How to insert values using Joins in asp.net stored procedure?

CREATE PROCEDURE [dbo].[K_HRM_Insert_VehicleAssign]
#vehiclename varchar(50),
#empname varchar(50),
#updatedby varchar(50),
#updatedon datetime
AS
BEGIN
INSERT INTO K_MasterEmpDetails ME
INNER JOIN K_HRM_Vehicle_Assign VA ON VA.[empname+id] = ME.Firstname +' '+ME.Lastname + ' - ' + ME.kjlid AS ME.Employee
(VA.vehiclename, ME.Employee, VA.updatedby, VA.updatedon)
VALUES (#vehiclename, #empname, #updatedby, GETDATE())
END
I am getting an error near ME...please help me
You can't use this syntax. Correct syntax is:
INSERT INTO Table
(COLUMNS)
VALUES
(value)
For correct insert into two tables you have to use transaction and insert into two table separately.
You're syntactically wrong. The correct syntax is what user said is basic
but you can use
Insert into Table
select statement
It will select the rows based on condition then insert into table.
and in select statement you can use join.

Problem in stored procedure

create procedure InsertQuestionEntry
#round_name varchar(40),
#question varchar(100),
#answer varchar(40),
#option1 varchar(20),
#option2 varchar(30),
#option3 varchar(30)
as
begin
insert into QuestionEntry(Question,Round_Name) values(#question,#round_name);
declare #quesion_id int
exec #quesion_id= select Question_ID from QuestionEntry;
insert into Answer(Question_ID,Answer,Option1,Option2,Option3) values(#quesion_id,#answer,#option1,#option2,#option3);
end
Here I want to retrieve the Question_ID from table QuestionEntry and use that Question_ID to another table Answer
But this didn't work.
So how can I use above way?
please help me
Instead of
insert into QuestionEntry(Question,Round_Name) values(#question,#round_name);
declare #quesion_id int
exec #quesion_id= select Question_ID from QuestionEntry;
use the following:
DECLARE #quesion_id int
INSERT INTO QuestionEntry(Question,Round_Name) values(#question,#round_name)
SET #quesion_id = SCOPE_IDENTITY()
You should not use "exec" there.
What exec does is:
Executes a command string or character
string within a Transact-SQL batch, or
one of the following modules: system
stored procedure, user-defined stored
procedure, scalar-valued user-defined
function, or extended stored
procedure.
You should use "set" or "select" instead of exec.
SET can only assign one variable at
a time, SELECT can make multiple
assignments at once. When assigning
from a query if there is no value
returned then SET will assign
NULL, where SELECT will not make
the assignment at all (so the variable
will not be changed from it's previous
value)
You can find more info about when to use SET or SELECT here: SET vs SELECT when assigning variables
Sample:
set #quesion_id = (select Question_ID from QuestionEntry)
select #quesion_id = (select Question_ID from QuestionEntry)
But that's also wrong way to get identity value from inserted record. If you have N users execute a same procedure at a same time it can happen that you will get wrong value (from last inserted record).
To do this properly you should use ##IDENTITY or even better SCOPE_IDENTITY(). More info: here.
After INSERT you can simply call:
SELECT #quesion_id = ##IDENTITY
--or
SELECT #quesion_id = SCOPE_IDENTITY()
Also, check your Question_ID is configured properly. It should be set to auto increment.
Sample:
Question_ID int IDENTITY(1,1)PRIMARY KEY CLUSTERED
The 1's following the IDENTITY keyword indicate the SEED number (value for first record in table) and increment property (0 or 1).
If your server's version is SQL Server 2005 or higher, you could also try something like this:
create procedure InsertQuestionEntry
#round_name varchar(40),
#question varchar(100),
#answer varchar(40),
#option1 varchar(20),
#option2 varchar(30),
#option3 varchar(30)
as
begin
insert into QuestionEntry(Question,Round_Name)
output inserted.Question_ID, #answer, #option1, #option2, #option3
into Answer (Question_ID, Answer, Option1, Option2, Option3)
values(#question,#round_name);
end

Categories