How can I email two queries as attachments in a SQL Server stored procedure?
Currently my code is only for one query. Thank you
ALTER PROCEDURE [dbo].[EmailDailyMembers]
AS
DECLARE #attach_query_result_as_file BIT = 1 -- yes, attach results as file
DECLARE #query_result_header BIT = 1 -- include column headers
DECLARE #query_result_separator CHAR(1) = char(9)
DECLARE #recordCount int
EXEC msdb.dbo.sp_send_dbmail
#recipients = 'aaa#gmail.com',
#subject = 'Report1',
#query_attachment_filename = 'Report1.csv',
#attach_query_result_as_file = 1, -- attach as a file, csv in this case
#query_result_separator = #query_result_separator,
#query = 'USE Revenue2 SELECT * FROM [Membership Report]',
#query_result_header = 1,
#query_result_no_padding = 1,
#exclude_query_output = 1
END
I was unable to find the solution for the issue and unable to execute a stored procedure. Am I missing anything in the stored procedure?
Main aim is to run multiple tables inserts into different tables (different metadata).
This is my stored procedure:
CREATE OR ALTER PROCEDURE newsample
AS
BEGIN
DECLARE #sqlquery varchar(max);
SET #sqlquery = 'insert into dbo.table1 select 2 as newcol;
insert into dbo.table2 select 2 as newcol;
insert into dbo.table3 select 2 as newcol;
insert into dbo.table4 select 2 as newcol;';
EXEC #sqlquery
END
EXEC dbo.newsample
Error:
Msg 2812, Level 16, State 62, Procedure dbo.newsample, Line 6 [Batch Start Line 9]
Could not find stored procedure 'insert into dbo.table1 select 2 as newcol'.
Appreciate your help.
Thank you
To execute arbitrary dynamic sql, you have to use exec(), not exec.
exec('select 0'); exec(#myDynamicSql);
If you use exec arg, then arg is a stored procedure or function, or the name of a stored procedure or function. Yep, it can be a function. And these can either be the literal names, or strings with the value of the name. All of the following works:
create procedure dbo.p as begin set nocount on; end;
go
create function dbo.funcwithoutargs() returns int as begin return 2; end
go
create function dbo.funcwithargs(#i int) returns int as begin return #i; end
go
declare #i int = 0, #module sysname;
exec dbo.p;
exec #i = dbo.funcwithoutargs;
exec #i = dbo.funcwithargs #i;
set #Module = 'dbo.p';
exec #module;
set #module = 'dbo.funcwithoutargs';
exec #i = #module;
The line
EXEC #sqlquery
Should be
EXEC sp_executesql #stmt = #sqlquery
I want to write a script, its function is to create a stored procedure and before create it the script should have to check if it exists in DB, so I write the procedure like below, If I comment out first 2 lines the scripts will create procedure successfully if the stored procedure not exist, but I add the first two lines, the SSMS will throw error:
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[User_Own_Restore_From_Job]') AND type in (N'P'))
CREATE PROCEDURE [dbo].[User_Own_Restore_From_Job]
#dbname varchar(500),
#backuppath varchar (500)
AS
BEGIN
SET NOCOUNT ON
DECLARE #XPConfig int = 1
--To keep the existing value
SELECT #XPConfig = cast(value AS INT) FROM sys.configurations WHERE name LIKE 'xp_cmdshell';
--Enabling xp_cmdshell for getting registry and creating directory structure
BEGIN TRY
EXEC sp_configure 'show advanced options', 1
RECONFIGURE with override
-- To enable the feature.
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE with override
END TRY
BEGIN CATCH
--empty
END CATCH
DECLARE #datapath nvarchar(500);
DECLARE #logpath nvarchar(500);
--read data and log path details from registry
EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\Setup', N'SQLDataRoot', #datapath OUTPUT
EXECUTE master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'DefaultLog', #logpath OUTPUT
SET #datapath=#datapath+'\Data';
--creating a database folder if not already exists on log and data drives
DECLARE #datapath2 varchar(500)=''
DECLARE #logpath2 varchar(500)=''
SET #DataPath2 = #datapath+ N'\'+#dbname
SET #LogPath2 = #logpath+ N'\'+#dbname
--reading all folders from DATA directory
DECLARE #DirTree TABLE (subdirectory nvarchar(255), depth INT)
INSERT INTO #DirTree(subdirectory, depth)
EXEC master.sys.xp_dirtree #DataPath
--creating a folder on DATA drive with database name if not already exists
IF NOT EXISTS (SELECT 1 FROM #DirTree WHERE subdirectory = #DBName)
EXEC master.dbo.xp_create_subdir #DataPath2
DELETE FROM #DirTree
--reading all folders from LOG directory
INSERT INTO #DirTree(subdirectory, depth)
EXEC master.sys.xp_dirtree #LogPath
--creating a folder on LOG drive with database name if not already exists
IF NOT EXISTS (SELECT 1 FROM #DirTree WHERE subdirectory = #DBName)
EXEC master.dbo.xp_create_subdir #LogPath2
DECLARE #PhysicalName nvarchar(260)=''
DECLARE #LogicalName nvarchar(128)=''
DECLARE #Type char(1)=''
DECLARE #text nvarchar(2000)=''
DECLARE #sql nvarchar(max)=''
DECLARE #restoredb TABLE ( LogicalName nvarchar(128), PhysicalName nvarchar(260),Type char(1)
,FileGroupName nvarchar(128),Size numeric(20,0),MaxSize numeric(20,0),FileID bigint,CreateLSN numeric(25,0),DropLSN numeric(25,0)
,UniqueID uniqueidentifier,ReadOnlyLSN numeric(25,0),ReadWriteLSN numeric(25,0),BackupSizeInBytes bigint,SourceBlockSize int
,FileGroupID int,LogGroupGUID uniqueidentifier,DifferentialBaseLSN numeric(25,0),DifferentialBaseGUID uniqueidentifier
,IsReadOnly bit,IsPresent bit,TDEThumbprint varbinary(32),SnapshotUrl nvarchar(128))
--reading header section of backup file
INSERT #restoredb EXECUTE(N'RESTORE FILELISTONLY FROM DISK = '''+#backuppath +'''')
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
BEGIN TRY
--***taking backup of all permissions**********************************
CREATE TABLE #Permissions (id int identity(1,1), qry nvarchar(4000),Stat bit default 0)
SET #sql='USE ['+#dbname+'];
-- Create Users If not exists
INSERT INTO #Permissions (qry)
select
txt=case when isnull(l.name,'''')=''''
then ''IF NOT EXISTS (SELECT 1 FROM sys.database_principals where name = '''''' + p.name + '''''') BEGIN CREATE USER ['' + p.name +''] FOR LOGIN ['' + p.name +''] END''
else ''IF NOT EXISTS (SELECT 1 FROM sys.database_principals where name = '''''' + p.name + '''''') BEGIN CREATE USER ['' + p.name +''] FOR LOGIN ['' + l.name +''] END''
end
from sys.database_principals p
left join master..syslogins l on l.sid = p.sid
where p.type NOT IN (''R'') AND p.name NOT IN (''dbo'',''guest'',''INFORMATION_SCHEMA'',''sys'')
UNION ALL
--Create Role If not exists
select
txt=case when isnull(l.name,'''')=''''
then ''IF NOT EXISTS (SELECT 1 FROM sys.database_principals where name = '''''' + p.name + '''''') BEGIN CREATE Role ['' + p.name +''] END''
else ''''
end
from sys.database_principals p
left join master..syslogins l on l.sid = p.sid
where p.type =''R'' and p.name NOT IN (''dbo'',''guest'',''INFORMATION_SCHEMA'',''sys'',
''db_owner'',''db_accessadmin'',''db_securityadmin'',''db_ddladmin'',''db_backupoperator'',''db_datareader'',''db_datawriter'',''db_denydatareader'',''db_denydatawriter'',''public'')
UNION ALL
--User Permissions
select
txt=case when isnull(p.type ,'''')<>''G''
then ''ALTER USER ['' + p.name +''] WITH DEFAULT_SCHEMA = ''+ isnull(p.default_schema_name, ''[NULL]'') +'';''
else ''''
end
+ '' EXEC sp_addrolemember '''''' + q.name + '''''', '''''' + p.name + '''''';''
from sys.database_principals p
join (select * from sys.database_principals a
join sys.database_role_members r on a.principal_id=r.role_principal_id) q
on q.member_principal_id = p.principal_id
where (p.type NOT IN (''R'')) AND p.name NOT IN (''dbo'',''guest'',''INFORMATION_SCHEMA'',''sys'')
UNION ALL
--Orphaned users
select
txt=case when isnull(l.name,'''')=''''
then ''Exec sp_change_users_login ''''update_one'''', '''''' + p.name + '''''', '''''' + p.name + '''''' ''
else ''Exec sp_change_users_login ''''update_one'''', '''''' + p.name + '''''', '''''' + l.name + '''''' ''
end
from sys.database_principals p
left join master..syslogins l on l.sid = p.sid
where p.type=''S'' AND p.name NOT IN (''dbo'',''guest'',''INFORMATION_SCHEMA'',''sys'')
'
--get all permissions in text format
EXECUTE sp_executesql #sql
--**Permissions end******************************************************************--
--**preparing a restore statement****************************************************--
SET #SQL='USE [master];ALTER DATABASE ['+#dbname+N'] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
ALTER DATABASE ['+#dbname+N'] SET SINGLE_USER;
ALTER DATABASE ['+#dbname+N'] set offline with NO_WAIT;'
--set database to single user mode
EXECUTE sp_executesql #sql
SET #SQL='RESTORE DATABASE ['+#dbname+N'] FROM DISK = '''+#backuppath+''' WITH FILE = 1'
--preparing move statement for all files
DECLARE c1 CURSOR FOR SELECT PhysicalName,LogicalName,Type FROM #restoredb
OPEN c1;
FETCH NEXT FROM c1 INTO #PhysicalName,#LogicalName,#Type -- fetch first value
WHILE ##fetch_status = 0
BEGIN
SET #text = RIGHT(#PhysicalName,CHARINDEX( '\', REVERSE(#PhysicalName)));
SET #SQL=#SQL+', MOVE '''+#LogicalName+N''''
IF (#Type = 'L')
SET #SQL=#SQL+' TO '''+#logpath+N'\'+#dbname+#text+N''''
ELSE
SET #SQL=#SQL+' TO '''+#datapath+N'\'+#dbname+#text+N''''
FETCH NEXT FROM c1 INTO #PhysicalName,#LogicalName,#Type -- fetch next value
END; --WHILE ##fetch_status = 0
CLOSE c1
DEALLOCATE c1;
-- replace ersetzt die alte db
SET #SQL=#SQL+', NOUNLOAD , replace , STATS = 10 ;'
--perform restore database operation
EXECUTE sp_executesql #sql
--setting database to online mode
SET #SQL='USE [master];
ALTER DATABASE ['+#dbname+N'] set ONLINE with NO_WAIT;
ALTER DATABASE ['+#dbname+N'] SET MULTI_USER; '
EXECUTE sp_executesql #sql
-- changes database owner to sa
SET #SQL='USE [' + #dbname + N'];
EXEC sp_changedbowner ''sa'' '
EXECUTE sp_executesql #sql
--**Database a restore is completed**********************************************--
--**restore permissions on database****************************************************--
DECLARE #ptxt nvarchar(4000)='',#Id int=0
WHILE 0=0
BEGIN
SELECT #ptxt='',#Id=0
SELECT TOP 1 #Id=Id, #ptxt=qry FROM #Permissions WHERE Stat=0 ORDER BY Id
IF Isnull(#Id,0)<=0 BREAK;
SET #ptxt= N'USE [' + #dbname + N'];' + #ptxt
--skip if any invalid login/user exists
BEGIN TRY
--print #ptxt
EXECUTE (#ptxt)
END TRY
BEGIN CATCH
--No action is needed
END CATCH
UPDATE #Permissions SET Stat=1 WHERE Id=#Id
END
END TRY
BEGIN CATCH
--setting database to online mode
SET #SQL='USE [master];
ALTER DATABASE ['+#dbname+N'] set ONLINE with NO_WAIT;
ALTER DATABASE ['+#dbname+N'] SET MULTI_USER; '
EXECUTE sp_executesql #sql
SELECT #ErrorMessage = ERROR_MESSAGE(), #ErrorSeverity = ERROR_SEVERITY(), #ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH
BEGIN TRY
EXEC sp_configure 'show advanced options', 1
RECONFIGURE with override
--Retain original setting
EXEC sp_configure 'xp_cmdshell', #XPConfig
RECONFIGURE with override
END TRY
BEGIN CATCH
SELECT #ErrorMessage = ERROR_MESSAGE(), #ErrorSeverity = ERROR_SEVERITY(), #ErrorState = ERROR_STATE();
RAISERROR (#ErrorMessage, -- Message text.
#ErrorSeverity, -- Severity.
#ErrorState -- State.
);
END CATCH
END
The error message like below:
Msg 156, Level 15, State 1, Line 4
Incorrect syntax near the keyword 'PROCEDURE'.
Msg 137, Level 15, State 2, Line 69
Must declare the scalar variable "#dbname".
Msg 137, Level 15, State 2, Line 70
Must declare the scalar variable "#dbname".
Msg 137, Level 15, State 2, Line 79
Must declare the scalar variable "#DBName".
Msg 137, Level 15, State 2, Line 90
Must declare the scalar variable "#DBName".
Msg 137, Level 15, State 2, Line 105
Must declare the scalar variable "#backuppath".
Msg 137, Level 15, State 2, Line 116
Must declare the scalar variable "#dbname".
Msg 137, Level 15, State 2, Line 177
Must declare the scalar variable "#dbname".
Msg 137, Level 15, State 2, Line 184
Must declare the scalar variable "#dbname".
Msg 137, Level 15, State 2, Line 198
Must declare the scalar variable "#dbname".
......
can anyone explain this for me how to fix this issue if I want to have a pre-check?
CREATE PROCEDURE has to be the only statement in a batch. To conditionally execute it, you can use dynamic SQL.
...
IF ...
BEGIN
EXECUTE(N'CREATE PROCEDURE ...');
END;
...
If you only need the conditional execution to prevent errors of the procedure already existing and your version of SQL Server is high enough, you can alternatively use the CREATE OR ALTER PROCEDURE ... statement. It's available since 2016, I believe. That way the procedure simply gets overwritten, if it already exists, without the statement throwing any errors because of the procedure already existing.
Basically the constraint is 'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
Depends on what you want, there are several option, each has different effect.
CREATE OR ALTER PROCEDURE
create or alter procedure [dbo].[User_Own_Restore_From_Job]
as
begin
...
end;
GO
the effect is the procedure is altered and the modify_date in sys.procedures will be updated every time it executes
DROP PROCEDURE + CREATE PROCEDURE
If you are using a earlier version of SQL Server without the CREATE OR ALTER option, you can check for existence of the store procedure, drop it and create it.
if object_id('dbo.User_Own_Restore_From_Job') is not null
drop procedure dbo.User_Own_Restore_From_Job
GO
create procedure [dbo].[User_Own_Restore_From_Job]
as
begin
...
end;
GO
the effect is the procedure is drop and create everytime it executes. And also the create_date and modify_date will be change also.
Don't execute if exists
this make use of set noexec settings. If the procedure exists set noexec to off (The subsequent commands will not be executed). And set it back to on at the end.
IF object_id('dbo.User_Own_Restore_From_Job') is not null
set noexec on;
GO
create procedure [dbo].[User_Own_Restore_From_Job]
as
begin
...
end;
GO
set noexec off;
I wrote the following script but it shows an error due to the + sign in #command argument. Could anyone suggest how I can get rid of this error?
USE msdb;
GO
--add a job
EXEC dbo.sp_add_job
#job_name = N'FullBackup';
GO
USE msdb;
GO
--add jobsteps to jobsteps
DECLARE #fileName VARCHAR(90);
DECLARE #db_name VARCHAR(20);
DECLARE #fileDate VARCHAR(20);
DECLARE #commandtxt VARCHAR(100);
SET #fileName = 'D:\SQL server\BackUp\';
--SET #db_name = 'AdventureWorks_';
SET #fileDate = CONVERT(VARCHAR(8), GETDATE(),112);
SET #fileName = #fileName + #db_name + RTRIM(#fileDate) + '.bak';
SET #commandtxt = N'''BACKUP DATABASE [AdventureWorks2012] TO DISK =N''' + #fileName + ''' WITH INIT';
EXEC sp_add_jobstep
#job_name = N'FullBackup',
#step_name = N'Weekly Full Backup',
#subsystem = N'TSQL',
#command = 'BACKUP DATABASE [AdventureWorks2012] TO DISK =N''' + #fileName + ''' WITH INIT',
#retry_attempts = 5,
#retry_interval = 5;
GO
--Create a Schedule for this job, backup, occurs once a week each friday at 11:59
EXEC sp_add_schedule
#schedule_name = N'WeeklyBackup1',
#freq_type = 8,
#freq_interval = 32,
#freq_recurrence_factor = 1,
#active_start_time = 235900;
GO
--attach the schedule to the job
EXEC sp_attach_schedule
#job_name = N'FullBackup',
#schedule_name = N'WeeklyBackup1';
GO
EXEC dbo.sp_add_jobserver
#job_name = N'FullBackup';
GO
Try this
SET #commandtxt = N'BACKUP DATABASE [AdventureWorks2012] TO DISK =N''' + #fileName + ''' WITH INIT'
EXEC sp_add_jobstep
#job_name = N'FullBackup',
#step_name = N'Weekly Full Backup',
#subsystem = N'TSQL',
#command = #Commandtxt,
#retry_attempts = 5,
#retry_interval = 5;
GO
.
.
.
How do I schedule a job using sp_add_job to run every 5 minutes indefinitely?
What are the exact values of #freq_type=4,#freq_interval=64, freq_subday_type=0x4,#freq_subday_interval=10, #freq_relative_interval=0 that I should be using ?
Try this.... #user2748147
Declare #job nvarchar(128),
#mycommand nvarchar(max),
#servername nvarchar(28),
#databasename nvarchar(50),
#startdate nvarchar(8),
#starttime nvarchar(8)
SET #job = 'myJob'
SET #mycommand = 'Select * from myTable'
SET #startdate = '20151219' -- The date December 19, 2015
SET #starttime = '160000' -- The time, 16:00:00
SET #servername = 'myservername'
SET #databasename = 'yourdatabasename'
--Add a job
EXEC dbo.sp_add_job
#job_name = #job ;
--Add a job step named process step. This step runs the stored procedure
EXEC sp_add_jobstep
#job_name = #job,
#step_name = N'process step',
#subsystem = N'TSQL',
#database_name = #databasename,
#command = #mycommand
--Schedule the job at a specified date and time
exec sp_add_jobschedule #job_name = #job,
#name = 'MySchedule',
#freq_type=4,
#freq_interval=4,
#freq_subday_type=0x4,
#freq_subday_interval = 5,
#active_start_date = #startdate,
#active_start_time = #starttime
-- Add the job to the SQL Server Server
EXEC dbo.sp_add_jobserver
#job_name = #job,
#server_name = #servername