First record in SQL Cursor missing a variable - sql-server

I am sending a newsletter in SQL server using a cursor. All is working fine except the first email has no html.
Here is the stored procedure:.....
DECLARE
#html varchar(max)
SET #html = (SELECT html from NewsLetter where nLID=#nLID)
DECLARE crsEmailList CURSOR FOR
SELECT email, ListID from lists where category=#Category AND (DLC < DATEADD(DAY, -1,GETDATE()) OR DLC IS NULL)
OPEN crsEmailList
FETCH NEXT FROM crsEmailList INTO #email, #ListID
while ##FETCH_STATUS = 0
BEGIN
DECLARE #UniqueKey varchar(20),
#UnSubscribeURL varchar(200),
#ClickURL varchar(200)
SET #UnSubscribeURL='<a href=''http://.../userfiles/OHP/UnSubscribe.aspx?listID=' + convert(varchar, #ListID) + '''>Unsubscribe</a>'
SET #ClickURL='<a href=''http://.../userfiles/OHP/clicked.aspx?Key=' + convert(varchar, #UniqueKey ) + '&URL='
EXEC [register_system_email_audits] #ListID, #email, #Date, #UniqueKey output
SET #html = (SELECT html from NewsLetter where nLID=#nLID)
SET #html = Replace(#html,'[keyvalue]', #UniqueKey)
SET #html = Replace(#html,'<a href=\''',#ClickURL)
SET #html = Replace(#html,'[UnSubscribe]', #UnSubscribeURL )
SET #html = Replace(#html,'[date]', DATENAME(month, getdate()) )
EXEC msdb.dbo.sp_send_dbmail
#profile_Name ='Local Server',
#recipients= #email ,
#subject = #Subject,
#body = #html,
#body_format='HTML'
FETCH NEXT FROM crsEmailList INTO #email, #ListID
END
I have tried moving the line SET #html = (SELECT html from NewsLetter where nLID=#nLID) to different locations but no positive results.

One very likely explanation is that one of the parameters you are plugging into the html string is NULL for the first iteration.
When I run this code:
DECLARE #Str varchar(max) = 'Hello [test]';
DECLARE #test varchar(max) = NULL;
SET #Str = REPLACE(#Str, '[test]', #test);
PRINT #Str;
I don't get 'Hello ' as a result. I get nothing. So apparently using REPLACE to plug a NULL value into a string has the same effect as concatenating the string with NULL: which is, it makes the whole thing NULL.
Try using ISNULL on each of the variables in your REPLACE statements, and see if that doesn't fix the problem.
SET #html = Replace(#html,'[keyvalue]', ISNULL(#UniqueKey,''))
Or alternately, do the NULL-handling further upstream in the proc, but make sure you can't be trying to replace a token with NULL.

Related

SQL Server - adding nvarchar variable to email with html formatting

