Stored Procedure to Email multiple recipients from query - tsql

I've created a query that pulls info and emails in the way that I need it to be presented to our clients. How can I turn this into a stored procedure fed by a query (query A)? I need it to run for each unique set of cmp_code and cmp_e_mail it returns. So for example if QueryA returns the following, I need the email query to run for each of them individually.
C0001 email1#asdf.com
C0002 email2#asdf.com
C0003 email3#asdf.com
QueryA:
SELECT DISTINCT
[cmp_code]
,[cmp_e_mail]
FROM Table1
Query to email:
DECLARE #email nvarchar(50)
DECLARE #cmp_code nvarchar (5)
DECLARE #profile nvarchar(50)
DECLARE #subject nvarchar(100)
DECLARE #querystr nvarchar (MAX)
set #email = QueryA.[cmp_e_mail]
set #cmp_code = QueryA.[cmp_code]
set #profile = 'Reports'
set #subject = 'Test'+#cmp_code
set #querystr = 'SELECT [Year],[Week],[DueDate]
FROM Table1
WHERE [cmp_code] = '''+#cmp_code+'''';
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #profile,
#recipients = #email,
#subject = #subject,
#body = 'This message is to inform you that we have not received your financial report for the following weeks.
Please remit as soon as possible and pay by the due date listed.',
#query = #querystr

try create a stored procedure like this one below that loops through the table and then calls another stored procedure passing in the data , be sure to deallocate and close the cursor at the end
Declare #Code nvarchar(50)
Declare #EmailAddress nvarchar(Max)
Declare dbCurSP Cursor
For SELECT DISTINCT [cmp_code] FROM Table1
Open dbCurSP
Fetch Next From dbCurSP Into #Code
While ##fetch_status = 0
Begin
-- find email address
SELECT #EmailAddress= [cmp_e_mail] FROM Table1 where [cmp_code]=#Code
execute SP_SendEmail #EmailAddress, #Code
Fetch Next From dbCurSP Into #Code
End
Close dbCurSP
Deallocate dbCurSP

Related

Value not Store in Dynamic SQL

