SQL Looping through results - sql

I do a select and it brings back a list of IDs. I then want to call another procedure for each ID that calculates a result, so I end up with a list of results.
How can I do this? I need some kind of loop but I am not very good at SQL.
Edit: Microsoft SQL 2008, and purely in SQL

Write a user defined function that takes in the ID and returns the calculated result you can then get that result for each ID with a query like this:
SELECT id, DatabaseYouUsed.dbo.functionYouWrote(id)
FROM DatabaseYouUsed.dbo.TableWithIDs

You can have a stored procedure that calls the select to get the IDs, use a cursor on the result list and call the other procedure for each ID. All inside a stored procedure.

If one row generates one result:
CREATE FUNCTION f(x int) RETURNS int AS BEGIN
RETURN x * 2
END
GO
SELECT id, x, dbo.f(x) FROM my_table
If one row might generate more than one result:
CREATE FUNCTION f(x int) RETURNS #r TABLE(result int) AS BEGIN
INSERT INTO #r VALUES(x)
INSERT INTO #r VALUES(x * 2)
RETURN
END
GO
SELECT t.id, t.x, r.result FROM my_table t CROSS APPLY dbo.f(t.x) r

Related

SQL Function to return value from multiple columns

I've been developing a few stored procedure and I have been repeating a portion of codes that derives a column based on a few other columns. So instead of copy this piece of code from one stored procedure to another, I'm thinking of having a function that takes the input columns and produces the output columns.
Basically, the function goes as:
SELECT columnA, columnB, columnC, myFunction(columnA, columnB) as columnD FROM myTable
As we can see, this function will take column A and column B as inputs, then return column D.
However, based on some research, it seems to have some performance issues when using UDF (user-defined) function like this. Is that true? What's the best way to handle this situation?
Thank you guys.
Scalar functions and multi statement table valued user defined functions can cause performance issues, because they implicitly turn your set based operation into a cursor based operation.
However, inline table valued user defined functions do not suffer from this problem. They're fast.
The difference is how you declare the fuction, and what the code looks like inside them. A multi statement function does what it says on the tin - it lets you have multiple statements. Like this:
create function slow() returns #t table(j int, k int) as
begin
declare #j int = 1; -- statement 1
declare #k int = 2; -- statement 2
insert #t values (#j, #k); -- statement 3
return; -- statement 4
end
An inline table valued function does not return a named table which is populated inside the function. It returns a select statement:
create function quick() returns table as
return
(
select j = 1, k = 2
);
The inline table valued function can be "inlined" into the outer select statement, in much the same way as a view. The difference, of course, being that the UDF can take parameters, whereas a view cannot.
You also have to use them differently. Use cross apply:
select t.columnA, t.columnB, u.j, u.k
from MyTable t
cross apply quick(t.columnA, t.columnB) u
In case it's not clear - yes, in your case you only want a "scalar" value back, but that's just a table valued function which returns a single column and a single row. So instead of writing a scalar function, write an inline table valued function that does the same job, and cross apply it.

Execute a stored procedure from within another stored procedure's SELECT statement?

