81

We have a few huge databases (20GB+) which mostly contain static lookup data. Because our application executes joins against tables in these databases, they have to be part of each developers local SQL Server (i.e. they can't be hosted on a central, shared database server).

We plan on copying a canonical set of the actual SQL Server database files (*.mdf and *.ldf) and attach them to each developer's local database.

What's the best way to find out the local SQL Server instance's data directory so we can copy the files to the right place? This will be done via an automated process, so I have to be able to find and use it from a build script.

Alex Aza
  • 76,499
  • 26
  • 155
  • 134
Aaron Jensen
  • 25,861
  • 15
  • 82
  • 91

15 Answers15

111

It depends on whether default path is set for data and log files or not.

If the path is set explicitly at Properties => Database Settings => Database default locations then SQL server stores it at Software\Microsoft\MSSQLServer\MSSQLServer in DefaultData and DefaultLog values.

However, if these parameters aren't set explicitly, SQL server uses Data and Log paths of master database.

Bellow is the script that covers both cases. This is simplified version of the query that SQL Management Studio runs.

Also, note that I use xp_instance_regread instead of xp_regread, so this script will work for any instance, default or named.

declare @DefaultData nvarchar(512)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'DefaultData', @DefaultData output

declare @DefaultLog nvarchar(512)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'DefaultLog', @DefaultLog output

declare @DefaultBackup nvarchar(512)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'BackupDirectory', @DefaultBackup output

declare @MasterData nvarchar(512)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer\Parameters', N'SqlArg0', @MasterData output
select @MasterData=substring(@MasterData, 3, 255)
select @MasterData=substring(@MasterData, 1, len(@MasterData) - charindex('\', reverse(@MasterData)))

declare @MasterLog nvarchar(512)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer\Parameters', N'SqlArg2', @MasterLog output
select @MasterLog=substring(@MasterLog, 3, 255)
select @MasterLog=substring(@MasterLog, 1, len(@MasterLog) - charindex('\', reverse(@MasterLog)))

select 
    isnull(@DefaultData, @MasterData) DefaultData, 
    isnull(@DefaultLog, @MasterLog) DefaultLog,
    isnull(@DefaultBackup, @MasterLog) DefaultBackup

You can achieve the same result by using SMO. Bellow is C# sample, but you can use any other .NET language or PowerShell.

using (var connection = new SqlConnection("Data Source=.;Integrated Security=SSPI"))
{
    var serverConnection = new ServerConnection(connection);
    var server = new Server(serverConnection);
    var defaultDataPath = string.IsNullOrEmpty(server.Settings.DefaultFile) ? server.MasterDBPath : server.Settings.DefaultFile;
    var defaultLogPath = string.IsNullOrEmpty(server.Settings.DefaultLog) ? server.MasterDBLogPath : server.Settings.DefaultLog;
}

It is so much simpler in SQL Server 2012 and above, assuming you have default paths set (which is probably always a right thing to do):

select 
    InstanceDefaultDataPath = serverproperty('InstanceDefaultDataPath'),
    InstanceDefaultLogPath = serverproperty('InstanceDefaultLogPath')
Joseph Woodward
  • 9,191
  • 5
  • 44
  • 63
Alex Aza
  • 76,499
  • 26
  • 155
  • 134
  • Doesn't this require installation of another package -- SMO SDK? – BSalita Jul 01 '13 at 18:41
  • 1
    @BSalita - if you have SQL server installed than you already have SMO installed. You just need to reference assemblies from `C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies`, for example `Microsoft.SqlServer.Smo.dll` and `Microsoft.SqlServer.ConnectionInfo.dll`. However, if you connect from the machine that does not have SQL server installed you would need to install SMO. Look up for 'Microsoft SQL Server 2012 Feature Pack' to find a standalone SMO installer. – Alex Aza Jul 02 '13 at 12:05
  • Thanks for including the C# sample - that was incredibly helpful. – TrailJon May 11 '15 at 15:46
  • 2
    It works but I'm getting error message in results. `RegQueryValueEx() returned error 2, 'The system cannot find the file specified.'` `Msg 22001, Level 1, State 1` `RegQueryValueEx() returned error 2, 'The system cannot find the file specified.'` `Msg 22001, Level 1, State 1` – Dima Korobskiy Aug 21 '15 at 15:10
  • Trying to identify this for SQL 2005, where the last solution doesn't work. However, after searching the ENTIRE registry for any keyword as you mentioned such as `Database default locations`, I cannot find it anywhere - not for SQL2005, SQL2008, or SQL2012. I searched the entire `HKEY_LOCAL_MACHINE` for that keyword, as well as the actual path(s) themselves. Not a trace, for any version. And trying via TSQL, I get the same error as @DKroot. Guess I'll have to cheat and use the directories of the master database files. – Jerry Dodge Nov 11 '19 at 22:52
62

Even though this is a very old thread, I feel like I need to contribute a simple solution. Any time that you know where in Management Studio a parameter is located that you want to access for any sort of automated script, the easiest way is to run a quick profiler trace on a standalone test system and capture what Management Studio is doing on the backend.

In this instance, assuming you are interested in finding the default data and log locations you can do the following:

SELECT
  SERVERPROPERTY('instancedefaultdatapath') AS [DefaultFile],
  SERVERPROPERTY('instancedefaultlogpath') AS [DefaultLog]
davetapley
  • 17,000
  • 12
  • 60
  • 86
TDrudge
  • 747
  • 5
  • 5
33

I stumbled across this solution in the documentation for the Create Database statement in the help for SQL Server:

SELECT SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1)
                  FROM master.sys.master_files
                  WHERE database_id = 1 AND file_id = 1
Aaron Jensen
  • 25,861
  • 15
  • 82
  • 91
  • 41
    Wow, surprisingly so many upvotes for the incorrect answer. This query will show the path of the master database data file, not default data files path. Default data files path may or may not be the same as master.mdf file path. This 'solution' will not work if you store user databases separately from system databases, which is not uncommon in big production environments. – Alex Aza Feb 18 '13 at 01:59
  • @AlexAza That's a lot more code than I wanted, which may just be my own problem. :-) [What about this answer?](http://stackoverflow.com/a/16681314/125356) – Aaron Jensen Mar 19 '14 at 19:53
  • Quick comment - knock the SUBSTRING starting_position value down to 0 to get the full clean path without the first character of the master.mdf file name. – mojo Jul 08 '14 at 07:34
  • We are using that solution because we don't want to open the registry to know the answer. But, we created a database in the place we want others to be created, and we are looking for that database information instead of "master". `SELECT physical_name FROM sys.master_files mf inner JOIN sys.databases d ON d.database_id = mf.database_id AND d.name = 'our-template-database' AND TYPE = 0` – foxontherock Jul 08 '14 at 14:38
  • 1
    @AlexAza I agree, it's a sloppy solution, but for older versions of SQL Server, other solutions don't seem to work, such as the registry. Hence the comment(s) on your answer. Reading from the registry almost seems like a hack too. – Jerry Dodge Nov 11 '19 at 22:59
27

For the current database you can just use:

select physical_name fromsys.database_files;

to specify another database e.g. 'Model', use sys.master_files

select physical_name from sys.master_files where database_id = DB_ID(N'Model');

Stephen Turner
  • 7,125
  • 4
  • 51
  • 68
Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • 3
    +1. Worth noting that this needs to be run in the context of the DB whose files you want. (I spent some time fruitlessly looking for a way to join it to `sysdatabases`). – EM0 Aug 28 '12 at 03:40
  • @EM0 not true. You can query `sys.master_files` and join `sys.databases` from anywhere. `select * from sys.master_files f join sys.databases d on d.database_id = f.database_id` – pim Jun 13 '18 at 16:29
18

As of Sql Server 2012, you can use the following query:

SELECT SERVERPROPERTY('INSTANCEDEFAULTDATAPATH') as [Default_data_path], SERVERPROPERTY('INSTANCEDEFAULTLOGPATH') as [Default_log_path];

(This was taken from a comment at http://technet.microsoft.com/en-us/library/ms174396.aspx, and tested.)

Nathan
  • 10,593
  • 10
  • 63
  • 87
8

Various components of SQL Server (Data, Logs, SSAS, SSIS, etc) have a default directory. The setting for this can be found in the registry. Read more here:

http://technet.microsoft.com/en-us/library/ms143547%28SQL.90%29.aspx

So if you created a database using just CREATE DATABASE MyDatabaseName it would be created at the path specified in one of the settings above.

Now, if the admin / installer changed the default path, then the default path for the instance is stored in the registry at

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\[INSTANCENAME]\Setup

If you know the name of the instance then you can query the registry. This example is SQL 2008 specific - let me know if you need the SQL2005 path as well.

DECLARE @regvalue varchar(100)

EXEC master.dbo.xp_regread @rootkey='HKEY_LOCAL_MACHINE',
        @key='SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10.MSSQLServer\Setup',
        @value_name='SQLDataRoot',
        @value=@regvalue OUTPUT,
        @output = 'no_output'

SELECT @regvalue as DataAndLogFilePath

Each database can be created overriding the server setting in a it's own location when you issue the CREATE DATABASE DBName statement with the appropriate parameters. You can find that out by executing sp_helpdb

exec sp_helpdb 'DBName'
Raj More
  • 47,048
  • 33
  • 131
  • 198
  • This doesn't help because the database I would be querying doesn't exist yet. Is there an equivalent stored procedure for instances (as opposed to databases)? – Aaron Jensen Dec 11 '09 at 06:54
  • 1
    I'm sorry, but this solution is not correct. This registry query returned `null` for me in the first place. I changed `xp_regread` to `xp_instance_regread` and fixed the registry path `Software\Microsoft\MSSQLServer\Setup` to make query work on every SQL server instance despite the version. However, the location that the query returns isn't even close to my default data or logs files location. Also, how come it returns just one value for both data and log files? They are set to different values in my environment. It is not uncommon to have data and log files on different disks. – Alex Aza Jul 02 '13 at 12:28
  • Reading from `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\[INSTANCENAME]\Setup` gives access to the instance directory, but not the actual Data/Log file default directories. It has `SQLDataRoot`, however it does not include the subdirectory `\Data` which is the real location. – Jerry Dodge Nov 11 '19 at 23:02
6

Keeping it simple:

use master
select DB.name, F.physical_name from sys.databases DB join sys.master_files F on DB.database_id=F.database_id

this will return all databases with associated files

Deptor
  • 333
  • 3
  • 9
2

From the GUI: open your server properties, go to Database Settings, and see Database default locations.

Note that you can drop your database files wherever you like, though it seems cleaner to keep them in the default directory.

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
1

Small nitpick: there is no data folder, only a default data folder.

Anyway, to find it, assuming you want to install for the first default instance:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\Setup\SQLDataRoot

If there's a named instance, MSSQL.1 becomes something like MSSQL10.INSTANCENAME.

Stu
  • 15,675
  • 4
  • 43
  • 74
  • This requires that I know the SQL Server instance name (e.g. MSSQL.1, MSSQL.2, MSSQL._N_). How do I get that instance name? – Aaron Jensen Dec 10 '09 at 19:25
  • For default instances, it's not the name -- it's just the number. Which instance to install on is up to you! To find out which instances are installed, look at HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL – Stu Dec 10 '09 at 21:55
  • I need to attach my databases in the same instance where the other application instances are located. Essentially, I would need to convert the friendly instance name (the one that shows up in SQL Server Enterprise Manager, and which is the same across all machines) to this internal, "numeric" identifier. – Aaron Jensen Dec 11 '09 at 06:53
  • So look in Instance Names, find the instance name, look up the identifier (i.e. the value), then go back and look in the appropriate key. – Stu Dec 11 '09 at 17:16
0

You can find default Data and Log locations for the current SQL Server instance by using the following T-SQL:

DECLARE @defaultDataLocation nvarchar(4000)
DECLARE @defaultLogLocation nvarchar(4000)

EXEC master.dbo.xp_instance_regread
    N'HKEY_LOCAL_MACHINE',
    N'Software\Microsoft\MSSQLServer\MSSQLServer',
    N'DefaultData', 
    @defaultDataLocation OUTPUT

EXEC master.dbo.xp_instance_regread
    N'HKEY_LOCAL_MACHINE',
    N'Software\Microsoft\MSSQLServer\MSSQLServer',
    N'DefaultLog', 
    @defaultLogLocation OUTPUT

SELECT @defaultDataLocation AS 'Default Data Location',
       @defaultLogLocation AS 'Default Log Location'
Regent
  • 5,502
  • 3
  • 33
  • 59
0

Expanding on "splattered bits" answer, here is a complete script that does it:

@ECHO off
SETLOCAL ENABLEDELAYEDEXPANSION

SET _baseDirQuery=SELECT SUBSTRING(physical_name, 1, CHARINDEX(N'master.mdf', LOWER(physical_name)) - 1) ^
 FROM master.sys.master_files WHERE database_id = 1 AND file_id = 1;
ECHO.
SQLCMD.EXE -b -E -S localhost -d master -Q "%_baseDirQuery%" -W >data_dir.tmp
IF ERRORLEVEL 1 ECHO Error with automatically determining SQL data directory by querying your server&ECHO using Windows authentication.
CALL :getBaseDir data_dir.tmp _baseDir

IF "%_baseDir:~-1%"=="\" SET "_baseDir=%_baseDir:~0,-1%"
DEL /Q data_dir.tmp
echo DataDir: %_baseDir%

GOTO :END
::---------------------------------------------
:: Functions 
::---------------------------------------------

:simplePrompt 1-question 2-Return-var 3-default-Val
SET input=%~3
IF "%~3" NEQ "" (
  :askAgain
  SET /p "input=%~1 [%~3]:"
  IF "!input!" EQU "" (
    GOTO :askAgain
  ) 
) else (
  SET /p "input=%~1 [null]: "
)   
SET "%~2=%input%"
EXIT /B 0

:getBaseDir fileName var
FOR /F "tokens=*" %%i IN (%~1) DO (
  SET "_line=%%i"
  IF "!_line:~0,2!" == "c:" (
    SET "_baseDir=!_line!"
    EXIT /B 0
  )
)
EXIT /B 1

:END
PAUSE
djangofan
  • 28,471
  • 61
  • 196
  • 289
0

i would have done a backup restore simply becuase its easier and support versioning. Reference data especially needs to be versioned in order to know when it started taking effect. A dettach attach wont give you that ability. Also with backups you can continue to provide updated copies without having to shut down the database.

0

Alex's answer is the right one, but for posterity here's another option: create a new empty database. If you use CREATE DATABASE without specifying a target dir you get... the default data / log directories. Easy.

Personally however I'd probably either:

  • RESTORE the database to the developer's PC, rather than copy/attach (backups can be compressed, exposed on a UNC), or
  • Use a linked server to avoid doing this in the first place (depends how much data goes over the join)

ps: 20gb is not huge, even in 2015. But it's all relative.

Community
  • 1
  • 1
piers7
  • 4,174
  • 34
  • 47
0
SELECT DISTINCT dbo.GetDirectoryPath(filename) AS InstanceDataPaths
FROM sys.sysaltfiles WHERE filename like '%.mdf' and filename not like '%\MSSQL\Binn\%'

SELECT DISTINCT dbo.GetDirectoryPath(filename) AS InstanceLogPaths
FROM sys.sysaltfiles WHERE filename like '%.ldf' and filename not like '%\MSSQL\Binn\%'

enter image description here

You can download detail SQL script from how to find the data directory for a SQL Server instance

frank tan
  • 131
  • 1
  • 4
-1

You will get default location if user database by this query:

declare @DataFileName nVarchar(500)

declare @LogFileName   nVarchar(500)


set @DataFileName = (select top 1 RTRIM(LTRIM(name)) FROM master.sys.master_files where database_id >4 AND file_id = 1)+'.mdf'
set @LogFileName =   (select top 1 RTRIM(LTRIM(name)) FROM master.sys.master_files where database_id >4 AND file_id = 2)+'.ldf'

select  
( SELECT top 1 SUBSTRING(physical_name, 1, CHARINDEX(@DataFileName, LOWER(physical_name)) - 1) 
FROM master.sys.master_files
WHERE database_id >4 AND file_id = 1) as 'Data File'
,

(SELECT top 1 SUBSTRING(physical_name, 1, CHARINDEX(@LogFileName, LOWER(physical_name)) - 1)
FROM master.sys.master_files
WHERE database_id >4 AND file_id = 2)  as 'Log File'
GrandMasterFlush
  • 6,269
  • 19
  • 81
  • 104
  • won't work if master database is at a different location than the default location (if for example the default location was changed after master was created) – Allanrbo Mar 19 '14 at 11:08