I just can't see what I'm doing wrong but it's probably a simple issue.
I use sp_send_dbmail to send an email notification to users. The body of the email is formatted with html.
When I do tests, the body is blank. I've narrowed it down to the #Name variable. If I add a simple string like 'John' then the body is not blank, but I'm doing something wrong with the #Name variable.
I would think the CAST of #Name is unnecessary, but I've just been trying different things to no avail.
How do I incorporate #Name in a correct way?
Below is the code with parts of the irrelevant html left out to shorten it:
Alter Proc [dbo].[SendPayslipEmail]
(#TVP EmailAddressTableType READONLY,
#Period NVARCHAR(50),
#Message NVARCHAR(MAX))
as
begin
DECLARE #ResTag INT
DECLARE #Email_Address VARCHAR(150)
DECLARE #emailHTML NVARCHAR(MAX) ;
DECLARE #Subject NVARCHAR(50);
DECLARE #Name NVARCHAR(50);
DECLARE #LoginURL NVARCHAR(50);
SET #Subject = (Select [Value] from [CustomerEmailSettings] where [Setting] = 'Payslip Email Subject')
SET #LoginURL = (Select [Value] from [CustomerEmailSettings] where [Setting] = 'LoginURL')
DECLARE cursorSendEmail CURSOR
FOR SELECT [Resource Tag],[E-mail Address] FROM #TVP;
OPEN cursorSendEmail
FETCH NEXT FROM cursorSendEmail INTO #ResTag,#Email_Address;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #Name = CAST((SELECT [Display Name] FROM DisplayName(#ResTag)) AS NVARCHAR(50))
SET #emailHTML =
N'<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">'
+N'<head>'
<!-- More html -->
+N'<p class=MsoNormal><b><span style=''color:#4485B8''>Dear '
+#Name
+N',<o:p></o:p></span></b></p>'
<!-- More html -->
+N'</html>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'PayslipEmails',
#recipients = #Email_Address,
#body = #emailHTML,
#body_format = 'HTML',
#subject = #Subject;
FETCH NEXT FROM cursorSendEmail INTO #ResTag,#Email_Address;
END
CLOSE cursorSendEmail
DEALLOCATE cursorSendEmail
end

SQL Server - BCP utility is not exporting multiple files

I'm trying to export some data to XML files on a specific location, from SQL, using BCP utility. The information within my stored procedure that is generating the XML are selected through a view which returns the following information:
GUID IDHeader IDLines
-------------------------------------------------------------
A5AF1E8B-C743-4B26-979E-9B83B0037253 GEA-00008 GEA-00008
A5AF1E8B-C743-4B26-979E-9B83B0037253 GEA-00008 GEA-00008
AA4919C5-8050-4DD3-A463-F04BC0AE3F72 GEA-00011 GEA-00011
AA4919C5-8050-4DD3-A463-F04BC0AE3F72 GEA-00011 GEA-00011
5F4E196C-A360-48B7-8127-3364C7268706 GEA-00012 GEA-00012
5F4E196C-A360-48B7-8127-3364C7268706 GEA-00012 GEA-00012
F0CD1949-F8E0-4A74-B273-E9F285B844DB GEA-00013 GEA-00013
F0CD1949-F8E0-4A74-B273-E9F285B844DB GEA-00013 GEA-00013
Based on the information above, I should have 4 XML files generated, but somehow only 1 file is generated and I do not understand why.
Part of my code looks like this:
BEGIN
DECLARE #XMLFinal varchar(MAX) = '';
DECLARE #nr nvarchar(100);
DECLARE #XML varchar(MAX);
DECLARE Records CURSOR FAST_FORWARD FOR
SELECT DISTINCT
GUID
FROM MyView;
OPEN Records;
FETCH NEXT FROM Records
INTO #nr;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #XML = '';
SET #XML = (SELECT TOP 1
IDHeader,
IDLines
FROM MyView
WHERE GUID = #nr
FOR XML PATH(''));
FETCH NEXT FROM Records
INTO #nr;
SET #XMLFinal = #XMLFinal + #XML;
END;
CLOSE Records;
DEALLOCATE Records;
IF #XMLFinal IS NOT NULL
AND NOT #XMLFinal LIKE '%ORD%'
BEGIN
INSERT INTO ExportOrder (XML,
Sent)
SELECT #XMLFinal,
'0';
DECLARE #XMLExport varchar(MAX);
DECLARE #Name varchar(MAX);
DECLARE #ID nvarchar(MAX);
SET #ID = (SELECT TOP 1 IDHeader FROM MyView WHERE GUID = #nr);
SET #Name = 'Order-' + #ID + '_' + FORMAT(GETDATE(), 'yyyyMMddHHmmss') + '.xml';
SET #XMLExport = #XML;
IF OBJECT_ID('tempdb..##x') IS NOT NULL
DROP TABLE ##x;
SELECT #XMLExport AS x
INTO ##x;
DECLARE #cmd nvarchar(4000);
DECLARE #pathAndFileName nvarchar(MAX);
DECLARE #result int;
SET #pathAndFileName = N'C:\MyFolder\' + #Name;
SET #cmd = N'C:\Binn\bcp.exe "SELECT * from ##x" queryout "' + #pathAndFileName + N'" -c -T -t; ';
EXEC #result = xp_cmdshell #cmd;
END;
END;
What am I doing wrong here? What should I do to generate all 4 files? Thanks

