How should I do to properly use the sp_msforeachDB to create and delete backups - sql-server-2008

I'm setting up a new way to create and delete backups in our internal environments. The space on the servers is a big issue so i really have to find a way to do a backup and delete the older one for each database, procedurally.
I came up with the idea of using the sp_MSforeachdb and simply do a backup. On success, it would delete the older backup that was done the day before. (we do 1 backup per night).
DECLARE #DBOPurpose Varchar(15)
DECLARE #Drive Varchar(1)
Set #DBOPurpose = 'Omnilabs'
DECLARE #command varchar(Max)
Select #command = '
If Exists (Select *
from sys.databases
where name like ''%?''
and name like ''%'+#DBOPurpose+'%'')
BEGIN
EXECUTE master.dbo.xp_create_Subdir '+#Drive+':\SQLBACKUP\'+#DBOPurpose+'
GO
BACKUP DATABASE [?] TO DISK = '+#Drive+':\SQLBackup\'+#DBOPurpose+'_Select Cast(Cast(Year(Getdate())As Varchar(150))+''_''+Cast(month(Getdate())As Varchar(150))+''_''+Cast (Day(Getdate()) As Varchar(150))+''_''+ cast(DATEPART(hour, GETDATE()) as varchar) + '''' + cast(DATEPART(minute, GETDATE()) as varchar)+ '''' + cast(DATEPART(Second, GETDATE()) as varchar)+ ''.bak'' As varchar(150))
END
Print N''TEST''
'
--Select #Command
EXEC sp_MSforeachdb #command
I was expecting that doing Select #Command would actually show me something, but its null. When I run this, it simply writes "Commands completed successfully." within a second, which doesn't make any sense.

Just saw my error, which was only caused by the hyphens that were not placed correctly. Here's the code:
If exists(Select * from #TempDBName where DBNAME = #DBOPurpose)
BEGIN
DECLARE #ERROR bit
DECLARE #command nvarchar(Max)
SET #Error = 0
SET #command = '
DECLARE #DBO VARCHAR(15)
Set #DBO = ''%'+#DBOPURPOSE+'%''
If Exists (Select *
from sys.databases
where name like ''%?''
and name like ''%''+#DBO+''%'')
BEGIN TRY
PRINT ''?''
EXECUTE master.dbo.xp_create_Subdir '''+#Drive+':\SQLBACKUP\TEST\?''
BACKUP DATABASE [?] TO DISK = '''+#Drive+':\SQLBackup\'+#DBOPurpose+'_' +(Cast(Cast(Year(Getdate())As Varchar(150))+'_'+Cast(month(Getdate())As Varchar(150))+'_'+Cast (Day(Getdate()) As Varchar(150))+'_'+ cast(DATEPART(hour, GETDATE()) as varchar) + cast(DATEPART(minute, GETDATE()) as varchar)+ cast(DATEPART(Second, GETDATE()) as varchar)+ '.bak''' As varchar(150)))+'
END TRY
BEGIN CATCH
Insert into BackupExecLog(ErrorDate, ErrorNumber, ErrorMessage)
Select Getdate(),
ERROR_NUMBER() as ErrorNumber,
ERROR_MESSAGE() as ErrorMessage
Set #Error = 1
END CATCH'
Select #Command
--EXEC sys.sp_MSforeachdb #command

Related

MySQL stored procedure issue, generate custom ID's

I had a stored procedure for making custom ID, but it was for SQL Server and I tried to convert it to MySQL with help of an online tool, and I get errors and I need help from you guys :(
This stored procedure will produce ID codes for my users, like for admin (AD001, AD002,...etc), first, it will look if there is no value it will generate the first value like (AD001) if there is a value it will get the value and increment it by 1.
This is my SQL Server version of the procedure which works:
create procedure admin_code
as
begin
declare #max int, #no varchar(50), #value varchar(50)
select #max = isnull(max(cast(right(id_user, 3) as int)), 0) + 1
from admin_information
if #max < 100
select #no = 'AD' + right('00000' + cast(#max as varchar(50)), 3)
else if #max >= 100
select #no = 'AD' + right('0' + cast(#max as varchar(50)), 4)
if #max >= 1000
select #no = 'AD' + right('0' + cast(#max as varchar(50)), 5)
print #no
select #no
return 0
end
And this is for MySQL
create procedure admin_code()
sp_lbl:
begin
declare v_max int;
declare no varchar(50);
declare value varchar(50);
select v_max=ISNULL( max(cast(RIGHT(id_user,3)as int)),0)+1 into v_max from admin_information;
if v_max <100 then
set v_no=CONCAT('AD',RIGHT('00000'+CAST(v_max as varchar(50)),3));
elseif
v_max>=100 then
-- SQLINES LICENSE FOR EVALUATION USE ONLY
set v_no=CONCAT('AD',RIGHT('0'+CAST(v_max as varchar(50)),4));
end if;
if v_max>=1000 then
-- SQLINES LICENSE FOR EVALUATION USE ONLY
set v_no=CONCAT('AD',RIGHT('0'+CAST(v_max as varchar(50)),5))
print;
end if; v_no
-- SQLINES LICENSE FOR EVALUATION USE ONLY
select v_no;
LEAVE sp_lbl 0;
end;
This is the online tool I used:
http://www.sqlines.com/online
Error:

OPENJSON in compatibility level 100 SQL SERVER 2016

I need to use the functionality of OPENJSON() in an old database with compatibility level 100. The server runs SQL SERVER 2016. So i came up with this idea: Create another DB "GeneralUTILS" (lvl 130) in the same server and call this function from lvl 100 DB:
CREATE FUNCTION [dbo].[OPENJSON_](#json NVARCHAR(MAX))
RETURNS #Results TABLE ([Key] nVARCHAR (4000) , [Value] NVARCHAR(MAX), [Type] INT)
AS
BEGIN
INSERT INTO #Results
SELECT * from OPENJSON(#json)
RETURN
END
But i don't have the WITH clause to modify the output table in the lvl 100 database.
Most important might be the question why you need this at all...
I hope I got correctly, what you need:
(Hint: This needs at least SQL-Server 2016)
--create two mock-up-databases
CREATE DATABASE dbOld;
GO
ALTER DATABASE dbOld SET COMPATIBILITY_LEVEL = 100; --v2008
GO
CREATE DATABASE dbForJsonIssues;
GO
ALTER DATABASE dbForJsonIssues SET COMPATIBILITY_LEVEL = 130; --v2016
GO
--Now we will create a stored procedure in the "higher" database
USE dbForJsonIssues;
GO
--Attention: replacing FROM is a very hacky way... Read the hints at the end...
--You might use parameters for the JSON-string and the JSON-path, but then you must use sp_executesql
CREATE PROCEDURE EXEC_Json_Command #Statement NVARCHAR(MAX), #TargetTable NVARCHAR(MAX)
AS
BEGIN
DECLARE #statementWithTarget NVARCHAR(MAX)=REPLACE(#Statement,'FROM',CONCAT(' INTO ',#TargetTable,' FROM'));
PRINT #statementWithTarget; --you can out-comment this line...
EXEC(#statementWithTarget);
END
GO
--Now we go into the "lower" database
USE dbOld;
GO
--A synonym is not necessary, but allows for easier code
CREATE SYNONYM dbo.ExecJson FOR dbForJsonIssues.dbo.EXEC_Json_Command;
GO
--This is how to use it
DECLARE #json NVARCHAR(MAX)=N'[{"someObject":[{"attr1":"11", "attr2":"12"},{"attr1":"21", "attr2":"22"}]}]';
DECLARE #Statement NVARCHAR(MAX)=CONCAT(N'SELECT * FROM OPENJSON(N''',#json,N''',''$[0].someObject'') WITH(attr1 INT,attr2 INT)');
--the target table will be created "on the fly"
--You can use ##SomeTarget too, but be careful with concurrencies in both approaches...
EXEC ExecJson #Statement=#Statement,#TargetTable='dbOld.dbo.SomeTarget';
SELECT * FROM SomeTarget;
--We can drop this table after dealing with the result
DROP TABLE SomeTarget;
GO
--Clean-up (carefull with real-data!)
USE master;
GO
DROP DATABASE dbOld;
DROP DATABASE dbForJsonIssues;
The most important concepts:
We cannot use the JSON-statements directly within the database, but we can create a statement on string base, pass it to the stored procedure and use EXEC() for its execution.
Using SELECT * INTO SomeDb.SomeSchema.SomeTargetTable FROM ... will create a table with the fitting structure. Make sure to use a table not existing in your database.
It is not really needed to pass the target table as parameter, you might place this in the statement yourself. Replacing the FROM in the stored procedure is a very shrewed way and could lead into troubles if from is found in another place.
You might use similar procedures for various needs...
Yeah. No way this would pass the smoke screen at our office. Anyway someone asked me to do something similar, but the use case was for parsing json arrays only. Since Json_Query and Json_Value are available I hacked this together just to give them something to work with. My colleague liked the results. Turns out he's much cooler than I am after he modified it.
Declare #Fields NVarchar(2000) = 'Name,Coolness'
Declare #Delimiter As Varchar(10) = ',';
Declare #Xml As Xml = Cast(('<V>' + Replace(#Fields, #delimiter, '</V><V>') + '</V>' ) As Xml);
Declare #Json Nvarchar(4000) = N'{"Examples":[{"Name": "Chris","Coolness": "10"},{"Name": "Jay","Coolness": "1"}]}';
Exec ('Begin Try Drop Table #JsonTemp End Try Begin Catch End Catch');
Create Table #JsonTemp (JsonNode Nvarchar(1000));
Declare #Max INTEGER = 100;
Declare #Index INTEGER = 0;
While #Index < #Max
Begin
Declare #Affected Integer = 0;
Declare #Select Nvarchar(200) = '''' + 'lax$.Examples[' + Convert(Nvarchar, #Index) + ']' + '''';
Declare #Statement Nvarchar(2000)= 'Select Json_Query(' + '''' + #Json + '''' + ', ' + #Select + ') Where Json_Query(' + '''' + #Json + '''' + ', ' + #Select + ') Is Not Null';
Insert Into #JsonTemp (JsonNode) Exec sp_executesql #Statement;
Set #Affected = ##RowCount;
If (#Affected = 0) Begin Break End
Set #Index = #Index + 1;
End
Declare #Table Table(Field NVarchar(200));
Declare #Selector NVarchar(500) = 'Json_Value(' + '''' + '{"Node":' + '''' + ' + ' + 'JsonNode' + ' + ' + '''' + '}' + '''' + ', ' + '''' + '$.Node.#Field' + '''' + ')';
Insert Into #Table(Field)
Select N.value('.', 'Varchar(10)') As Field
From #XML.nodes('V') As A(N);
Declare #Selectors Varchar(8000);
Select #Selectors = Coalesce(#Selectors + ', ', '') + Replace(#Selector, '#Field', Field) + ' As ' + Field
From #Table
Exec ('Select ' + #Selectors + ' From #JsonTemp');

scheduled execution of all TSQL scripts in a folder

I have a bunch of TSQL scripts that need to be executed on a daily basis.
I know I can use Job Agent to execute them but that requires me to change the actual job.
What I would like to do is create a job that simply says:
execute all TSQL-scripts in <some folder>
A bonus would be if a filter could be used based on the filename of the script: So that one job would execute all the files whose name start with a 'd', another job would execute all those with a 'w' in the name.
Can this be done? How can this be done?
I read some things that spoke of using the Windows-scheduler to run the SQLCMD-utility.
I'd rather have the SQL Server do the scheduling and executing. Is Powershell the way to go? If so, where and how to start? (Never had to use it so never gave it much attention :/)
Thanks for thinking with me!
Henro
To execute a script from a file you can use:
DECLARE #dir varchar(100) = 'C:\MyDir\'
DECLARE #file varchar(100) = 'myScript.sql'
DECLARE #cmd varchar(100) = 'sqlcmd -S ' + ##SERVERNAME + ' -i ' + #dir + #file
EXECUTE dbo.xp_cmdshell #command_string = #cmd
To get the list of files from a dir you can use
CREATE TABLE #tbl (Name varchar(400))
DECLARE #cmd varchar(100) = 'dir ' + #dir + ' *.sql'
INSERT #tbl EXECUTE dbo.xp_cmdshell #command_string = #cmd
DELETE FROM #tbl WHERE ISDATE(SUBSTRING(Name,1,10)) = 0
UPDATE #tbl SET Name = SUBSTRING(Name, 40, 100)
I guess you can do one thing:
Put all the scripts starting with name 'd' in one sproc.Here you have to put Go after each of your scripts in sproc.
Similarly create one more sproc with all the scripts starting with letter 'w'
Then schedule these jobs in sql server agent.
João, thanks for your help. Using your code I produced this: set dateformat dmy
DECLARE #scripts varchar(100) = 'C:\MYSCRIPTS\' -- folder with scripts
DECLARE #project varchar(100) = 'PROJECTX' -- specific folder
DECLARE #Identifier varchar(1) = 'D' -- All files of which the name starts with a 'T'
DECLARE #files varchar(100) = #scripts + #project + '\' + #Identifier + '*.sql'
CREATE TABLE #tbl1 (Name varchar(400))
DECLARE #cmd varchar(100) = 'dir ' + #files
INSERT #tbl1 EXECUTE master.dbo.xp_cmdshell #command_string = #cmd
DELETE FROM #tbl1 WHERE ISDATE(SUBSTRING(Name,1,10)) = 0
UPDATE #tbl1 SET Name = SUBSTRING(Name,37, 100)
CREATE TABLE #tbl2 (Counter smallint Primary Key IDENTITY(1,1), Name varchar(400))
INSERT INTO #tbl2 (Name)
Select #scripts + #project + '\' + Name from #tbl1
DECLARE #i int
DECLARE #NumRows int
DECLARE #File2BExecuted varchar(100)
SET #i = 1
SET #NumRows = (SELECT COUNT(*) FROM #tbl2)
IF #NumRows > 0
WHILE (#i <= (SELECT MAX(Counter) FROM #tbl2))
BEGIN
SELECT #File2BExecuted = Name FROM #tbl2 WHERE Counter = #i
DECLARE #script varchar(100) = 'sqlcmd -S ' + ##SERVERNAME + ' -i ' + #File2BExecuted
EXECUTE master.dbo.xp_cmdshell #command_string = #script
SET #i = #i + 1
END
drop table #tbl1
drop table #tbl2

how to use openrowset to execute a stored procedure with parameters

I'm creating a stored procedure which gets some parameters and in turn these parameters are sent to another stored procedure which I'm calling from openrowset but I'm getting some syntax errors.
CREATE PROCEDURE UpdatePrevFYConfigData
-- Add the parameters for the stored procedure here
#startDate datetime,
#endDate datetime,
#productGroup varchar(8000) = 'All',
#projectType varchar(500) = 'All',
#businessUnit nvarchar(50) = 'All',
#developmentLocation nvarchar(100) = 'All'
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #start varchar(50)
declare #end varchar(50)
set #start = cast(#startDate as varchar(40))
set #end = cast(#endDate as varchar(40))
-- Insert statements for procedure here
select round(avg(a.DeviationDeadline),2) as DeviationDeadline,
round(avg(a.DeviationDefinition),2) as DeviationDefinition,
round(avg(a.DeviationRDCosts),2) as DeviationRDCosts,
round(avg(a.FunctionsAdded) + avg(a.FunctionsDeleted),2) as NotRealizedFuncs,
round(avg(a.DeviationPM2000Aufwand),2) as DeviationPM200Aufwand,
round(avg(b.Defect),2) as Defect
into #tempTable
from openrowset('SQLNCLI',
'Server=.\sqlexpress;Trusted_Connection=yes;',
'SET NOCOUNT ON;SET FMTONLY OFF;EXEC [BSC_DB].dbo.SelectScorecardGraphData
'''+#start+''',
'''+#end+''',
'''+#productGroup+''',
'''+#projectType+''',
''1'',
''0'',
''All'',
''Current'',
'''+#businessUnit+''',
'''+#developmentLocation+'''
') as a,
openrowset('SQLNCLI', 'Server=.\sqlexpress;Trusted_Connection=yes;', 'SET NOCOUNT ON;SET FMTONLY OFF;EXEC [BSC_DB].dbo.GetSPCDefectDistributionData
'''+cast(#startDate as varchar(40))+''',
'''+cast(#endDate as varchar(40))+''',
''Defect'',
'''+#projectType+''',
'''+#productGroup+''',
'''+#businessUnit+''',
'''+#developmentLocation+'''') as b
update dbo.EA_ProcessScorecard_Config_Tbl
set EPC_Deviation = case EPC_Metric
when 'PM200' then (select DeviationDefinition from #tempTable)
when 'PM300' then (select DeviationDeadline from #tempTable)
when 'Cost' then (select DeviationRDCosts from #tempTable)
when 'PM150' then (select DeviationPM200Aufwand from #tempTable)
when 'Defect' then (select Defect from #tempTable)
when 'Funcs' then (select NotRealizedFuncs from #tempTable)
END
where EPC_Description = 'PrevFY' and EPC_FYYear = '0'
drop table #tempTable
END
GO
I'm not able to create it and I get the error message:
Msg 102, Level 15, State 1, Procedure UpdatePrevFYConfigData,
Line 38 Incorrect syntax near '+'.
... but if I use hard coded values for the parameters it works!!
Please help!
Both OPENROWSET and OPENDATASOURCE should be used only for accessing external data for, let's say, quick and dirty solutions, or when it is not possible to configure a permanent linked server. These functions do not provide all of the functionality available from a linked server.
The arguments of OPENROWSET and OPENDATASOURCE do not support variables. They have to be specified as string-literal. If variables need to be passed in as arguments to these functions, a query string containing these variables can be constructed dynamically and executed using the EXEC statement.
Similar to (not syntax checked)
DECLARE #sqlCommand varchar(1000)
SET #sqlCommand = 'SELECT *
FROM OPENROWSET(''SQLNCLI'',''server=.\sqlexpress;Trusted_Connection=yes'',''SET NOCOUNT ON;SET FMTONLY OFF;EXEC [BSC_DB].dbo.SelectScorecardGraphData ''''' + cast(#param1 as varchar(10)) + ''''',''' + cast(#param2 as varchar(n)) ''')'
EXEC #sqlCommand
And so on...
Hope that helps. Kind regards,
Stefan
-- FOR USING OPENROWSETS
EXEC sp_configure 'Ad Hoc Distributed Queries'
,1
RECONFIGURE
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = 'INSERT INTO #TABLESIZESYEAR SELECT NULL AS [TABLE NAME], * FROM OPENROWSET
(''SQLOLEDB'',''Server=(local);TRUSTED_CONNECTION=YES;'',''set fmtonly off EXEC one.[dbo].[InvestigateDataGrowthByYearAndClient] #pDATECOLUMN =' + #YEARCOLUMN + ' ,
#pTABLENAME = ' + #TABLENAME + ' WITH RESULT SETS(
([YEAR NAME] NVARCHAR(5) NULL
, [NUMBER OF ROWS] CHAR(11)
, [RESERVED SPACE] VARCHAR(18)
, [DATA SPACE] VARCHAR(18)
, [INDEX SIZE] VARCHAR(18)
, [UNUSED SPACE] VARCHAR(18) )
)
;'') '
DECLARE #ParmDefinition NVARCHAR(500) = '#pDATECOLUMN NVARCHAR(20)
,#YEARCOLUMN NVARCHAR(20)
,#pTABLENAME NVARCHAR(60)';
EXECUTE sp_executesql #sql
,#ParmDefinition
,#YEARCOLUMN = #YEARCOLUMN
,#pDATECOLUMN = #YEARCOLUMN
,#pTABLENAME = #TABLENAME

How to CREATE TYPE type_name AS existing_table_name

Is there a way to create table type in SQL Server 2008 based on scheme of existing table?
CREATE TABLE A (id INT, name VARCHAR(30))
CREATE TYPE type_a AS TABLE.A
Something like that.
No, this kind of composable DML is not yet possible. Microsoft has rejected this suggestion in the past, but with enough votes (e.g. more than 1!) it may get reconsidered in the future:
http://connect.microsoft.com/SQLServer/feedback/details/294130/table-valued-parameters-add-support-for-create-type-type-from-table-table-name-options-syntax-construct
You can use following stored procedure to create a type with same schema existing table may have.
Create PROCEDURE [dbo].[Sp_DefineTypeOutOfTableSchema]
#TableNames NVARCHAR(500)
AS
BEGIN
DECLARE #TableName NVARCHAR(100)
DECLARE #strSQL NVARCHAR(max)
DECLARE #strSQLCol NVARCHAR(1000)
DECLARE #ColName NVARCHAR(100)
DECLARE #ColDataTaype NVARCHAR(50)
DECLARE #ColDefault NVARCHAR(50)
DECLARE #ColIsNulable NVARCHAR(50)
DECLARE #ColCharMaxlen NVARCHAR(50)
DECLARE #ColNumPrec NVARCHAR(50)
DECLARE #ColNumScal NVARCHAR(50)
IF LEN(#TableNames) > 0 SET #TableNames = #TableNames + ','
WHILE LEN(#TableNames) > 0
BEGIN
SELECT #TableName = LTRIM(SUBSTRING(#TableNames, 1, CHARINDEX(',', #TableNames) - 1))
DECLARE schemaCur CURSOR FOR
SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME =#TableName
OPEN schemaCur
SELECT #strSQL=''
FETCH NEXT FROM schemaCur
INTO #ColName,#ColDataTaype,#ColIsNulable,#ColDefault,#ColCharMaxlen,#ColNumPrec,#ColNumScal
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #strSQLCol=''
SELECT #strSQLCol= '['+#ColName+'] '+'[' + #ColDataTaype +'] '
IF #ColDataTaype='nvarchar' or #ColDataTaype='char' or #ColDataTaype='varchar' or #ColDataTaype='vchar'
BEGIN
SELECT #strSQLCol=#strSQLCol+ '(' + #ColCharMaxlen +') '
END
ELSE IF #ColDataTaype='numeric' or #ColDataTaype='decimal'
BEGIN
SELECT #strSQLCol=#strSQLCol +'(' + #ColNumPrec +',' +#ColNumScal + ') '
END
IF #ColIsNulable='YES'
BEGIN
SELECT #strSQLCol=#strSQLCol+ 'NULL '
END
ELSE
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' NOT NULL '
END
IF #ColDefault IS NOT NULL
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' DEFAULT(' +#ColDefault + '),'
END
ELSE
BEGIN
SELECT #strSQLCol=#strSQLCol+ ' ,'
END
SELECT #strSQL=#strSQL+#strSQLCol
--print #strSQL
FETCH NEXT FROM schemaCur
INTO #ColName,#ColDataTaype,#ColIsNulable,#ColDefault,#ColCharMaxlen,#ColNumPrec,#ColNumScal
END
CLOSE schemaCur
DEALLOCATE schemaCur
--print #strSQL
SELECT #strSQL=left( #strSQL, len(#strSQL)-1)
--print #strSQL
IF EXISTS (SELECT * FROM sys.types WHERE IS_TABLE_TYPE = 1 AND name = 't_' +#TableName)
BEGIN
EXEC('DROP TYPE t_' +#TableName )
END
SELECT #strSQL = 'CREATE TYPE t_' + #TableName + ' AS TABLE (' + #strSQL + ')'
--print #strSQL
EXEC (#strSQL)
SELECT #TableNames = SUBSTRING(#TableNames, CHARINDEX(',', #TableNames) + 1, LEN(#TableNames))
END
END
you can use it like this
Exec Sp_DefineTypeOutOfTableSchema 'Table1name,Table2name'
You could experiment with creating a function that pulled the table definition out of sysobjects, systypes, syscolumns, syscomments, etc., and built a CREATE statement out of it. You'd just have to make sure to grab all of the important pieces (columns, constraints, comments, etc.) from the various sys tables.
Then call it like... EXEC myCREATEtable #template_table_name or some such...