passing array as parameters, stored procedures don't update result - asp.net-mvc

I have an int[] and pass it as parameter to a procedure, when the method is called, no error is reported but nothing is updated in the database. There may be something wrong in my Add() since the Stored Procedure is written by another programmer. I cannot modify this stored procedure so everything must be solved in the Add().
FYI, the Add() takes an array of userIDs and a groupID as parameters, format them and run the stored procedure. The stored procedure inserts userIDs and groupID into the DB
For example:
if userIDs=[1,2,3] and groupID=4,
then I want the following data to be inserted into the DB
userID groupID
1 4
2 4
3 4
The stored procedure
USE [xyz]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spSaveSomething]
#groupID INT,
#userIDs TEXT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #handle INT
EXEC sp_xml_preparedocument #handle OUTPUT, #userIDs
DELETE FROM tbl1 WHERE i_GroupID = #groupID AND i_NetworkUserID IN (SELECT [ID] FROM OPENXML (#handle, 'ROOT/VAL', 1) WITH ([ID] INT))
INSERT INTO tbl1 (i_NetworkUserID, i_GroupID)
SELECT [ID], #groupID FROM OPENXML (#handle, 'ROOT/VAL', 1) WITH ([ID] INT)
EXEC sp_xml_removedocument #handle
END
The Add()
public void AddUsers(int[] UserIDs, int GroupID)
{
List<int> testList = new List<int>();
foreach (int id in UserIDs)
{
testList.Add(id);
}
XmlSerializer xs = new XmlSerializer(typeof(List<int>));
MemoryStream ms = new MemoryStream();
xs.Serialize(ms, testList);
string resultXML = UTF8Encoding.UTF8.GetString(ms.ToArray());
SqlParameter param1 = new SqlParameter("#userIDs", resultXML);
SqlParameter param2 = new SqlParameter("#groupID", GroupID);
context.Database.ExecuteSqlCommand("spSaveSomething #groupID, #userIDs",
param2, param1);
}

Convert the int[] into a string with Comma seprated and then in Stored Prodedure, us in ie
WHERE i_NetworkUserID in #UserIds

