I'm trying to send out csv files through sp_send_dbmail and one of the fields (outcome) has commas in it. When running the code below, it will automatically separate some of the outcomes as two columns.
I've tried changing #query_result_separator, but then it wouldn't separate anything at all, except for the outcome field, which eventually gives me two columns.
declare #filename NVARCHAR(MAX)
DECLARE #prefix varchar(50)
DECLARE #q varchar(max)
DECLARE #body NVARCHAR(MAX)
SET #body = 'abc'
SET #filename = concat('abc_',replace(cast(cast(getdate() as date) as varchar),'-','_'),'.csv')
SET #prefix = '[sep=,' + CHAR(13) + CHAR(10) + 'ID]'
SET #q ='set nocount on; select right(''"=""'' + cast(ID as nvarchar) + ''"""'', 24)' + #prefix + ',Outcome, isnull(cast(Outcomedate as varchar),'''') as OutcomeDate,isnull(cast(Alerts as varchar),'''') as Alerts
from database.abc.defg'
EXEC msdb.dbo.sp_send_dbmail
#body = #body,
#body_format ='HTML',
#recipients = 'abc#defg.com',
#subject = 'abc',
#query = #q ,
#attach_query_result_as_file = 1,
#query_attachment_filename = #filename,
#query_result_separator = ',',
#query_result_no_padding = 1,
#query_result_width = 500;
Related
I am trying to make a query that I can run from Python with dynamic table name and date. In the process of this, I have tried the following query in SSMS, but it is producing an error message. How can I use variables for table name and a date, and get the query to work?
DECLARE #table_name VARCHAR(50)='table_name';
DECLARE #valid_to datetime = getdate();
EXEC('UPDATE '+ #table_name + '
SET valid_flag = 0,
valid_to = '+ #valid_to +'
where valid_flag=1')
I get the following error message:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '10'
Use sp_executesql and parameters:
DECLARE #table_name VARCHAR(50) = 'table_name';
DECLARE #valid_to datetime = getdate();
DECLARE #sql NVARCHAR(max) = N'
UPDATE '+ #table_name + N'
SET valid_flag = 0,
valid_to = #valid_to
WHERE valid_flag = 1
';
EXEC sp_executesql #sql, N'#valid_to datetime', #valid_to=#valid_to;
EDIT:
As recommended by Larnu a comment:
DECLARE #table_name sysname = 'table_name';
DECLARE #valid_to datetime = getdate();
DECLARE #sql NVARCHAR(max) = N'
UPDATE '+ QUOTENAME(#table_name) + N'
SET valid_flag = 0,
valid_to = #valid_to
WHERE valid_flag = 1
';
EXEC sp_executesql #sql, N'#valid_to datetime', #valid_to=#valid_to;
I want to use sp_send_dbmail to send table.
i did that:
create PROCEDURE [dbo].[sp_ABC]
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#MailSubject nvarchar(500),
#dataTable nvarchar(max),
#HTMLcontent nvarchar(max),
#query nvarchar(max)
select *
into #T1
from my_table
SET #MailSubject = N'aaa'
SET #HTMLcontent = #HTMLcontent + #dataTable + '</Table>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'AAA',
#body_format = 'HTML',
#recipients = 'ABC#gmail.com',
#body = #HTMLcontent,
#subject = #MailSubject
Drop table #T1
END
but i didn't get any mail.
My question is how do I put the table I pulled into the email body?
Procedure only to create a HTML table:
CREATE PROCEDURE [dbo].[PRC_HTML_TABLE] (#TABLE AS NVARCHAR(MAX)=NULL OUTPUT) AS BEGIN
IF #TABLE IS NULL BEGIN
PRINT 'Create a HTML code from a table to send in mail' --Vathaire 29/07/2019
RETURN
END
IF LEFT(#TABLE, 1) != '#' BEGIN
PRINT 'Use the parameter #TABLE as the name of your table (from Tempdb) and with OUTPUT, it will return the HTML'
PRINT 'ENVIE O #TABLE COMO O NOME DA SUA TABELA (EM TEMPDB) E COM OUTPUT, IRÁ RETORNAR O HTML'
RETURN
END
DECLARE #TEMPDBDBOTABLE AS NVARCHAR(MAX) = 'TEMPDB.DBO.' + #TABLE
EXEC MF_DROP #FULLXML
CREATE TABLE #FULLXML (X NVARCHAR(MAX))
DECLARE #COLS VARCHAR(MAX) = STUFF(
(SELECT ',"' + NAME + '" = ISNULL(CONVERT(NVARCHAR(MAX), ' + NAME + '), '''')'
FROM TEMPDB.sys.columns
WHERE OBJECT_ID = OBJECT_ID(#TEMPDBDBOTABLE) FOR XML PATH('')), 1, 1, '') --ISNULL needed so it dont break the table Vathaire 06/09/2019
INSERT INTO #FULLXML
EXEC('SELECT (SELECT ' + #COLS
+ ' FROM ' + #TABLE + ' FOR XML PATH(''tr''))')
SET #COLS = STUFF((SELECT ',' + NAME FROM TEMPDB.sys.columns WHERE OBJECT_ID = OBJECT_ID(#TEMPDBDBOTABLE) FOR XML PATH('')), 1, 1, '')
DECLARE #COL AS VARCHAR(MAX)
, #E AS NVARCHAR(MAX) = '</tr></thead><tbody>' + (SELECT * FROM #FULLXML) + '</tbody></table>'
, #H AS NVARCHAR(MAX) = '<table border=1><thead><tr>'
WHILE #COLS IS NOT NULL BEGIN
EXEC MF_SPLIT #COLS OUTPUT, #COL OUTPUT
SELECT
#H = #H + '<th>' + #COL + '</th>'
, #E = REPLACE(REPLACE(#E, '<' + #COL + '>', '<td>'), '</' + #COL + '>', '</td>')
END
SET #TABLE = #H + #E
END
within this process: MF_SPLIT code
Example:
--> Create a temp table
SELECT TOP 10 *
INTO #TEMP
FROM SYS.all_objects
--> Declare a variable using the name of the table
DECLARE #MAILBODY VARCHAR(MAX) = '#TEMP'
--> Call the procedure with OUTPUT
EXEC PRC_HTML_TABLE #MAILBODY OUT
SET #MAILBODY = 'Dears,<BR><BR>This is the result:<BR><BR>' + #MAILBODY + '<BR><BR>'
EXEC MSDB..sp_send_dbmail
'[profile_name]'
,'[recipients]'
,#BODY=#MAILBODY
,#BODY_FORMAT='HTML' --> Important
There is a procedure in which an Excel file is created, the result of the selection is saved in it. This file is attached to the letter and sent by mail. But the problem is that the file is created and sent empty. Manually everything works, but not together.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[sp_sendMail]
#SID INT,
#EMAIL VARCHAR(512),
#CUR INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE
#MAILID INT,
#TABLEHTML VARCHAR(MAX),
#CMD SYSNAME,
#FILENAME NVARCHAR(MAX),
#SQL NVARCHAR(MAX),
#PID INT,
#DATE_START DATE,
#DATE_END DATE
SELECT
#PID = PATIENTS_ID,
#DATE_START = DATE_START,
#DATE_END = DATE_END
FROM
TABLE
WHERE
ID = #SID
EXEC SP_EXECUTESQL N'sp_configure ''show advanced options'', 1; reconfigure; '
EXEC ('RECONFIGURE')
EXEC SP_EXECUTESQL N'sp_configure ''xp_cmdshell'', 1; reconfigure;'
EXEC ('RECONFIGURE')
SET #FILENAME = 'D:\' + CAST(#SID AS VARCHAR) + '_' + REPLACE(CONVERT(VARCHAR, GETDATE(), 104), '.', '') + '_' + CAST(#CUR AS VARCHAR) + '.xlsx'
SET #CMD = 'copy D:\Example.xlsx ' + #FILENAME
EXEC MASTER..XP_CMDSHELL #CMD;
SET #SQL = 'INSERT INTO OPENROWSET(''Microsoft.ACE.OLEDB.12.0'',
''Excel 12.0;Database=' + #FILENAME + ';HDR=YES'',
''SELECT DATE, CODE, LABEL, CNT, CU, SCO, DISCOUNT FROM [Sheet1$]'')
SELECT
CONVERT(VARCHAR, ORDER.DATE, 104) DATE,
EMPLOYEE.CODE CODE,
EMPLOYEE.LABEL LABEL,
CAST(ORDER.CNT AS INT) CNT,
CAST(ORDER.PRICE AS MONEY) CU,
CAST(ORDER.PRICE_TO_PAY AS MONEY) SCO,
CAST(ORDER.DISCOUNT AS INT) DISCOUNT
FROM
ORDER
JOIN
EMPLOYEE ON EMPLOYEE._ID = ORDER._ID
WHERE
(CONVERT(DATE, ORDER.DATE) >= CONVERT(DATE, #DATE_START)
AND CONVERT(DATE, ORDER.DATE) <= CONVERT(DATE, #DATE_END))
AND (ORDER.ID in (#PID))
ORDER BY ORDER.DATE'
EXEC SP_EXECUTESQL
#SQL,
N'#DATE_START DATE, #DATE_END DATE, #PATID INT',
#DATE_START = #DATE_START,
#DATE_END = #DATE_END,
#PID = #PID
SET #TABLEHTML =
N'<H1>Hello!</H1>' +
N'<span>num: </span>' + CAST(#SID AS VARCHAR(MAX)) +
N'<span>, email: </span>' + CAST(#EMAIL AS VARCHAR(MAX)) +
N'<span> sent! </span>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'site.com',
#recipients = 'admin#site.com',
#subject = 'subj',
#body = #tableHTML,
#body_format = 'HTML',
#file_attachments = #FILENAME,
#mailitem_id = #mailid output
IF (#MAILID > 0 AND ##ERROR = 0)
UPDATE TABLE SET [SIGN] = 1, [SENT] = GETDATE(), SENDER = #CUR WHERE ID = #SID
EXEC SP_EXECUTESQL N'sp_configure ''show advanced options'', 1; reconfigure; '
EXEC ('RECONFIGURE')
EXEC SP_EXECUTESQL N'sp_configure ''xp_cmdshell'', 0; reconfigure;'
EXEC ('RECONFIGURE')
END
Yes, JGFMK was right!
If execute this code, then openrowset will be done:
if( 1 = 0)
begin
INSERT INTO OPENROWSET('Microsoft.ACE.OLEDB.12.0',
'Excel 12.0;Database=D:\file;HDR=YES',
'SELECT DATE, CODE, LABEL, CNT, CU, SCO, DISCOUNT FROM [Sheet1$]')
SELECT..
end
How to make it work by condition ?
I want to send DB Mail having two queries and send two files as a attachment having result of first query in one file and second in another file.
Below is my code which send only one file as attachment,
DECLARE #sql NVARCHAR(max)= '',
#sql2 NVARCHAR(max)= '',
#prefix nvarchar(100),
#RefundSubject AS VARCHAR(100)
set #sql='SET NOCOUNT ON select * from table1 '
set #sql2='SET NOCOUNT ON select * from table2'
DECLARE #b NVARCHAR(max)
declare #prevDate varchar(30)=CONVERT(VARCHAR(30),GETDATE()-1,105)
SET #RefundSubject =' Transactions ' + ' ' + 'For Date ' +#prevDate;
SET #b = '</style><br/>Hi Sir,<br/><br/>Kindly find attached details.<br/><br/>'
declare #filenames nvarchar(50)
SET #prefix = 'Report.csv'
exec('USE msdb')
EXEC msdb..sp_send_dbmail #profile_name='Mail',
#recipients='mansi07091#gmail.com',
#subject = #RefundSubject
,#body = #b
,#body_format='HTML'
,#query = #sql
,#attach_query_result_as_file = 1
,#query_result_separator = '^'
,#query_result_no_padding = 1
,#query_result_header = 1
,#query_attachment_filename =#prefix
SET #sql = ''
Try as below
#file_attachments=’D:\temp\blog.csv;D:\temp\sample.csv
What is the best way to achieve this
INSERT INTO #TableName (#ColumnNames)
EXEC sp_executesql #SQLResult;
Where #TableName, #ColumnNames, #SQLResult are varchar variables
I am trying to avoid do a separate insert for each table.
The best way is to write (or generate) all reqiured procedures for all table. 23 tables times 4 procedures (insert, update, delete and select) that can be generated automatically is nothing in dev time and pain compared to the so called "generic solution".
It's a path to poor perfomance, unreadable code, sql injection hazard and countless debuging hours.
First of all I appreciate all your comments. And I agree that SQL dynamic is a pain to debug (Thanks God, management studio has this possibility). And, of course there are hundreds of different solutions
I solved it in this way finally, more or less I try to explain why this solution of SQL dynamic. The client uses xlsx spreadsheets to enter certain data, so I read the spreadsheets and I insert the (data depends on the spreadsheet to insert into the proper table). Later the data in the tables are exported to XML to send a third party sofware.
SET #SEL = N'';
DECLARE sel_cursor CURSOR
FOR (SELECT sc.name as field
FROM sys.objects so INNER JOIN sys.columns sc ON so.[object_id]=sc.[object_id]
WHERE so.name= #TableName and sc.name not in ('InitDate', 'EndDate', 'Version', 'Status'));
SET #SEL = ''; set #i = 0;
OPEN sel_cursor
FETCH NEXT FROM sel_cursor INTO #field
WHILE ##FETCH_STATUS = 0
BEGIN
set #sel = #sel + ', '+ #field
set #i = 1;
FETCH NEXT FROM sel_cursor INTO #field
END
CLOSE sel_cursor;
DEALLOCATE sel_cursor;
SET #SQL = N''
SET #SQL = #SQL + N'SELECT * INTO XLImport FROM OPENROWSET'
SET #SQL = #SQL + N'('
SET #SQL = #SQL + N'''Microsoft.ACE.OLEDB.12.0'''+','
SET #SQL = #SQL + N'''Excel 12.0 Xml; HDR=YES;'
SET #SQL = #SQL + N'Database='+#file +''''+ ','
SET #SQL = #SQL + N'''select * from ['+ #SheetName + '$]'''+');'
EXEC sp_executesql #SQL
SET #SQL = N'';
SET #SQL = #SQL + N'
SELECT '+''''+CAST(#initDate AS VARCHAR(10))+'''' +', '+ ''''+CAST(#endDate AS VARCHAR(10))+''''
+ ', '+ CAST(#version AS VARCHAR(2)) +', ' +''''+#status+''''
+ #SEL
+' FROM DBO.XLImport '
DECLARE cols_cursor CURSOR
FOR (Select COLUMN_NAME From INFORMATION_SCHEMA.COLUMNS where table_name = #tableName);
SET #SEL = ''; set #i = 0;
OPEN cols_cursor
FETCH NEXT FROM cols_cursor INTO #field
WHILE ##FETCH_STATUS = 0
BEGIN
set #sel = #sel + #field + ', '
set #i = 1;
FETCH NEXT FROM cols_cursor INTO #field
END
CLOSE cols_cursor;
DEALLOCATE cols_cursor;
SET #SEL = LEFT(#SEL, LEN(#SEL) - 1) -- remove last ,
SET #SQL = N''
SET #SQL = #SQL + N'SELECT * INTO XLImport FROM OPENROWSET'
SET #SQL = #SQL + N'('
SET #SQL = #SQL + N'''Microsoft.ACE.OLEDB.12.0'''+','
SET #SQL = #SQL + N'''Excel 12.0 Xml; HDR=YES;'
SET #SQL = #SQL + N'Database='+#file +''''+ ','
SET #SQL = #SQL + N'''select * from ['+ #SheetName + '$]'''+');'
EXEC sp_executesql #SQL
SET #SQLString =
N'INSERT INTO '+ #TableName + '('+ #SEL +') ' + #SQL;
EXEC sp_executesql #SQLString
Use EXECUTE sp_executesql #sql, here is example:
create proc sp_DynamicExcuteStore
#TableName varchar(50),
#ColumnNames varchar(50),
#SQLResult varchar(max)
as
declare #sql nvarchar(max) = '
INSERT INTO '+#TableName+' ('+#ColumnNames+')
EXEC sp_executesql '+#SQLResult
EXECUTE sp_executesql #sql
go
create proc sp_test
as
select 'test' + convert(varchar,RAND())
go
CREATE TABLE [dbo].[Test](
[text1] [nvarchar](500) NULL
) ON [PRIMARY]
GO
DECLARE #return_value int
EXEC #return_value = [dbo].[sp_DynamicExcuteStore]
#TableName = N'Test',
#ColumnNames = N'text1',
#SQLResult = N'proc_test'
SELECT 'Return Value' = #return_value
GO
SELECT TOP 1000 [text1]
FROM [test].[dbo].[Test]