Update a table from two comma separated parameter as input - c#

I have a Gridview in front end where Grid have two columns : ID and Order like this:
ID Order
1 1
2 2
3 3
4 4
Now user can update the order like in front end Gridview:
ID Order
1 2
2 4
3 1
4 3
Now if the user click the save button the ID and order data is being sent to Stored Procedure as #sID = (1,2,3,4) and #sOrder = (2,4,1,3)
Now if I want to update the order and make save I want to store it into database. Through Stored procedure how can update into the table so that the table is updated and while select it gives me the results like:
ID Order
1 2
2 4
3 1
4 3

There is no built in function to parse these comma separated string. However, yo can use the XML function in SQL Server to do this. Something like:
DECLARE #sID VARCHAR(100) = '1,2,3,4';
DECLARE #sOrder VARCHAR(10) = '2,4,1,3';
DECLARE #sIDASXml xml = CONVERT(xml,
'<root><s>' +
REPLACE(#sID, ',', '</s><s>') +
'</s></root>');
DECLARE #sOrderASXml xml = CONVERT(xml,
'<root><s>' +
REPLACE(#sOrder, ',', '</s><s>') +
'</s></root>');
;WITH ParsedIDs
AS
(
SELECT ID = T.c.value('.','varchar(20)'),
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RowNumber
FROM #sIDASXml.nodes('/root/s') T(c)
), ParsedOrders
AS
(
SELECT "Order" = T.c.value('.','varchar(20)'),
ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RowNumber
FROM #sOrderASXml.nodes('/root/s') T(c)
)
UPDATE t
SET t."Order" = p."Order"
FROM #tableName AS t
INNER JOIN
(
SELECT i.ID, p."Order"
FROM ParsedOrders p
INNER JOIN ParsedIDs i ON p.RowNumber = i.RowNumber
) AS p ON t.ID = p.ID;
Live Demo
Then you can put this inside a stored procedure or whatever.
Note that: You didn't need to do all of this manually, it should be some way to make this gridview update the underlying data table automatically through data binding. You should search for something like this instead of all this pain.

You could use a table valued parameter to avoid sending delimiter-separated values or even XML to the database. To do this you need to:
Declare a parameter type in the database, like this:
CREATE TYPE UpdateOrderType TABLE (ID int, Order int)
After that you can define the procedure to use the parameter as
CREATE PROCEDURE UpdateOrder (#UpdateOrderValues UpdateOrderType readonly)
AS
BEGIN
UPDATE t
SET OrderID = tvp.Order
FROM <YourTable> t
INNER JOIN #UpdateOrderValues tvp ON t.ID=tvp.ID
END
As you can see, the SQL is trivial compared to parsing XML or delimited strings.
Use the parameter from C#:
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText = "dbo.UpdateOrder";
command.CommandType = CommandType.StoredProcedure;
//create a table from your gridview data
DataTable paramValue = CreateDataTable(orderedData)
SqlParameter parameter = command.Parameters
.AddWithValue("#UpdateOrderValues", paramValue );
parameter.SqlDbType = SqlDbType.Structured;
parameter.TypeName = "dbo.UpdateOrderType";
command.ExecuteNonQuery();
}
where CreateDataTable is something like:
//assuming the source data has ID and Order properties
private static DataTable CreateDataTable(IEnumerable<OrderData> source) {
DataTable table = new DataTable();
table.Columns.Add("ID", typeof(int));
table.Columns.Add("Order", typeof(int));
foreach (OrderData data in source) {
table.Rows.Add(data.ID, data.Order);
}
return table;
}
(code lifted from this question)
As you can see this approach (specific to SQL-Server 2008 and up) makes it easier and more formal to pass in structured data as a parameter to a procedure. What's more, you're working with type safety all the way, so much of the parsing errors that tend to crop up in string/xml manipulation are not an issue.

