I have a stored procedure that I want to run on every row in a table that matches a where clause, the procedure already exists on the server and is used in other places so it cannot be modified for these changes.
The stored procedure returns a scalar value, I need to store this value in a column in the table, I've tried using the update:
UPDATE tbl SET tbl.Quantity =
EXEC checkQuantity #ProductID = tbl.ProductID, #Quantity = tbl.Quantity
FROM orders tbl WHERE orderNumber = #orderNumber
But this of course doesn't work, is there a way to do this without multiple queries, reading the line info, running the proc in a loop then updating the original line?
No there is no way to do this without multiple queries. This is one of the few scenarios where a cursor or loop is necessary.
Unless you can replace your stored procedure with a user-defined function, which can be run in the context of a single query.
Related
I have a stored procedure that returns a result, let's say it return rows of products. But each product status is not in our hand(can't get it). Our DBA just gave us another stored procedure to get the status of a product. We need to get individual product status by calling their SP. Let's say we have Product table,
CREATE TABLE PRODUCTS
(
ID INT,
Name NVARCHAR(100)
)
CREATE PROCEDURE GetProducts
AS
BEGIN
INSERT INTO #TEMPTABLE
SELECT * FROM PRODUCTS; -- Yes Too Much simplified
-- Create cursor and set additional status in #TEMPTABLE
END;
EXEC GetStatus #ProductId; -- SP That need to get status
The problem is that GetStatus is only way to get the status and this sp sometimes return 2 columns, sometimes 4 and sometimes 8. The return columns will always include Status column for sure.
If columns names is fixed then there is no problem. Is there is a way to create dynamic table at the time of executing SP.
Tried this but not working,
WITH DynamicTable AS
(
EXEC GetStatus
)
The answer to your question is no. There is no good way to get the value of a specific column returned by a stored procedure that can return a dynamic set of columns.
I say no "good" way, because of course there's a WAY. You can write an INSERT EXEC statement for every possible set of columns that the procedure can return and wrap each one in a TRY..CATCH block. If the first one errors, try the next one. As soon as you hit one that doesn't error, get the Status from it, and skip the rest.
That's the answer to your question. The solution to your problem, however, is to replace the GetStatus stored procedure with a Table-valued function. Then you can select from it, join to it, etc. I think the function would have to always return a consistent number of columns, but that would be better anyway, and the columns that aren't needed in a specific case could just be left empty or NULL.
I have a stored procedure which returns a few columns from a SELECT. Now I need to grab 2 columns out of those columns in my new stored procedure and use them.. I am trying to do this using EXEC method. Is it possible to do this?
Ex : Original stored procedure:
CREATE PROCEDURE myBaseProcedure
#stId INT
AS
BEGIN
SELECT Name,
Address,
StudentId,
Grade
FROM Student
WHERE StudentId = #stId
END
New stored procedure:
CREATE PROCEDURE myNextProcedure
BEGIN
EXEC myBaseProcedure 19 -- Here I need to grab only StudentId and Name??
END
Given that you cannot dump to a temp table or table variable since the base stored procedure might sometimes add columns, there are three approaches that would do this:
You can effectively SELECT from a stored procedure using either OPENROWSET or OPENQUERY
You can use SQLCLR to create a table-valued function that executes the procedure, returns a struct of just the fields that you want, which will be the only fields that you read or "get" from the SqlDataReader.
You can use SQLCLR to create a stored procedure that executes the procedure to get a SqlDataReader, and instead of returning the SqlDataReader to SqlContext.Pipe.Send(), you would use SendResultsStart, SendResultsRow, and SendResultsEnd. You would create a SqlDataRecord of just the fields you wanted, and those would also be the only fields that you read or "get" from the SqlDataReader. While this still leaves you with a stored procedure, the filtering of the fields is done within the CLR-based proc so the output is guaranteed to be just the fields you want, regardless of how the result set structure of the base stored procedure changes. In this way you could create a local temp table to dump the results to, which would be better for JOINing to other tables. This method also allows for you to pass in a list of fields to the CLR-based stored procedure that would be parsed and used as the fields to dynamically construct the SqlDataRecord with as well as to dynamically determine which fields to get from the SqlDataReader. That would be a little more complicated but also quite a bit more flexible :).
You don't need to create a new stored procedure for this, you can integrate the stored proc call in a simple query using OpenQuery or use a temporary table.
Using OPENQUERY
SELECT Name,
Address
FROM OPENQUERY(ServerName, 'EXEC myBaseProcedure 19')
-- WHERE your_field = expected_value --> if you need to add filters
Using Temp table
Declare #MyTempTable Table (columns definitions)
Insert #MyTempTable Exec myBaseProcedure 19
Select Name,
Address
FROM #MyTempTable
I have a stored procedure which accepts a Client number and returns certain data. I have another stored procedure which internally calls the first one and needs to use the Row count from the first stored procedure.
Is there anywhere I can achieve this without using a temporary table (to get the result set) or using an OUT parameter in the first stored procedure.Thanks.
Since you already calling your stored procedure from first one, you already have ability to get number of rows returned by using ##ROWCOUNT function. Below is example of using `##RowCount
CREATE PROC test1
AS
BEGIN
SELECT *
FROM sys.tables
END
CREATE PROC Test2
AS
BEGIN
SET NOCOUNT ON;
EXEC Test1
PRINT ##ROWCOUNT
END
EXEC Test2
In my example I just printed that value out, but you can just as simply assign that to a variable in second procedure and use it for something else.
I have written stored procedure which has 2 Insert queries and 1 Update query inside it. Of all these,either insert queries or update query are executed at a time. Now my problem is to get ROWCOUNT in each case. Say suppose if insert operations are executed,then I want stored procedure to return ##ROWCOUNT to the calling application, so that the application will be aware of whether the required operations executed correctly or not. Can anyone suggest/tell me how can I get the rows affected from the stored procedure?
Use Output parameters in your stored procedures to return the RowCount of your inserts / updates.
Refer MSDN link for more information on how to use Output params
You can have multiple output params so you can have 2 different output params one each for your insert and the 3rd for your update statement.
Example:
CREATE PROCEDURE GetEmployeeData
#employeeID INT,
#managerID INT **OUTPUT**
AS
BEGIN
....
....
Additionally, you can always concatenate the rowcounts of your 2 Inserts / Update using delimiters and return them as one value eg: "10;0" - However that is the old fashioned and "I would not recommend" approach.
Also, you could create a table variable and return the table with rows = number of Inserts / updates and the value of the column = RowCount affected.
I'm converting some data in SQL Server 2005. I have a table update like this:
update Invoices set Invoices.InvoiceReference = 'NewRef'
where Invoices.InvoiceReference='Unknown'
But what I'd like to plug in instead of 'NewRef' is the output from a stored procedure that uses parameters from the columns of the Invoices table. The stored procedure itself does updates to another table. Is it possible? Something like this below (which is wrong of course :)
DECLARE #Ref nvarchar(20)
update Invoices set Invoices.InvoiceReference = (
EXEC InvoiceGenerateRef
#ClientCode = Invoices.ClientCode,
#EventCode = Invoices.EventCode,
#Ref = #Ref OUTPUT
SELECT #Ref)
where Invoices.InvoiceReference='Unknown'
Do I need to use a cursor or is the syntax just wrong?
Thanks,
Chris.
I think you would be better off changing your stored procedure into either a function or a view (depending on what you actually do in the proc).
I think what you are after is to join to the resultset of a stored proc which would not work.
You are almost there, the correct way to achieve what you are looking to do would be to define an output parameter as part of your stored procedure definition.
This paramter can then be used as part of your update statement.
DECLARE #Ref nvarchar(20)
EXEC InvoiceGenerateRef
#ClientCode = N'ABC2',
#EventCode = N'X1'
#Ref = #Ref OUTPUT
update Invoices
set Invoices.InvoiceReference = #Ref
where Invoices.InvoiceReference='Unknown'
by using OPENROWSET you can query your stored procedure results just like a view:
http://blogs.technet.com/wardpond/archive/2005/08/01/the-openrowset-trick-accessing-stored-procedure-output-in-a-select-statement.aspx
so for your case this might be useful.
1) You could change InvoiceGenerateRef so that it could optionally save the generated Ref into InvoiceReference field. Presumably you would also have to provide parameters to define the selection
2) You could us e cursor to step round each row in
SELECT ...
FROM Invoices
WHERE Invoices.InvoiceReference='Unknown'
and pass the details to InvoiceGenerateRef and then update the row. This is bad IMHO and will be slow (Your best bet is a set-based solution)
3) You could select the appropriate Invoices.ID's into a temporary table which would be in scope for the InvoiceGenerateRef so that it could iterate that (i.e. the choice of WHICH rows to update is external to the SProc, but the SProc does the actual updating)
CREATE TABLE #TEMP
(
T_ID int NOT NULL
)
INSERT INTO #TEMP (T_ID)
SELECT ID
FROM Invoices
WHERE Invoices.InvoiceReference='Unknown'
EXEC InvoiceGenerateRef #ACTION='UpdateFromTemporaryTable'
4) You could change InvoiceGenerateRef to a function that performed the same task:
UPDATE U
SET U.InvoiceReference =
dbo.MyInvoiceGenerateRefFunction(U.ClientCode, U.EventCode)
FROM Invoices AS U
WHERE U.InvoiceReference='Unknown'
MyInvoiceGenerateRefFunction would have to be deterministic (I think!)
This would be my preferred choice