sp_add_jobstep in SQL Server - sql

I have written this in SQL:
EXEC sp_add_jobstep
#job_name = N'Every 5 minutes log backup ',
#step_name = N'set T-sql command',
#subsystem = N'TSQL',
#command=N'DECLARE #fileName NVARCHAR(256) BACKUP LOG MM TO DISK =''F:\BackUp\Log\M-''' +dbo.UDF_Gregorian_To_Persian_WithDash(cast(GETDATE() as date))+'''-'''+ REPLACE(cast(isnull(convert(char(5), GETDATE(), 108),'''''') as nvarchar),''':''','''''') + '''.TRN''',
#retry_attempts = 1,
#retry_interval = 1 ;
GO
but SQL says
"Incorrect syntaxt near +" !
Please help me!

You can pass variables into EXEC or literal values but what you're not allowed to use are expressions.
So do your string concatenation as a separate step first:
declare #cmd nvarchar(max)
set #cmd = 'DECLARE #fileName NVARCHAR(256) BACKUP ... ' + dbo.UDF_Gregorian_To_Persian_WithDash(...) + ' ... '
EXEC sp_add_jobstep
#job_name = N'Every 5 minutes log backup ',
#step_name = N'set T-sql command',
#subsystem = N'TSQL',
#command=#cmd,
#retry_attempts = 1,
#retry_interval = 1 ;

A bit of a mess with those apostophes. What you want is:
DISK ='F:\BackUp\Log\M-x-y.TRN'
But by looking at the current string it would rather be:
DISK ='F:\BackUp\Log\M-'x'-'y'.'TRN'
So, I hope this will work better:
#command = N'DECLARE #fileName NVARCHAR(256) BACKUP LOG MM TO DISK = ''F:\BackUp\Log\M-' + cast(dbo.UDF_Gregorian_To_Persian_WithDash(cast(GETDATE() as date)) as nvarchar) + '-' + REPLACE(cast(isnull(convert(char(5), GETDATE(), 108),'''''') as nvarchar),''':''','''''') + '.TRN'''
A tip would be to print this line so you can see exactly how the output will be. In my example above the output will be something like:
..DISK ='F:\BackUp\Log\M-2015-04-27-08:31.TRN'
Edit:
Not sure what UDF_Gregorian_To_Persian_WithDash returns, but unless it is a nvarchar you'll need to CAST() that too (updated my example above with the cast).

Related

How to convert and store image on SQL Server

