I am trying to update all null values of a column with Uuid (generated with the help of a stored procedure GetOptimizedUuid). While doing so I am getting an error
Subquery returned more than 1 value
I could understand the causes of error but none of my fix helped out.
I tried out with some loops but it doesn't fix
BEGIN
DECLARE #no INT;
DECLARE #i INT;
SET #no = (SELECT COUNT(id) FROM table1)
SET #i = 0;
WHILE #i < #no
BEGIN
DECLARE #TempUuid TABLE(SeqUuid UNIQUEIDENTIFIER, OptimizedUuid UNIQUEIDENTIFIER)
INSERT INTO #TempUuid
EXECUTE [Sample].[dbo].[GetOptimizedUuid]
UPDATE table1
SET col2 = (SELECT OptimizedUuid FROM #TempUuid)
WHERE col2 IS NULL;
SET #i = #i + 1;
END
END
Help me to sort out this, Thanks!
Not entirely sure what you're doing - what do you need to call this GetOptimizedUuid stored procedure? Can't you just use NEWID() to get a new GUID?
Anyway - assuming you have to call this stored procedure, I assume you'd call it once before the loop, to get the ID's you need - and then you get the top (1) UUID from the table and update one row in your database table - and then you also need to remove that UUID that you've just used from the temp table, otherwise you keep re-using the same ID over and over again....
Try something like this:
BEGIN
DECLARE #no INT;
DECLARE #i INT;
SET #no = (SELECT COUNT(id) FROM table1)
SET #i = 0;
-- define and fill the table *ONCE* and *BEFORE* the loop
DECLARE #TempUuid TABLE(SeqUuid UNIQUEIDENTIFIER, OptimizedUuid UNIQUEIDENTIFIER)
INSERT INTO #TempUuid
EXECUTE [Sample].[dbo].[GetOptimizedUuid]
-- declare a UUID to use
DECLARE #NewUuid UNIQUEIDENTIFIER;
WHILE #i < #no
BEGIN
-- get the first UUID from the temp table
SELECT TOP (1) #NewUuid = OptimizedUuid
FROM #TempUuid;
-- update your table
UPDATE table1
SET col2 = #NewUuid
WHERE col2 IS NULL;
-- *REMOVE* that UUID that you've used from the table
DELETE FROM #TempUuid
WHERE OptimizedUuid = #NewUuid;
SET #i = #i + 1;
END
END
I wrote the below procedure to retrieve the number of parameters in a stored procedure.But it doesn't return accurate values and also some of the procedures don't appear in the quesry I have written.How can I edit this to list all the procedures and return their number of parameters?
CREATE PROC test_op #name varchar(100), #count int OUTPUT
AS
SELECT #count =COUNT (INFORMATION_SCHEMA.PARAMETERS.PARAMETER_NAME)
FROM INFORMATION_SCHEMA.PARAMETERS
GROUP BY INFORMATION_SCHEMA.PARAMETERS.SPECIFIC_NAME,INFORMATION_SCHEMA.PARAMETERS.SPECIFIC_SCHEMA
DECLARE #count1 int
EXEC test_op usp_GetDBTime, #count=#count1 OUTPUT
Print #count1
You are grouping by the parameter name, so the query returns multiple values. One of them is getting assigned to the output variable.
If you want the number for a single procedure, then use where instead of group by:
CREATE PROC test_op (
#name varchar(100),
#count int OUTPUT
)
AS
BEGIN
SELECT #count =COUNT(p.PARAMETER_NAME)
FROM INFORMATION_SCHEMA.PARAMETERS p
WHERE p.SPECIFIC_NAME = #name;
END;
DECLARE #count1 int;
EXEC test_op 'usp_GetDBTime', #count = #count1 OUTPUT
Print #count1;
You might want to include the schema name in the procedure.
The most significant issue is that you don't filter by #name. However, even inside that, you could have problems with duplicate names in different schemas. This should work reasonably reliably - addressing both of those issues, while also supporting a better range of #name formats ('Foo' vs 'dbo.Foo', etc):
ALTER PROC test_op #name sysname -- or CREATE the first time
AS
DECLARE #id int = OBJECT_ID(#name);
DECLARE #schema sysname = OBJECT_SCHEMA_NAME(#id);
SET #name = OBJECT_NAME(#id);
RETURN (
SELECT COUNT(INFORMATION_SCHEMA.PARAMETERS.PARAMETER_NAME)
FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_NAME = #name AND SPECIFIC_SCHEMA = #schema)
GO
DECLARE #count1 int;
EXEC #count1 = test_op 'test_op' -- etc
Print #count1
I have a trigger ,but I need to get the updated record's primary key (like as inserting the data SELECT #Id= ##IDENTITY) thus, I can pass it to where condition. How can I do that?
ALTER TRIGGER [dbo].[CariBakiyeBorcAktar]
ON [dbo].[BakimKartiDegisenParcalar]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Id int
DECLARE #CariId int
DECLARE #SId int
DECLARE #MId int
declare #Tutar decimal
declare #Bakiye decimal
declare #s decimal = 0
DECLARE #ParcaId int
--how I can I get the last updateed record Identity like this??
--and pass it to update query as a where condition
SELECT #Id= ##IDENTITY
set #SId=(select SId from CariBakiye where Id =#Id)
select #CariId=tblk.CariId ,#MId=tblk.MId, #SId= tblk.SId,#Tutar=tblk.Tutar from (
SELECT tbl.CariId , tbl.MId,tbl.SId,tbl.Tutar from (select cb.MId,SUM(bk.Tutar) as Tutar,bk.SId,cb.Id as CariId FROM [BakimKartiDegisenParcalar] bk
join CariBakiye cb on cb.SId=bk.SId
where bk.SId =cb.SId group by bk.SId,cb.MId,cb.Id ) as tbl
) as tblk where SId = #SId
set #Bakiye = #s-#Tutar
update CariBakiye set Borc=#Tutar,Bakiye=#Bakiye where Id=#CariId
print #Id
-- Insert statements for trigger here
END
As Martin said, you have to understand that SQL Server triggers are per statement, not per row. So in context of your trigger you have two tables - inserted and deleted, where you could find all information about data updated. If you really want to do per row processing, you could use cursor:
ALTER TRIGGER [dbo].[CariBakiyeBorcAktar] ON [dbo].[BakimKartiDegisenParcalar]
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #Id int
DECLARE #CariId int
DECLARE #SId int
DECLARE #MId int
declare #Tutar decimal
declare #Bakiye decimal
declare #s decimal = 0
DECLARE #ParcaId int
declare tr_cursor cursor local fast_forward for
select ID from inserted
while 1 = 1
begin
fetch tr_cursor into #Id
if ##fetch_status <> 0 break
set #SId=(select SId from CariBakiye where Id =#Id)
select #CariId=tblk.CariId ,#MId=tblk.MId, #SId= tblk.SId,#Tutar=tblk.Tutar from (
SELECT tbl.CariId , tbl.MId,tbl.SId,tbl.Tutar from (select cb.MId,SUM(bk.Tutar) as Tutar,bk.SId,cb.Id as CariId FROM [BakimKartiDegisenParcalar] bk
join CariBakiye cb on cb.SId=bk.SId
where bk.SId =cb.SId group by bk.SId,cb.MId,cb.Id ) as tbl
) as tblk where SId = #SId
set #Bakiye = #s-#Tutar
update CariBakiye set Borc=#Tutar,Bakiye=#Bakiye where Id=#CariId
print #Id
-- Insert statements for trigger here
end
close tr_cursor
deallocate tr_cursor
END
I have a sql function and using sql server 2005.
dbo.util (#dailyDate,#userId)
Now I want to call this function for each #userId for a particular Date.So I am writing a Stored Procedure.
Create PROCEDURE [dbo].[DailyAttendenceTemp]
#dailyDate nvarchar(10)
WITH EXEC AS CALLER
AS
Select * FROM dbo.util (#dailyDate,#userId) //I think error is here.
WHERE #userId in (SELECT UserId From TTransactionLog1)
GO
but when I execute the procedure it give the error that-
SQL Server Database Error: Must declare the scalar variable "#userId".
So please tell me how to correct the procedure so that I give only date as a parameter and it run for the same function for each #userId.
I got the answer,,now I am using While loop and it solve my problem.......
DECLARE #i int
DECLARE #userid nvarchar(10)
DECLARE #numrows int
DECLARE #tempUserId_table TABLE (
idx smallint Primary Key IDENTITY(1,1)
, userid nvarchar(10)
)
INSERT #tempUserId_table
SELECT distinct UserID FROM TUser
-- enumerate the table
SET #i = 1
SET #numrows = (SELECT COUNT(*) FROM #tempUserId_table)
IF #numrows > 0
WHILE (#i <= (SELECT MAX(idx) FROM #tempUserId_table))
BEGIN
-- get the next userId primary key
SET #userid = (SELECT userid FROM #tempUserId_table WHERE idx = #i)
Select * FROM dbo.util (#dailyDate,#userid)
-- increment counter for next userId
SET #i = #i + 1
END
Here's my scenario:
Let's say I have a stored procedure in which I need to call another stored procedure on a set of specific ids; is there a way to do this?
i.e. instead of needing to do this:
exec p_MyInnerProcedure 4
exec p_MyInnerProcedure 7
exec p_MyInnerProcedure 12
exec p_MyInnerProcedure 22
exec p_MyInnerProcedure 19
Doing something like this:
*magic where I specify my list contains 4,7,12,22,19*
DECLARE my_cursor CURSOR FAST_FORWARD FOR
*magic select*
OPEN my_cursor
FETCH NEXT FROM my_cursor INTO #MyId
WHILE ##FETCH_STATUS = 0
BEGIN
exec p_MyInnerProcedure #MyId
FETCH NEXT FROM my_cursor INTO #MyId
END
My Main goal here is simply maintainability (easy to remove/add id's as the business changes), being able to list out all Id's on a single line... Performance shouldn't be as big of an issue
declare #ids table(idx int identity(1,1), id int)
insert into #ids (id)
select 4 union
select 7 union
select 12 union
select 22 union
select 19
declare #i int
declare #cnt int
select #i = min(idx) - 1, #cnt = max(idx) from #ids
while #i < #cnt
begin
select #i = #i + 1
declare #id = select id from #ids where idx = #i
exec p_MyInnerProcedure #id
end
What I do in this scenario is create a table variable to hold the Ids.
Declare #Ids Table (id integer primary Key not null)
Insert #Ids(id) values (4),(7),(12),(22),(19)
-- (or call another table valued function to generate this table)
Then loop based on the rows in this table
Declare #Id Integer
While exists (Select * From #Ids)
Begin
Select #Id = Min(id) from #Ids
exec p_MyInnerProcedure #Id
Delete from #Ids Where id = #Id
End
or...
Declare #Id Integer = 0 -- assuming all Ids are > 0
While exists (Select * From #Ids
where id > #Id)
Begin
Select #Id = Min(id)
from #Ids Where id > #Id
exec p_MyInnerProcedure #Id
End
Either of above approaches is much faster than a cursor (declared against regular User Table(s)). Table-valued variables have a bad rep because when used improperly, (for very wide tables with large number of rows) they are not performant. But if you are using them only to hold a key value or a 4 byte integer, with a index (as in this case) they are extremely fast.
use a static cursor variable and a split function:
declare #comma_delimited_list varchar(4000)
set #comma_delimited_list = '4,7,12,22,19'
declare #cursor cursor
set #cursor = cursor static for
select convert(int, Value) as Id from dbo.Split(#comma_delimited_list) a
declare #id int
open #cursor
while 1=1 begin
fetch next from #cursor into #id
if ##fetch_status <> 0 break
....do something....
end
-- not strictly necessary w/ cursor variables since they will go out of scope like a normal var
close #cursor
deallocate #cursor
Cursors have a bad rep since the default options when declared against user tables can generate a lot of overhead.
But in this case the overhead is quite minimal, less than any other methods here. STATIC tells SQL Server to materialize the results in tempdb and then iterate over that. For small lists like this, it's the optimal solution.
You can try as below :
declare #list varchar(MAX), #i int
select #i=0, #list ='4,7,12,22,19,'
while( #i < LEN(#list))
begin
declare #item varchar(MAX)
SELECT #item = SUBSTRING(#list, #i,CHARINDEX(',',#list,#i)-#i)
select #item
--do your stuff here with #item
exec p_MyInnerProcedure #item
set #i = CHARINDEX(',',#list,#i)+1
if(#i = 0) set #i = LEN(#list)
end
I usually use the following approach
DECLARE #calls TABLE (
id INT IDENTITY(1,1)
,parameter INT
)
INSERT INTO #calls
select parameter from some_table where some_condition -- here you populate your parameters
declare #i int
declare #n int
declare #myId int
select #i = min(id), #n = max(id) from #calls
while #i <= #n
begin
select
#myId = parameter
from
#calls
where id = #i
EXECUTE p_MyInnerProcedure #myId
set #i = #i+1
end
CREATE TABLE #ListOfIDs (IDValue INT)
DECLARE #IDs VARCHAR(50), #ID VARCHAR(5)
SET #IDs = #OriginalListOfIDs + ','
WHILE LEN(#IDs) > 1
BEGIN
SET #ID = SUBSTRING(#IDs, 0, CHARINDEX(',', #IDs));
INSERT INTO #ListOfIDs (IDValue) VALUES(#ID);
SET #IDs = REPLACE(',' + #IDs, ',' + #ID + ',', '')
END
SELECT *
FROM #ListOfIDs
Make a connection to your DB using a procedural programming language (here Python), and do the loop there. This way you can do complicated loops as well.
# make a connection to your db
import pyodbc
conn = pyodbc.connect('''
Driver={ODBC Driver 13 for SQL Server};
Server=serverName;
Database=DBname;
UID=userName;
PWD=password;
''')
cursor = conn.cursor()
# run sql code
for id in [4, 7, 12, 22, 19]:
cursor.execute('''
exec p_MyInnerProcedure {}
'''.format(id))