Getting the sql server default backup folder on Linux with T-SQL - sql-server

For SQL Server on Windows for getting default backup folder we can use master.dbo.xp_instance_regread:
DECLARE #HkeyLocal nvarchar(18) = N'HKEY_LOCAL_MACHINE';
DECLARE #MSSqlServerRegPath nvarchar(31) = N'SOFTWARE\Microsoft\MSSQLServer';
DECLARE #InstanceRegPath sysname = #MSSqlServerRegPath + N'\MSSQLServer';
DECLARE #BackupDirectory nvarchar(512)
if 1=isnull(cast(SERVERPROPERTY('IsLocalDB') as bit), 0)
SET #BackupDirectory=cast(SERVERPROPERTY('instancedefaultdatapath') as nvarchar(512))
else
EXEC master.dbo.xp_instance_regread #HkeyLocal, #InstanceRegPath, N'BackupDirectory', #BackupDirectory OUTPUT;
SELECT #BackupDirectory AS SQLServerBackupDirectory;
But for Linux it does not work. Can anyone help with T-SQL approach (I only find SSMS solution here)?

I've never found a non-registry way to get the backup directory reliably in t-sql. I usually just do a replace of /data/ to /backup/ since at default install it's relation to the data directory is clear. This does not directly answer your question but it's a working way provided you have not done individual directory customization during install.
-- for one of my databases, [dharma] in my linux test-bed.
declare #bkup nvarchar(1024)
set #bkup = replace(cast(SERVERPROPERTY('instancedefaultdatapath') as nvarchar(512)),'/data/','/backup/') + 'dharma.bak'
backup database dharma to disk = #bkup

In SQL Server v2019, Microsoft enhanced ServerProperty to expose InstanceDefaultBackupPath. Spoke of same here ( https://learningintheopen.org/2020/06/08/sql-server-configuration-default-directories/ )

Related

T-SQL query to return the location of an application .exe

What is the best way to find the path of an application, that could be installed anywhere on multiple types of PC/operating system.
I need to do this in SQL Server, the version could be 2008 all the way up to 2014.
I have the following that works well and returns what I need.
DECLARE #findpath varchar(500) = 'powershell.exe -noprofile (get-itemproperty -literalpath ''HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\App.exe'').''(default)'''
DECLARE #foundpath table(id int identity(1,1), [path] varchar(500))
INSERT #foundpath
EXEC xp_cmdshell #findpath
My problem is the application doesn't always exist under that registry location so I need an alternative way of finding it.
Presuming the application is installed to the same directory as the SQL database files, I have tried using the WHERE command but I can't always guarantee the application is going to be installed to the same location, and sometimes it may be installed to a different drive letter.
IF (SELECT left([path],16) FROM #foundpath WHERE id = 1) = 'Get-ItemProperty'
BEGIN
DECLARE #where varchar(500) = 'WHERE /R '+(
SELECT distinct vs.volume_mount_point
FROM sys.master_files AS f WITH (NOLOCK)
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.[file_id]) AS vs )+' '+'App.exe'
INSERT #foundpath
EXEC xp_cmdshell #where
END
Perhaps you can list the apps shown in the GUI of "Add or remove programs" using the command line. Maybe you can use wmic product get description (I ran it in PowerShell), which I found by looking around online. It took a while to run and doesn't include all my apps.
Perhaps the app does something consistently that you can rely on. For example, it might set an env variable (e.g., PATH) regardless of where the installation directory is.

SQL Server 2012 -- xp_fileexist returns 0 when checking for file on local machine

