I'm trying to create a copy of a table (no data, just the schema) using SQL Server Management Objects (SMO), Transfer class. The only thing I haven't figured out is how to specify what server to copy to, when the servers are on different hosts. In my case, I want to copy from 10.1.2.x to 10.1.2.y. Is there any way to specify this, or does this class not support it?
Perhaps there are better C# solutions?
static void CreateTableFromTable(string fromConnection, string toConnection, string dbName, string tablename, bool copyData = false)
{
Server fromServer = new Server(new ServerConnection(new SqlConnection(fromConnection)));
Database db = fromServer.Databases[dbName];
Transfer transfer = new Transfer(db);
transfer.CopyAllObjects = false;
transfer.DropDestinationObjectsFirst = false;
transfer.CopySchema = false; //Database schema? Or Table schema? I DO NOT want to overwrite the db schema
transfer.CopyData = copyData;
transfer.DestinationServer = "?";
transfer.DestinationDatabase = dbName;
transfer.Options.IncludeIfNotExists = true;
transfer.ObjectList.Add(db.Tables[tablename]);
transfer.TransferData();
}
Have you tried import and export data wizard even importing with table data ,command line or GUI in SQL server 2005/8 and Mysql /MysqlWorkbench.
I'm not sure if you found another solution - or got this one working. If you did not the SMO Scripter object might be worth a look.
This MSDN example could be helpful. You could script the tables and dependencies you want and then open a connection to the destination database and execute the scripts.
static void Main(string[] args)
{
Server sourceServer = new Server("server");
String dbName = "database";
// Connect to the local, default instance of SQL Server.
// Reference the database.
Database db = sourceServer.Databases[dbName];
// Define a Scripter object and set the required scripting options.
Scripter scripter = new Scripter(sourceServer);
scripter.Options.ScriptDrops = false;
scripter.Options.WithDependencies = true;
scripter.Options.Indexes = true; // To include indexes
scripter.Options.DriAllConstraints = true; // to include referential constraints in the script
// Iterate through the tables in database and script each one. Display the script.
foreach (Table tb in db.Tables)
{
// check if the table is not a system table
if (tb.IsSystemObject == false)
{
Console.WriteLine("-- Scripting for table " + tb.Name);
// Generating script for table tb
System.Collections.Specialized.StringCollection sc = scripter.Script(new Urn[] { tb.Urn });
foreach (string st in sc)
{
//ado.net to destination
Console.WriteLine(st);//SqlCommand.ExecuteNonQuery();
}
Console.WriteLine("--");
}
}
}
Did you try to use SELECT ... INTO statement?
For example:
SELECT * INTO DestDatabase.TableName FROM SourceDatabase.TableName
If you don't want to copy data, just add a condition which will be return nothing, ex: WHERE Id = 0
Related
In a C# application, I know using ADO.NET we can execute a backup command like this:
BACKUP DATABASE MyDb TO DISK='C:\\MyDb.bak'
and take a database backup and store it at some given location.
I want to take backup of database in-memory i.e. return the backup script content (schema and data) which I can later save as .sql file at some location.
Is this possible?
I achieved it using SQL Server Management Objects (SMO). Thanks to all the friends who helped in comments.
First, install Microsoft.SqlServer.SqlManagementObjects from nuget package manager.
The working code:
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
var script = new StringBuilder();
Server server = new Server(new ServerConnection(new SqlConnection(connectionString)));
Database database = server.Databases[databaseName];
ScriptingOptions options = new ScriptingOptions
{
ScriptData = true,
ScriptSchema = true,
ScriptDrops = false,
Indexes = true,
IncludeHeaders = true
};
foreach (Table table in database.Tables)
{
foreach (var statement in table.EnumScript(options))
{
script.Append(statement);
script.Append(Environment.NewLine);
}
}
File.WriteAllText(backupPath + databaseName + ".sql", script.ToString());
I am attempting to export and import a database schema and the data within. I'm using SMO (Server Management Objects) to do so, but while the export of the schema is working just fine, setting ScriptData to true in the scripting options will produce an exception.
Server server = new Server(new ServerConnection(sqlCon));
Database database = server.Databases["SpectroGrass"];
ScriptingOptions options = new ScriptingOptions();
options.ScriptSchema = true;
options.ScriptData = false;
options.ScriptDrops = false;
string scriptData = String.Join("\r\n", database.Tables[tableName].EnumScript(options).ToList());
Will produce a schema and is working.
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
CREATE TABLE [SpectroGrassAdmin].[Treatment]
(
[TRM_ID] [int] NOT NULL,
[TRM_Name] [varchar](150) COLLATE Latin1_General_CI_AS NULL,
[TRM_Crop] [varchar](50) COLLATE Latin1_General_CI_AS NOT NULL
) ON [PRIMARY]
But using the ScriptData option ...
options.ScriptSchema = false;
options.ScriptData = true;
options.ScriptDrops = false;
string scriptData = String.Join("\r\n", database.Tables[tableName].EnumScript(options).ToList());
will generate an exception.
Login failed for user 'SpectroGrassAdmin'.
I'm not sure why I'm getting this specific error. I can only assume the problem lies somewhere else, because logging into the server and connecting to the database is working fine if I only want the schema exported.
Am I missing some other options to be able to create a script for the table data?
Try to simplify it as much as possible.
The peculiarity of smo is that by default it points/connects to the default sql instance.
Does it happen that you have a default instance on the machine you are running your app and you are trying to smo to another instance? (either secondary on your machine or a remote instance on another machine).
The following was taken from an example of smo using ServerConnection, which enables the connection information to be reused.
// https://learn.microsoft.com/en-us/sql/relational-databases/server-management-objects-smo/create-program/connecting-to-an-instance-of-sql-server?view=sql-server-ver15#connecting-to-an-instance-of-sql-server-by-using-sql-server-authentication-in-visual-c
//// compile with:
// /r:Microsoft.SqlServer.Smo.dll
// /r:Microsoft.SqlServer.ConnectionInfo.dll
// /r:Microsoft.SqlServer.Management.Sdk.Sfc.dll
using System;
using System.Linq;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
namespace SMOConsoleApp
{
class Program
{
static void Main()
{
// For remote connection, remote server name / ServerInstance needs to be specified
ServerConnection srvConn2 = new ServerConnection("machinename"/* <--default sql instance on machinename*/); // or (#"machinename\sqlinstance") for named instances
srvConn2.LoginSecure = false;
srvConn2.Login = "sql_login_goes_here";
srvConn2.Password = "password_goes_here";
Server srv3 = new Server(srvConn2);
Console.WriteLine("servername:{0} ---- version:{1}", srv3.Name, srv3.Information.Version); // connection is established
Console.WriteLine(srv3.ConnectionContext); //check connection context
ScriptingOptions scriptoptions = new ScriptingOptions();
scriptoptions.ScriptSchema = true;
scriptoptions.ScriptData = true;
scriptoptions.ScriptDrops = false;
Database mydb = srv3.Databases["master"];
/*
--execute this in ssms, in the master db
select *
into dbo.testsysobjects
from sys.objects
*/
Table mytable = mydb.Tables["testsysobjects", "dbo"];
Console.WriteLine("database: {0} ---- table: {1} ---- rowcount: {2}", mytable.Parent.Name, mytable.Name, mytable.RowCount); //check table
string scriptData = String.Join("\r\n", mytable.EnumScript(scriptoptions).ToList());
Console.Write(scriptData);
srvConn2.Disconnect();
}
}
}
I have a database in SQL Server 2008 R2, that uses the Simple recovery model.
The database contains a filegroup, where the bulk of the data resides (>20GB of images). These images are not critical for the application.
I want to backup the database from C# using Sql Server SMO. But I only want to backup the database structure (the PRIMARY filegroup; everything except the non-essential images). I want to do this in order to keep the backup size small.
In my C# code, I am setting the backup action to BackupActionType.Files, and I am only including the PRIMARY filegroup inside the DatabaseFileGroups collection, so it should only backup the database structure, and not the images.
But when I run the backup, I get this exception:
System.Data.SqlClient.SqlError: The primary filegroup cannot be backed up as a file backup because the database is using the SIMPLE recovery model. Consider taking a partial backup by specifying READ_WRITE_FILEGROUPS.
My question is, how can I specify READ_WRITE_FILEGROUPS from inside C# code, using Sql Server SMO? The exception shows me how to do so in T-SQL, but I want to do the same thing in C#.
Here is the code I am using:
class Program
{
static string DbName = PATH_TO_DATABASE;
static string connString = CONNECTION_STRING;
static void Main(string[] args)
{
ServerConnection serverConn = new ServerConnection();
serverConn.ConnectionString = connString;
Server server = new Server(serverConn);
Backup backup = new Backup() { Database = DbName };
backup.Action = BackupActionType.Files;
backup.DatabaseFileGroups.Add("PRIMARY");
backup.Devices.AddDevice("D:\\backup.bak", DeviceType.File);
backup.Initialize = true;
backup.ContinueAfterError = false;
backup.Incremental = false;
backup.Complete += (snd, e) => { Console.WriteLine("Complete"); };
backup.PercentComplete += (snd, e) => { Console.WriteLine("Percent " + e.Percent); };
backup.SqlBackup(server);
serverConn.Disconnect();
}
}
solution is very simple.
Just in SQLSERVER rigth-click on database and in Properties Window in Option tab change Recovery Mode To Bulk-logged
secound Solution by T-SQL:
USE [master]
GO
ALTER DATABASE [databasename] SET RECOVERY BULK_LOGGED WITH NO_WAIT
GO
I have make Some Differential Backup for a Database, which will take the Backup for the Last Modified Data which will be appended to the previously happend Full Backup file. Now, when i am trying to take Restore of the .bak file entire Data is getting Backup, is it possible to take the Backup only the Last Backup taken data i wanted? Can, any one help me on this.
private void RestoreDataBase(Server MyServer, Database MyDataBase, string DevicePath, string Type)
{
try
{
progressBar1.Value = 0;
Restore restoreDB = new Restore();
restoreDB.Action = RestoreActionType.Database;
restoreDB.Database = MyDataBase.Name;
restoreDB.Devices.AddDevice(DevicePath, DeviceType.File);
restoreDB.ReplaceDatabase = true;
restoreDB.NoRecovery = true;
restoreDB.PercentComplete += new PercentCompleteEventHandler(rstDatabase_PercentComplete);
restoreDB.Complete += new ServerMessageEventHandler(rstDatabase_Complete);
restoreDB.SqlRestore(MyServer);
}
catch (Exception ex)
{
WriteToListView(ex.Message.ToString());
writetoLog(ex.Message.ToString());
}
}
The above is the coding i am using, and the database is Sql Server 2008, The coding i am using for Differential Backup is as follows
private void BackupDataBaseDifferential(Server MyServer, Database MyDataBase, string DestinationPath, string Type)
{
try
{
WriteToListView("Taking the Differential Backup for " + MyDataBase.Name);
Backup backDB = new Backup();
backDB.Action = BackupActionType.Database;
backDB.Database = MyDataBase.Name;
backDB.Devices.AddDevice(DestinationPath, DeviceType.File);
backDB.BackupSetName = "Sql Database Backup Differential";
backDB.BackupSetDescription = "Sql Database Backup - DifferentialType";
backDB.ExpirationDate = DateTime.Now.AddDays(5);
backDB.Initialize = false;
backDB.Incremental = true;
if (Type == "Manual")
{
progressBar1.Value = 0;
backDB.PercentComplete += new PercentCompleteEventHandler(bd_PercentComplete);
backDB.Complete += new ServerMessageEventHandler(bd_Complete);
}
else if (Type == "Automatic")
{
backDB.PercentComplete += CompletionStatusInPercent;
backDB.Complete += Backup_Completed;
}
backDB.SqlBackup(MyServer);
}
catch (Exception ex)
{
WriteToListView(ex.Message.ToString());
writetoLog(ex.Message.ToString());
}
}
So, if i use the above coding the last data which is modified in the Database (which was already taken Backup Fully previously) will be taken. So, my problem is when i use the RestoreDataBase() method the entire Database is getting restore, bcoz all the Full Backup data and the Differential Backup data will be in only one file ex: Sample.bak. If i want only the last modified data is it not possible to take by Specifying the Date of the Data modified to the Database?
Maybe silly, but if you export your db as .sql, when you need to restore only some data, you can read file and "filter" rows to restore...
Anyway there is another thread talking about this, take a look here.
I am trying to make a copy of a database to a new database on the same server. The server is my local computer running SQL 2008 Express under Windows XP.
Doing this should be quite easy using the SMO.Transfer class and it almost works!
My code is as follows (somewhat simplified):
Server server = new Server("server");
Database sourceDatabase = server.Databases["source database"];
Database newDatbase = new Database(server, "new name");
newDatbase.Create();
Transfer transfer = new Transfer(sourceDatabase);
transfer.CopyAllObjects = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newDatbase.Name;
transfer.CopySchema = true;
transfer.CopyData = true;
StringCollection transferScript = transfer.ScriptTransfer();
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand switchDatabase = new SqlCommand("USE " + newDatbase.Name, conn))
{
switchDatabase.ExecuteNonQuery();
}
foreach (string scriptLine in transferScript)
{
using (SqlCommand scriptCmd = new SqlCommand(scriptLine, conn, transaction))
{
int res = scriptCmd.ExecuteNonQuery();
}
}
}
What I do here is to first create a new database, then generate a copy script using the Transfer class and finally running the script in the new database.
This works fine for copying the structure, but the CopyData option doesn't work!
Are there any undocumented limits to the CopyData option? The documentation only says that the option specifies whether data is copied.
I tried using the TransferData() method to copy the databse without using a script but then I get an exception that says "Failed to connect to server" with an inner exception that says "A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)"
I also tried to enable Named Pipes on the server, but that doesn't help.
Edit:
I found a solution that works by making a backup and then restoring it to a new database. It's quite clumsy though, and slower than it should be, so I'm still looking for a better solution.
Well, after contacting Microsft Support I got it working properly, but it is slow and more or less useless. Doing a backup and then a restore is much faster and I will be using it as long as the new copy should live on the same server as the original.
The working code is as follows:
ServerConnection conn = new ServerConnection("rune\\sql2008");
Server server = new Server(conn);
Database newdb = new Database(server, "new database");
newdb.Create();
Transfer transfer = new Transfer(server.Databases["source database"]);
transfer.CopyAllObjects = true;
transfer.CopyAllUsers = true;
transfer.Options.WithDependencies = true;
transfer.DestinationDatabase = newdb.Name;
transfer.DestinationServer = server.Name;
transfer.DestinationLoginSecure = true;
transfer.CopySchema = true;
transfer.CopyData = true;
transfer.Options.ContinueScriptingOnError = true;
transfer.TransferData();
The trick was to set the DestinationDatabase property. This must be set even if the target is that same as the source. In addition I had to connect to the server as a named instance instead of using the other connection options.
Try setting SetDefaultInitFields to true on the Server object.
I had the same issue with the SMO database object running slowly. I guess this is because sql server doesn't like to retrieve entire objects and collections at once, instead lazy loading everything, causing a round-trip for each field, which for an entire database is pretty inefficient.
I had a go at getting this working and have come up with an answer that doesn't use the Transfer class. Here is the Method i used:
public bool CreateScript(string oldDatabase, string newDatabase)
{
SqlConnection conn = new SqlConnection("Data Source=.;Initial Catalog=" + newDatabase + ";User Id=sa;Password=sa;");
try
{
Server sv = new Server();
Database db = sv.Databases[oldDatabase];
Database newDatbase = new Database(sv, newDatabase);
newDatbase.Create();
ScriptingOptions options = new ScriptingOptions();
StringBuilder sb = new StringBuilder();
options.ScriptData = true;
options.ScriptDrops = false;
options.ScriptSchema = true;
options.EnforceScriptingOptions = true;
options.Indexes = true;
options.IncludeHeaders = true;
options.WithDependencies = true;
TableCollection tables = db.Tables;
conn.Open();
foreach (Table mytable in tables)
{
foreach (string line in db.Tables[mytable.Name].EnumScript(options))
{
sb.Append(line + "\r\n");
}
}
string[] splitter = new string[] { "\r\nGO\r\n" };
string[] commandTexts = sb.ToString().Split(splitter, StringSplitOptions.RemoveEmptyEntries);
foreach (string command in commandTexts)
{
SqlCommand comm = new SqlCommand(command, conn);
comm.ExecuteNonQuery();
}
return true;
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("PROGRAM FAILED: " + e.Message);
return false;
}
finally
{
conn.Close();
}
}
Here is my solution:
I have a Database named is Olddatabase
I backup it to E:\databackup\Old.bak
If you want to create a Duplicate Database from Olddatabase in the same server with name NewDatabase
3.1 You can use command in query tool : EXEC OldDatabase.dbo.sp_helpfile;
to determinat path of OldDatabase is stored in case you want to save NewDatabase in the same folder.
or You can save NewDatabase in new Path which you want
use this command in Query tool
RESTORE DATABASE NewDatabase FROM DISK = 'E:\databackup\Old.bak'
WITH MOVE 'OldDatabase' TO 'E:\New path (or the same path)\NewDatabase_Data.mdf',
MOVE 'OldDatabase_log' TO 'E:\New path (or the same path)\NewDatabase_Log.ldf';
Note: you can Use these command obove in c# with : Create a Store procedure in sql which include Above commands. And you can call the store procedure in C #