Converting a stored Procedure into a user defined Function - sql

I am trying to convert a procedure into function that chooses n parameters from a table and for each n, chooses k column names from another table. it finally has to create a table with some fixed columns + n*k columns which change with input parameters. k is also different for each n. I wrote the procedure using a dynamically created query.
For the function, I figured I could run a cursor or while loop to fetch each n and feed the corresponding column names into a table variable. declaring the variables and setting their values inside the loop felt like the efficient way to go.
Finally I'll pivot the variables and join them to form the required table.
The problem lies therein. Declaring, or even if I already declared them, then calling the table variables in the loop sequentially to assign them.
{
Declare #plan_section table (
Roll identity (1,1)
Plan_id int
Plan_section_id int
)
Insert into #plan_section
Select plan_id, plan_section_id from t_plan_section where plan_id=#plan_id
Declare #n int, #plan_count int
Set #n =1
Set #plan_count = (select count(plan_section_id) from t_plan_section where plan_id = #plan_id)
While #n<#plan_count
Begin
Declare Variable_’+cast(#n, varchar(2))+’ table ( ‘#plan_section_name’ varchar(100) )
Insert into variable_’+#n+’
Select metric from t_plan_section_metric a join #plan_section b using(plan_section_id) where b.roll = #n
set #n = #n+1
End
}
here's a query snippet i used but didn't work.
Please consider that there are n plan section corresponding to each plan_id, and k column names being retrieved into n table variables declared within the loop.
Any and all suggestions are welcomed. if you have another approach, by all means do share.

Related

update SQL rows with multidimensional array as source