I would like to store large images in SQL Server and later use them in Reporting Services, Power BI and Analysis Services.
I found some help, but I still don't understand what is the best way to store them and how to convert these images in the correct format.
Should I convert them to Base64? How do I do that?
I found good explanations on Convert Image DataType To String in SQL Server, Storing images in SQL Server?, but none of them worked with me.
So I have on my database, the path of the image, the image it self and the extension as below:
SELECT
NM_DIRETORIO AS NM_PATH ,
NM_FOTO AS NM_PICTURE,
TP_EXTENSAO AS TP_EXTENSION
FROM D_GB_FOTOS
As I saw it on from this video SSRS - Read images from the SQL Server database, he uses varbinary(max) to store images, but I don't how he converted to that. Also, from Chriss Webb's: Storing Large Images In Power BI Datasets, he uses Base64 to show on Power BI.
So my question is, since I'll use large images, how to I convert a simple image (path + picture) to store in my SQL Server database?
Information:
SQL Server 2019 (v15.0.18330.0)
SQL Server Management Objects (SMO) v16.100.37971.0
Microsoft SQL Server Management Studio v18.5
EDIT:
Based on #Peter Schneider answer, I have created a cursor for updating the table, with that value. But I got error on the where clause (e.g: TABLE.ID_COLUMN).
My cursor:
DECLARE #ID_FOTO INT;
DECLARE #CD_ARQUIVO VARCHAR(4000);
DECLARE #CD_ARQUIVO_VARBINARY VARCHAR(4000);
DECLARE #tsql NVARCHAR (4000);
DECLARE CUR CURSOR FOR SELECT ID_FOTO, CD_ARQUIVO, NM_DIRETORIO + '\' + NM_FOTO + TP_EXTENSAO AS CD_ARQUIVO_VARBINARY FROM D_GB_FOTOS WHERE LINORIGEM <> 'CARGA MANUAL'
OPEN CUR
FETCH NEXT FROM CUR INTO #ID_FOTO, #CD_ARQUIVO, #CD_ARQUIVO_VARBINARY
WHILE #CD_ARQUIVO IS NULL BEGIN
SET #tsql = 'UPDATE D_GB_FOTOS' +
'SET CD_ARQUIVO = (SELECT CD_ARQUIVO.* from Openrowset(Bulk' + #CD_ARQUIVO + ', Single_Blob) CD_ARQUIVO)' +
'WHERE ' + #ID_FOTO + ' = D_GB_FOTOS.ID_FOTO;'
PRINT (#tsql)
EXEC (#tsql)
FETCH NEXT FROM cur INTO #ID_FOTO, #CD_ARQUIVO
END
CLOSE cur
DEALLOCATE cur
EDIT 2:
Some adjustments to the query, but there is one final problem where it keeps updating, and doesn't stop with the final ID of the table:
DECLARE #ID_FOTO INT;
DECLARE #CD_ARQUIVO VARCHAR(4000);
DECLARE #CD_ARQUIVO_VARBINARY VARCHAR(4000);
DECLARE #tsql NVARCHAR (4000);
DECLARE #ID_FOTO_MAX INT;
SET #ID_FOTO_MAX = (SELECT MAX(ID_FOTO) AS ID_FOTO FROM D_GB_FOTOS);
DECLARE CUR CURSOR FOR SELECT ID_FOTO, CD_ARQUIVO, (NM_DIRETORIO + '\' + NM_FOTO + TP_EXTENSAO) AS CD_ARQUIVO_VARBINARY FROM D_GB_FOTOS WHERE LINORIGEM <> 'CARGA MANUAL';
OPEN CUR
FETCH NEXT FROM CUR INTO #ID_FOTO, #CD_ARQUIVO, #CD_ARQUIVO_VARBINARY
WHILE (#ID_FOTO <= #ID_FOTO_MAX) BEGIN
SET #tsql = 'UPDATE D_GB_FOTOS ' +
'SET CD_ARQUIVO = (SELECT CD_ARQUIVO from Openrowset(Bulk ''' + #CD_ARQUIVO_VARBINARY + ''', Single_Blob) CD_ARQUIVO)' +
' WHERE D_GB_FOTOS.ID_FOTO = ' + CONVERT(VARCHAR(10),#ID_FOTO) + ';'
PRINT ('ID_FOTO: ' + CONVERT(VARCHAR(10),#ID_FOTO))
PRINT ('ID_FOTO_MAX: ' + CONVERT(VARCHAR(10),#ID_FOTO_MAX))
PRINT ('SELECT STATEMENT: ' + #tsql)
EXEC (#tsql)
FETCH NEXT FROM cur INTO #ID_FOTO, #CD_ARQUIVO, #CD_ARQUIVO_VARBINARY
END
CLOSE cur
DEALLOCATE cur
Example from where I printed the code above:
(1 linha afetada) ID_FOTO: 6529 ID_FOTO_MAX: 6531
(1 linha afetada) ID_FOTO: 6530 ID_FOTO_MAX: 6531
(1 linha afetada) ID_FOTO: 6531 ID_FOTO_MAX: 6531
(1 linha afetada) ID_FOTO: 6531 ID_FOTO_MAX: 6531
(1 linha afetada) ID_FOTO: 6531 ID_FOTO_MAX: 6531
You can use OpenRowSet to read the image from disk and insert it into your table
INSERT INTO YourTableName (ID, VarbinaryMaxColumn) VALUES (1,
(SELECT * FROM OPENROWSET(BULK N'C:\Temp\Testimage.png', SINGLE_BLOB) AS VarbinaryMaxColumn)
)

sql server concating or replacing, which one is better (faster)

I have to generate a very long procedure every time for a reporting system, so i created a template for my procedure and replacing the parts are needed to, but i could do it with Concat or +(&)
for example:
set #query = '... and (
--#InnerQueries
)'
set #query = replace(#query,'--#InnerQueries',#otherValues)
vs
set #query += ' and exists (...)'
if(#xxx is not null)
set #query += 'and not exists (...)'
with replace approach it's more readable and maintainable for me, but for sake of optimization, what about Concat and attaching string together?
with replace: there are a lot of searching but less string creation
and with concat: lot's of string creation but no searching
so any idea?
I assume you're talking about using CONCAT or REPLACE to build an SQL then run it. If ultimately you'll process fewer than 100 REPLACEments, I'd go with that approach rather than CONCAT because it's more readable.
If however, you're talking about using concat/replace to create report output data and you will e.g. be carrying out 100 REPLACE operations per row on a million rows, I'd do the CONCAT route
update 2:
there could be something missing here:
if i change first variable :#sourceText_Replace
to a max value of 8000 character, and continue to add to it:
set #sourceText_Replace += '8000 character length'
set #sourceText_Replace +=#sourceText_Replace
set #sourceText_Replace +=#sourceText_Replace
set #sourceText_Replace +=#sourceText_Replace
set #sourceText_Replace +=#sourceText_Replace
set #sourceText_Replace +=#sourceText_Replace
set #sourceText_Replace +=#sourceText_Replace
it works fine, even if go up until: 16384017 character length
so any idea here is as good as mine
orginal answer:
to summarize (and if i didnt make any mistakes):
if you are searching in a long text, dont even think about using replace, it took seconds not milliseconds, but for concat obviously does not make any difference
in the blew code, in first try(small text), i just used variables default values and did not append to them,
but for second try(long Text) , i just append result from previous loop run
for long text, i did not bothered to run the loop more than 20 time, because it took over minutes.
smallText: set #destSmallText_Replace =
longText: set #destSmallText_Replace +=
here is the code for test:
SET NOCOUNT ON
drop table if exists #tempReplace
drop table if exists #tempConcat
create table #tempReplace
(
[txt] nvarchar(max) not null
)
create table #tempConcat
(
[txt] nvarchar(max) not null
)
declare #sourceText_Replace nvarchar(max) = 'small1 text to replace #textToBeReplaced after param text'
declare #text_Replace nvarchar(max) = #sourceText_Replace
declare #textToSearch nvarchar(max) = '#textToBeReplaced'
declare #textToReplace nvarchar(max) = 'textToBeReplaced'
declare #concat_Start nvarchar(max) = 'small1 text to replace'
declare #concat_End nvarchar(max) = 'after param text'
declare #text_Concat nvarchar(max) = #concat_Start
declare #whileCounter int =0
declare #maxCounter int = 5
declare #startTime datetime = getdate();
declare #endTime datetime = getdate();
begin
set #startTime = getDate();
while(#whileCounter <=#maxCounter)
begin
--long text
set #text_Replace += replace(#sourceText_Replace,#textToSearch,#textToReplace + convert(nvarchar(10), #whileCounter)) + #textToSearch
--small text
--set #text_Replace = replace(#sourceText_Replace,#textToSearch,#textToReplace + convert(nvarchar(10), #whileCounter)) + #textToSearch
--print #destSmallText_Replace
insert into #tempReplace values(#text_Replace)
set #whileCounter+=1
end
set #endTime = getDate();
print 'passedTime ' + Convert(nvarchar(20), DATEPART(millisecond, #endTime) - DATEPART(millisecond, #startTime))
end
begin
set #whileCounter = 0;
set #startTime = getDate();
while(#whileCounter <=#maxCounter)
begin
set #text_Concat += concat(#concat_Start,#textToReplace + convert(nvarchar(10), #whileCounter),#concat_End) + #textToSearch
--print #sourceSmallText_Concat
insert into #tempConcat values(#text_Concat)
set #whileCounter+=1
end
set #endTime = getDate();
print 'passedTime ' + Convert(nvarchar(20), DATEPART(millisecond, #endTime) - DATEPART(millisecond, #startTime))
end

Sending query results via email via email attachment every first day of every month on SQL Server 2012

My requirement:
Send the query result via email attachment on first day of every month.
The work I've been doing manually:
I have to run this query every first day of each month by changing the date range.
Then I export the result acquired in .csv format and send this csv file as an attachment
I needed suggestions from you people on how shall I automate this process:
Shall I set up a Job on SQL Server 2012, but yes, the I'll have to modify the date range.
Please suggest on how to move forward.
Any help, much appreciated.
As you mentioned, Create a Job and schedule it to run on first day of every month. Considering you have enough knowledge on creating a job.
Go to Job properties -> schedules -> and make the following setting
Occurs every first day of every 1 month(s) at 12:00:00. Schedule will
be used starting on 07-12-2016.
Change the timing(at which time it should run on first day of month) based on your business requirement. It can be set under Daily frequency-> Occurs once at:
This process can also be automated by another way by using a Windows batch file.You can schedule it using Windows scheduler.
Below will be contents of batch file
Echo off
sqlcmd -u <username> -p <password> -S <server name> -d <database name> -i <sql file location> -o <output result file location>
Powershell.exe -executionpolicy remotesigned -File <location of powershell file>
The powershell file trigger an email when bat file is run.
Contents of powershell file
$smtpserver = "<smtp server name>"
$from="mail id"
$to="<mail id"
$a = Get-Date
$subject= "<subject line> `r"+ $a.Month +"/"+$a.Day +"/"+ $a.Year
$body=""
$attachment="File location"
Thanks,`n "
$mailer = new-object Net.Mail.SMTPclient($smtpserver)
$msg = new-object Net.Mail.MailMessage($from,$to,$subject,$body,$data1,$a)
$msg.IsBodyHTML = $true
$mailer.send($msg)
I use SQL Agent for send results via email like this:
/*
First you should set up SQL Mail Profile.
Please change #dbName, #SQLscript, #mailbody and mail account values. When changing your #SQLscript value be careful that replace (with CTRL+H) single quota (') to double quotas ('').
*/
DECLARE #dbName nvarchar(50), #SQLscript nvarchar(4000), #subject varchar(100), #mailfrom varchar(100), #mailbody nvarchar(4000), #jobName varchar(100)
SELECT #jobName = name from msdb..sysjobs where job_id = $(ESCAPE_NONE(JOBID))
SELECT #mailfrom = ##SERVICENAME + ' <' + cast(SERVERPROPERTY('ComputerNamePhysicalNETBIOS') as varchar(50)) + '#domain.com>'
SELECT #subject = N'SQL Server Job Result [Job: ' + #jobName + ']'
SELECT #dbName = 'Database'
SELECT #SQLscript = '
INSERT INTO [Database].[Schema].[Table] (
Column1
,Column2
) VALUES (
''Value1''
,''Value2'' )
'
SELECT #mailbody = N'
Depending on case number 1234-5678-90 your script executed on <b>' + ##SERVERNAME + N'</b> instance and <b>' + #dbName + '</b> database. Script info and results are shown below. <br><br>' +
'<b>Script: </b><br>' + #SQLscript + '<br><br>' +
'<b>Result: </b><br>'
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'sqlmailprofile',
#recipients = '<mail1#domain.com>;<mail2#domain.com>',
#copy_recipients = '<mail3#domain.com>',
#from_address = #mailfrom,
#reply_to = '<mail3#domain.com>',
#subject = #subject,
#body = #mailbody,
#body_format = 'HTML',
#importance = 'HIGH',
#execute_query_database = #dbName,
#query = #SQLscript
/* If you want to send results with attached file:
#attach_query_result_as_file = 1,
#query_attachment_filename = 'script_output.csv',
#query_result_separator=#tab,
#query_result_width =32767,
#query_result_no_padding=1,
#exclude_query_output=1,
#query_result_header=1
*/

Returning values from dynamic SQL by using sp_executesql with an output variable (Or) Error in appending the variable data at the end name dynamically

I just face a problem while working on a stored procedure,
My situation is as below,
I'm calling a stored procedure inside another stored procedure like for example,
EXEC [SP_ADMIN_INSERT_ITEM_STOCK_DETAILS]
#stk_tran_no = #cash_purchase_no,
#stk_tran_date = GetDate(),
#tran_type = 'Cash Purchase',
#item_code = #item_code,
#quantity = #quantity
Currently in the above code we are passing current date to the parameter #stk_tran_date.
But now I need to pass date to #stk_tran_date by fetching that from some other table like,
select #stk_tran_date = Convert(datetime,cash_purchase_date,103) from Cash_Purchase_14 where cash_purchase_no = 'GOH-9/2014'
If you observe my table name is like Cash_Purchase_14 where 14 is a dynamic value which changes every year, as this is 2014 financial year so it looks like Cash_Purchase_14, next year it will be Cash_Purchase_15.
Because of this i use to write these quires first as string then I'll execute them as shown below,
declare #SQL nvarchar(4000)
set #SQL =N' Declare #cash_purchase_date1 datetime
set #cash_purchase_date1 = (select cash_purchase_date from Cash_Purchase_'+ #Financialyearpart +' where cash_purchase_no = ''' + #cash_purchase_no + ''')
print #cash_purchase_date1'
exec executesql #SQL
But I need the value of the variable #cash_purchase_date1 outside this block like below,
EXEC [SP_ADMIN_INSERT_ITEM_STOCK_DETAILS]
#stk_tran_no = #cash_purchase_no,
#stk_tran_date = #cash_purchase_date1,
#tran_type = 'Cash Purchase',
#item_code = #item_code,
#quantity = #quantity
but it is giving an error like, "declare the variable #cash_purchase_date1"
In Other case i tried like calling the stored procedure in side the string like,
SET #SQL =' Declare #cash_purchase_date1 datetime
set #cash_purchase_date1 = (select cash_purchase_date from Cash_Purchase_'+ #Financialyearpart +' where cash_purchase_no = ' + #qt + #cash_purchase_no + #qt +')
print #cash_purchase_date1
EXEC [SP_ADMIN_INSERT_ITEM_STOCK_DETAILS]
#stk_tran_no = ' + #qt + #cash_purchase_no + #qt +',
#stk_tran_date = #cash_purchase_date1,
#tran_type = ''Cash Purchase'',
#item_code = ' + #qt + #item_code + #qt +',
#quantity = ' + #quantity
exec executesql #SQL
In this scenario the value of #cash_purchase_date1 is not replacing it simply retains the same.
Please help to get the value of the variable outside the block.
Or
How can I append the value 14 at the end of the table name dynamically using a variable.
I Tried like
Declare #cash_purchase_date1 datetime
set #cash_purchase_date1 = cash_purchase_date from Cash_Purchase_+ #Financialyearpart
I think i made the problem bit complicated while explaining. Please help me in solving the issue.
Thanks in advance.
You can return values from dynamic sql by using sp_executesql with an output variable:
declare #SQL nvarchar(4000);
declare #cash_purchase_date datetime;
set #SQL = N'select #cash_purchase_date = cash_purchase_date from Cash_Purchase_' + #Financialyearpart + ' where cash_purchase_no = ''' + #cash_purchase_no + '''';
exec sp_executesql #SQL, N'#cash_purchase_date datetime OUTPUT', #cash_purchase_date = #cash_purchase_date OUTPUT;
I think this will solve your problem.

Passing in a date variable or check if no date is passed in then use getdate()

I would like to pass in a date variable using sqlcmd and do a checking condition where if there is no date passing in, then set the variable to getdate().
Can anyone advise me on this? Thank you. Below is my sql script.( the getdate() part should be modify)
--Declare variable to store back up file location
DECLARE #DBBackupFile as nvarchar(128);
SET #DBBackupFile = N'C:\sqlbackup\MOL_POSHistory\MOL_POSHistory_backup_' + convert(varchar(12),(year(GETDATE()) * 10000) + (month(GETDATE()) * 100) + Day(GETDATE()) -4 ) + N'0201.bak';
--Retrive the Logical file name of the database from backup.
RESTORE FILELISTONLY
FROM DISK = #DBBackupFile
GO
--Make Database to single user Mode
ALTER DATABASE [MOL_POSHistory]
SET SINGLE_USER WITH
ROLLBACK IMMEDIATE
--Restore Database
DECLARE #DBBackupFile as nvarchar(128);
SET #DBBackupFile = N'C:\sqlbackup\MOL_POSHistory\MOL_POSHistory_backup_' + convert(varchar(12),(year(GETDATE()) * 10000) + (month(GETDATE()) * 100) + Day(GETDATE()) -4 ) + N'0201.bak';
USE master;
RESTORE DATABASE [MOL_POSHistory] FROM DISK = #DBBackupFile WITH FILE = 1,
MOVE N'MOL_POSHistory' TO N'D:\SQLData01\MOL_POSHistory.mdf',
MOVE N'MOL_POSHistory_log' TO N'F:\SQLLog\MOL_POSHistory.ldf',
NOUNLOAD, REPLACE, STATS = 10
--Set database to multi user mode
ALTER DATABASE [MOL_POSHistory] SET MULTI_USER
GO
You will need to call the SQLCMD like this making use of the –v parameter:
// Date format is YYYYMMDD
sqlcmd -S YOUR_SERVER -v inputDate="20010101" -i "YOUR_INPUT_FILE" -E
You can access the parameters passed in by SQLCMD (inputDate in this example) like this:
DECLARE #inputDate NVARCHAR(50)
DECLARE #date DATETIME
SET #inputDate = '$(inputDate)'
IF (LEN(ISNULL(#inputDate, '')) = 0) BEGIN
SET #date = GETDATE()
PRINT 'Using default date'
END
ELSE BEGIN
SET #date = #inputDate
PRINT 'Using varaible date'
END