for me, your approach a little bit complicated.
I suggest to rewrite your procedure like
alter procedure [dbo].[spSaveSomething]
(
#groupID int,
#userIDs nvarchar(max)
)
as
begin
set nocount on
declare #tmp_Users table (ID int primary key)
insert into #tmp_Users
select T.C.value('.', 'int')
from #userIDs.nodes('List/ID') as T(C)
delete from tbl1
where
i_GroupID = #group_ID and
i_NetworkUserID in (select ID from #tmp_Users)
insert into tbl1 (i_NetworkUserID, i_GroupID)
select ID, #groupID
from #tmp_Users
-- actually it's strange - you deleting users and then inserting
-- may be you wanted to do this:
-- delete from tbl1
-- where
-- i_GroupID = #group_ID and
-- i_NetworkUserID not in (select ID from #tmp_Users)
-- insert into tbl1 (i_NetworkUserID, i_GroupID)
-- select U.ID, #groupID
-- from #tmp_Users as U
-- where
-- not exists (
-- select T.*
-- from tbl1 as T
-- where T.i_GroupID = #group_ID and T.i_NetworkUserID = U.ID
-- )
end
and your xml generation like
var userIDs = new List<int>() { 1, 2, 3 };
var userXML = new XElement("List", a.Select(x => new XElement("ID", x))).ToString();
// so userXML = "<List><ID>1</ID><ID>2</ID><ID>3</ID></List>"
hope that helps

Related

why does Stored procedure not return the 'Out' parameter in bigquery?

I am writing below Stored Procedure in BigQuery but not sure where did I do wrong.
The 'Out' Parameter does not return anything.
Stored Procedure:
create procedure if not exists test.GetNumTherapistSessions(
in LastNamee string,
out NumSessione int64
)
begin
select count(s.SessionNum) as NumSession from test.Session s
inner join test.Therapist t on s.TherapistID=t.TherapistID
where t.LastName=LastNamee;
end
and Here is how I declare the output parameter and how to call it:
declare NumSession int64;
call test.GetNumTherapistSessions("Risk",NumSession);
Here is the output:
So far everything seems right, but when I select the NumSession, it returns Null:
select NumSession;
Output:
Try SET NumSessione = (select count...);
create procedure if not exists test.GetNumTherapistSessions(
in LastNamee string,
out NumSessione int64
)
begin
SET NumSessione = (
select count(s.SessionNum) as NumSession from test.Session s
inner join test.Therapist t on s.TherapistID=t.TherapistID
where t.LastName=LastNamee
);
end
As shown in this docs of bigquery Link you can use SET to assign any values you need to a specific Variables.
Note that bigquery don't have SELECT INTO statement inside procedure.
Examples:
SET (value1, value2, value3) = (SELECT AS STRUCT c1, c2, c3 FROM table_name WHERE condition)
From this answer:select-into-with-bigquery
Another Example:
UPDATE dataset.Inventory
SET quantity = quantity +
(SELECT quantity FROM dataset.NewArrivals
WHERE Inventory.product = NewArrivals.product),
supply_constrained = false
WHERE product IN (SELECT product FROM dataset.NewArrivals)
Another examples can be found here: Link

Passing Multi Value Paramter in stored proc

Firstname emp_Fullname
--------------------------------------
chetan Patel, Chetan
mike Shah, Mike
ronie Desai, Ronie
create proc stored_procedure
#firstnamer varchar(max)
#fullname varchar(max)
as
begin
select ......
from....
where Firstname in (SELECT Value FROM dbo.FnSplit(#firstname,','))
--and emp_Fullname in (SELECT Value FROM dbo.FnSplit(#fullname,','))
I want result for below statement
exec stored_procedure 'chetan,ronie', 'Patel, Chetan,Shah, Mike'
How can I pass more than 2 emp_fullname in parameter in given stored procedure? Below is my function dbo.FnSplit that worked for multi value Firstname parameter but not working multi value fullname parameter.
ALTER FUNCTION [dbo].[FnSplit]
(
#List nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table (Id int identity(1,1), Value nvarchar(100))
AS
BEGIN
WHILE(Charindex(#SplitOn, #List) > 0)
BEGIN
INSERT INTO #RtnValue (value)
SELECT
VALUE = ltrim(rtrim(Substring(#List, 1, Charindex(#SplitOn, #List) - 1)))
SET #List = SUBSTRING(#List, Charindex(#SplitOn, #List) + len(#SplitOn), len(#List))
END
INSERT INTO #RtnValue (Value)
SELECT
VALUE = ltrim(rtrim(#List))
RETURN
END
Firstname in (SELECT Value FROM dbo.FnSplit(#firstname,'|'))
and emp_Fullname in (SELECT Value FROM dbo.FnSplit(#fullname,'|'))
and I figured that still in SSRS double click on dataset click parmater instead of default value choose expression and set it to "join(#firstname.value,"|")" and samething for other "join(#fullname.value,"|")" and now run it. Multi valye parameter should work find by doing above procedure.
Thanks to my self lol:) it took me 3 days to figured, thought you guys can use it!

insert multi value with SP

I have two tables.
1- student table & 2- Score table
I want to insert value at student table & insert multi value at Score table with SP to SQL Server 2008.
for EX:
ALTER proc [dbo].[InsertIntoScore]
(
#DateReg datetime,
#stdLastName nvarchar(50),
#stdFirstName nvarchar(50),
#Description nvarchar(500),
multi value as score table...
)
AS
DECLARE #Id AS INT
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO Student(DateReg,stdLastName,stdFirstName,[Description])
VALUES (#DateReg,#stdLastName,#stdFirstName,#Description)
set #Id = SCOPE_IDENTITY()
insert multi value at Score table...
COMMIT
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK
END CATCH
please help me...
You should use Table-Valued Parameters
Create a Sql Type for the table you will pass in
CREATE TYPE dbo.ScoreType AS TABLE ( ScoreID int, StudentID int, etc.... )
pass your datatable from C# code into the stored procedure using the above defined type
ALTER proc [dbo].[InsertIntoScore]
( #DateReg datetime, #stdLastName nvarchar(50), #stdFirstName nvarchar(50),
#Description nvarchar(500), #tvpScore ScoreType)
AS
.....
INSERT INTO dbo.Score (ScoreID, StudentID, ....)
SELECT dt.ScoreID, #id, .... FROM #tvpScore AS dt;
in c# pass the datatable in this way
SqlCommand insertCommand = new SqlCommand("InsertIntoScore", sqlConnection);
SqlParameter p1 = insertCommand.Parameters.AddWithValue("#tvpScore", dtScore);
p1.SqlDbType = SqlDbType.Structured;
p1.TypeName = "dbo.ScoreType";
.....
insertCommand.ExecuteNonQuery();

ExecuteStoreQuery with TVP parameters

I have a stored procedure in my database that takes a table value parameter, a list of IdTable objects which contain a single integer Id column.
I have an entity model for the database and want to do the following...
ProjectEntities projectEntities = new ProjectEntities ();
DataTable stationIds = new DataTable();
stationIds.Columns.Add("Id");
stationIds.Rows.Add(1);
stationIds.Rows.Add(2);
SqlParameter parameter = new SqlParameter("#stationIds",stationIds);
parameter.TypeName = "IdTable";
var parameters = new object[] {parameter};
var results = projectEntities .ExecuteStoreQuery<ProjectSummary>("exec ProjectSummary", parameters);
var count = results.Count();
This runs and returns no results, when it should return a bunch of ProjectSummary entities.
When I profile this in SQL Profiler, I get the following
declare #p3 IdTable
insert into #p3 values(N'1')
insert into #p3 values(N'2')
exec sp_executesql N'exec ProjectSummary',N'#stationIds [IdTable] READONLY',#stationIds=#p3
If I declare the stored procedure to be
ALTER PROCEDURE [dbo].[ProjectSummary]
#stationIds [dbo].[IdTable] READONLY
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT * FROM #stationIds
...
Then I get not results back, it looks like the TVP parameter is coming through empty.
Where as if I manually execute
declare #p3 IdTable
insert into #p3 values(N'1')
insert into #p3 values(N'2')
EXEC [ProjectSummary]
#stationIds = #p3
GO
I get the values 1 and 2 returned from the SELECT query.
So, it looks like I want to use EXEC rather than SP_EXECUTESQL when I run ExecuteStoreCommand. Given the code example above, how on earth do I do that?
Turns out the ExecuteStoreQuery call was incorrect, it should be
SqlParameter stations = new SqlParameter { ParameterName = "p0", Value = ids, TypeName = "[dbo].[IdTable]", SqlDbType = SqlDbType.Structured };
var parameters = new object[] { stations };
var results = projectEntities.ExecuteStoreQuery<ProjectSummary>("exec ProjectSummary #p0", parameters);
So I needed to name parameter and add the #p0 to the exec command.

Advantage Database Server: slow stored procedure performance

I have a question about a performance of stored procedures in the ADS. I created a simple database with the following structure:
CREATE TABLE MainTable
(
Id INTEGER PRIMARY KEY,
Name VARCHAR(50),
Value INTEGER
);
CREATE UNIQUE INDEX MainTableName_UIX ON MainTable ( Name );
CREATE TABLE SubTable
(
Id INTEGER PRIMARY KEY,
MainId INTEGER,
Name VARCHAR(50),
Value INTEGER
);
CREATE INDEX SubTableMainId_UIX ON SubTable ( MainId );
CREATE UNIQUE INDEX SubTableName_UIX ON SubTable ( Name );
CREATE PROCEDURE CreateItems
(
MainName VARCHAR ( 20 ),
SubName VARCHAR ( 20 ),
MainValue INTEGER,
SubValue INTEGER,
MainId INTEGER OUTPUT,
SubId INTEGER OUTPUT
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
DECLARE #SubName VARCHAR ( 20 );
DECLARE #MainValue INTEGER;
DECLARE #SubValue INTEGER;
DECLARE #MainId INTEGER;
DECLARE #SubId INTEGER;
#MainName = (SELECT MainName FROM __input);
#SubName = (SELECT SubName FROM __input);
#MainValue = (SELECT MainValue FROM __input);
#SubValue = (SELECT SubValue FROM __input);
#MainId = (SELECT MAX(Id)+1 FROM MainTable);
#SubId = (SELECT MAX(Id)+1 FROM SubTable );
INSERT INTO MainTable (Id, Name, Value) VALUES (#MainId, #MainName, #MainValue);
INSERT INTO SubTable (Id, Name, MainId, Value) VALUES (#SubId, #SubName, #MainId, #SubValue);
INSERT INTO __output SELECT #MainId, #SubId FROM system.iota;
END;
CREATE PROCEDURE UpdateItems
(
MainName VARCHAR ( 20 ),
MainValue INTEGER,
SubValue INTEGER
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
DECLARE #MainValue INTEGER;
DECLARE #SubValue INTEGER;
DECLARE #MainId INTEGER;
#MainName = (SELECT MainName FROM __input);
#MainValue = (SELECT MainValue FROM __input);
#SubValue = (SELECT SubValue FROM __input);
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = #MainName);
UPDATE MainTable SET Value = #MainValue WHERE Id = #MainId;
UPDATE SubTable SET Value = #SubValue WHERE MainId = #MainId;
END;
CREATE PROCEDURE SelectItems
(
MainName VARCHAR ( 20 ),
CalculatedValue INTEGER OUTPUT
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
#MainName = (SELECT MainName FROM __input);
INSERT INTO __output SELECT m.Value * s.Value FROM MainTable m INNER JOIN SubTable s ON m.Id = s.MainId WHERE m.Name = #MainName;
END;
CREATE PROCEDURE DeleteItems
(
MainName VARCHAR ( 20 )
)
BEGIN
DECLARE #MainName VARCHAR ( 20 );
DECLARE #MainId INTEGER;
#MainName = (SELECT MainName FROM __input);
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = #MainName);
DELETE FROM SubTable WHERE MainId = #MainId;
DELETE FROM MainTable WHERE Id = #MainId;
END;
Actually, the problem I had - even so light stored procedures work very-very slow (about 50-150 ms) relatively to plain queries (0-5ms). To test the performance, I created a simple test (in F# using ADS ADO.NET provider):
open System;
open System.Data;
open System.Diagnostics;
open Advantage.Data.Provider;
let mainName = "main name #";
let subName = "sub name #";
// INSERT
let cmdTextScriptInsert = "
DECLARE #MainId INTEGER;
DECLARE #SubId INTEGER;
#MainId = (SELECT MAX(Id)+1 FROM MainTable);
#SubId = (SELECT MAX(Id)+1 FROM SubTable );
INSERT INTO MainTable (Id, Name, Value) VALUES (#MainId, :MainName, :MainValue);
INSERT INTO SubTable (Id, Name, MainId, Value) VALUES (#SubId, :SubName, #MainId, :SubValue);
SELECT #MainId, #SubId FROM system.iota;";
let cmdTextProcedureInsert = "CreateItems";
// UPDATE
let cmdTextScriptUpdate = "
DECLARE #MainId INTEGER;
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = :MainName);
UPDATE MainTable SET Value = :MainValue WHERE Id = #MainId;
UPDATE SubTable SET Value = :SubValue WHERE MainId = #MainId;";
let cmdTextProcedureUpdate = "UpdateItems";
// SELECT
let cmdTextScriptSelect = "
SELECT m.Value * s.Value FROM MainTable m INNER JOIN SubTable s ON m.Id = s.MainId WHERE m.Name = :MainName;";
let cmdTextProcedureSelect = "SelectItems";
// DELETE
let cmdTextScriptDelete = "
DECLARE #MainId INTEGER;
#MainId = (SELECT TOP 1 Id FROM MainTable WHERE Name = :MainName);
DELETE FROM SubTable WHERE MainId = #MainId;
DELETE FROM MainTable WHERE Id = #MainId;";
let cmdTextProcedureDelete = "DeleteItems";
let cnnStr = #"data source=D:\DB\test.add; ServerType=local; user id=adssys; password=***;";
let cnn = new AdsConnection(cnnStr);
try
cnn.Open();
let cmd = cnn.CreateCommand();
let parametrize ix prms =
cmd.Parameters.Clear();
let addParam = function
| "MainName" -> cmd.Parameters.Add(":MainName" , mainName + ix.ToString()) |> ignore;
| "SubName" -> cmd.Parameters.Add(":SubName" , subName + ix.ToString() ) |> ignore;
| "MainValue" -> cmd.Parameters.Add(":MainValue", ix * 3 ) |> ignore;
| "SubValue" -> cmd.Parameters.Add(":SubValue" , ix * 7 ) |> ignore;
| _ -> ()
prms |> List.iter addParam;
let runTest testData =
let (cmdType, cmdName, cmdText, cmdParams) = testData;
let toPrefix cmdType cmdName =
let prefix = match cmdType with
| CommandType.StoredProcedure -> "Procedure-"
| CommandType.Text -> "Script -"
| _ -> "Unknown -"
in prefix + cmdName;
let stopWatch = new Stopwatch();
let runStep ix prms =
parametrize ix prms;
stopWatch.Start();
cmd.ExecuteNonQuery() |> ignore;
stopWatch.Stop();
cmd.CommandText <- cmdText;
cmd.CommandType <- cmdType;
let startId = 1500;
let count = 10;
for id in startId .. startId+count do
runStep id cmdParams;
let elapsed = stopWatch.Elapsed;
Console.WriteLine("Test '{0}' - total: {1}; per call: {2}ms", toPrefix cmdType cmdName, elapsed, Convert.ToInt32(elapsed.TotalMilliseconds)/count);
let lst = [
(CommandType.Text, "Insert", cmdTextScriptInsert, ["MainName"; "SubName"; "MainValue"; "SubValue"]);
(CommandType.Text, "Update", cmdTextScriptUpdate, ["MainName"; "MainValue"; "SubValue"]);
(CommandType.Text, "Select", cmdTextScriptSelect, ["MainName"]);
(CommandType.Text, "Delete", cmdTextScriptDelete, ["MainName"])
(CommandType.StoredProcedure, "Insert", cmdTextProcedureInsert, ["MainName"; "SubName"; "MainValue"; "SubValue"]);
(CommandType.StoredProcedure, "Update", cmdTextProcedureUpdate, ["MainName"; "MainValue"; "SubValue"]);
(CommandType.StoredProcedure, "Select", cmdTextProcedureSelect, ["MainName"]);
(CommandType.StoredProcedure, "Delete", cmdTextProcedureDelete, ["MainName"])];
lst |> List.iter runTest;
finally
cnn.Close();
And I'm getting the following results:
Test 'Script -Insert' - total: 00:00:00.0292841; per call: 2ms
Test 'Script -Update' - total: 00:00:00.0056296; per call: 0ms
Test 'Script -Select' - total: 00:00:00.0051738; per call: 0ms
Test 'Script -Delete' - total: 00:00:00.0059258; per call: 0ms
Test 'Procedure-Insert' - total: 00:00:01.2567146; per call: 125ms
Test 'Procedure-Update' - total: 00:00:00.7442440; per call: 74ms
Test 'Procedure-Select' - total: 00:00:00.5120446; per call: 51ms
Test 'Procedure-Delete' - total: 00:00:01.0619165; per call: 106ms
The situation with the remote server is much better, but still a great gap between plaqin queries and stored procedures:
Test 'Script -Insert' - total: 00:00:00.0709299; per call: 7ms
Test 'Script -Update' - total: 00:00:00.0161777; per call: 1ms
Test 'Script -Select' - total: 00:00:00.0258113; per call: 2ms
Test 'Script -Delete' - total: 00:00:00.0166242; per call: 1ms
Test 'Procedure-Insert' - total: 00:00:00.5116138; per call: 51ms
Test 'Procedure-Update' - total: 00:00:00.3802251; per call: 38ms
Test 'Procedure-Select' - total: 00:00:00.1241245; per call: 12ms
Test 'Procedure-Delete' - total: 00:00:00.4336334; per call: 43ms
Is it any chance to improve the SP performance? Please advice.
ADO.NET driver version - 9.10.2.9
Server version - 9.10.0.9 (ANSI - GERMAN, OEM - GERMAN)
Thanks!
The Advantage v10 beta includes a variety of performance improvements directly targeting stored procedure performance. Here are some things to consider with the current shipping version, however:
In your CreateItems procedure it would likely be more efficient to replace
#MainName = (SELECT MainName FROM __input);
#SubName = (SELECT SubName FROM __input);
#MainValue = (SELECT MainValue FROM __input);
#SubValue = (SELECT SubValue FROM __input);
with the use of a single cursor to retrieve all parameters:
DECLARE input CURSOR;
OPEN input as SELECT * from __input;
FETCH input;
#MainName = input.MainName;
#SubName = input.SubName;
#MainValue = input.MainValue;
#SubValue = input.SubValue;
CLOSE input;
That will avoid 3 statement parse/semantic/optimize/execute operations just to retrieve the input parameters (I know, we really need to eliminate the __input table altogether).
The SelectItems procedure is rarely ever going to be as fast as a select from the client, especially in this case where it really isn't doing anything except abstracting a parameter value (which can easily be done on the client). Remember that because it is a JOIN, the SELECT to fill the __output table is going to be a static cursor (meaning an internal temporary file for the server to create and fill), but now in addition you have the __output table which is yet another temporary file for the server, plus you have additional overhead to populate this __output table with data that has already been place in the static cursor temp table, just for the sake of duplicating it (server could do a better job of detecting this and replacing __output with the existing static cursor reference, but it currently doesn't).
I will try to make some time to try your procedures on version 10. If you have the test tables you used in your testing feel free to zip them up and send them to Advantage#iAnywhere.com and put attn:JD in the subject.
There is one change that would help with the CreateItems procedure. Change the following two statements:
#MainId = (SELECT MAX(Id)+1 FROM MainTable);
#SubId = (SELECT MAX(Id)+1 FROM SubTable );
To this:
#MainId = (SELECT MAX(Id) FROM MainTable);
#MainId = #MainId + 1;
#SubId = (SELECT MAX(Id) FROM SubTable );
#SubId = #SubId + 1;
I looked at the query plan information (in Advantage Data Architect) for the first version of that statement. It looks like the optimizer does not break that MAX(id)+1 into the component pieces. The statement select max(id) from maintable can be optimized using the index on the ID field. It appears that max(id)+1 is not optimized. So making that change would be fairly significant particularly as the table grows.
Another thing that might help is to add a CACHE PREPARE ON; statement to the top of each script. This can help with certain procedures when running them multiple times.
Edit The Advantage v10 beta was released today. So I ran your CreateItems procedure with both v9.1 and the new beta version. I ran 1000 iterations against the remote server. The speed difference was significant:
v9.1: 101 seconds
v10 beta: 2.2 seconds
Note that I ran a version with the select max(id) change I described above. This testing was on my fairly old development PC.

Resources