Execute a stored procedure with multiple IDs - sql-server

I have a PersonInformation table that contains the information below:
| PersonID | Name | Status
+----------+------+------------
| 1234 | John | Active
| 5678 | Mary | Inactive
| 1090 | Tery | Active
| 1554 | Cary | Inactive
I also have a stored procedure called SpStats that does some calculations using PersonID and returns the results of stat_a, stat_b.
I need to execute the SpStats stored procedure for each active person in the PersonInformation table using their PersonID.
It is basically
SELECT PersonId, Name, Status, {Exec SpStats for related PersonID}
FROM PersonInformation
WHERE status = 'Active'
The expected result is this:
| PersonID | Name | Status | stat_a | stat_b |
+----------+------+--------+--------+--------+
| 1234 | John | Active | 25 | 45 |
| 1090 | Tery | Active | 10 | 67 |

If you can create a user defined table function that will do what your stored procedure does, you could use cross apply to get it's results for each row in the select.
Assuming it accepts the PersonID value, it can be done like this:
SELECT PersonId, [Name], [Status], stat_a, stat_b
FROM PersonInformation
CROSS APPLY dbo.UDFStats(PersonID) as stats
WHERE [Status] = 'Active'
Note that CROSS APPLY will not work with stored procedures.

Create your own custom type like so
--Table Valued Parameter Type used for passing lists of Int IDs.
CREATE TYPE [MySchema].[UniqueIDListType] AS TABLE
(
[ID] Int NOT NULL
);
Then in your stored proc set the input parameter to your type
CREATE PROCEDURE [MySchema].[MyProc]
#MyIDList [MySchema].[UniqueIDListType] READONLY
AS
And there you go, you can use your parameter like a table in your select
WHERE EXISTS (SELECT * FROM #MyIDList WHERE ID = PersonID);
You will get a full result set for each id then. Pass it in to the proc like you would any other parameter, you'll just need to format the ids into this single collection. And the bonus is you can reuse your new table valued parameter everywhere you need to do this again. Reusability!! Yay!
If you can't do this, then I'm assuming you can't with your requirements or permissions make a function either, I would then just loop through programmatically and make a table variable in your sql, call the proc for each person, insert that result in your table, and join that on your final select result set when you're all done.

If you have a table
CREATE TABLE Country (
CountryId UNIQUEIDENTIFIER
,Abbreviation VARCHAR(10)
,Name VARCHAR(100)
)
And a User Defined Function
CREATE FUNCTION udfConcatenate(#value1 VARCHAR(4000), #value2 VARCHAR(4000))
RETURNS VARCHAR(8000)
AS
BEGIN
RETURN CONCAT(#value1, #value2)
/* HERE YOU CAN EXECUTE YOU SOTRED PROCEDURE AND RETURN THE RESULT */
END
You can execute
select c.CountryId, dbo.udfConcatenate(c.Abbreviation, c.Name) from country AS C
The User Defined Function can be applied for each row.
So, the User Defined Function execute you Sotred Procedure and return the result.
And, you use the User Defined Function in you SELECT.
EDIT
WHILE way
DECLARE #abb VARCHAR(10)
DECLARE #name VARCHAR(200)
DECLARE cur1 CURSOR FOR
SELECT Abbreviation, Name FROM Country
OPEN cur1
FETCH NEXT FROM cur1 INTO #abb, #name
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT dbo.udfConcatenate(#abb, #name)
/* HERE YOU CAN EXECUTE THE SP AND STORE THE RESULTS IN A TEMP TABLE */
FETCH NEXT FROM cur1 INTO #abb, #name
END
CLOSE cur1
DEALLOCATE cur1
Hope this help you.

Related

Multiple entries in to table variable using Output INTO clause

My use case is as below :
Create table Emp
(
id int ,
fname varchar(100),
lname varchar(100)
)
declare #aud table (prop varchar(1000),[oldval] varchar(100),[toval] varchar(100))
INSERT INTO EMP(id, fname, lname)
OUTPUT 'fname', '', inserted.fname INTO #aud (prop, [oldval], [toval])
VALUES (1,'malcolm','dsouza')
SELECT * FROM #aud
I want the data to be formed like:
prop oldval toval
------------------------
fname malcolm
lname dsouza
I know we cannot have have multiple inserts using OUTPUT INTO clause, but is there any other way I can form a data for inserted rows as column based?
You need to try below steps:
OUTPUT clause simply inserts the rows in to the table variable i.e., one row to one row mapping. You need to modify your table definition accordingly.
Now, You can use CROSS APPLY to get the result you want.
**TEST SETUP**
Create table #Emp(
id int ,
fname varchar(100),
lname varchar(100)
)
TEST
declare #aud table(oldfname varchar(100),fname varchar(100),oldlname varchar(100), lname varchar(100));
INSERT INTO #EMP(id,fname,lname)
OUTPUT '' as oldfname,inserted.fname, '' as oldlname, inserted.lname INTO #aud
values (1,'malcolm','dsouza');
SELECT property, oldval, newval as oldValue
FROM #aud
CROSS APPLY
( values('fname',oldfname,fname),('lname',oldlname, lname)) as t(property,oldVal, newVal)
Result Set
+----------+--------+----------+
| property | oldval | oldValue |
+----------+--------+----------+
| fname | | malcolm |
| lname | | dsouza |
+----------+--------+----------+

How to perform multiple updates in stored procedure? [duplicate]

This question already has answers here:
Passing a varchar full of comma delimited values to a SQL Server IN function
(27 answers)
Closed 3 years ago.
I will update some columns and some rows from table1 to table2 togetherly based of model column.
ex. table1 in DATA1DB database
| id | name | address | color | model |
| 1 avi aadd blue mod1
| 2 bref ddff red mod2
| 3 cind ffdd red mod1
| 4 davi ffgg green mod1
table2 in DATA2DB database
| id | name | address | color | model |
| 1 avi aadd red mod1
| 2 bref dddd red mod2
| 3 cind ffff red mod1
| 4 davi gggg red mod1
when execute update based id=(1,3,4), i want table2 to be the same as table1.
in my store procedure i have this code
ALTER PROCEDURE [dbo].[updatemultiple]
#id varchar (5)
AS BEGIN SET NOCOUNT ON
begin
UPDATE DATA2DB.table2
SET [DATA2DB].table2.address= [DATA1DB].table1.address,
[DATA2DB].table2.color = [DATA1DB].table1.color,
FROM [DATA2DB].table2
INNER JOIN [DATA1DB].table1
ON [DATA2DB].table2.id = [DATA1DB].table2.id
where LTRIM(RTRIM([DATA1DB].table1.id)) = LTRIM(RTRIM(#id))
I want result table2 in DATA2DB database
| id | name | address | color | model |
1 avi aadd blue mod1
3 cind ffdd red mod1
4 davi ffgg green mod1
I assume you want to update the DB2 whenever the DB1 gets updated.
If so, you can add an update trigger to your table in DB1.
ALTER TRIGGER [dbo].[tr_UPDATE_DB2TABLE]
ON [DATA1DB].table1
AFTER UPDATE
AS BEGIN
SET NOCOUNT ON;
UPDATE DATA2DB.table2
SET [DATA2DB].table2.address= [DATA1DB].table1.address,
[DATA2DB].table2.color = [DATA1DB].table1.color,
FROM [DATA2DB].table2
INNER JOIN Inserted I ON I.Id = [DATA1DB].table1.Id
INNER JOIN [DATA1DB].table1 ON [DATA2DB].table2.id = [DATA1DB].table2.id
where LTRIM(RTRIM([DATA1DB].table1.model)) = LTRIM(RTRIM(I.model))
END
Each time the table in DB1 gets updates it will update the table in DB2.
Took me a while to understand what your problem is...
If I understand you right, you want to execute your procedure with #id=('1,3,4')
Then you want to take each single id (1 and 3 and 4) and run the update for each id with the value from table1.. If this is all correct your sp could look like this:
ALTER PROCEDURE [dbo].[updateMultiple] #id VARCHAR(5)
AS
BEGIN
DECLARE #single_id INT;
DECLARE my_cursor CURSOR
FOR SELECT value
FROM STRING_SPLIT(#id, ',')
WHERE RTRIM(value) <> '';
OPEN my_cursor;
FETCH NEXT FROM my_cursor INTO #single_id;
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE [db2]
SET
[db2].[address] = [db1].[address],
[db2].[color] = [db1].[color]
-- declare #id varchar(5) = (1,3,4);Select *
FROM [data2db] [db2]
JOIN [data1db] [db1] ON [db2].[id] = [db1].[id]
WHERE [db1].[id] = #single_id;
FETCH NEXT FROM my_cursor INTO #single_id;
END;
CLOSE my_cursor;
DEALLOCATE my_cursor;
END;
GO
You need to split your input string into each single id and perform the update..
This is possible, but probably not the best way to do it... Just saying...
Hope it helps.
PS: In my example, I've used two tables in the same DB, but it should work over two different databases as well.
EDIT: Or it should even work without cursor:
ALTER PROCEDURE [dbo].[updateMultiple] #id VARCHAR(5)
AS
BEGIN
UPDATE [db2]
SET
[db2].[address] = [db1].[address],
[db2].[color] = [db1].[color]
--declare #id varchar(5) = '1,3,4';Select *
FROM [data2db] [db2]
JOIN [data1db] [db1] ON [db2].[id] = [db1].[id]
WHERE [db1].[id] IN
(
SELECT value
FROM STRING_SPLIT(#id, ',')
WHERE RTRIM(value) <> ''
);
END;
GO

update one table from another selected table

I select one column from a table and generated the second column by select case:
(select Id , case
when education=0 then '0::ALL'
when education=1 then '1::HIGH_SCHOOL'
when education=2 then '2::UNDERGRAD'
when education=3 then '3::ALUM'
when education=4 then '4::HIGH_SCHOOL_GRAD'
when education=5 then '5::SOME_COLLEGE'
when education=6 then '6::ASSOCIATE_DEGREE'
when education=7 then '7::IN_GRAD_SCHOOL'
when education=8 then '8::SOME_GRAD_SCHOOL'
when education=9 then '9::MASTER_DEGREE'
when education=10 then '10::PROFESSIONAL_DEGREE'
when education=11 then '11::DOCTORATE_DEGREE'
when education=12 then '12::UNSPECIFIED'
end as myeducation
from ids_table where Id = '4fcc-a519-15db04651b91')
assuming it returns:
------------------------------------------------
| Id myeducation |
| 4fcc-a519-15db04651b91, 9::MASTER_DEGREE |
------------------------------------------------
in the same table (ids_table), I have an empty column is called: allEducations
I want to set allEducations = myeducation where id (of the table above that I "created") is equal to the id of the table (ids_table)
before:
ids_table:
----------------------------------------------
| Id allEducation |
| 4fcc-a519-15db04651b91, |
------------------------------------------------
after:
----------------------------------------------
| Id allEducation |
| 4fcc-a519-15db04651b91, 9::MASTER_DEGREE |
------------------------------------------------
I tried to do something like:
`;WITH b AS (THE SQL QUERY ABOVE) update ids_table c set c.allEducations = b.myeducation where c.id = b.id'
any help appreciated!
This should be enough:
begin tran updateEducation
update ids_table set allEducations =
case
when education=0 then '0::ALL'
when education=1 then '1::HIGH_SCHOOL'
when education=2 then '2::UNDERGRAD'
when education=3 then '3::ALUM'
when education=4 then '4::HIGH_SCHOOL_GRAD'
when education=5 then '5::SOME_COLLEGE'
when education=6 then '6::ASSOCIATE_DEGREE'
when education=7 then '7::IN_GRAD_SCHOOL'
when education=8 then '8::SOME_GRAD_SCHOOL'
when education=9 then '9::MASTER_DEGREE'
when education=10 then '10::PROFESSIONAL_DEGREE'
when education=11 then '11::DOCTORATE_DEGREE'
when education=12 then '12::UNSPECIFIED'
end
---- if it is not good
-- rollback
---- if it is good
-- commit

Access to any element of a table

I have built a table in a stored procedure :
declare #storeTable table
(
path varchar(1000) not null,
nbdays int,
offset int
)
insert #storeTable
select PATH, NUMBER, OFFSET from
FILENAME f left outer join ...
Let's say that my table have 4 rows, like this:
Path1 | 3 | 1
Path2 | 9 | -1
Path3 | 2 | 3
Path4 | 5 | 0
I would like to know how I can have access to any element from this table.
For instance, I would like to use the value -1 of the offset on the row 2, in order to include it at the end of the Path2 (and thus modify the path2)
Any clue?
Instead of a variable what I do is to create a temp table. Something like this:
CREATE TABLE [dbo].[#storeTable] (
[path] varchar(1000) not null,
[nbdays] int,
[offset] int
) ON [PRIMARY]
Then you can do select or joins/updates with your regular tables. Temp table is deleted when you exist SP. Hope it helps you
Select path+cast(offset as varchar) as path
From #storeTable

Is there a way to write a function in SQL, which will return a column that you can call in a select statement?

For example, I want to be able to write function 'f()' that I can call as written below. (Note that #tbl is a user-defined memory table that I've previously created with columns Var1 and Var2, and that my function 'f' calls for a parameter of user-defined table type.)
SELECT t1.Var1, t1.Var2, f(#tbl) OVER (PARTITION BY t1.Var1)
From #tbl as t1
It seems like user defined functions will either return table values or scalar values. I need to be able to return a column though, because I want to apply the function over a given column by group.
Scalar-Valued Function will give you column
with Scalar Function
eg:
1 create the function
CREATE FUNCTION [dbo].[duplicate] (#number int)
returns double
as
begin
declare #duplicate double
set #duplicate = #number * 2
return #duplicate
end
/****/
2 use the function
We have a table "numbers" like:
id | val
1 | 2
2 | 5
3 | 3
then
select id, duplicate(val) as dup
from numbers
the result:
id | dup
1 | 4
2 | 10
3 | 6

Resources