Getting Empty Output

Iam trying to Execute Dynamic Query, But Iam getting Empty Output.Where I'm Wrong?
SET #SQL=N'
SELECT GETDATE(),'+#AMUID+','+ #BNO +
',LOT,BARCODEID,'+#ACTWT+',TARE_QUANTITY,''NA'',STAGE,0,0,0,
'+ #UNAME+ ','+#PR + ',GETDATE()
FROM DISPENSE_HOLD_START WHERE BATCH_NO='''+#BNO+''' AND BARCODEID='''+
#BARID +''' and IS_OUT=0';
PRINT(#SQL)
Here #AMUID,#BNO,#ACTWT etc are Input Parameters Declared with NVARCHAR(MAX) data type.They are dynamic Iam getting nothing When I Print this.
as you are not executing the query but printing it
this is same as
3+3=6 and "3"+"3"="33"
the dynamic queries are typically executed like this(simplest method)
DECLARE #IntVariable int;
DECLARE #SQLString nvarchar(500);
DECLARE #ParmDefinition nvarchar(500);
/* Build the SQL string one time.*/
SET #SQLString =
N'SELECT BusinessEntityID, NationalIDNumber, JobTitle, LoginID
FROM AdventureWorks2012.HumanResources.Employee
WHERE BusinessEntityID = #BusinessEntityID';
SET #ParmDefinition = N'#BusinessEntityID tinyint';
/* Execute the string with the first parameter value. */
SET #IntVariable = 197;
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#BusinessEntityID = #IntVariable;
/* Execute the same string with the second parameter value. */
SET #IntVariable = 109;
EXECUTE sp_executesql #SQLString, #ParmDefinition,
#BusinessEntityID = #IntVariable;
Case#1 :
In your Above Query, You are using PRINT instead of EXEC. So Make sure you are executing the Query in your actual Code
EXEC(#SQL)
Case#2 :
If you are executing this, there is a possibility that if any of the Parameter Value is NULL, then the entire String will become NULL
If you add any Value to NULL the Result will be NULL
SELECT
'ABC',
'ABC'+NULL
Gives Me this
So Use ISNULL
SET #SQL = N'
SELECT
GETDATE(),
'+ISNULL(#AMUID,'')+','+ISNULL(#BNO,'')+',LOT,
BARCODEID,'+ISNULL(#ACTWT,'')+',TARE_QUANTITY,''NA'',STAGE,0,0,0,
'+ISNULL(#UNAME,'')+','+ISNULL(#PR,'')+',GETDATE()
FROM DISPENSE_HOLD_START
WHERE BATCH_NO='''+ISNULL(#BNO,'')+'''
AND BARCODEID='''+ISNULL(#BARID,'')+'''
and IS_OUT=0';
EXEC(#SQL);
Case#3
Looking at your Code, I think you don't need a Dynamic SQL here. Instead, you can directly go Like this
SELECT
GETDATE(),
#AMUID,
#BNO,
LOT,
BARCODEID,
#ACTWT,
TARE_QUANTITY,
'NA',
STAGE,
0,
0,
0,
#UNAME,
#PR,
GETDATE()
FROM DISPENSE_HOLD_START
WHERE BATCH_NO = #BNO
AND BARCODEID = #BARID
AND IS_OUT = 0
Works just fine for me
declare #val nvarchar(100) = 'tommy can you see me'
declare #SQL nvarchar(100) = N'SELECT GETDATE(), ''' + #val + '''';
PRINT(#SQL);

sql server 2012: How do I deal with varbinary for #query variable in send_dbmail?

My intent is to monitor the cdc process through a stored proc managed by a sql agent job. I am looking to see if I have captured any data since the last time the job executed. The IntervalHours represents how many hours since the last batch ran. Here is the code snippet that is the setup for the notify/not-notify decision:
declare #FromLSN binary(10)
declare #ToLSN binary(10)
declare #BeginTime datetime
declare #EndTime datetime
declare #hasCurrentChanges int
declare #SendLowFloorMessage bit
select #BeginTime = dateadd(hh,-#IntervalHours,getdate())
SET #EndTime = GETDATE();
-- Map the time interval to a change data capture query range.
SET #FromLSN = [myInstance].sys.fn_cdc_map_time_to_lsn('smallest greater
than or equal', #BeginTime);
SET #ToLSN = [myInstance].sys.fn_cdc_map_time_to_lsn('largest less than
or equal', #EndTime);
-- Return the count of the net changes occurring within the query window.
SELECT #hasCurrentChanges = count(*) FROM
[myInstance].cdc.fn_cdc_get_net_changes_dbo_CRM_POSTran(#FromLSN, #ToLSN,
'all');
-- Here is the decision --
IF isnull(#hasCurrentChanges,0) <= #LowFloor
begin
set #SendLowFloorMessage = 1;
end
-- Here is the notification. This is where I would need the #qry to dub the value of #FromLSN and #ToLSN into the text of the query so it can execute. What do I need to cast the value to in order for this to succeed?
DECLARE #bdy nvarchar(1000);
DECLARE #sbj nvarchar(50)
DECLARE #MailRecipients VARCHAR(50)
DECLARE #qry nvarchar(max)
SET #MailRecipients = 'paula.ditallo#gmail.com'
--Send email with results of long-running jobs
EXEC msdb.dbo.sp_send_dbmail
#profile_name = #mailProfile
,#recipients = #MailRecipients
,#query = #qry
,#execute_query_database = 'InternalResource'
,#body = #bdy
,#subject = #sbj
,#attach_query_result_as_file = 1;
I am assuming that you have a good handle on sp_send_dbmail and just do not know how to create a dynamic TSQL for the #query using the varbinary data types. Here is one way to convert the data using the XML DOM.
Here is a reference to my website showing how to send an email with an attachment for 2008 R2.
http://craftydba.com/?p=1025.
This is almost the same for 2012 - Books On Line.
http://technet.microsoft.com/en-us/library/ms190307.aspx.
The sample code below takes two hex number and converts them into a call to the cdc.fn_cdc_get_net_changes_.
http://msdn.microsoft.com/en-us/library/bb522511.aspx
-- Sample data
DECLARE #from_lsn VARBINARY(10);
SET #from_lsn = 0x5BAA61E4C9B93F3F0682;
DECLARE #to_lsn VARBINARY(10);
SET #to_lsn = 0x5BAA61E4C9B93F3F0782;
-- Display the results
DECLARE #tsql VARCHAR(MAX);
SELECT #tsql =
'SELECT * FROM cdc.fn_cdc_get_net_changes_HR_Department(' +
char(39) + '0x' + CAST('' AS XML).value('xs:hexBinary(sql:variable("#from_lsn"))', 'VARCHAR(20)') + char(39) + ',' +
char(39) + '0x' + CAST('' AS XML).value('xs:hexBinary(sql:variable("#to_lsn"))', 'VARCHAR(20)') + char(39) + + ',' +
char(39) + 'all' + char(39) +');';
-- Show the data
PRINT #tsql
Here is the output from the snippet.
SELECT *
FROM cdc.fn_cdc_get_net_changes_HR_Department
('0x5BAA61E4C9B93F3F0682','0x5BAA61E4C9B93F3F0782','all');

SQL Server Substring with CharIndex used on nested XML tags

This is the stored procedure I'm using to get 5 digits from an xml:
CREATE PROCEDURE [dbo].[SP_KINGPRICE_InsertJournalFromPost]
(
#ResponseID bigint,
#TransactionDetailID bigint
)
AS
BEGIN
DECLARE #info as varchar(max) = '', #Reference as varchar(max) = ''
SET #info = (SELECT SUBSTRING(Response, CHARINDEX('<GlJournal>',Response) + 11,5)
FROM SysproIntegration..ifmTransactionDetailResponse
WHERE TransactionDetailResponseID = #ResponseID)
SET #Reference = (SELECT DISTINCT Reference
FROM GenJournalDetail
WHERE Journal = CAST(#info as DECIMAL(5,0)))
INSERT INTO ZJournalRecords
(JournalNumber,Reference)
VALUES (#info,#Reference)
END
The XML has a tag like this:
<GLJournal>12345</GLJournal>
If the XML document has only one of these tags, I have no worries. The SP works fine. The problem comes in when there are two nested <GLJournal> tags. The xml would then look something like:
<GLJournal>
<SomeTag/>
<SomeTag2/>
<GLJournal>12345</GLJournal>
</GLJournal>
How can I get the 5 digit value of the nested tag? (It will always be 5 digits)
I have thought of using a try catch, but that doesn't seem like an elegant solution.
EDIT:
Also, part of the problem is, I don't know when there will be one GlJournal tags, or two.
I decided to count the number of occurrences of the tag, and based on that, substring a certain position.
DECLARE #info as varchar(max) = '', #Reference as varchar(max) = '', #inputString as varchar(max), #searchString as varchar(max), #numberOfTags as int
SET #searchString = '<GlJournal>'
SET #inputString = (SELECT Response FROM SysproIntegration..ifmTransactionDetailResponse WHERE TransactionDetailResponseID = #ResponseID)
SET #numberOfTags = (LEN(#inputString) -
LEN(REPLACE(#inputString, #searchString, ''))) /
LEN(#searchString)
IF #numberOfTags = 1
BEGIN
SET #info = (SELECT SUBSTRING(Response, CHARINDEX('<GlJournal>',Response) + 11,5) FROM SysproIntegration..ifmTransactionDetailResponse WHERE TransactionDetailResponseID = #ResponseID)
SET #Reference = (SELECT DISTINCT Reference FROM GenJournalDetail WHERE Journal = CAST(#info as DECIMAL(5,0)))
END
ELSE
BEGIN
SET #info = (SELECT SUBSTRING(Response, CHARINDEX('<GlJournal>',Response) + 69,5) FROM SysproIntegration..ifmTransactionDetailResponse WHERE TransactionDetailResponseID = #ResponseID)
SET #Reference = (SELECT DISTINCT Reference FROM GenJournalDetail WHERE Journal = CAST(#info as DECIMAL(5,0)))
END
Here's an int test you might adapt:
declare #xml xml, #tags smallint
SET #tags = 1
-- Load #xml...
--SELECT #xml = Response
--FROM SysproIntegration..ifmTransactionDetailResponse
--WHERE TransactionDetailResponseID = #ResponseID
-- ... or for test...
IF #tags = 1 BEGIN
-- Test 1 GLJournal tag
SET #xml = '12346'
END ELSE IF #tags = 2 BEGIN
-- Test 2 GLJournal tags
SET #xml = '
12345
'
END ELSE BEGIN
-- Test no GLJournal tags
SET #xml = ''
END
DECLARE #i int; SET #i = -1
-- Try one tag
SELECT #i = ParamValues.ID.query('GLJournal').value('.','int')
FROM #xml.nodes('/') as ParamValues(ID)
IF #i = 0
-- Try two tags
SELECT #i = ParamValues.ID.query('GLJournal').value('.','int')
FROM #xml.nodes('/GLJournal') as ParamValues(ID)
SELECT #i
-- INSERT INTO ZJournalRecords... other stuff

Resources