I would like to execute a stored procedure X from within the SELECT statement of stored procedure Y, so that X's value can be returned as part of Y's data.
I am trying the following syntax, but it's apparently not valid.
SELECT name, type, (EXEC X #type=type)
FROM table
As I hope you can see above, I need to pass the current row's type value to procedure X to get the proper return value.
Disclaimer: I probably just don't know what I'm doing.
The approach what you have tried is invalid. Instead of the X as the stored procedure convert it as user-defined function. like the below
Create function dbo.fnGetTypeDetail
(
#type varchar(50)
)
returns varchar(100)
As
Begin
return --do your operation;
End
And replace your query as:
SELECT name, type, dbo.fnGetTypeDetail(type) AS TypeDetail
FROM table
For sample, I created a scalar function. Based on your requirement you can create inline table valued function as per the example
You can't EXEC a stored proc inside a SELECT statement.
What you can do is INSERT..EXEC a stored proc into a temp table, and then run a SELECT statement that queries that temp table, while joining to other tables if desired.
Psuedo-example:
INSERT INTO #Tmp (Column1) EXEC X;
SELECT Name, Type, (SELECT Column1 FROM #tmp)
FROM MyTable

How to selectively return rows inside a stored procedure on SQL Server?

I have a base stored procedure simply returning a select from the database, like this:
CREATE PROCEDURE MyProcedure
AS
BEGIN
SELECT * FROM MyTable
END
GO
But now I need to execute some logic for every row of my select. According to the result I need to return or not this row. I would have my select statement running with a cursor, checking the rule and return or not the row. Something like this:
CREATE PROCEDURE MyProcedure
AS
BEGIN
DECLARE CURSOR_MYCURSOR FOR SELECT Id, Name FROM MyTable
OPEN CURSOR_MYCURSOR
FETCH NEXT FROM CURSOR_MYCURSOR INTO #OUTPUT1, #OUTPUT2
WHILE (##FETCH_STATUS=0)
BEGIN
IF (SOME_CHECK)
SELECT #OUTPUT1, #OUTPUT2
ELSE
--WILL RETURN SOMETHING ELSE
END
END
GO
The first problem is that everytime I do SELECT #OUTPUT1, #OUTPUT2 the rows are sent back as different result sets and not in a single table as I would need.
Sure, applying some logic to a row sounds like a "FUNCTION" job. But I can't use the result of the function to filter the results being selected. That is because when my check returns false I need to select something else to replace the faulty row. So, I need to return the faulty rows so I can be aware of them and replace by some other row.
The other problem with this method is that I would need to declare quite a few variables so that I can output them through the cursor iteration. And those variables would need to follow the data types for the original table attributes and somehow not getting out of sync if something changes on the original tables.
So, what is the best approach to return a single result set based on a criteria?
Thanks in advance.
I recommend use of cursors but easy solution to your question would be to use table variable or temp table
DECLARE #MyTable TABLE
(
ColumnOne VARCHAR(20)
,ColumnTwo VARCHAR(20)
)
CREATE TABLE #MyTable
(
ColumnOne VARCHAR(20)
,ColumnTwo VARCHAR(20)
)
than inside your cursors you can insert records that match your logic
INSERT INTO #MyTable VALUES (#Output1, #Output2)
INSERT INTO #MyTable VALUES (#Output1, #Output2)
after you done with cursor just select everything from table
SELECT * FROM #MyTable
SELECT * FROM #MyTable

SQL Query using a function to select data

What is the best way of writing an sql query that calls a user defined function as part of the query before produce the selected output. i.e. I want to do something like below please where I the user defined function does some calculations on the table data.
select field1, field2 from table1 where function(table1.field3, table1.field4) > 10
Your (scalar) function:
CREATE FUNCTION my_function (#a AS int, #b AS int)
RETURNS int
BEGIN
RETURN #a * #b
END
Your query:
SELECT field1, field2
FROM table1
WHERE dbo.my_function(table1.field3, table1.field4) > 10
Don't forget the dbo in dbo.my_function. It is required for user defined scalar functions.
Stored procedure may play well or just to write a math/string condition. Depends on how complicated is your condition.

Get the count of rows in a stored procedure result regardless of number/type of columns

I have developed the following code:
CREATE PROCEDURE [dbo].[Test01]
AS
BEGIN
SELECT * FROM TestTable
END
CREATE PROCEDURE [dbo].[Test02]
AS
BEGIN
DECLARE #tmp TABLE
(
TestID int,
Test nvarchar(100),
)
INSERT INTO #tmp
EXEC Test01
SELECT COUNT(*) FROM #tmp
END
But if I add or remove a column on TestTable I must to modify #tmp otherwise the result is:
Column name or number of supplied values does not match table definition
How can I solve this problem?
Try specify the columns manually:
SELECT a, b FROM TestTable
and
INSERT INTO #tmp (a, b)
This should fix the error you've mentioned.
My first comment would be that SELECT * is frowned upon unless you actually know what you are doing. I would strongly advise against it in your particular use case, precisely because it can get you into the trouble you have identified.
In your particular case, for the specific SP written, you have not used SET NOCOUNT ON, so you can retrieve the count using
SELECT ##ROWCOUNT
But then as Martin has commented, that's just some sample code you threw together. Otherwise, why even use 2 SPs.