String concatenation problems in SQL Server - sql-server

ALTER PROCEDURE [dbo].[InsertSMS]
-- Add the parameters for the stored procedure here
#SmsMsgDesc Nvarchar(Max)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [Tbl_Log]([LogDescription])VALUES (#SmsMsgDesc)
declare #LogID int;
SET #LogID = CAST(SCOPE_IDENTITY() AS INT)
INSERT INTO [Tbl_SMS]
([SmsMsgDesc])
VALUES
**(#SmsMsgDesc+CAST(#LogID AS NVarchar(12)))**
END
Problem here is sometimes concatenation does not concatenate the last string I don't know why
Even if I do it like this
INSERT INTO [Tbl_SMS]
([SmsMsgDesc])
VALUES
**(#SmsMsgDesc+'Test')**
the constant 'Test' sometimes doesn't appear at the end of the string this drives me crazy please help !
i'm calling this procedure using the following C# function :-
public int InsertSMSDB(string Message)
{
try
{
//int LogID;
SqlConnection Conn=new SqlConnection(SmsDBConnection);
SqlCommand Comm = new SqlCommand("InsertSMS", Conn);
Comm.CommandType = System.Data.CommandType.StoredProcedure;
Comm.Parameters.AddWithValue("#SmsMsgDesc", Message);
Conn.Open();
int RowEffected=Comm.ExecuteNonQuery();
Conn.Close();
if (RowEffected > 0)
{
return RowEffected;
}
else
{
return -1;
}
}
catch (SqlException ex)
{
return -1;
}
}
Finally Some information may help in investegating this case in the Application there is 2 threads access the Tbl_SMS one Thread for Insertion and 1 Thread for Selection

If the value passed to procedure #SmsMsgDesc is null then it will not concatenate
try this to avoid the null value
VALUES
(isnull(#SmsMsgDesc,'')+CAST(#LogID AS NVarchar(12)))
Alternatively
you could change the procedure header
ALTER PROCEDURE [dbo].[InsertSMS]
#SmsMsgDesc Nvarchar(Max)=''

Related

ExecuteNonQuery always returns -1

I have created a stored procedure for deleting record. In this stored procedure I am first checking for the usage of data which I am going to delete. If it is being used, then the stored procedure will return -2 otherwise it deletes the record.
But the problem is that even the record exists its return -1 instead of -2. I have also set the NOCOUNT OFF but don't know where is the problem.
I know this question is already answered by setting NOCOUNT OFF but its not working for me
ALTER PROCEDURE [dbo].[spDeletePIDNumber]
#Id int
AS
BEGIN
SET NOCOUNT OFF;
-- Insert statements for procedure here
if(exists(select * from tblBills where PID = #Id))
begin
return -2
end
else
begin
Delete from HelperPIDNumber
where Id = #Id
end
END
public int DeletePIDNumber(int Id)
{
try
{
int result = 0;
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.connection))
{
var cmd = new SqlCommand("spDeletePIDNumber", conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Id", Id);
conn.Open();
result = cmd.ExecuteNonQuery();
}
return result;
}
catch
{
throw;
}
}
From the ExecuteNonQuery documentation:
Executes a Transact-SQL statement against the connection and returns the number of rows affected.
Having SET NOCOUNT ON; in your procedure explicitely tells to SQL Server not to return a row count. In that case the return of the ExecuteNonQuery function is -1.
Also if the procedure does not affect any rows, it will not return a row count either even if NOCOUNT is OFF. In that case the return will also be -1.
What you appear to want to do is get the return value of the stored procedure. You will not get that from the result of ExecuteNonQuery. Please refer to this question on StackOverflow: Getting return value from stored procedure in ADO.NET
Generally ExecuteNonQuery will return number of affected records. It will return -1 in two cases:
When SET NOCOUNT ON has been set. From your code, its clear, you have SET NOCOUNT OFF and so this is not an issue at your case.
If number of affected rows is nothing, it will return -1. In your case, it looks like you are checking the data exists from one table tblBills and delete from another table HelperPIDNumber. So there is more chance there will be no matching record and nothing deleted.
Please check the point # 2 above.
if( exists(select * from tblBills where PID = #Id))
begin
return -2
end
else
begin
Delete from HelperPIDNumber where Id = #Id
end
use cmd.ExecuteScalar() instead of cmd.ExecuteNonQuery() ascmd.ExecuteNonQuery() return only the number of affected rows and not the value you are selecting.

Receiving one out of few Result Sets in stored procedure

I have a stored procedure that works fine but it has inside it three "select"s.
The selects are not from an inner temporary table.
This is mainly the format of the procedure:
ALTER PROCEDURE [dbo].[STProce]
#param1 int,
#param2 int,
#param3 int,
#param4 int,
#param5 int
AS
select #param1 as p1, #param2 as p2, #param3 as p3
.
.
.
select #param4 as p4
.
.
.
select #param5 as p5
I'm executing the procedure from another procedure and need to catch it there.
I created a table and inserts into it the "exec" from the procedure, like that:
CREATE TABLE #stalledp
(
RowNumber INT,
fldid INT,
fldLastUpdated datetime,
fldCreationDate datetime,
fldName nvarchar(255),
fldPending nvarchar(255)
)
INSERT INTO #stalledp (RowNumber,fldid,fldLastUpdated,fldCreationDate,fldName,fldPending)
EXEC spDebuggerViews_GetStuckWorkflowInstances #workflowSpaceId='00000000-0000-0000-0000-000000000000',#pageNum=1,#pageSize=100000,#orderByColumn=N'fldid',#sortOrder=1,#workflowInstanceId=0,#stuckInstanceType=1,#createdDateFrom='1900-01-01 00:00:00',#createdDateTo='9999-01-01 23:59:59',#updatedDateFrom='1900-01-01 00:00:00',#updatedDateTo='9999-01-01 23:59:59'
Afterwards I receive this error:
Column name or number of supplied values does not match table definition.
The order and name of columns of the table is exactly like the procedure returns.
Is there a possibility to catch only one of the tables that the procedure returns and avoid the other? I cannot change the procedure at all.
I tried declaring a table the same fields as the first select of the procedure and I get an error says that
Thank you in advance!
If all of the result sets returned are of the same structure, then you can dump them to a temp table as you are trying to do. However, that only gets you so far because if the data in the fields cannot be used to determine which result set a particular row came from, then you just have all of the result sets with no way to filter out the ones you don't want.
The only way to interact with multiple result sets individually, regardless of them having the same or differing structures, is through app code (i.e. a client connection). And if you want to do this within the context of another query, then you need to use SQLCLR.
The C# code below shows a SQLCLR stored procedure that will execute a T-SQL stored procedure that returns 4 result sets. It skips the first 2 result sets and only returns the 3rd result set. This allows the SQLCLR stored procedure to be used in an INSERT...EXEC as desired.
The code for the T-SQL stored proc that is called by the following code is shown below the C# code block. The T-SQL test proc executes sp_who2 and only return a subset of the fields being returned by that proc, showing that you don't need to return the exact same result set that you are reading; it can be manipulated in transit.
C# SQLCLR proc:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public class TheProc
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void Get3rdResultSetFromGetStuckWorkflowInstances()
{
int _ResultSetsToSkip = 2; // we want the 3rd result set
SqlConnection _Connection = null;
SqlCommand _Command = null;
SqlDataReader _Reader = null;
try
{
_Connection = new SqlConnection("Context Connection = true;");
_Command = _Connection.CreateCommand();
_Command.CommandType = CommandType.StoredProcedure;
_Command.CommandText = "tempdb.dbo.MultiResultSetTest";
// (optional) add parameters (but don't use AddWithValue!)
// The SqlDataRecord will be used to define the result set structure
// and act as a container for each row to be returned
SqlDataRecord _ResultSet = new SqlDataRecord(
new SqlMetaData[]
{
new SqlMetaData("SPID", SqlDbType.Char, 5),
new SqlMetaData("Status", SqlDbType.NVarChar, 30),
new SqlMetaData("Login", SqlDbType.NVarChar, 128),
new SqlMetaData("HostName", SqlDbType.NVarChar, 128),
new SqlMetaData("BlkBy", SqlDbType.VarChar, 5),
new SqlMetaData("DBName", SqlDbType.NVarChar, 128)
});
SqlContext.Pipe.SendResultsStart(_ResultSet); // initialize result set
_Connection.Open();
_Reader = _Command.ExecuteReader();
// Skip a predefined number of result sets
for (int _Index = 0;
_Index < _ResultSetsToSkip && _Reader.NextResult();
_Index++) ;
// Container used to move 1 full row from the result set being read
// to the one being sent, sized to the number of fields being read
Object[] _TempRow = new Object[_Reader.FieldCount];
while (_Reader.Read())
{
_Reader.GetValues(_TempRow); // read all columns
_ResultSet.SetValues(_TempRow); // set all columns
SqlContext.Pipe.SendResultsRow(_ResultSet); // send row
}
}
catch
{
throw;
}
finally
{
if(SqlContext.Pipe.IsSendingResults)
{
SqlContext.Pipe.SendResultsEnd(); // close out result set being sent
}
if(_Reader != null && !_Reader.IsClosed)
{
_Reader.Dispose();
}
_Command.Dispose();
if (_Connection != null && _Connection.State != ConnectionState.Closed)
{
_Connection.Dispose();
}
}
return;
}
}
T-SQL test proc:
USE [tempdb]
SET ANSI_NULLS ON;
IF (OBJECT_ID('dbo.MultiResultSetTest') IS NOT NULL)
BEGIN
DROP PROCEDURE dbo.MultiResultSetTest;
END;
GO
CREATE PROCEDURE dbo.MultiResultSetTest
AS
SET NOCOUNT ON;
SELECT 1 AS [ResultSet], 'asa' AS [test];
SELECT 2 AS [ResultSet], NEWID() AS [SomeGUID], GETDATE() AS [RightNow];
EXEC sp_who2;
SELECT 4 AS [ResultSet], CONVERT(MONEY, 131.12) AS [CashYo];
GO
EXEC tempdb.dbo.MultiResultSetTest;
To do:
Adjust _ResultSetsToSkip as appropriate. If you only want the first result set, simply remove both _ResultSetsToSkip and the for loop.
Define _ResultSet as appropriate
Set _Command.CommandText to be "spDebuggerViews_GetStuckWorkflowInstances"
Create the necessary parameters via SqlParameter (i.e. #workflowSpaceId='00000000-0000-0000-0000-000000000000',#pageNum=1,#pageSize=100000,#orderByColumn=N'fldid',#sortOrder=1,#workflowInstanceId=0,#stuckInstanceType=1,#createdDateFrom='1900-01-01 00:00:00',#createdDateTo='9999-01-01 23:59:59',#updatedDateFrom='1900-01-01 00:00:00',#updatedDateTo='9999-01-01 23:59:59')
If needed, add input parameters to the SQLCLR proc so that they can be used to set the values of certain SqlParameters
Then use as follows:
INSERT INTO #stalledp
(RowNumber,fldid,fldLastUpdated,fldCreationDate,fldName,fldPending)
EXEC Get3rdResultSetFromGetStuckWorkflowInstances;
There is a way to get the first record set but the others, I'm afraid, you're out of luck.
EXEC sp_addlinkedserver #server = 'LOCALSERVER', #srvproduct = '',
#provider = 'SQLOLEDB', #datasrc = ##servername
SELECT * FROM OPENQUERY(LOCALSERVER, 'EXEC testproc2')
EDIT: If you only need to check the other result set for columns to be not null you could predefine the expected results sets like so:
EXEC testproc2 WITH RESULT SETS (
(a VARCHAR(MAX) NOT NULL, b VARCHAR(MAX) NOT NULL),
(a VARCHAR(MAX) NOT NULL)
);
If the query within the stored procedure returns null values a exception is raised at that point in procedure. This will only work on sql server 2012 and upwards though.

ADO.NET - Trouble Getting Output Parameter

My DBA created the following Stored Proc which he insists works fine when called in SQL Server:
CREATE procedure [dbo].[GetParentID]
#SSHIP_AppID as varchar(50),
#ParentID as varchar(150) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT #ParentID = a.iBuild_GUID
FROM dbo.XRef_iBuild_SSHIP as a
WHERE a.SSHIP_appId = #SSHIP_AppID
AND a.SSHIP_appId <> ''
END
I have created the following ADO.NET Wrapper but I am having trouble getting the output parameter. I keep getting back "OUTPUT" as its value:
private string GetParentId(string appId)
{
var connection = new SqlConnection();
string parentId = String.Empty;
try
{
connection.ConnectionString = "...)
var command = new SqlCommand("GetParentId", connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#SSHIP_AppID", appId));
command.Parameters.Add(new SqlParameter("#ParentID", ParameterDirection.Output));
connection.Open();
command.ExecuteNonQuery();
parentId = (command.Parameters["#ParentId"].Value).ToString();
}
catch (Exception ex)
{
LogError(appId, ex.ToString(), "Interface12 - Cannot get ParentId", null, 0);
}
finally
{
connection.Close();
}
return parentId;
}
}
What am I doing wrong?
In new SqlParameter("#ParentID", ParameterDirection.Output) the 2nd argument is treated as the object value argument and apparently converted to a string.
(This implicit conversion is, in my opinion, a design flaw in ADO.NET. It should throw an exception for any unknown input type.).
Choose a better overload.

How to save byte[] using a procedure?

This stored procedure does not save the data, it seems to be a problem with the VARBINARY. I am passing a byte[] to it, but then it doesn't work. If I send this parameter as NULL it works.
I'm calling the procedure with the following code:
public Community AddCommunity(string name, string description, byte[] picture, User owner, int? venue, int communityID)
{
using (var database = new Database())
{
return database.Scope.GetSqlQuery<Community>("QP_AddCommunity ?, ?, ?, ?, ?, ?", "VARCHAR Name, VARCHAR Description, VARBINARY Picture, INTEGER Owner, INTEGER Venue, INTEGER ID").GetResult(name, description, picture, owner.ID, venue, communityID);
}
}
The procedure is the following:
CREATE PROCEDURE [dbo].[QP_AddCommunity]
#Name VARCHAR(120),
#Description VARCHAR(MAX),
#Picture VARBINARY(MAX),
#Owner INTEGER,
#Venue INTEGER,
#ID INTEGER
AS
BEGIN
SET NOCOUNT ON;
IF(SELECT COUNT(*) FROM QT_Community WHERE ID = #ID) = 0
INSERT INTO QT_Community(Name, [Description], Picture, [Owner], Venue) VALUES(#Name, #Description, #Picture, #Owner, #Venue);
ELSE
UPDATE QT_Community SET Name = #Name, [Description] = #Description, Picture = #Picture, [Owner] = #Owner, Venue = #Venue WHERE ID = #ID;
SELECT * FROM QT_Community WHERE ID = ##IDENTITY;
END
What's wrong with this code? Isn't VARBINARY a byte[] ?
This code works when executing on SQL Server Management Studio.
DECLARE #X varbinary(20)
Set #X = CAST('Testing' As varbinary(20))
EXECUTE [QP_AddCommunity] 'aaaaa', 'descricao', #X, 216, NULL, 0;
But when calling from the GetSqlQuery method with something on the byte[] the transaction says it's not active and not dirty. BUT if the byte[] is null it works as it should.
i found that it is impossible as this answer shows
Hello gaurav, currently our
GetSqlQuery method cannot operate
properly with parameters of type
LongVarBinary or VarBinary, thus
making it impossible for the stored
procedure to work as expected. We are
aware of this problem and we are
working on fixing it. As a work around
you should try and use Linq to achieve
your goal. Greetings, Petar the
Telerik team
Accordingly to this table it seems either BLOB, BINARY, VARBINARY would be valid types for [] of primitive type.
You could try to ask on their forums, maybe someone will be able to help you.
Try using the .WRITE method. On your INSERT, insert 0x for Picture, then update independently.
UPDATE QT_Community
SET Picture.Write (#Picture, 0, DATALENGTH(Picture))
WHERE ID = #ID
Example (Ado.Net):
byte[] ba = UlongsToBytes(ul);
try
{
string source = #"packet size=4096;integrated security=SSPI;data source=MyPC\MyNamedInstance;persist security info=False;initial catalog=Sandbox";
SqlConnection conn = new SqlConnection(source);
conn.Open();
SqlCommand a = new SqlCommand("INSERT BigintsTarget(bi) SELECT * FROM dbo.ParseImageIntoBIGINTs(#BIGINTs)", conn);
a.CommandType = System.Data.CommandType.Text;
a.Parameters.Add(new SqlParameter("#BIGINTs", System.Data.SqlDbType.Image,2147483647));
for(int q=0; q<10; q++)
{
a.Parameters[0].Value = ba;
int res = a.ExecuteNonQuery();
}
d2 = DateTime.Now;
SqlCommand b = new SqlCommand("INSERT BigintsTarget1(bi) SELECT * FROM dbo.ParseVarcharMAXIntoBIGINTs(#BIGINTs)", conn);
b.CommandType = System.Data.CommandType.Text;
b.Parameters.Add(new SqlParameter("#BIGINTs", System.Data.SqlDbType.VarChar,2147483647));
for(int q=0; q<10; q++)
{
b.Parameters[0].Value = sss;
int res = b.ExecuteNonQuery();
}
//b.ExecuteNonQuery();
conn.Close();
}
catch(Exception ex)
{
string s = ex.Message;
int t=0;
t++;
}
}

Incorrect syntax near stored procedure error

I'm updating a long list of records. In my code, everything run as predicted until it execute the query. I get an
Incorrect syntax near 'TempUpdatePhysicalCityStateZip'
(my stored procedure name). I've tested it with SQL Server Management Studio and it runs fine. So, I'm not quite sure where I got it wrong. Below is my stored procedure and code:
ALTER PROCEDURE [dbo].[TempUpdateCityStateZip]
#StoreNo nvarchar (11),
#City nvarchar(50),
#State nvarchar(2),
#Zip nvarchar(5)
AS
BEGIN
SET NOCOUNT ON;
UPDATE StoreContact
SET City = #City, State = #State, Zip = #Zip
WHERE StoreNo = #StoreNo
END
Here is my code:
Dictionary<string, string> CityStateZipList = getCityStateZipList(dbPath);
using (SqlConnection conn = new SqlConnection(dbPath))
{
conn.Open();
SqlCommand cmdUpdate = new SqlCommand("TempUpdateCityStateZip", conn);
foreach (KeyValuePair<string, string> frKeyValue in CityStateZipList)
{
cmdUpdate.Parameters.Clear();
string[] strCityStateZip = frKeyValue.Value.Split(' ');
cmdUpdate.Parameters.AddWithValue("StoreNo", frKeyValue.Key.ToString());
foreach (String i in strCityStateZip)
{
double zipCode;
if (i.Length == 2)
{
cmdUpdate.Parameters.AddWithValue("State", i);
}
else if (i.Length == 5 && double.TryParse(i, out zipCode))
{
cmdUpdate.Parameters.AddWithValue("Zip", i);
}
else
{
cmdUpdate.Parameters.AddWithValue("City", i);
}
}
cmdUpdate.ExecuteNonQuery();
}
}
I believe you can get that puzzling error message if you don't specify the command type:
cmdUpdate.CommandType = CommandType.StoredProcedure;
Don't you need the # sign before the parameter?
cmdUpdate.Parameters.AddWithValue("#State", i);
FWIW, Thats kind of a dirty piece of code there, you will probably have many issues trying to maintain that. For performance reasons you may want to parse out the CityStateZipList before you open the connection, that way you aren't keeping it open longer than you need.

Resources