Receiving one out of few Result Sets in stored procedure - sql-server

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.

Related

Insert new rows into tables?

I am trying to execute a stored procedure which performs an Insert action but I am getting an error (as shown in the screenshot). Following is my PB code and the subsequent one is the declaration of my params in the stored procedure.
DECLARE sp_insert_EndorsementUnderlying_AP PROCEDURE FOR
#EndNo = :endnum1,
#PolicyId = :policyid1
USING SQLCA;
EXECUTE sp_insert_EndorsementUnderlying_AP;
CHOOSE CASE SQLCA.sqlcode
CASE 0
// Execute successful; no result set
COMMIT;
CASE ELSE
MessageBox ("INSERT of New Endorsement Rows Failed", &
string (SQLCA.sqldbcode) + " = " + &
SQLCA.sqlerrtext)
RETURN
END CHOOSE
Stored procedure declaration:
ALTER PROCEDURE [dbo].[sp_insert_EndorsementUnderlying_AP]
#EndNo SMALLINT,
#PolicyId INT
AS
BEGIN
Error message:
[enter image description here][1]
[1]: https://i.stack.imgur.com/error msg screen shot
The syntax in PB to declare a procedure is:
DECLARE **PBPROCNAME** PROCEDURE FOR **DATABASEPROCNAME**
#parm1 = :pbvariable1, #parm2 = :pbvariable2 USING SQLCA;
Which is not what you have in your example. Can't comment on the error since your link does not work.
Try this one (note the slight difference procedure name - this is not compulsory but it helps make the difference between both worlds):
DECLARE pb_insert_EndorsementUnderlying_AP PROCEDURE FOR sp_insert_EndorsementUnderlying_AP
#EndNo = :endnum1,
#PolicyId = :policyid1
USING SQLCA;
EXECUTE pb_insert_EndorsementUnderlying_AP;
CHOOSE CASE SQLCA.sqlcode
CASE 0
// Execute successful; no result set
COMMIT;
CASE ELSE
MessageBox ("INSERT of New Endorsement Rows Failed", &
string (SQLCA.sqldbcode) + " = " + &
SQLCA.sqlerrtext)
RETURN
END CHOOSE

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.

SQL Server stored procedure Nullable parameter