Goal:
I want to update existing entries in my SQL table [dbo.properties]. The SQL command is executed in PHP. The PHP file again receives an array, as new data source. This array contains a unique ID, propertyName and the actual value.
Problem:
How do I loop, in SQL, through an array and make sure to update the values at the correct point?
My SQL table look like:
[id] as [int] increments by DB
[property] as [varchar(50)]
[value] as [varchar(50)]
The passed array look like:
0: Object {id:'30', property:'sugar', value:'20g'}
1: Object {id:'37', property:'salt', value:'10g'}
2: Object {id:'38', property:'chocolate', value:'120g'}
I know how to do it with single data or to delete multiple values with a list. But I its tough to find anything similar for my case. Especually as I need to update all in one query and the amount of rows is dynamic. Means it could be that only one item is updated or 10.
Pseudo SQL query for better understand
BEGIN TRANSACTION [updateProperties]
BEGIN TRY
UPDATE properties SET
// Somehow iterate through array
property = ('array[Pos][Entry1]'),
value = ('array[Pos][Entry2]')
WHERE id = ('array[Pos][Entry0]')
COMMIT TRANSACTION [updateProperties]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [updateProperties]
END CATCH;
If you pass this in as a proper JSON array, you can use OPENJSON to join to your table
DECLARE #json nvarchar(max) = N'[
{"id":30, "property":"sugar", "value":"20g"},
{"id":37, "property":"salt", "value":"10g"},
{"id":38, "property":"chocolate", "value":"120g"}
]';
UPDATE p
SET property = j.property,
value = j.value
FROM properties p
JOIN OPENJSON(#json)
WITH (
id int,
[property] varchar(50),
[value] varchar(50)
) j ON j.id = p.id;
You can use MERGE if you want to update existing and insert new rows.
On a side note, I would advise you not to store value as a varchar. Instead, split it into amount and unit.

SQL Server Stored Procedure Multiple Insert in a single table from Array

I am using a stored procedure to insert records into a table. And do this at least 12 times in a loop to insert multiple records which is very inefficient.
here is the procedure as CREATED
Create PROC [dbo].[SP_INSERT_G_SAMPLING]
#GameID INT,
#ScoreID INT
as
begin
INSERT INTO GAMESCORE (GAMEID, SCOREID) VALUES
(#GameID, #ScoreID)
end
I pass on the values ex(1,3) and loop with more values from the website.
I want to however pass on all the values at one time like (1,3),(4,5),(8,9)
and then alter the above procedure to receive and insert multiple rows.
ALTER PROC [dbo].[SP_INSERT_G_SAMPLING]
#totalinsert nvarchar(Max)
INSERT INTO GAMESCORE (GAMEID, SCOREID) VALUES
(#totalinsert)
with #totalinsert being like (1,3),(4,5),(8,9) pushed from the webpage.
any help is greatly appreciated
What you're going to have to do is write a table valued function which accepts the multi-value string and breaks it out into a table object. If you can change your source to use a record delimiter instead of having comma sets it would be slightly easier to process. An example of that would look like this.
The below is pure psuedo and has not been validated in any way, just meant to give you a rough idea of where to go.
ex: #TotalInsert = 1,2|4,5|8,9
DECLARE #Results TABLE
(
value1 INT,
value2 INT
)
DECLARE #setlist VARCHAR(max);
WHILE Len(#TotalInsert) > 0
BEGIN
SET #setlist = LEFT(#totalinsert, Charindex('|', #totalinsert))
INSERT INTO #results
SELECT LEFT(#setlist, Charindex(',', #setlist) - 1),
RIGHT(#setlist, Charindex(',', Reverse(#setlist)) + 1)
SET #totalinsert = RIGHT(#totalinsert, Len(#totalinsert) - Len(#setlist))
END
I'm assuming you're using .NET for your website since you're also using SQL Server.
Have a look at table valued parameters, this page also includes a nice example of how to use the table valued parameters in .NET.
Check here for a better example of making a stored procedure with a table valued parameter in T-SQL.
Here is the full discussion:
http://www.sommarskog.se/arrays-in-sql-2005.html#XMLlist%20of%20values
Personally, I sent xml to the stored procedure, I "shred it" into #variable or #temp tables, then I do my INSERT/UPDATE/MERGE/DELETE from there.
Here is a fuller discussion on xml-shredding.
http://pratchev.blogspot.com/2007/06/shredding-xml-in-sql-server-2005.html
My personal trick is to create a strong dataset, populate the strong dataset with rows, and use the ds.GetXml() to send the xml down to the TSQL. With a strong dataset, I get strong-typing when populating the values. But at the end of the day, dataset is just some super fancy xml.

How to use a variable as the seed to an identity column while creating tables

I want to put the set the seed of the identity column of a table based on some variable.
For eg. something like that.
DECLARE #seed INT
SELECT #seed = MAX(id)
FROM tblSomeTable
Now, using #seed, I want to seed of my new table.
For eg. something like this:
DECLARE #tempTable TABLE (
ID INT IDENTITY(#seed,1) PRIMARY KEY, DESC NVARCHAR(50)
)
This actually throws an error saying incorrect syntax. Is there any way to achieve this?
Use fully dynamic SQL and embed the seed value in the string containing the CREATE TABLE statement (or DECLARE ... TABLE statement). Most DDL statements do not allow variables in them.

How to set the result of exec stored procedure to a variable?

I need to exec a stored procedure and store its scalar result to a local variable inside a stored procedure.
How to implement?
E.G.
CREATE PROCEDURE [dbo].GetNthNo AS
DECLARE #a INT
DECLARE #d INT
DECLARE #n INT
DECLARE #S INT
SET #S=EXEC spGetNthNo #a,#d,#n
SELECT #S
Please help.
Instead of:
SET #S=EXEC spGetNthNo #a,#d,#n
You need:
EXEC #S = spGetNthNo #a,#d,#n
And then within the procedure, you need something like:
RETURN 100
or:
RETURN #x
for the value you want for #S after the procedure executes.
You can also use output parameters. Combined example:
IF OBJECT_ID('tempdb..#example') IS NOT NULL DROP PROCEDURE #example
GO
CREATE PROCEDURE #example
#output_param INT OUTPUT
AS BEGIN
SET #output_param = 100
RETURN 200
END
GO
DECLARE #return INT, #param INT
EXEC #return = #example #output_param = #param OUTPUT
SELECT #return as [return value], #param as [output parameter]
Try something like that
CREATE PROCEDURE Test
#RetVal INT OUT
AS
BEGIN
SET #RetVal = 99
END
DECLARE #X INT
EXEC Test #X OUT
PRINT #X
Edit: [comment following posting of T-SQL snippet in question]
You seem to need a wrapper, around the spGetNthNo Stored Procedure, maybe because this existing procedure doesn't return its result in the way that is desired. An alternative to a wrapper may simply be to modify [ever so slightly] spGetNthNo itself, so it works as desired in the first place (provided the method is not currently in use with its existing API).
Regardless of whether the changes will be in the original SP or in a wrapper, there are two distinct ways of retrieving data from an SP:
With output variables (as shown above)
By having the SP return a "recordset", i.e. a table made of rows (records) and columns (fields). (This is done by having a SELECT statement towards the end of the SP, as show in the question snippet)
With the output variable approach, the data is readily placed in the variables by the time the SP returns. With the recordset apporach, the calling logic needs to "consume" the data returned, in a similar fashion that it would from a SELECT statement.
Aside from the way the returned data is consumed, there are a few differences between these approaches.
The most obvious one is that the "recordset" approach allows the returning more values: one would have to explicitly declare say 30 variables (with some naming convention aimed at helping with the two-dimensional nature of the table) to emulate a the returns of a SP which "SELECT TOP 10 a, b, c FROM myTable". Also the SP would have to explicitly set each of the these output variables.
Another related but more subtle difference is that the recordset approach allows returning a number of rows and columns that is undefined at the time of the call. The number and types of the variables do not need to be expressed beforehand, rather they come with the metadata surrounding the recordset.
In short: the output variable approach is more suited to return a fixed set of a few variables, such as status code, maximum or minim value (or other aggregate values and calculations), or also, a few fields from a expected single record. The Recordset approach is used when the purpose of the stored procedure is to effectively provide a table-like result, or when it returns very many values, such as a long [and evolving] list of aggregate values, etc.

How can one iterate over stored procedure results from within another stored procedure....without cursors?

I'm not sure if this is something I should do in T-SQL or not, and I'm pretty sure using the word 'iterate' was wrong in this context, since you should never iterate anything in sql. It should be a set based operation, correct? Anyway, here's the scenario:
I have a stored proc that returns many uniqueidentifiers (single column results). These ids are the primary keys of records in a another table. I need to set a flag on all the corresponding records in that table.
How do I do this without the use of cursors? Should be an easy one for you sql gurus!
This may not be the most efficient, but I would create a temp table to hold the results of the stored proc and then use that in a join against the target table. For example:
CREATE TABLE #t (uniqueid int)
INSERT INTO #t EXEC p_YourStoredProc
UPDATE TargetTable
SET a.FlagColumn = 1
FROM TargetTable a JOIN #t b
ON a.uniqueid = b.uniqueid
DROP TABLE #t
You could also change your stored proc to a user-defined function that returns a table with your uniqueidentifiers. You can joing directly to the UDF and treat it like a table which avoids having to create the extra temp table explicitly. Also, you can pass parameters into the function as you're calling it, making this a very flexible solution.
CREATE FUNCTION dbo.udfGetUniqueIDs
()
RETURNS TABLE
AS
RETURN
(
SELECT uniqueid FROM dbo.SomeWhere
)
GO
UPDATE dbo.TargetTable
SET a.FlagColumn = 1
FROM dbo.TargetTable a INNER JOIN dbo.udfGetUniqueIDs() b
ON a.uniqueid = b.uniqueid
Edit:
This will work on SQL Server 2000 and up...
Insert the results of the stored proc into a temporary table and join this to the table you want to update:
INSERT INTO #WorkTable
EXEC usp_WorkResults
UPDATE DataTable
SET Flag = Whatever
FROM DataTable
INNER JOIN #WorkTable
ON DataTable.Ket = #WorkTable.Key
If you upgrade to SQL 2008 then you can pass table parameters I believe. Otherwise, you're stuck with a global temporary table or creating a permanent table that includes a column for some sort of process ID to identify which call to the stored procedure is relevant.
How much room do you have in changing the stored procedure that generates the IDs? You could add code in there to handle it or have a parameter that lets you optionally flag the rows when it is called.
Use temporary tables or a table variable (you are using SS2005).
Although, that's not nest-able - if a stored proc uses that method then you can't dumpt that output into a temp table.
An ugly solution would be to have your procedure return the "next" id each time it is called by using the other table (or some flag on the existing table) to filter out the rows that it has already returned
You can use a temp table or table variable with an additional column:
DECLARE #MyTable TABLE (
Column1 uniqueidentifer,
...,
Checked bit
)
INSERT INTO #MyTable
SELECT [...], 0 FROM MyTable WHERE [...]
DECLARE #Continue bit
SET #Continue = 1
WHILE (#Continue)
BEGIN
SELECT #var1 = Column1,
#var2 = Column2,
...
FROM #MyTable
WHERE Checked = 1
IF #var1 IS NULL
SET #Continue = 0
ELSE
BEGIN
...
UPDATE #MyTable SET Checked = 1 WHERE Column1 = #var1
END
END
Edit: Actually, in your situation a join will be better; the code above is a cursorless iteration, which is overkill for your situation.