How to configure a database so that filestream data is stored on a non local path?
To enable filestream at db level I do first:
ALTER DATABASE MyDatabase ADD
FILEGROUP FileStreamFileGroup CONTAINS FILESTREAM;
GO
Then:
ALTER DATABASE MyDatabase ADD FILE (
NAME = MyDatabaseFileStreamFile,
FILENAME = 'c:\Test')
TO FILEGROUP FileStreamFileGroup ;
GO
Now instead of
c:\Test
I want to set a network path, for example:
\\Fileserver\Test
but this doesn't work:
ALTER DATABASE MyDatabase ADD FILE (
NAME = MyDatabaseFileStreamFile,
FILENAME = '\\Fileserver\Test') -- THIS IS NOT ACCEPTED
TO FILEGROUP FileStreamFileGroup ;
GO
How to achieve the desired result?
This is not supported. Although filestream data may be accessed remotely by clients, it must be local with respect to the Sql Server instance hosting it.
Related
When a database is created a file group with a data file is created by default. Is it possible to add more than data file into a primary file group.
To add an data-file to primary filegroup:
USE [master]
GO
ALTER DATABASE [your_database]
ADD FILE
(NAME = N'logical_name',
FILENAME = N'C:\my_data.mdf', -- <--Path to physical file
SIZE = 1GB,
FILEGROWTH = 100MB
)
TO FILEGROUP [PRIMARY]
TITLE: Microsoft SQL Server Management Studio
Attach database failed for Server '(localdb)\mssqllocaldb'. (Microsoft.SqlServer.Smo)
ADDITIONAL INFORMATION:
At least one file is needed for Database Attach. (Microsoft.SqlServer.Smo)
I am trying to attach this .mdf database file to my LocalDb instance. It's fine if I can to it to SQL Server too. I have .ldf file in the same directory
For completion's sake - Jim's comment solves (half) the problem and gets you going.
The other "half" of the problem is - what if you ultimately want to rename the physical database file? The answer is available in this CodeProject post.
Steps:
ALTER DATABASE to set the new physical filenames (data file and log file)
Won't take effect until SQL Server is restarted or the database taken offline and brought back online
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName', FILENAME = '<Full-Path-Required>\NewDbName.mdf');
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName_log', FILENAME = '<Full-Path-Required>\NewDbName_log.ldf');
ALTER DATABASE again to set new logical file names (again, data and log files)
Takes effect immediately
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName', NEWNAME = 'NewDbName');
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName_log', NEWNAME = 'NewDbName_log');
Take offline and bring back online or restart SQL Server
Using SQL Server Management Studio:
Right-click on the renamed database and click Take Offline under Tasks.
Right-click on the (offline) database and click Bring Online under Tasks.
Using T-SQL:
ALTER DATABASE [CurrentName] SET OFFLINE WITH ROLLBACK IMMEDIATE; (sets it to offline and disconnects any clients)
ALTER DATABASE [CurrentName] SET ONLINE;
Full code:
-- Find "CurrentName" (without quotes) and replace with the current database name
-- Find "NewDbName" (without quotes) and replace with the new database name
USE [CurrentName];
-- Change physical file names:
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName', FILENAME = '<Full-Path-Required>\NewDbName.mdf');
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName_log', FILENAME = '<Full-Path-Required>\NewDbName_log.ldf');
-- Change logical names:
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName', NEWNAME = 'NewDbName');
ALTER DATABASE [CurrentName] MODIFY FILE (NAME = 'CurrentName_log', NEWNAME = 'NewDbName_log');
-- Take offline and back online
USE [master]
GO
ALTER DATABASE [CurrentName] SET OFFLINE WITH ROLLBACK IMMEDIATE;
-- Then navigate to <Full-Path-Required> and rename the files
ALTER DATABASE [CurrentName] SET ONLINE;
If you don't recall the previous filenames, open the .mdf file in a hex editor and at around offset 0x19D you'll see a UTF-16 (2 byte/char) string of that filename
None of these answers were quick to the point answers so I thought I would just add my answer to point out my findings (based off of everyone's contributions here)...
The Situation:
You have a database file and a log file but not a backup of them. You are trying to ATTACH the database (more than likely in an effort to recover from a server that went down).
The Problem:
You have changed the name of the MDF and LDF files to something different than they were originally. You need to rename them back to the original names then try to ATTACH.
How To Rename the DB files (the easy way):
After you have ATTACHED the MDF and LDF files successfully you then
want to make a BAK (backup) file by backing up the database.
Next you want to drop/delete the database from the SQL server.
Next you want to RESTORE the database. This is where you can go into the
FILES section (on left) that will let you change the Restore As
file name to what you want the MDF and LDF files to be named as.
I would then go ahead and make ANOTHER backup of that new database
again so that this time the backup contains the correct file names
you want.
I've had to move/rename DBs several times. In case you're in the same boat, here's a script that uses variables to avoid typing the new/old names over and over.
It uses the same logic from Jesse's answer, other than automatically starting the DB back up for you. I assume you need to turn it back on after moving/renaming the physical files, hence the removal of that statement. Please comment if this assumption is incorrect.
However, to reflect the logical rename in SSMS, you still need to right click -> rename. That appears to be the same without using the EXECUTE/REPLACE method below.
---------- CHANGE THESE ----------
-- Keep names identical to only move locations
DECLARE #CurrDbName AS varchar(255) = 'CurrentDbName'
DECLARE #NewDbName AS varchar(255) = 'NewDbName'
DECLARE #PathToFolder AS varchar(255) = '<FullPathMinusFilename>\'
---------- DECLARE TEMPLATES ----------
-- Use DB
DECLARE #USE_DB AS varchar(255) = 'USE [{CurrDbName}]'
-- Change physical file names
DECLARE #SET_PHYS_MDF AS varchar(255) = 'ALTER DATABASE [{CurrDbName}] MODIFY FILE (NAME = ''{CurrDbName}'', FILENAME = ''{PathToFolder}{NewDbName}.mdf'')'
DECLARE #SET_PHYS_LDF AS varchar(255) = 'ALTER DATABASE [{CurrDbName}] MODIFY FILE (NAME = ''{CurrDbName}_log'', FILENAME = ''{PathToFolder}{NewDbName}_log.ldf'')'
-- Change logical names (LOG = "logical", not "log")
If (#CurrDbName != #NewDbName)
BEGIN
DECLARE #SET_LOG_MDF AS varchar(255) = 'ALTER DATABASE [{CurrDbName}] MODIFY FILE (NAME = ''{CurrDbName}'', NEWNAME = ''{NewDbName}'')'
DECLARE #SET_LOG_LDF AS varchar(255) = 'ALTER DATABASE [{CurrDbName}] MODIFY FILE (NAME = ''{CurrDbName}_log'', NEWNAME = ''{NewDbName}_log'')'
END
-- Take offline
DECLARE #SET_OFFLINE AS varchar(255) = 'ALTER DATABASE [{CurrDbName}] SET OFFLINE WITH ROLLBACK IMMEDIATE'
---------- START DOING STUFF ----------
DECLARE #SQL_SCRIPT AS varchar(255)
-- Use DB
SET #SQL_SCRIPT = REPLACE(#USE_DB, '{CurrDbName}', #CurrDbName)
EXECUTE (#SQL_SCRIPT)
-- Change physical file names
SET #SQL_SCRIPT = REPLACE(REPLACE(REPLACE(#SET_PHYS_MDF, '{CurrDbName}', #CurrDbName), '{NewDbName}', #NewDbName), '{PathToFolder}', #PathToFolder)
EXECUTE (#SQL_SCRIPT)
SET #SQL_SCRIPT = REPLACE(REPLACE(REPLACE(#SET_PHYS_LDF, '{CurrDbName}', #CurrDbName), '{NewDbName}', #NewDbName), '{PathToFolder}', #PathToFolder)
EXECUTE (#SQL_SCRIPT)
-- Change logical names (LOG = "logical", not "log")
If (#CurrDbName != #NewDbName)
BEGIN
SET #SQL_SCRIPT = REPLACE(REPLACE(#SET_LOG_MDF, '{CurrDbName}', #CurrDbName), '{NewDbName}', #NewDbName)
EXECUTE (#SQL_SCRIPT)
SET #SQL_SCRIPT = REPLACE(REPLACE(#SET_LOG_LDF, '{CurrDbName}', #CurrDbName), '{NewDbName}', #NewDbName)
EXECUTE (#SQL_SCRIPT)
END
-- Take offline
USE [master]
SET #SQL_SCRIPT = REPLACE(#SET_OFFLINE, '{CurrDbName}', #CurrDbName)
EXECUTE (#SQL_SCRIPT)
-- Now turn off the database, rename/move physical files, and bring the database back online
This is my first answer, apologies if it's not of sufficient quality.
Command line turned out to be much more forgiving with the renamed files. Note that this isn't a fire-and-forget script...run each portion separately, paying attention to the names that need to be changed:
--#1 Attach the db
USE [master]
GO
CREATE DATABASE RenamedDB ON
( FILENAME = N'<PathToRenamedFile>\renamedDBFile.mdf' ),
( FILENAME = N'<PathToRenamedFile>\renamedDBFile_log.ldf' )
FOR ATTACH
GO
--#2 Get the old logical file names:
USE RenamedDB
select * from sys.database_files
--#3 Rename the old logical files
ALTER DATABASE RenamedDB MODIFY FILE (NAME=N'OldLogicalDBName', NEWNAME=N'renamedDBFile')
GO
ALTER DATABASE RenamedDB MODIFY FILE (NAME=N'OldLogicalLogName', NEWNAME=N'renamedDBFile_log')
GO
--#4 check for the new names
select * from sys.database_files
I have tried the FILESTREAM feature for MSSQL (2008R2 Data Center) on a local database, to experiment. The real database is running on a server. I have setup the whole FILESTREAM, using this query:
/* CREATE FILESTREAM AND FILESTREAM TABLE */
USE [master]
GO
ALTER DATABASE SenONew
ADD FILEGROUP [FileStream]
CONTAINS FILESTREAM
GO
ALTER DATABASE SenONew
ADD FILE
(
NAME = 'fsSenONew',
FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\SenONew.ndf'
)
TO FILEGROUP [FileStream]
GO
USE [SenONew]
GO
CREATE TABLE Filestore(
FileID int PRIMARY KEY,
RowID uniqueidentifier ROWGUIDCOL NOT NULL UNIQUE DEFAULT NEWSEQUENTIALID(),
FileDescr nvarchar(max),
FileIndex varbinary(max) FILESTREAM NULL)
GO
And I was experimenting with adding a few files then deleting them.
Now since this was only meant to be an experiment, I also want to get rid of it. I'm using my local server for the development of the database that will be used on the real server, thus I'm creating BackUp's on my local server then Restore this on the real server, so it gets updated (software is in development, so the database structure changes much as well as the data and I need to do a full restore to the real server, where the software is being tested on).
After hours of searching, I couldn't find anything on my problem.
I understand that I need to:
Remove the database table storing the FILESTREAM information
I need to remove the FILE of the FILESTREAM
Remove the filegroup
So I'm using this query to get rid of everything I set up in the first place:
/* DROP FILESTREAM TABLE AND FILEGROUP */
USE SenONew
DROP TABLE Filestore
GO
ALTER DATABASE SenONew
REMOVE FILE fsSenONew
ALTER DATABASE SenONew
REMOVE FILEGROUP [FileStream]
GO
So I do everything as I should and it completes without error as well. So when I enter my filegroups, files and my file location, I see they are all completely removed:
But when I do a BACKUP of my local database (which include the deleted FILESTREAM, file path and filegroup) and try to restore the server with it, I get errors.
SQL to create a BACKUP:
/* CREATE BACKUP OF DATABASE WITHIN CURRECT CONNECTION */
DECLARE #FileName2 nvarchar(250)
SELECT #FileName2 = (SELECT 'C:\SenO BackUp\' + convert(nvarchar(200),GetDate(),112) + ' SenONew.BAK')
BACKUP DATABASE SenONew TO DISK=#FileName2
GO
Then do the Restore on the server:
/* CREATE RESTORE OF DATABASE WITHIN REAL SERVER CONNECTION */
use master
alter database SenONew set offline with rollback immediate;
DECLARE #FileName2 nvarchar(250)
SELECT #FileName2 = (SELECT '' + convert(nvarchar(200),GetDate(),112) + ' SenONew.BAK')
RESTORE DATABASE SenONew
FROM DISK = #FileName2
alter database SenONew set online with rollback immediate;
I get the following error:
*(Msg 5121, Level 16, State 2, Line 7
The path specified by "C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\SenONew.ndf" is not in a valid directory.
Msg 3156, Level 16, State 3, Line 7 File 'fsSenONew' cannot be restored to 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\SenONew.ndf'. Use WITH MOVE to identify a valid location for the file.
Msg 3119, Level 16, State 1, Line 7 Problems were identified while planning for the RESTORE statement. Previous messages provide details.
Msg 3013, Level 16, State 1, Line 7 RESTORE DATABASE is terminating abnormally. )*
I deleted the .ndf FILESTREAM location, why is it a specified path? Also, why is fsSenONew trying to restore? I can't get my head around it. Are there paths internally that I need to delete?
You can check:
SELECT * FROM SenONew.sys.data_spaces WHERE name = 'FileStream'
it should return 0 rows.
There is a procedure to remove FILESTREAM features from a SQL Server 2008 database :
ALTER TABLE Filestore DROP column FileIndex
GO
ALTER TABLE Filestore SET (FILESTREAM_ON="NULL")
GO
ALTER Database SenONew REMOVE FILE fsSenONew
GO
ALTER Database SenONew REMOVE FILEGROUP [FileStream]
GO
as described in this article. But the steps you did should do the same thing.
Your problem is certainly strange, but I suggest that you try using following
USE SenONew
EXEC Sp_help
EXEC Sp_helpfile
EXEC Sp_helpfilegroup
You may find something suspicious there like another table using that FILEGROUP.
I have done exactly the steps you describe and cannot reproduce your problem. Check how your Restore database screen looks like.
1.Remove the FILESTREAM attribute from columns and tables. You'll need to move data to a new column.
ALTER TABLE MyTable
ADD FileData varbinary(max) NULL;
GO
update MyTable
set FileData = FileStreamData
GO
ALTER TABLE MyTable
DROP column FileStreamData
GO
ALTER TABLE MyTable SET (FILESTREAM_ON="NULL")
GO
EXEC sp_RENAME 'MyTable.FileData', 'FileStreamData', 'COLUMN'
GO
2.Remove files from the FILESTREAM and drop the FILE and FILESTEAM.
ALTER DATABASE [MyDatabase] SET RECOVERY Simple
GO
EXEC SP_FILESTREAM_FORCE_GARBAGE_COLLECTION
ALTER DATABASE [MyDatabase] REMOVE FILE [MyFile]
GO
ALTER DATABASE [MyDatabase] REMOVE FILEGROUP [MyFileGroup]
GO
ALTER DATABASE [MyDatabase] SET RECOVERY FULL
GO
This is my script that worked for me. It was a command missing, to empty the file:
ALTER TABLE FilesTable DROP column FileContent
GO
ALTER TABLE FilesTable SET (FILESTREAM_ON="NULL")
GO
USE mydbname
GO
DBCC SHRINKFILE (N'filestreamfile', EMPTYFILE)
GO
EXEC sp_filestream_force_garbage_collection #dbname = N'mydbname'
ALTER Database mydbname REMOVE FILE filestreamfile
GO
ALTER Database mydbname REMOVE FILEGROUP FILESTREAMGROUP
GO
IF COL_LENGTH('FilesTable','FileContent') IS NULL
BEGIN
ALTER TABLE FilesTable ADD FileContent [varbinary](max) NULL
END
GO
When I run this statement in SQL Server:
USE [master]
GO
ALTER DATABASE <DBName> ADD FILEGROUP <[FGName]>
Where is the corresponding .ndf file created on disk?
When I run that query, it's successful and I am able to find the new file group being added to the sys.filegroups table.
But when I run
select * from sys.database_files
I am not able to find the files created for the new file group. I couldn't find it in the disk as well. What am I doing wrong?
Could somebody help with this..?
Try this:
In SQL Server you will have to explicitly add the .ndf files to the different filegroups. If you dont mention any filegroup then by default it will be added to primary filegroup.
So whenever you add a filegroup it is just a logical name and it does not have any physical location.
use yourDBname
select * from sys.database_files
ALTER DATABASE yourDBname ADD FILE (NAME = name1, FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\DB11.ndf', SIZE=100MB, MAXSIZE=500GB, FILEGROWTH=50);
ALTER DATABASE yourDBname ADD FILEGROUP BusyTables
select * from sys.database_files
To create a new database and populate it from a backup I typically do:
CREATE DATABASE MyDatabase -- I create an empty database
-- I retrieve the filepath of mdf and ldf files
DECLARE #FileData varchar(1000)
DECLARE #FileLog varchar(1000)
set #FileData = (select filename from MyDatabase.dbo.sysfiles where fileid = 1)
set #FileLog = (select filename from MyDatabase.dbo.sysfiles where fileid = 2)
-- I Restore the database from backup by substituting the mdf and ldf files
RESTORE DATABASE MyDatabase
FROM DISK = 'c:\Test\Test.bak'
WITH REPLACE,
MOVE 'MyApp_Data' TO #FileData, -- I forced the name to be MyApp_Data for simplicity
MOVE 'MyApp_Log' TO #FileLog -- I forced the name to be MyApp_Log for simplicity
How to do the same for the filestream data too? May I force the folder creation for filestream data or should I create it manually?
May you also comment on my approach?
I think you can use the MOVE option with the filestream data as well. You can run the following command to view the file list in your backup file. This will help identify the filestream file.
RESTORE FILELISTONLY
FROM DISK = 'c:\Test\Test.bak'
As for your overall approach, I think you may be doing more than you need to do. You don't have to create a database before you restore a backup to it. The RESTORE command will take care of the create DB steps.
So, your RESTORE command would be the same as your using, except that you can exclude the REPLACE option. You can specify your destination file information in the MOVE option.