You can use charindex like
DECLARE #id VARCHAR(MAX)
DECLARE #order VARCHAR(MAX)
SET #id='1,2,3,4,'
SET #order='2,4,1,3,'
WHILE CHARINDEX(',',#id) > 0
BEGIN
DECLARE #tmpid VARCHAR(50)
SET #tmpid=SUBSTRING(#id,1,(charindex(',',#id)-1))
DECLARE #tmporder VARCHAR(50)
SET #tmporder=SUBSTRING(#order,1,(charindex(',',#order)-1))
UPDATE dbo.Test SET
[Order]=#tmporder
WHERE ID=convert(int,#tmpid)
SET #id = SUBSTRING(#id,charindex(',',#id)+1,len(#id))
SET #order=SUBSTRING(#order,charindex(',',#order)+1,len(#order))
END

Related

How to create multiple tables dynamiclly in view by using following data in list

My requirement is to create multiple tables dynamic
here is part numbers .table number
1. table number 1 has 2 records and table number 2 has also 2 records.
firstly create table no. 1 and fill its records
after that create new second table no. 2 and its records and soo on according to table number
in some case i have only part no. but in this we create another something html in which we display those data which does not have table no.
Declare table variable to store id and tableNo.
declare #TempTable table(id int identity,TableNo int)
Insert into this table of all distinct TableNo column data..
INSERT INTO #TempTable
SELECT DISTINCT TabloNo
FROM TableName
Declare following variable and set its value like...
declare #counter int, #totalTable int, #NoOfTable int, #StrquryInser nvarchar(max)
select #totalTable = count(id) from #TempTable
set #counter = 1
set #StrquryInser = ''
In while block select table data according to tableNo and insert it into #temp1, #temp2...dynamically using this code...
begin
select #NoOfTable = TableNo
from #TempTable
where id = #counter
set #StrquryInser = #StrquryInser + ' select * into #temp' +cast(#counter as varchar(10)) + ' from TableName where isnull([TabloNo],0)='+cast(isnull(#NoOfTable,0) as nvarchar(10))
set #StrquryInser=#StrquryInser + ' select * from #temp' +cast(#counter as varchar(10))
set #counter=#counter+1
end
execute the string Query #StrquryInser like...
exec(#StrquryInser)
In above query you can store your table data in multiple temp variables like temp1, #temp2 etc..
Apart form these #temp table you can also store your data in permanent table using above logic..

How to process an integer list row by row sent to a procedure in SQL Server 2008 R2?

I have an ArrayList (in C#) that contains some int numbers (those are IDs in a table), I want to select some data for each number(s) in this ArrayList and return a table variable or a #temporary table :)
I found a solution for passing this ArrayList as an user-defined table type to my stored procedure:
CREATE TYPE [dbo].[integer_list_tbltype] AS TABLE(
[n] [int] NOT NULL,
PRIMARY KEY CLUSTERED ([n] ASC)
WITH (IGNORE_DUP_KEY = OFF)
)
GO
CREATE PROCEDURE [dbo].[Sp_apr_get_apraisors]
(#listNumbers INTEGER_LIST_TBLTYPE readonly)
AS
....
but I didn't find an efficient way to read this array as easily as in C# :(
Is there any way to write a loop for each of these numbers and save data in a temp table and finally return it to C#??
SQL is set based, so your best option is to write a single select statement that would join your input table to the tables containing the data you would like to look up. The select statement would be the result set to be sent back to your application. Then if you want to use straight ADO.Net, you can use the SqlDataReader class to read back into C#, or you could use an ORM like Linq2Sql, Entity Framework, or NHibernate. By the way, if you must do a loop in Sql, please avoid cursors. They are slow and unnecessarily complicated both to manage and to develop. Use a while loop instead.
I would suggest you change the procedure parameter to varchar(n) and then send in those values as comma-delimited string.
DECLARE #IDs VARCHAR(MAX)
SELECT #IDs = '1,2,3'
DECLARE #ID INT
WHILE LEN(#IDs) > 0
BEGIN
SELECT #ID = CONVERT(INT, LEFT(#IDs, CHARINDEX(',', #IDs + ',') -1)
-- Do something with the ID here...
SELECT #IDs = STUFF(#IDs, 1, CHARINDEX(',', #IDs + ','), '')
END
mmmmm :), after 24h (!) search aorund the www , i found my problem Answer, #Toni's answer helped me on this :) Tanx #Toni :*
1) first define stored procedure
CREATE PROCEDURE [spName]( #list_entry VARCHAR(max)=NULL)
AS
BEGIN
SELECT [Column1,column2,...]
FROM [TABLE(s)]
WHERE ( #list_entry IS NULL
OR Column1 IN (SELECT value FROM Fn_split(#list_person, ',')) )
END
2) write a function to split items (comma delimited)
CREATE FUNCTION [dbo].[fn_Split](#text varchar(8000), #delimiter varchar(20) = ' ')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value varchar(8000)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
GO
3) pass my array as a comma-delimited string from .NET
//defin sample array list or your data
ArrayList array = new ArrayList();
//fill array with some data
for (int i = 1000; i<1010;i++)
array.Add(i);
//define connection and command
using(SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["connString"].ConnectionString))
{
connection.Open();
SqlCommand cmd = new SqlCommand("",connection);
cmd.Parameters.AddWithValue("#list_entry", SqlDbType.varchar,8000,Get_comma_delimited_string(array));
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "yourSpName";
cmd.ExecuteNonQuery();
}
/// <summary>
/// Resturns a comma delimited string (sepearte each item in list with ',' )
/// </summary>
public string Get_comma_delimited_string(ArrayList arrayList)
{
string result = string.Empty;
foreach (object item in arrayList)
result += item.ToString() + ",";
return result.Remove(result.Length - 1);
}

stored procedure calling from c# and iteration issue in Merge

I have over a million records in the list. I pass all records at once from table to stored procedure .In stored procedure i have to have iteration to go thorugh all the rows in the table and for each row it takes table row modified date based on jobid and checks if it exist in database and based on it either it updates or insert the record. I feel that my procedure is not correct, would be glad if someone help on this.
foreach (No_kemi no_list in newforSQL)
{
DataTable _dt = new DataTable("table");
_dt.Columns.Add("JobID", typeof(string));
_dt.Columns.Add("CreatedDate", typeof(datetime));
_dt.Columns.Add("ModifiedDate", typeof(datetime));
_dt.Columns.Add("DbDate", typeof(datetime));
_dt.Columns.Add("SubGUID", typeof(string));
_dt.Columns.Add("eType", typeof(string));
// adding over a million records in the table
_dt.Rows.Add(no_list.ID,no_list.CreatedDate,no_list.ModifiedDate,no_list.DbDate,no_list.SubGUID,no_list.eType);
}
using (SqlCommand sqlCommand = new SqlCommand())
{
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandText = "Process_NO_table";
sqlCommand.Connection = connection;
SqlParameter typeParam = sqlCmd.Parameters.AddWithValue("#track", _dt);
typeParam .SqlDbType = SqlDbType.Structured;
sqlCmd.ExecuteNonQuery();
}
my tabletype and procedure:
CREATE TYPE TrackType AS TABLE
(
t_Id uniqueidentifier, t_JobID nvarchar(50), t_CreatedDate datetime2(7), t_ModifiedDate datetime2(7), t_DbDate datetime2(7)
t_SubGUID nvarchar(MAX), t_eType nvarchar(MAX)
);
GO
ALTER/CREATE PROCEDURE [dbo].[Process_NO_table] // i will change to alter after i create it
#track TrackType READONLY
AS
// i need to iterate all the rows of the table(over a million)
Declare #rows INT
Declare #i int = 0
Declare #count int = (SELECT COUNT(*) FROM #track)
DECLARE #is INT
WHILE (#i < #count)
BEGIN
-- first i check modified date from the database table
SELECT #is = COUNT(*) FROM NO_table WHERE [JobID] IN (SELECT [t_JobID] FROM #track)
MERGE [dbo].[NO_table] AS [Target]
USING #track AS [Source]
-- if the database modifed date is less than the modifeid date from the proceduretable(#track) then it updates the records
ON [Target].[ModifiedDate] < [Source].[t_ModifiedDate] AND JobID = t_JobID
WHEN MATCHED THEN
UPDATE SET [JobID] = [Source].[t_JobID],
[CreatedDate] = [Source].[t_CreatedDate]
[DbDate]= [Source].[t_DbDate]
[ModifiedDate] = [Source].[t_ModifiedDate]
[SubGUID] = [Source].[t_SubGUID]
[eType] = [Source].[t_eType]
-- if the database modifed dateis not existing then it insert the record
MERGE [dbo].[NO_table] AS [Target]
USING #track AS [Source]
ON (#is != 0)
WHEN NOT MATCHED THEN
INSERT INTO [NO_table] ( [JobID], [CreatedDate], [ModifiedDate], [DbDate], [SubGUID], [eType] )
VALUES ( [Source].[t_JobID], [Source].[t_CreatedDate], [Source].[t_ModifiedDate], [Source].[t_DbDate], [Source].[t_SubGUID], [Source].[t_eType] );
SET #i = #i + 1
END
GO
I think you have a large number of syntax errors in your SQL (assuming MS SQL), but your merge condition is probably giving you the invalid syntax near WHERE, because you need to use AND, not WHERE.
ON [Target].[ModifiedDate] < [Source].[t_ModifiedDate] WHERE JobID = t_JobID
should be
ON [Target].[ModifiedDate] < [Source].[t_ModifiedDate] AND JobID = t_JobID
The Select Top 1 and the WHEN MATCHED THEN after the null check for #dbmoddate need to go away as well, as those are also causing syntax issues.
The insert after the null check for #dbmoddate needs a table specified so it actually knows what to insert into.
You also need to end your merge statement with a semicolon.
UPDATED ANSWER:
Now that you have this more cleaned up, I can better see what you're trying to do. At a high level, you want to simply update existing records where the modified date is less than the modified date of on your custom type. If there does not exist a record in your table that does exist in your custom type, then insert it.
With that said, you don't actually need to loop because you aren't doing anything with your loop. What you currently have and what I'm posting below this is all set-based results, not iterative.
You can make this much simpler by getting rid of the merge statements and doing a simple Update and Insert like I have below. The merge would make more sense if your condition between the two statements was the same (i.e. if you didn't have the check for modified date, then merge would be OK) because then you can use the keywords WHEN MATCHED and WHEN NOT MATCHED and have it in one single merge statement. I personally stay away from MERGE statements because they tend to be a little buggy and there are a number of things you have to watch out for.
I think this solution will be better in the long run as it is easier to read and more maintainable...
CREATE TYPE TrackType AS TABLE
(
t_Id uniqueidentifier, t_JobID nvarchar(50), t_CreatedDate datetime2(7), t_ModifiedDate datetime2(7), t_DbDate datetime2(7)
,t_SubGUID nvarchar(MAX), t_eType nvarchar(MAX)
);
GO
CREATE PROCEDURE [dbo].[Process_NO_table] -- i will change to alter after i create it
#track TrackType READONLY
AS
-- i need to iterate all the rows of the table(over a million)
Update [NO_table]
SET [JobID] = T.[t_JobID],
[CreatedDate] = T.[t_CreatedDate],
[DbDate]= T.[t_DbDate],
[ModifiedDate] = T.[t_ModifiedDate],
[SubGUID] = T.[t_SubGUID] ,
[eType] = T.[t_eType]
From #track T
Where [NO_table].[JobID] = T.[t_JobID]
And [NO_table].[ModifiedDate] < T.[t_ModifiedDate]
Insert [NO_Table]
(
[JobID],
[CreatedDate],
[ModifiedDate],
[DbDate],
[SubGUID],
[eType]
)
Select T.[t_JobID],
T.[t_CreatedDate],
T.[t_ModifiedDate],
T.[t_DbDate],
T.[t_SubGUID],
T.[t_eType]
From #track T
Where Not Exists (Select 1 From [NO_table] where T.[t_JobID] = [NO_table].[JobID])
GO

How to use a set of strings in a WHERE statement of SQL?

Sorry i am not sure how to titled the question well. I want to select few records in sql where a particular column is a set of strings.
Example . I have a table student and has columns ID and name. ID has records 1,2,3,4,5,6 . NAme has A,B,C,D,E,F.
I want to return C,D,E WHERE ID=[3,4,5].
I tried
SELECT FROM student WHERE ID=2,3,4
it gives error, ID=2,3,4 ='2,3,4' and it reads ID as a single columns. I am confused.
Also in my case, ID set are returned in a storedprocedure variable. that is like #ID
SELECT * FROM STUDENT WHERE ID=#ID
#ID above is a variable of a string type holding the set {1,2,3}. Please any help would be appreciated.
Try this:
SELECT * FROM student WHERE ID IN (2,3,4)
Syntax:
test_expression IN
( subquery | expression [ ,...n ]
)
Read more about IN operator here.
WHERE ID=2,3,4 and WHERE ID='2,3,4' are invalid syntax for SQL.
Looks like you can use IN (Transact-SQL) on your situation.
Determines whether a specified value matches any value in a subquery
or a list.
SELECT FROM student WHERE ID IN (2, 3, 4)
Also you might take a look Jeff's question Parameterize an SQL IN clause
If you are passing #ID as a variable with comma separated list of ids, WHERE IN (#ID) will not work.
I think best thing would be to use a Table Valued function to split them first and then query the table. Please check here for a Split() function.
Usage:
SELECT * FROM STUDENT
WHERE ID IN (
SELECT items FROM dbo.Split(#ID, ',') --Split function here
)
If you want to fitler multiple values in Select, you should use "in ()":
SELECT * FROM student WHERE ID in (2,3,4)
OR
SELECT * FROM student WHERE ID between 2 and 4
OR
SELECT * FROM student WHERE ID = 2 OR ID = 3 OR ID = 4
In this case take the first one.
The last one is very slow and not recommended in this scenario.
Please check this out
Select * from Student where Id IN ('2','3','4')
and check this out
Select Username from Student where ID IN ' + '('+ #Id +')'
where #Id=2,3,4
Select * from Student where Id='2'
union all
Select * from Student where Id='3'
union all
Select * from Student where Id='4'
Based on your comment below, you don't want to convert ID to an int. Instead, use LIKE to compare:
SELECT * from STUDENT
WHERE ', '+#ID+', ' LIKE ', '+CAST(ID as NVARCHAR(255)) + ', ';
However, the query will not be indexed. If you want the query to be indexed, then use dynamic SQL:
DECLARE #query NVARCHAR(max) = 'SELECT * FROM STUDENT WHERE ID IN ('+ #ID +')';
EXEC sp_executesql #query;
Since you are using Stored Procedure, that also has only equality compare i.e. id = 1, so either you have too execute three queries by splitting the input by comma separated values.
OR you can add a new procedure with a custom function to server with the SQL
CREATE FUNCTION dbo.myparameter_to_list (#parameter VARCHAR(500)) returns #myOutput TABLE (mytempVal VARCHAR(40))
AS
begin
DECLARE #TempTable table
(
mytempVal VARCHAR(40)
)
DECLARE #MySplittedValue varchar(40), #PositionOfComma int
SET #par = LTRIM(RTRIM(#parameter))+ ','
SET #PositionOfComma = CHARINDEX(',', #parameter, 1)
IF REPLACE(#parameter, ',', '') <> ''
BEGIN
WHILE #PositionOfComma > 0
BEGIN
SET #MySplittedValue = LTRIM(RTRIM(LEFT(#par, #PositionOfComma - 1)))
IF #MySplittedValue <> ''
BEGIN
INSERT INTO #TempTable (mytempVal) VALUES (#MySplittedValue) --Use conversion if needed
END
SET #par = RIGHT(#par, LEN(#par) - #PositionOfComma)
SET #PositionOfComma = CHARINDEX(',', #par, 1)
END
END
INSERT #myOutput
SELECT mytempVal
FROM #TempTable
RETURN
END
In your stored procedure you would use it like this:
Create Procedure StudentSelectFromSet
#Ids VARCHAR(MAX)
AS
SELECT * FROM student Stud
WHERE Stud.Id IN(SELECT value FROM dbo.myparameter_to_list (#Ids))
and then execute this new procedure as you were accessing earlier.

SQL Server (2008) Pass ArrayList or String to SP for IN()

I was wondering how I can pass either an ArrayList, List<int> or StringBuilder comma delimited list to a stored procedure such that I find a list of IDs using IN():
#myList varchar(50)
SELECT *
FROM tbl
WHERE Id IN (#myList)
In C# I am currently building the list as a string which is comma delimeted; however when using nvarchar(50) for example, as the type for the param in the stored procedure - I get an error as it can't convert '1,2,3' to int which it expects between the IN().
Any ideas? Much appreciated.
Pete
You could use a User Defined function such as
CREATE function [dbo].[csl_to_table] ( #list nvarchar(MAX) )
RETURNS #list_table TABLE ([id] INT)
AS
BEGIN
DECLARE #index INT,
#start_index INT,
#id INT
SELECT #index = 1
SELECT #start_index = 1
WHILE #index <= DATALENGTH(#list)
BEGIN
IF SUBSTRING(#list,#index,1) = ','
BEGIN
SELECT #id = CAST(SUBSTRING(#list, #start_index, #index - #start_index ) AS INT)
INSERT #list_table ([id]) VALUES (#id)
SELECT #start_index = #index + 1
END
SELECT #index = #index + 1
END
SELECT #id = CAST(SUBSTRING(#list, #start_index, #index - #start_index ) AS INT)
INSERT #list_table ([id]) VALUES (#id)
RETURN
END
Which accepts an nvarchar comma separated list of ids and returns a table of those ids as ints. You can then join on the returned table in your stored procedure like so -
DECLARE #passed_in_ids TABLE (id INT)
INSERT INTO #passed_in_ids (id)
SELECT
id
FROM
[dbo].[csl_to_table] (#your_passed_in_csl)
SELECT *
FROM
myTable
INNER JOIN
#passed_in_ids ids
ON
myTable.id = ids.id
In SQL 2008 there are table-valued-parameters, that make a friendly alternative to parsing CSV; see here for an example.
Otherwise, another option is xml - the xml data type in SQL Server allows you to read this pretty easily (although it takes more transfer bytes).

Categories

Resources