The SQL server exists on the same machine I am physically logged into, and xp_fileexist is failing to recognize any files on the D drive, which is not a network drive. I already configured xp_cmdshell and restarted the SQL server instance. Any other ideas?
Yup, we had the same problem. On SQL Server 2008, our legacy xp_fileexist code worked fine, but on SQL Server 2012... nope.
It would work if we ran the xp_fileexist command as ourselves (with Admin rights) but not when we ran it as a SQL Server user, who didn't exist as an Active Directory user. Even if we changed the security on that folder to give Everyone full permissions, the xp_fileexist would fail, always returning a 0, as if a file within that folder didn't exist.
However, what did work was to use dir from within a Stored Procedure, and test if the file existed that way. (Yeah, I know... I'm rolling my eyes myself... this is dodgy..)
Here's the Stored Procedure I wrote, based on suggestions on this site :
CREATE PROCEDURE [dbo].[DoesFileExist]
(
#directoryName NVARCHAR(500),
#filename NVARCHAR(500)
)
AS
BEGIN
-- Does a file exist in a particular folder ?
--
-- EXEC [dbo].[DoesFileExist] 'D:\MyFiles', 'SomeExcelFile.xls'
--
DECLARE #bFileExists INT
DECLARE #cmd nvarchar(300);
SELECT #cmd = 'dir ' + #directoryName + '\' + #filename;
DECLARE #dir TABLE ([output] varchar( 2000 ))
INSERT INTO #dir
EXEC master.dbo.xp_cmdshell #cmd;
-- Uncomment the following line, if you want to see what
-- a "dir" looks like from SQL Server !!
-- SELECT * FROM #dir
if EXISTS(SELECT * FROM #dir WHERE [output] LIKE '%' + #filename + '%' )
BEGIN
-- File *was* found in this folder
SET #bFileExists = 1
END
ELSE
BEGIN
-- File was NOT found in this folder
SET #bFileExists = 0
END
SELECT #bFileExists
END
You can call this SP simply by passing it a folder name and a filename:
EXEC [dbo].[DoesFileExist] 'D:\MyFiles', 'SomeExcelFile.xls'
And yes, strangely, it does seem to work, for the SQL Server users who can't use xp_fileexist.
Remember that, to use this code, your SQL Server user must have permissions to use xp_cmdshell :
GRANT EXECUTE ON xp_cmdshell TO YourSQLServerUser

Configure SQL Server startup parameters from batch file

I need to add start-up trace flags to a SQL Service from a batch file. I have struggled to work out how to do this, and have finally come-up with the following, slightly clumsy approach, of using undocumented TSQL procedure and then calling it with SQLCMD. The problem is that it throws an error: RegCreateKeyEx() returned error 5, 'Access is denied.' However, another script on the same server, allows me to change other registry values, such as TCP Port. Any idea what I'm doing wrong, or a more elegant way of doing it?
DECLARE #VALUE nvarchar(200)
DECLARE #Key nvarchar(2000)
SET #VALUE = '-T1118 -3604 -E'
SET #Key = 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL11.' + (select SUBSTRING(##SERVERNAME,CHARINDEX('\',##SERVERNAME)+1,LEN(##SERVERNAME)-1)) + '\MSSQLServer\Parameters'
EXECUTE master..xp_regwrite
'HKEY_LOCAL_MACHINE',
#Key,
'SQLArg3',
'REG_SZ',
#VALUE
The expression to create the #Key variable elavuates to: SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL11.MYINSTANCE\MSSQLServer\Parameters
which seems correct to me.
The registry key for the default instance of SQL Server is:
SET #Key = 'SOFTWARE\Microsoft\Microsoft SQL Server
\MSSQL11.MSSQLSERVER\MSSQLServer\Parameters';
(I wrapped the key above for readability - you need this all on one line.)
Unless you have named instances, you can use this #Key instead of yours. If you have named instances on the servers, you'd need to inspect SOFTWARE\Microsoft\Microsoft SQL Server\InstalledInstances to obtain the name(s) of the instance(s) on the server.
I imagine there is more elegant way of doing this using PowerShell.

How can I copy an entire database to another on the same server?

I want to copy a database A into database B (same SQL Server) entirely - what would be the easiest SQL or utility command to do that? (database B should be 100% erased before the copy)
Note that it can be non-SQL thing, but in any case I need to run it as one-click. So some utility that does that is also acceptable as far as I can run copying with one click or from command line.
SQL Server 2008R2 or 2012.
Best, Askar
I made a soultion based on David's thoughts. It looks like this (Prod - source DB, Demo - destination copy)
DECLARE #backup varchar(300) = 'e:\Prod.bak';
DECLARE #logic_db_file varchar(300) = 'koe';
DECLARE #logic_log_file varchar(300) = 'koe_log';
DECLARE #out_db_file varchar(300) = 'c:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\Demo.mdf';
DECLARE #out_log_file varchar(300) = 'c:\Program Files\Microsoft SQL Server\MSSQL11.SQLEXPRESS\MSSQL\DATA\Demo_log.ldf';
PRINT N'Backing up Production database.'
BACKUP DATABASE Prod
TO DISK = #backup WITH FORMAT;
PRINT N'Restoring FILELISTONLY, see log file for data.'
-- Restore the files to Demo
RESTORE FILELISTONLY
FROM DISK = #backup
PRINT N'Getting exclusive access to Demo'
Alter database Demo set single_user with rollback immediate
drop database Demo
PRINT N'Restoring backup to Test database'
RESTORE DATABASE Demo
FROM DISK = #backup
WITH REPLACE,
MOVE #logic_db_file TO #out_db_file,
MOVE #logic_log_file TO #out_log_file
GO
-- if you want you can do some SQL fixes to newly made DB
PRINT N'Changing normal users to demo users, if account type is OK'
USE Demo;
UPDATE USER_ACCOUNTS SET ACCOUNT_TYPE=3 WHERE (STATUS=1) AND (ACCOUNT_TYPE=1);
GO

How to have database name as variable in an SP?

We use stored procedures exclusively here, and that raises a little problem. We cross reference two different databases like dbdev..table1 in dev, dbqa..table1 in qa, and dbprod..table1 in production.
So every time we deploy to a different environment, we have to search and replace from dbdev to dbqa or dbprod.
Is there a way to use synonym or whatever sql server mechanics to solve problem?
Use sqlcmd variables, which are supported by sqlcmd deployment of .sql provisioning scripts,a s well as by VSDB projects. So your provisioning script looks like:
create procedure usp_myProc
as
select .. from [$(crossdb)]..table1;
go
When deploying it in production you run sqlcmd /E /I provisoning.sql /v crossdb=dbprod, while the QA deployment will be done via sqlcmd /E /I provisioning.sql /v crossdb=dbqa. See Using sqlcmd with Scripting Variables.
As a side note, I am working on a project that allows sqlcmd variables to be used from .Net SqlClient (SqlConnection, SqlCommand): the dbutilsqlcmd project.
SQL Server 2005 supports synonyms, so you can create synonym1 to refer to dbdev..table1 in dev environment, and to dbprod..table1 in prod environment. Your SP's (and probably views) just operate on the synonyms.
Update:
The easiest way to create synonyms:
exec sys.sp_MSforeachtable
'print ''CREATE SYNONYM '' + REPLACE(''?'', ''].['', ''].[syn_'') +
'' FOR [my_database].?
GO'''
(there is a line break before GO)
Run and paste result into new query window.
No.
It is not possible to create a Synonym for a database. That is a popular request though.
Is it really necessary to rename your databases for dbdev, dbqa, dbprod etc. though?
Dynamic sql
(forgive potential typos, but the concept is there)
Declare #dbname nvarchar(255), #sql nvarchar(max)
set #dbname = 'db1'
Set #sql = 'Select * From ' + #dbname + '.dbo.table1'
exec sp_executesql #sql
You can have the database name as a parameter of your stored procedure, then use Dynamic SQL to construct your queries.
Ex:
CREATE PROC MyStoredProcedure #DBName VARCHAR(50)
AS
DECLARE #SQL VARCHAR(MAX)
SET #SQL = 'SELECT * FROM ' + #DBName + '.dbo.table1'
EXEC sp_executesql #SQL
Then you would simply call your stored procedure with the appropriate DB Name:
EXEC MyStoredProcedure 'dbdev'

Resources