I have one table with āNā number of rows, I am trying to fetch one by one rows and want to perform some operation.
Can anyone help me with this stored procedure?
For more details: I am trying to retrieve mail id from 'UserMaster' table one by one, and I want to use this mail id to send mail to particular user, I am able to send mail to user,
Just struck retrieving them one by one
declare #count as int
declare #inti as int
select COUNT(UserId) from tblUserMaster where AccessLevelId = '1'
SET #inti = 1
while #inti <= #count
BEGIN
select #mail = Email_ID
from User_Master
where AccessLevelId = '1'
rownum(#inti) -- here I need to retrieve one bye one row
#inti ++;
EXEC sp_send_dbmail #profile_name = 'PinalProfile',
#recipients = #mail,
#subject = 'Test message',
#body = 'This is the body of the test message.
Congrates Database Mail Received By you Successfully.'
END
There are multiple ways to do this, I think simplest way is to insert your resultset into a temp object with an identity column, then loop through temp object. Here's some pseudo code:
DECLARE #t1 table (id int identity, email varchar(50)),
#count int,
#i int = 1,
#email varchar(50);
INSERT INTO #t1 (email)
select Email_ID
from User_Master
where AccessLevelId = '1';
select #count = count(*) from #t1;
while #i <= #count
BEGIN
select #email = email from #t1 where id = #i
EXEC sp_send_dbmail #profile_name = 'PinalProfile',
#recipients = #mail,
#subject = 'Test message',
#body = 'Test body';
set #i = #i + 1;
END
Related
I have a function with several product ID's in a table. I want to perform this update and set these variables to the desired product ID's. Right now, this updates only the first productID from my function that splits the multiple values of my variable. I'm aware a CTE would be ideal here, but I'm not great at the syntax.
My desired outcome is for each value in #CSV, execute the update statement. Any assistance would be great.
DECLARE
#MaxAllowance INT = 25,
#AllowanceType INT = 2,
#AllowancePeriod INT = 2,
#Account_ID INT = 13379,
#CSV VARCHAR(50) = '506,280,281,282,286',
#count INT
DECLARE #DiscountClass_ID INT =
(
SELECT DiscountClass_ID FROM Account WHERE Account_ID = #Account_ID
);
SELECT #count =
(
SELECT COUNT(*) FROM dbo.FN_csv_tolist(#CSV)
);
declare #ProductID int
declare #rn int
declare ItemCursor cursor local static forward_only read_only for
select Value,
row_number() over(partition by Value order by Value) as rn
FROM dbo.FN_csv_tolist(#CSV)
open ItemCursor
fetch next from ItemCursor
into #ProductID, #rn
WHILE #count > 0
begin
update dbo.ProdTerm
SET MaxAllowance = #MaxAllowance,
AllowanceType = #AllowanceType,
AllowancePeriod = #AllowancePeriod
WHERE Product_ID = #ProductID
SET #count = #count - 1;
fetch next from ItemCursor
into #ProductID, #rn
end
close ItemCursor
deallocate ItemCursor
I am a little confused by your code, I am not sure of the reason for the need of a cursor, as the code could be a lot simpler. One way would be:
DECLARE #MaxAllowance INT = 25,
#AllowanceType INT = 2,
#AllowancePeriod INT = 2,
#CSV VARCHAR(50) = '506,280,281,282,286';
WITH Test
AS
(
SELECT [Value]
FROM dbo.FN_csv_tolist(#CSV)
)
UPDATE PT
SET MaxAllowance = #MaxAllowance,
AllowanceType = #AllowanceType,
AllowancePeriod = #AllowancePeriod
FROM dbo.ProdTerm AS PT
INNER JOIN Test AS T ON PT.Product_ID = T.Value;
I have some users in SSRS which I want to delete.
where I try to run:
DELETE FROM dbo.Users
WHERE dbo.Users.UserName = 'domain\user'
I'm getting the following error:
Msg 547, Level 16, State 0, Line 1
The DELETE statement conflicted with the REFERENCE constraint
"FK_Catalog_CreatedByID". The conflict occurred in database "ReportServer",
table "dbo.Catalog", column 'CreatedByID'.
The statement has been terminated.
How can I delete users (and all their permissions from SSRS database?
I run into the same problem that I wanted to permanently remove users from the SSRS database. So I made this SQL syntax (BACKUP YOUR DB BEFORE!):
DECLARE #username varchar(255); SET #username = '<username>';
DECLARE #ownerid uniqueidentifier; SET #ownerid = (SELECT UserID FROM Users WHERE UserName = #username)
BEGIN TRAN
UPDATE Subscriptions SET ModifiedByID = OwnerID WHERE ModifiedByID = #ownerid
DELETE FROM Subscriptions WHERE OwnerID = #ownerid
DELETE FROM Schedule WHERE CreatedById = #ownerid
DELETE FROM PolicyUserRole WHERE UserID = #ownerid
DELETE FROM Users WHERE UserID = #ownerid
COMMIT
Replace the <username> with the user you want to remove and that's it. But be aware that this also removes other data from this user as well.
The error is clear that you have data in other tables that references the Users table which you are trying to delete.
So you have two options:
Remove the constraints from the Users table and then delete the record and reapply all the constraints again.
Remove the data from the referenced tables where your User table is getting referenced.
select UserID, u.UserName
into #rs_users
from
ReportServer.dbo.Users as u
where userid = 1 -- expression
delete from [ReportServer].[dbo].[PolicyUserRole]
where UserID in (select UserID from #rs_users)
declare #user_name nvarchar(100), #pattern_start nvarchar(300), #pattern_end nvarchar(100), #result nvarchar(max)
declare #start_position int, #end_position int, #sec_data_id uniqueidentifier
DECLARE user_cursor CURSOR
FOR
select UserName
from #rs_users
OPEN user_cursor;
FETCH NEXT FROM user_cursor INTO #user_name
WHILE ##FETCH_STATUS = 0
BEGIN
set #pattern_start = '<Policy><GroupUserName>' + #user_name + '</GroupUserName><GroupUserId>'
set #pattern_end = '</Policy>'
DECLARE secdata_cursor CURSOR
FOR
select SecDataID
from
[ReportServer].[dbo].SecData as sec
where
sec.XmlDescription like '%' + #pattern_start + '%'
OPEN secdata_cursor;
FETCH NEXT FROM secdata_cursor INTO #sec_data_id
WHILE ##FETCH_STATUS = 0
BEGIN
select
#start_position = PATINDEX ( '%' + #pattern_start + '%' , XmlDescription ) ,
#end_position = CHARINDEX(#pattern_end, cast(XmlDescription as nvarchar(max)), #start_position),
#result = SUBSTRING ( XmlDescription , #start_position , #end_position+len(#pattern_end)-#start_position )
from [ReportServer].[dbo].SecData as sec
where SecDataID=#sec_data_id
-- replace user to empty
if #start_position > 0 and #end_position > 0 and len(#result) > 0
begin
update [ReportServer].[dbo].SecData
set XmlDescription = replace(cast(XmlDescription as nvarchar(max)),#result,'')
where SecDataID=#sec_data_id
end
FETCH NEXT FROM secdata_cursor INTO #sec_data_id
END;
CLOSE secdata_cursor;
DEALLOCATE secdata_cursor;
FETCH NEXT FROM user_cursor INTO #user_name
END;
CLOSE user_cursor;
DEALLOCATE user_cursor;
I've managed to use EXEC sp_executesql in a one off statement to do a dynamic lookup, but am unable to adjust the code to create a function since EXEC is not allowed in functions. It works in procedures and I've managed to get output via PRINT for a single lookup by using a temporary table, but really that was just me struggling to find a workaround. Ideally I'd like to be able to create a scalar-value function.
The reason that I need a dynamic lookup is because the column name is stored in another table.
Here's a quick breakdown of the tables:
Questions:
Columns: Q_Group, Q_Nbr, Question_Desc, Data_Field
Sample data: 'R3', 5, 'Do you have any allergies?', 'TXT_04'
Responses:
Columns: Order_Nbr, Q_Group, TXT_01, TXT_02, TXT_03, TXT_04, etc.
Data: 999, 'R3', 'blah', 'blah', 'blah', 'NO'
Orders will be assigned a particular set of questions 'Q_Group' and often a particular question will be the same across various different sets of questions. The problem is that when the set/groups of questions were set up, the questions may not have been added in the same order, and thus the responses go into different columns.
So here's where I'm at...
I can get 'TXT_04' from the Data_Field column in Questions and use EXEC sp_executesql to do a lookup for a single order, but am struggling to find a way to accomplish this as a function of some sort.
DECLARE #col_name VARCHAR(6)
DECLARE #sql VARCHAR(100)
SET #col_name = SELECT Data_Field FROM QUESTIONS WHERE Q_Group = 'R3'
AND Question_Desc = 'Do you have any allergies?'
SET #sql = 'SELECT ' + #col_name + ' FROM RESPONSES WHERE Order_Nbr = 999'
EXEC sp_executesql #sql
I'm just at a loss as to how this could be incorporated into a function so that I could get responses for several orders in a result set. Any workarounds possible? Maybe I'm totally off base using EXEC sp_executesql?
Thanks.
Edit...
Okay, I've changed the title to reflect that I'm going to consider this solved with a procedure instead of a function, as it ended up getting the output that I wanted. Which was a table with all of the corresponding responses.
Here's the code that I settled on. I decided to use LIKE to match the Question_Desc instead of equals, and then included the Question_Desc in the results, so that it could be used a bit more broadly. Thankfully it's pretty quick to run currently. Although that could always change as the database grows!
CREATE PROCEDURE get_all_responses (#question_txt VARCHAR(255))
AS
DECLARE #response_col VARCHAR(35)
DECLARE #t TABLE (order_nbr int, question_txt VARCHAR(255), response_col VARCHAR(35), response VARCHAR(255))
DECLARE #i TABLE (id INT PRIMARY KEY IDENTITY(1,1), response_col VARCHAR(35))
DECLARE #u TABLE (order_nbr int, response VARCHAR(255))
DECLARE #sql VARCHAR(200)
INSERT #t
SELECT Order_Nbr, Question_Desc, Data_Field, NULL
FROM Responses
JOIN (
SELECT Q_Group, Question_Desc, Data_Field
FROM Questions
WHERE Question_Desc LIKE #question_txt
) #Q ON Q_Group = #Q.Q_Group
WHERE Q_Group <> '0'
ORDER BY Data_Field, Order_Nbr
-- Stop if no results found and return empty result set
IF (SELECT COUNT(*) FROM #t) = 0
BEGIN
SELECT order_nbr, question_txt, response FROM #t
RETURN
END
INSERT #i SELECT response_col FROM #t GROUP BY response_col
DECLARE #row_nbr int
DECLARE #last_row int
SET #row_nbr = 1
SET #last_row = (SELECT COUNT(*) FROM #i)
-- Iterate through each Data_Field found
WHILE #row_nbr <= #last_row
BEGIN
SET #response_col = (SELECT response_col FROM #i WHERE id = #row_nbr)
SET #sql = 'SELECT Order_Nbr, ' + #response_col + ' FROM Responses WHERE NullIf(' + #response_col + ','''') IS NOT NULL'
INSERT INTO #u
EXEC (#sql)
UPDATE #t
SET response = y.response
FROM #t AS x
INNER JOIN #u AS y ON x.order_nbr = y.order_nbr
SET #row_nbr = #row_nbr + 1
END
-- Remove results with no responses
DELETE FROM #t WHERE response IS NULL
SELECT order_nbr, question_txt, response FROM #t
RETURN
You will not be able to execute dynamic SQL from within a function but you could do this with a stored procedure and capture the output.
DECLARE #col_name VARCHAR(6), #param NVARCHAR(50), #myReturnValue VARCHAR(50)
SET #param = N'#result VARCHAR(50) OUTPUT'
DECLARE #sql VARCHAR(100)
SET #col_name = SELECT Data_Field FROM QUESTIONS WHERE Q_Group = 'R3'
AND Question_Desc = 'Do you have any allergies?'
SET #sql = 'SELECT #result = ' + #col_name + ' FROM RESPONSES WHERE Order_Nbr = 999'
EXEC sp_executesql #sql, #param, #result = #myReturnValue output
--manipulate value here
print #myReturnValue
You could also create a temp table and do an insert into from exec sp_executesql.
I have a userid table
UserId
JHOSMI
KALVIE
etc...
What I would like to do is create a select statement and pass user id, if the userid already exists then append 1 to the id, This gets complicated if you already have JHOSMI, JHOSMI1, then I want to return JHOSMI2.
Really appreciate help here.
Thanks in advance
edited 21-Jul
this is what i got so far.. but not working the way
select #p AS StaffID,
#old_p := #p,
#Cnt := #Cnt+1 As Lvl,
(SELECT #p :=Concat(#i, #Cnt)
FROM departmenttaff
WHERE upper(trim(UserId)) = upper(trim(StaffID))
AND upper(trim(department)) like upper(trim('SERVICE'))
) AS dummy
FROM (
SELECT
#i := upper(trim('JOHSMI')),
#p := upper(trim('JOHSMI')),
#old_p :='',
#Cnt:=0
) vars,
departmenttaff p
WHERE #p <> #old_p
order by Lvl Desc LIMIT 1;
This will do exactly what you want. You will need a unique constraint on your column.
You might also need to add in error code if success = 0.
This is in MSSQL, you will need to add the relevant commands for MySQL. I do not have MySQL so I cannot test it.
NOTE: You can replace the try catch with some IF EXISTS logic. I just prefer the try catch because its more stable for multiple threads.
begin tran
select * from #tmp
declare #success bit
declare #name varchar(50)
declare #newname varchar(50)
declare #nextid int
declare #attempts int
set #name = 'brad2something'
set #success = 0
set #attempts = 0
while #success = 0 and #attempts < 5 begin
begin try
set #attempts = #attempts + 1 -- failsafe
set #newname = #name
if exists (select * from #tmp where username = #name) begin
select #nextid = isnull(max(convert(int, substring(username, LEN(#name) + 1, 50))), 0) + 1
from #tmp where username like #name + '%' and isnumeric(substring(username, LEN(#name) + 1, 50)) = 1
set #newname = #name + CONVERT(varchar(20), #nextid)
end
insert into #tmp (username) values (#newname)
set #success = 1
end try begin catch end catch
end
--insert into #tmp (username)
--select
select #success
select * from #tmp
rollback
/*
drop table #tmp
create table #tmp (
username varchar(50) not null unique
)
insert into #tmp (username)
select 'brad'
union all select 'brad1'
union all select 'brad2something5'
union all select 'brad2'
union all select 'laney'
union all select 'laney500'
*/
I noticed you want to back fill data. If you want to back fill then this will work. It is extremely inefficient but there is no way around it. There is optimizing code you can put in for when an "error" occurs to prevent all previous counts from happening, but this will work.
begin tran
select * from #tmp
declare #success bit
declare #name varchar(50)
declare #newname varchar(50)
declare #nextid int
declare #attempts int
set #name = 'laney'
set #success = 0
set #attempts = 0
set #nextid = 1
while #success = 0 and #attempts < 5 begin
begin try
if exists (select * from #tmp where username = #name) begin
set #newname = #name + CONVERT(varchar(20), #nextid)
while exists (select * from #tmp where username = #newname) begin
set #nextid = #nextid + 1
set #newname = #name + CONVERT(varchar(20), #nextid)
end
end else
set #newname = #name
set #attempts = #attempts + 1 -- failsafe
insert into #tmp (username) values (#newname)
set #success = 1
end try begin catch end catch
end
--insert into #tmp (username)
--select
select #success
select * from #tmp
rollback
/*
drop table #tmp
create table #tmp (
username varchar(50) not null unique
)
insert into #tmp (username)
select 'brad'
union all select 'brad1'
union all select 'brad2something5'
union all select 'brad2'
union all select 'laney'
union all select 'laney500'
*/
Is it mandatory to have the count in same column? its better to have it in a different integer column. Anyways, if this is the requirement then select userid from table where userid like 'JHOSMI%', then do extract the number using mysql substr function.
For other people who might find this, here's a version in PostgreSQL:
create or replace function uniquify_username(varchar) returns varchar as $$
select $1 || coalesce((max(num) + 1)::varchar, '')
from
(select
substring(name, '^(.*?)[0-9]*$') as prefix,
coalesce(substring(name, '.*([0-9]+)$'), '0')::integer as num
from user1) users
where prefix = $1
$$ LANGUAGE sql;
I think it could be adapted to MySQL (though probably not as a stored procedure) but I don't have a MySQL server handy to do the conversion on.
Put a UNIQUE constraint on the column.
You didn't say what language you are using, so use this pseudo code
counter = 0
finished = false
while finished = false
{
try
{
if counter >= 1 then name = name + counter
counter = counter + 1
insert into table (name)
}
}
This code is extremely finicky. But will get the job done and there is no real other way to do this except for in sql, and you will always have some type of try catch to avoid two processes running at the same time. This way you use the unique key constraint to force the error, and supress it because it is expected.
I in no way condone using try/catch for business logic like this, but you are putting yourself in a situation thats unavoidable. I would say put the ID in a seperate column and make a unique constraint on both fields.
Proper solution:
Columns: Name, ID, Display Name
Unique constraint on: Name, ID
Display Name is a computed column (virtual) is Name + ID
If you do it this way, then all you have to do is INSERT INTO table (name, (select max() from table))
We have an stored procedure that we created so that user can write comma separated search tags in their software product's admin. So he can add comma-separated tags and in case if he wants to edit them, we read from the table all the tags, recreate them as comma-separated values (CSV) in stored procedure and returns that to the calling code. What happened recently, the user complained that he could not see the new CSVs he wrote. I looked into it and found out that the stored procedure is truncating the string when it reads values from database and creates CSV string. The string is of type nvarchar, and because its exceeding the max characters of 4000 limit, the values gets truncated. Any ideas on how to work out that problem.
Find my code underneath.
BEGIN
BEGIN
Declare #Synonyms Table
(
RowID int Identity(1,1),
SynonymID int,
[Synonym] nvarchar(4000)
);
SET NOCOUNT ON;
Insert #Synonyms(SynonymID, [Synonym])
Select distinct SynonymID, [Synonym] From RF_SearchSynonyms with(nolock) Where SearchTermID = #SearchTermID And ActiveInd = 1
If((Select COUNT(RowID) From #Synonyms) <> 0)
BEGIN
Declare #CurrentRow int = (Select MIN(RowID) From #Synonyms),
#TotalRows int = (Select MAX(RowID) From #Synonyms),
#Synonyms_CSV nvarchar(4000) = '';
WHILE #CurrentRow <= #TotalRows
BEGIN
Declare #TempSyn nvarchar(500);
Select #TempSyn = [Synonym] + ',' From #Synonyms Where RowID = #CurrentRow;
Set #Synonyms_CSV = #Synonyms_CSV + LTRIM(RTRIM(LOWER(#TempSyn)));
SET #CurrentRow = #CurrentRow + 1
END
END
Else
BEGIN
Set #Synonyms_CSV = '';
END
END
BEGIN
Declare #SKUs Table
(
RowID int Identity(1,1),
SkuID int,
SKU nvarchar(15)
);
SET NOCOUNT ON;
Insert #SKUs(SkuID, SKU)
Select distinct SkuID, SKU From RF_SearchSkus with(nolock) Where SearchTermID = #SearchTermID And ActiveInd = 1
If((Select COUNT(RowID) From #SKUs) <> 0)
BEGIN
Declare #CurrentRow1 int = (Select MIN(RowID) From #SKUs),
#TotalRows1 int = (Select MAX(RowID) From #SKUs),
#Skus_CSV nvarchar(4000) = '';
WHILE #CurrentRow1 <= #TotalRows1
BEGIN
Declare #TempSku nvarchar(15);
Select #TempSku = SKU + ',' From #SKUs Where RowID = #CurrentRow1;
Set #Skus_CSV = #Skus_CSV + LTRIM(RTRIM(#TempSku));
SET #CurrentRow1 = #CurrentRow1 + 1
END
END
Else
BEGIN
Set #Skus_CSV = '';
END
END
BEGIN
Declare #Combined varchar(8000),
#syn_len int = 0,
#sku_len int = 0;
Select #syn_len = LEN(#Synonyms_CSV);
Select #sku_len = LEN(#Skus_CSV);
Select #Combined = #Synonyms_CSV + '-_-' + #Skus_CSV;
Select #Synonyms_CSV + '-_-' + #Skus_CSV;
END
END
I can't use text and ntext as they do not play nice with concatenation operations.
Thanks.
How are your declaring the string parameter?
nvarchar(max)
supports up to 2^32-1 (2GB)
See this link.