Problem:
When values are provided to the following script then executed using a setup in C# like below (or in SQL Server environment) the values do not update in the database.
Stored procedure:
-- Updates the Value of any type of PropertyValue
-- (Type meaining simple Value, UnitValue, or DropDown)
CREATE PROCEDURE [dbo].[usp_UpdatePropertyValue]
#PropertyValueID int,
#Value varchar(max) = NULL,
#UnitValue float = NULL,
#UnitOfMeasureID int = NULL,
#DropDownOptionID int = NULL
AS
BEGIN
-- If the Property has a #Value, Update it.
IF #Value IS NOT NULL
BEGIN
UPDATE [dbo].[PropertyValue]
SET
Value = #Value
WHERE
[dbo].[PropertyValue].[ID] = #PropertyValueID
END
-- Else check if it has a #UnitValue & UnitOfMeasureID
ELSE IF #UnitValue IS NOT NULL AND #UnitOfMeasureID IS NOT NULL
BEGIN
UPDATE [dbo].[UnitValue]
SET
UnitValue = #UnitValue,
UnitOfMeasureID = #UnitOfMeasureID
WHERE
[dbo].[UnitValue].[PropertyValueID] = #PropertyValueID
END
-- Else check if it has just a #UnitValue
ELSE IF #UnitValue IS NOT NULL AND #UnitOfMeasureID IS NULL
BEGIN
UPDATE [dbo].[UnitValue]
SET
UnitValue = #UnitValue
WHERE
[dbo].[UnitValue].[PropertyValueID] = #PropertyValueID
END
-- Else check if it has a #DropDownSelection to update.
ELSE IF #DropDownOptionID IS NULL
BEGIN
UPDATE [dbo].[DropDownSelection]
SET
SelectedOptionID = #DropDownOptionID
WHERE
[dbo].[DropDownSelection].[PropertyValueID] = #PropertyValueID
END
END
When I do an execution of this script, like below, it does not update any values.
Example execution:
String QueryString = "EXEC [dbo].[usp_UpdatePropertyValue] #PropertyValueID, #Value, #UnitValue, #UnitOfMeasureID, #DropDownOptionID";
SqlCommand Cmd = new SqlCommand(QueryString, this._DbConn);
Cmd.Parameters.Add(new SqlParameter("#PropertyValueID", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#Value", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#UnitValue", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#UnitOfMeasureID", System.Data.SqlDbType.Int));
Cmd.Parameters.Add(new SqlParameter("#DropDownOptionID", System.Data.SqlDbType.Int));
Cmd.Parameters["#PropertyValueID"].Value = Property.Value.ID; // 1
Cmd.Parameters["#Value"].IsNullable = true;
Cmd.Parameters["#Value"].Value = DBNull.Value;
Cmd.Parameters["#UnitValue"].IsNullable = true;
Cmd.Parameters["#UnitValue"].Value = DBNull.Value;
Cmd.Parameters["#UnitOfMeasureID"].IsNullable = true;
Cmd.Parameters["#UnitOfMeasureID"].Value = DBNull.Value;
Cmd.Parameters["#DropDownOptionID"].IsNullable = true;
Cmd.Parameters["#DropDownOptionID"].Value = 2; // Current Value in DB: 3
Details:
After running an execute (via C# code or SQL Server environment) it does not update dbo.DropDownSelection.SelectedOptionID. I'm guessing that it might be because dbo.DropDownSelection.SelectedOptionID is non-nullable and the parameter I'm using to set it is nullable (despite that when setting it shouldn't ever be null). Upon execution the return value is 0. If I run one of the Updates outside of the procedure they work perfectly, hence my suspicion that it has to do with null-able types.
Question(s):
Could this be because the parameters to the stored procedure are nullable and the fields I'm setting aren't?
If not, what could it be?
It looks like you're passing in Null for every argument except for PropertyValueID and DropDownOptionID, right? I don't think any of your IF statements will fire if only these two values are not-null. In short, I think you have a logic error.
Other than that, I would suggest two things...
First, instead of testing for NULL, use this kind syntax on your if statements (it's safer)...
ELSE IF ISNULL(#UnitValue, 0) != 0 AND ISNULL(#UnitOfMeasureID, 0) = 0
Second, add a meaningful PRINT statement before each UPDATE. That way, when you run the sproc in MSSQL, you can look at the messages and see how far it's actually getting.
You can/should set your parameter to value to DBNull.Value;
if (variable == "")
{
cmd.Parameters.Add("#Param", SqlDbType.VarChar, 500).Value = DBNull.Value;
}
else
{
cmd.Parameters.Add("#Param", SqlDbType.VarChar, 500).Value = variable;
}
Or you can leave your server side set to null and not pass the param at all.

String concatenation problems in 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)=''

how to discards the scalar value in stored procedure?

Please any one tell me how to discards the scalar value in stored procedure?
this is my SP
CREATE PROCEDURE testdata
AS
BEGIN
SET NOCOUNT ON;
SELECT c.CustomerName,c.CustomerCode, a.Address, a.Email,a.phone,a.ZipCode
from Customer c
join Address a on c.CustomerCode = a.CustomerCode
END
GO
This is my C# code
CustomerDataEntities enty = new CustomerDataEntities();
var productEntity = enty.testdata123();
// error foreach statement cannot operate on variables of type int
foreach (var prd in productEntity)
{
}
return CustomerList;
Thanks.
Based just on what you're showing here, I'm assuming your stored procedure is set up as an imported function. I would guess that you are not mapping the results of this function to an entity type.

Resources