I've different different tables to categorically store data and a log table where all the transactions log are recorded
e.g. 1) VoucherNO, Add, ...
2) VoucherNO, Delete, ..
After I backup the database and restore in another server for my Reporting Purpose. That time I want to ensure all the log data and transaction are available in TestDB if not then I remove log from 'AUD_USER_ACTIVITY'.
To find the transaction exist or not, I create a dynamic sql select statement and check whether record is exist or not.
Basis on #RecExist Value I do the action like if records is not available in TestDB the log will be remove, if record exist immediately break this loop and going for next procedure
But #RecExist variable is not updating in Dynamic SQL Execution. Please guide me
declare #MvDocNo varchar(50)
DECLARE #SCtr as DECIMAL(10,0)
declare #LocationCode varchar(4)
declare #UName Nvarchar(40)
declare #toe varchar(30)
declare #QryTxt as nvarchar(MAX);
Declare #RecExist as INT =0;
SET #RecExist=0
WHILE #RecExist=0
BEGIN
select top 1 #MvDocNo=DOCNO, #SCtr=SrlNo,#LocationCode =DMLTYPE,#UName=TABLENAME
FROM R_AUDDB..AUD_USER_ACTIVITY
WHERE DBNAME='TestDB' and DMLTYPE not in ('AD','D','PD') ORDER BY SRLNO DESC;
select top 1 #toe=docno from TestDB..M_TYPEOFENTRY where TBLNAME=#UName;
set #QryTxt='Select #RecExist=1 From R_TestDB..'+#UName+ ' Where '+#toe+'='''+#MvDocNo+''''
exec (#QryTxt)
IF #RecExist=0
BEGIN
DELETE R_AUDDB..AUD_USER_ACTIVITY WHERE SRLNO=#SCtr
END
END
The following code sample demonstrates how to check for a row in a table with a specific column and value using dynamic SQL. You ought to be able to change the values of the first three variables to reference a table and column in your database for testing.
Note that SQL injection is still possible: there is no validation of the table or column names.
-- Define the table to check and the target column name and value.
declare #TableName as SysName = 'Things';
declare #ColumnName as SysName = 'ThingName';
declare #TestValue as NVarChar(32) = 'Beth';
-- Create a SQL statement to check for a row in the target table with the specified column name and value.
declare #SQL as NVarChar(1024);
declare #Result as Bit;
-- Note that only object names are substituted into the statement at this point and QuoteName() is used to reduce problems.
set #SQL = N'select #iResult = case when exists ( select 42 from dbo.' + QuoteName( #TableName ) +
N' where ' + QuoteName( #ColumnName ) + N' = #iTestValue ) then 1 else 0 end;'
select #SQL as SQL;
-- Execute the SQL statement.
-- Note that parameters are used for all values, i.e. the target value and return value.
execute sp_executesql #stmt = #SQL,
#params = N'#iTestValue NVarChar(32), #iResult Bit output',
#iTestValue = #TestValue, #iResult = #Result output
-- Display the result.
select #Result as Result;

Dynamic SQL with nvarchar(max) variable (again)

I have read about a dozen articles here and I am still stumped with this issue.
I am building a dynamic select statement that will update a view on a monthly schedule.
set ansi_nulls on
go
set quoted_identifier on
go
alter procedure [dbo].[Proc_Name_SP]
as
begin
set nocount on
set quoted_identifier off
declare #dbname varchar(10), #schema_id int, #schema_name varchar(10),
#jacro varchar(10), #rec_cnt int, #tot_rec int
declare #SQL_Main nvarchar(max), #SQL_Final nvarchar(max),
#SQL_schema nvarchar(2000), #SQL_Union nvarchar(max)
declare iteration cursor global static for
-- Begin statement for cursor array
select distinct db, code
from linkedserver.db.schema.Directory
where current_stage = 'live'
order by db
-- End statement for cursor array
-- get total number of cursor iterations to know when to stop
-- "union" statements
select #tot_rec = count(*) from (select distinct db, code
from [linkedserver].db.schema.Directory
where current_stage = 'live') as cur
-- begin loop
open iteration
fetch first from iteration into #dbname, #jacro
while ##fetch_status=0
begin
-- the schema used is not consistent. Because of the linked server it was
-- necessary to get the Schema_ID from the sys.tables and then pull the
-- schema name from sys.schema
set #SQL_schema = 'select #sch_id = schema_id from [linkedserver].'+#dbname+'.sys.tables where name = ''Manuscript'''
execute sp_executesql #SQL_schema, N'#sch_id int OUTPUT', #sch_id = #schema_id output
--print #schema_id
set #SQL_schema ='select #sch_name = name from [linkedserver].'+#dbname+'.sys.schemas where schema_id = '+cast(#schema_id as varchar)+''
execute sp_executesql #SQL_schema, N'#sch_name nvarchar(10) OUTPUT', #sch_name = #schema_name output
--print #schema_name
--building Select statement
set #SQL_Main ='
select jcode.Code as BILLING_ACRO
,s.start_dt as BILLING_DATE
,cmpt_ms_nm as MANUSCRIPT
,isnull(jcode.billing_type, ''reviewed'') as Billing_type
from [linkedserver].'+#dbname+'.'+#schema_name+'.Manuscript as m
join [linkedserver].'+#dbname+'.'+#schema_name+'.Step as s on m.ms_id = s.ms_id and m.ms_rev_no = s.ms_rev_no
join (select j_id, Code, billing_type from [linkedserver].db.schema.Directory where db = '''+#dbname+''') as jcode on jcode.j_id = m.j_id
where jcode.Code = '''+#jacro+'''
and m.ms_rev_no = 0
and s.stage_id = 190
and isnull(cmpt_ms_nm, '''') <> ''''
and s.step_id = (select min(s2.step_id)
from [linkedserver].'+#dbname+'.'+#schema_name+'.Step as s2
where s2.stage_id = 190
and s2.ms_id = m.ms_id
and s2.ms_rev_no = m.ms_rev_no)
'
set #rec_cnt = isnull(#rec_cnt, 0) + 1
if #SQL_Union is null
begin
set #SQL_Union = #SQL_Main
end
else if #tot_rec <> #rec_cnt
begin
set #SQL_Union = #SQL_Union + ' union ' + #SQL_Main
end
else
begin
set #SQL_Union = #SQL_Union + #SQL_Main
end
--print #rec_cnt
fetch next from iteration into #dbname, #jacro --next database
end -- while ##FETCH_STATUS=0
close iteration
deallocate iteration
-- build new view
print len(#SQL_Union)
set #SQL_Final = '
ALTER VIEW [dbo].[View_Name_VW]
AS
'+#SQL_Union+'
'
execute sp_executesql #SQL_Final
--grab string variables to table for troubleshooting
insert into Output_SQL(SQL_Final, SQL_Final_Len, SQL_Union, SQL_Union_Len)
select #SQL_Final, LEN(#SQL_Final), #SQL_Union, LEN(#SQL_Union)
set nocount off
end
go
I have read that others have had problems with this type of truncation and I have tried multiple suggestions but in the end the I am getting capped at 68274 in this code with nvarchar(max). For troubleshooting, I am saving the results of the variables and the len of these variables to a table to eliminate the SSMS cap on the display of strings.
I have tried cast(#varible as nvarchar(max)) on the right side of the = sign. I have changed the data type lengths (as the select that is being built is not that large, it is just large after it has been union for each unique customer)
I am open to any suggestions as I have tried many variations of datatype declarations for these variables.

two table input parameters in stored procedure

I am working on C# project which needs a stored procedure which will take two table names as inputs.
First table will copy data to a temp table which has two columns URL & channelID. This URL column is then matched with other input table's URL column & if match is found then it will update channel id from temp table to other tables channel ID.
I have written stored procedure as
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
END
While EXISTS(SELECT * From #Temp ) > 0
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channelid
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channelid
End
I don't have much knowledge in TSQL and my above code has errors.
Incorrect syntax near '>'.
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 20
Must declare the scalar variable "#channelid".
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 22
Must declare the scalar variable "#channelid".
Please suggest what changes needs to done
I don't have MS SQL server handy to test it, but you declare your variable as #channel_Id, and later try to use it as #channelid (without the underscore) so you get errors about the undeclared variable.
I've corrected your SP and this is how it should look
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
While EXISTS(SELECT * From #Temp )
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channel_Id
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channel_Id
End
END

Union select statements within the while loop T-SQL

I'm trying to use cursors to dynamically produce a result set. following is the code
DECLARE # MilestoneName VARCHAR(100),
#MilestoneSts VARCHAR(100),
#ProjectPre VARCHAR(10),
#ProjectID VARCHAR(10),
#Center VARCHAR(20),
#CenterPre VARCHAR(20),
#Source VARCHAR(20),
#Actual INT;
SET #MilestoneName = null;
SET #MilestoneSts = null;
SET #ProjectPre = null;
SET #CenterPre = null;
DECLARE s_cursor CURSOR FOR
SELECT ProjectID, Center, Source, Actual
FROM #MILESTONE
OPEN s_cursor
FETCH NEXT FROM s_cursor INTO #ProjectID, #Center, #Source, #Actual
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT ##FETCH_STATUS sts, #ProjectID PID, #Center Center, #Source Source, #Actual Actual
FETCH NEXT FROM s_cursor INTO #ProjectID, #Center, #Source, #Actual
END
CLOSE s_cursor
DEALLOCATE s_cursor
However using that I'm able to produce 79 results of single rows but I want to union all those rows into one result.. any possible solution will be highly appreciated..
just checking, why are you using a cursor for this?
This sproc could be replaced by just saying
SELECT ProjectID, Center, Source, Actual
FROM #MILESTONE
But maybe I'm missing somehting here?
If there's logic you left out in your code look at this post: Multi-statement Table Valued Function vs Inline Table Valued Function
GJ

Array-like access to variables in T-SQL

In my stored procedure I have multiple similar variables #V1, #V2 ... #V20 (let's say 20 of them) FETCHED from a record. How would I use dynamic SQL to make 20 calls to another stored procedure using those variables as parameters?
Of course #V[i] syntax is incorrect, but it expresses the intent
fetch next from maincursor into #status, #V1, #V2, ...
while #i<21
begin
-- ??? execute sp_executesql 'SecondSP', '#myParam int', #myParam=#V[i]
-- or
-- ??? execute SecondSP #V[i]
set #i = #i+1
end
As others have said, set up a temporary table, insert the values that you need into it. Then "iterate" through it executing the necessary SQL from those values. This will allow you to have 0 to MANY values to be executed, so you don't have to set up a variable for each.
The following is a complete sample of how you may go about doing that without cursors.
SET NOCOUNT ON
DECLARE #dict TABLE (
id INT IDENTITY(1,1), -- a unique identity column for reference later
value VARCHAR(50), -- your parameter value to be passed into the procedure
executed BIT -- BIT to mark a record as being executed later
)
-- INSERT YOUR VALUES INTO #dict HERE
-- Set executed to 0 (so that the execution process will pick it up later)
-- This may be a SELECT statement into another table in your database to load the values into #dict
INSERT #dict
SELECT 'V1Value', 0 UNION ALL
SELECT 'V2Value', 0
DECLARE #currentid INT
DECLARE #currentvalue VARCHAR(50)
WHILE EXISTS(SELECT * FROM #dict WHERE executed = 0)
BEGIN
-- Get the next record to execute
SELECT
TOP 1 #currentid = id
FROM #dict
WHERE executed = 0
-- Get the parameter value
SELECT #currentvalue = value
FROM #dict
WHERE id = #currentid
-- EXECUTE THE SQL HERE
--sp_executesql 'SecondSP', '#myParam int', #myParam =
PRINT 'SecondSP ' + '#myParam int ' + '#myParam = ' + #currentvalue
-- Mark record as having been executed
UPDATE d
SET executed = 1
FROM #dict d
WHERE id = #currentid
END
Use a #TempTable
if you are at SQL Server 2005 you can create a #TempTable in the parent stored procedure, and it is available in the child stored procedure that it calls.
CREATE TABLE #TempTable
(col1 datatype
,col2 datatype
,col3 datatype
)
INSERT INTO #TempTable
(col1, col2, col3)
SELECT
col1, col2, col3
FROM ...
EXEC #ReturnCode=YourOtherProcedure
within the other procedure, you have access to #TempTable to select, delete, etc...
make that child procedure work on a set of data not on one element at a time
remember, in SQL, loops suck performance away!
Why not just use the table variable instead, and then just loop through the table getting each value.
Basically treat each row in a table as your array cell, with a table that has one column.
Just a thought. :)
This seems like an odd request - will you always have a fixed set of variables? What if the number changes from 20 to 21, and so on, are you constantly going to have to be declaring new variables?
Is it possible, instead of retrieving the values into separate variables, to return them each as individual rows and just loop through them in a cursor?
If not, and you have to use the individual variables as explained, here's one solution:
declare #V1 nvarchar(100)
set #V1 = 'hi'
declare #V2 nvarchar(100)
set #V2 = 'bye'
declare #V3 nvarchar(100)
set #V3 = 'test3'
declare #V4 nvarchar(100)
set #V4 = 'test4'
declare #V5 nvarchar(100)
set #V5 = 'end'
declare aCursor cursor for
select #V1
union select #V2 union select #V3
union select #V4 union select #V5
open aCursor
declare #V nvarchar(100)
fetch next from aCursor into #V
while ##FETCH_STATUS = 0
begin
exec TestParam #V
fetch next from aCursor into #V
end
close aCursor
deallocate aCursor
I don't really like this solution, it seems messy and unscalable. Also, as a side note - the way you phrased your question seems to be asking if there are arrays in T-SQL. By default there aren't, although a quick search on google can point you in the direction of workarounds for this if you absolutely need them.