I am taking backups of certain sql server databases programmatically using c#. I figured that Microsoft.SqlServer.Management.Smo and some other libraries are made for this purpose. Now I can backup a database. Very nice. Here is the code :
var server = new Server(#"" + InstanceName);
var backuper = new Backup();
try
{
backuper.Action = BackupActionType.Database;
backuper.Database = DbName;
backuper.Devices.AddDevice(DbName + ".bak", DeviceType.File);
backuper.BackupSetName = DbName + " - Yedek";
backuper.BackupSetDescription = "Açık Bulut Depo - " + DbName + " - Yedek";
backuper.ExpirationDate = DateTime.Now.AddYears(20);
server.ConnectionContext.Connect();
backuper.SqlBackup(server);
}
catch(Exception ex){//..}
My question here is how can I get the path of the device that the database backed up into?
I know I can specify my own path as :
backuper.Devices.AddDevice("C:\SOMEPATH\" + DbName + ".bak", DeviceType.File);
Then I can actually know where it is, but what I want to do is back it up to its default location and get its path. Please help me out with this.
Correct Answer to this duplicate can be found here: https://stackoverflow.com/a/8791588/331889
Server.BackupDirectory;
Given you are already using SMO Objects it should be the simplest answer.
From this blog post, you could use the function below:
http://www.mssqltips.com/sqlservertip/1966/function-to-return-default-sql-server-backup-folder/
CREATE FUNCTION dbo.fn_SQLServerBackupDir()
RETURNS NVARCHAR(4000)
AS
BEGIN
DECLARE #path NVARCHAR(4000)
EXEC master.dbo.xp_instance_regread
N'HKEY_LOCAL_MACHINE',
N'Software\Microsoft\MSSQLServer\MSSQLServer',N'BackupDirectory',
#path OUTPUT,
'no_output'
RETURN #path
END;
I normally execute the below stored procedure immediately after backuper.SqlBackup(server);
to return the most recent backup destination path. I use this approach because using SMO, i give the application user the flexibility of backing up to any location / drive or even to a USB disk. So a user may decide not to backup to the default backup location and i want to return that location after the backup process is successfully completed.
USE [YouDatabaseNameHere]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: SQL.NET Warrior
-- =============================================
CREATE PROCEDURE [dbo].[GetBackupHistory]
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SQLVer SQL_VARIANT
,#DBName VARCHAR(128)
,#NumDays SMALLINT
,#SQL VARCHAR(1024)
,#WhereClause VARCHAR(256)
SET #DBName = Null
;
SET #NumDays = 14
;
SET #SQLVer = CONVERT(INTEGER, PARSENAME(CONVERT(VARCHAR(20),SERVERPROPERTY('ProductVersion')),4));
SET #WhereClause = 'WHERE a.type IN (''D'',''I'')
And a.backup_start_date > GETDATE()- ' + CAST(#NumDays AS VARCHAR)+''
IF #DBName IS NOT NULL
BEGIN
SET #WhereClause = #WhereClause + '
AND a.database_name = '''+ #DBName +''''
END
SET #SQL = '
SELECT TOP 1 a.database_name,a.backup_start_date
,b.physical_device_name AS BackupPath
,a.position
,a.type
,a.backup_size/1024/1024 AS BackupSizeMB
,' + CASE
WHEN #SQLVer < 10
THEN '0'
ELSE 'a.compressed_backup_size/1024/1024'
END + ' AS CompressedBackMB
FROM msdb.dbo.backupset a
INNER JOIN msdb.dbo.backupmediafamily b
ON a.media_set_id = b.media_set_id
' + #WhereClause + '
ORDER BY a.backup_start_date DESC;';
--PRINT #SQL
EXECUTE (#SQL);
END;
GO
Related
I have a procedure that can be changed dynamically by user for multi column and I write it in SQL when I run it. Everything is OK in SQL and Server Explorer in Visual Studio but when I want use it in C# and call it, it just return 0 always.
Can anybody help me?!
CREATE PROCEDURE [dbo].[PDaynamicActualBy2Column]
#Colname1 nvarchar(100),
#VarCol1 nvarchar(100),
#Colname2 nvarchar(100),
#VarCol2 nvarchar(100),
#VarWeekNum nvarchar(100)
as
DECLARE #temp nvarchar(1500)
set #temp='SELECT SUM([dbo].[WeekActualTemp].[ACTUAL]) from [dbo].[MAINTB] join [dbo].[WeekActualTemp] on [dbo].[MAINTB].[UQ]=[dbo].[WeekActualTemp].[UQ]
where [dbo].[WeekActualTemp].[WeekNO]='+#VarWeekNum+' And [dbo].[MAINTB].'+#Colname1+' = '''+#VarCol1+''' And [dbo].[MAINTB].'+#Colname2+' = '''+#VarCol2+''''
exec (#temp)
This does not address the problem in the C#, however, it does address the huge injection issue you have in your code. Like mentioned, don't inject your parameters and properly quote your dynamic object names. This results in something like the below:
CREATE PROC dbo.PDaynamicActualBy2Column #Colname1 sysname, #VarCol1 nvarchar(100), #Colname2 sysname, #VarCol2 nvarchar(100), #VarWeekNum int AS --Assumed #VarWeekNum is an int, as why else is it called "num"?
BEGIN
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'SELECT SUM(WAT.Actual) AS ActualSum' + #CRLF +
N'FROM dbo.MAINTB MTB' + #CRLF +
N' JOIN dbo.WeekActualTemp WAT ON MTB.UQ = WAT.UQ' + #CRLF +
N'WHERE WAT.WeekNO = #VarWeekNum' + #CRLF +
N' AND MTD.' + QUOTENAME(#Colname1) + N' = #VarCol1' + #CRLF +
N' AND MTD.' + QUOTENAME(#Colname2) + N' = #VarCol2;';
--PRINT #SQL; Your Best Friend
EXEC sp_executesql #SQL, N'#VarCol1 nvarchar(100),#VarCol2 nvarchar(100),#VarWeekNum int', #VarCol1, #VarCol2, #VarWeekNum;
END;
GO
Because you're only returning a single scalar value, you could also use an OUTPUT parameter as well, instead of a SELECT to the display the value. This would look like the below:
CREATE PROC dbo.PDaynamicActualBy2Column #Colname1 sysname, #VarCol1 nvarchar(100), #Colname2 sysname, #VarCol2 nvarchar(100), #VarWeekNum int, #ActualSum int OUTPUT AS --Assumes Actual is an int in your table. Use an appropriate data type
BEGIN
DECLARE #SQL nvarchar(MAX),
#CRLF nchar(2) = NCHAR(13) + NCHAR(10);
SET #SQL = N'SELECT #ActualSum = SUM(WAT.Actual)' + #CRLF +
N'FROM dbo.MAINTB MTB' + #CRLF +
N' JOIN dbo.WeekActualTemp WAT ON MTB.UQ = WAT.UQ' + #CRLF +
N'WHERE WAT.WeekNO = #VarWeekNum' + #CRLF +
N' AND MTD.' + QUOTENAME(#Colname1) + N' = #VarCol1' + #CRLF +
N' AND MTD.' + QUOTENAME(#Colname2) + N' = #VarCol2;';
--PRINT #SQL; Your Best Friend
EXEC sp_executesql #SQL, N'#VarCol1 nvarchar(100),#VarCol2 nvarchar(100),#VarWeekNum int, #ActualSum int OUTPUT', #VarCol1, #VarCol2, #VarWeekNum, #ActualSum OUTPUT; --Again, assumes Actual is an int.
END;
GO
Notice, as mentioned in the comments, I get rid of the 3+ part naming for your columns, and alias your tables instead. I then use those aliases to reference to correct object. I've also put "Your best Friend" in the code, should you need to debug it.
Note: As mentioned in a different answer, zero is likely because the SP is returning 0 to mean success. This is a documented and intentional feature of Stored Procedures:
Unless documented otherwise, all system stored procedures return a value of 0. This indicates success and a nonzero value indicates failure.
As the SP above is likely successful, the RETURN value is 0; to denote success. You shouldn't be looking at the RETURN value, but the dataset, or in the latter example the OUTPUT parameter's value. I am sure there are questions on SO for how to use an OUTPUT parameter in linq.
Zero in return value of your stored procedure means it executed successfully.
You should return a value by "RETURN" statement or "SELECT" statement for table in return.
In LINQ you cannot call an SP that have a meta output that is dynamic, you have to write your SP with "select" output and make a model and then go to SP and edit it again.
Alter PROCEDURE [dbo].[PDaynamicActualBy2Column]
#Colname1 nvarchar(100),
#VarCol1 nvarchar(100),
#Colname2 nvarchar(100),
#VarCol2 nvarchar(100),
#VarWeekNum nvarchar(100)
as
DECLARE #temp nvarchar(1500)
set #temp='SELECT SUM([dbo].[WeekActualTemp].[ACTUAL]) from [dbo].[MAINTB] join [dbo].[WeekActualTemp] on [dbo].[MAINTB].[UQ]=[dbo].[WeekActualTemp].[UQ]
where [dbo].[WeekActualTemp].[WeekNO]='+#VarWeekNum+' And [dbo].[MAINTB].'+#Colname1+' = '''+#VarCol1+''' And [dbo].[MAINTB].'+#Colname2+' = '''+#VarCol2+''''
-- exec (#temp)
SELECT top 0
SUM([dbo].[WeekActualTemp].[ACTUAL]) as sum
from [dbo].[MAINTB] join [dbo].[WeekActualTemp] on [dbo].[MAINTB].[UQ]=[dbo].[WeekActualTemp].[UQ]
then import SP in your LINQ then comment "select" and uncomment "exec" .
Not worried about SQL Injection or anything of the like, just trying to get this to work. Using SSMS and Visual Studio.
I have C# code that passes a variable, GlobalVariables.username, to an SQL parameter.
private void btnNext_Click(object sender, EventArgs e)
{
if (checkIntrotoPublicSpeaking.Checked || checkEffectiveOralCommunication.Checked || checkProfComm.Checked)
{
List<SqlParameter> sqlOralComm = new List<SqlParameter>();
sqlOralComm.Add(new SqlParameter("Username", GlobalVariables.username));
sqlOralComm.Add(new SqlParameter("IntrotoPublicSpeaking", cboxIntrotoPublicSpeaking.Text));
sqlOralComm.Add(new SqlParameter("EffectiveOralCommunication", cboxEffectiveOralCommunication.Text));
sqlOralComm.Add(new SqlParameter("ProfComm", cboxProfComm.Text));
DAL.ExecSP("CreateOralComm", sqlOralComm);
}
}
I've been reading into Dynamic SQL and saw that to pass the table name as a parameter, you have to construct it manually and execute it as "SET..." etc, etc. I've been trying slightly different modifications of the last 3 lines below. Each time, I'm greeted with an "invalid syntax near ..." exception pertaining to different parts of that line. In stack exchange it's broken into 3 lines but in SSMS it's one line, a little easier to read.
Status is nvarchar column and Course is an int column.
ALTER PROCEDURE [dbo].[CreateOralComm]
-- Add the parameters for the stored procedure here
#Username nvarchar(30),
#IntrotoPublicSpeaking nvarchar(3),
#EffectiveOralCommunication nvarchar(3),
#ProfComm nvarchar(3)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE #sql as nvarchar(max)
SET #sql = 'UPDATE ' + #Username + ' SET Grade = ' +
#IntrotoPublicSpeaking + ' Status = "Completed" WHERE Course = 7600105';
EXEC sp_executesql #sql;
END
GO
I know that global variable works, I have another line of code that's just a MessageBox displaying the value and it's correct. Just can't get those last few lines of SQL to work. I'm trying out just this first part, #IntrotoPublicSpeaking, before I move onto the other 2.
Any help would be really appreciated.
Two things here:
DECLARE #sql as nvarchar(max)
SET #sql = 'UPDATE ' + #Username + ' SET Grade = ' +
#IntrotoPublicSpeaking + ' Status = "Completed" WHERE Course = 7600105';
EXEC sp_executesql #sql;
Missing comma before Status and I think you do need to use single quotes
DECLARE #sql as nvarchar(max)
SET #sql = 'UPDATE ' + #Username + ' SET Grade = ' +
#IntrotoPublicSpeaking + ', Status = ''Completed'' WHERE Course = 7600105';
EXEC sp_executesql #sql;
I m loading table data into cache .And binding it to dropdown .But if there any new value added in datatable then that cache should invalid and new cache should get create.I am using Asp.Net 3.5 ,linq to SQL
Folloing is my .aspx code
public List<Employee> GetEmployeeData()
{
//get from cache
object obj = HttpRuntime.Cache.Get(Constants.Constants.cacheEmployee);
if (obj != null)
{
return obj as List<Employee>;
}
else // get from database
{
using (TestdbEntities entities = new TestdbEntities(connectionString))
{
try
{
IEnumerable<Employee> employee= entities.Employees.OrderBy(l => l.EmployeeName);
System.Web.Caching.SqlCacheDependencyAdmin.EnableNotifications(connectionString);
if (!System.Web.Caching.SqlCacheDependencyAdmin.GetTablesEnabledForNotifications(connectionString).Contains ("master.Employee"))
System.Web.Caching.SqlCacheDependencyAdmin.EnableTableForNotifications(connectionString, "master.Employee");
System.Web.Caching.SqlCacheDependency sqldep = new System.Web.Caching.SqlCacheDependency(connectionString, "master.Employee"); ;
HttpRuntime.Cache.Insert(Constants.Constants.cacheEmployee, employee.ToList(), sqldep);
return employee.ToList();
}
catch (Exception ex)
{
throw ex;
}
}
}
}
But i am getting following error
Please make sure the database name and the table name are valid. Table names must conform to the format of regular identifiers in SQL.
Please suggest
The other answer is correct that the "." in the table name is the issue. For some reason the AspNet_SqlCacheRegisterTableStoredProcedure wasn't written to handle different schemas.
I must credit this link with cluing me into the issue:
http://forums.asp.net/t/1249281.aspx?SQL+Cache+Dependency+with+custom+schema
However, I came up with my own re-write of the stored proc below that is a little better :) It's a little cleaner, handles the situation even if [ are included in the name, and also keeps the schema name in the AspNet_SqlCacheTablesForChangeNotification table in case you have the same table name in two different schemas.
DECLARE #triggerName AS NVARCHAR(3000)
DECLARE #fullTriggerName AS NVARCHAR(3000)
DECLARE #canonTableName NVARCHAR(3000)
DECLARE #quotedTableName NVARCHAR(3000)
DECLARE #schemaName NVARCHAR(3000)
DECLARE #schemaAndTableName NVARCHAR(3000)
--Replace these if they are in the param, we'll add them back later for the #canonTableName only
SET #tableName = REPLACE(REPLACE(#tableName, '[', ''), ']', '')
IF(CHARINDEX('.',#tableName) <> 0)
BEGIN
SET #schemaName = SUBSTRING(#tableName,0,CHARINDEX('.',#tableName))
SET #tableName = SUBSTRING(#tableName,CHARINDEX('.',#tableName) + 1,LEN(#tableName) - CHARINDEX('.',#tableName))
SET #schemaAndTableName = #schemaName + '.' + #tableName
END
ELSE
BEGIN
SET #schemaName = 'dbo' --If the param doesn't have a schema, use the default
SET #schemaAndTableName = #tableName --If they didn't pass in the schema, we don't want it in the AspNet_SqlCacheTablesForChangeNotification table
END
SET #triggerName = #tableName + '_AspNet_SqlCacheNotification_Trigger'
SET #fullTriggerName = '[' + #schemaName + '].[' + #triggerName + ']'
SET #canonTableName = '[' + #schemaName + '].[' + #tableName + ']'
/* First make sure the table exists */
IF (SELECT OBJECT_ID(#schemaAndTableName, 'U')) IS NULL
BEGIN
RAISERROR ('00000001', 16, 1)
RETURN
END
BEGIN TRAN
/* Insert the value into the notification table */
IF NOT EXISTS (SELECT tableName FROM dbo.AspNet_SqlCacheTablesForChangeNotification WITH (NOLOCK) WHERE tableName = #schemaAndTableName)
IF NOT EXISTS (SELECT tableName FROM dbo.AspNet_SqlCacheTablesForChangeNotification WITH (TABLOCKX) WHERE tableName = #schemaAndTableName)
INSERT dbo.AspNet_SqlCacheTablesForChangeNotification
VALUES (#schemaAndTableName, GETDATE(), 0)
/* Create the trigger */
SET #quotedTableName = QUOTENAME(#schemaAndTableName, '''')
IF NOT EXISTS (SELECT name FROM sysobjects WITH (NOLOCK) WHERE name = #triggerName AND type = 'TR')
IF NOT EXISTS (SELECT name FROM sysobjects WITH (TABLOCKX) WHERE name = #triggerName AND type = 'TR')
EXEC('CREATE TRIGGER ' + #fullTriggerName + ' ON ' + #canonTableName +'
FOR INSERT, UPDATE, DELETE AS BEGIN
SET NOCOUNT ON
EXEC dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure N' + #quotedTableName + '
END
')
COMMIT TRAN
The problem is the . in the table name master.Employee. The EnableTableForNotifications method calls the stored procedure AspNet_SqlCacheRegisterTableStoredProcedure in the database. Inside that is a call to the OBJECT_ID function, and I think that is parsing the table name as being a table called Employee in the master database. (When I first looked at this question, I was thinking the same way).
If you were to replace the . with an underscore _ in the table name, then your code will run correctly.
(You might also want to review your code in the case where the data isn't in the cache; data can disappear from the cache for any number of reasons but currently your code enables the database up for notifications every time, regardless of whether it's already enabled. The Single Responsibility Principle applies here to the GetEmployeeData method; it should only be concerned with getting data from the cache or database and not with setting up caching and notifications, that code should be broken out into a separate method.)
I need some help conceptualizing a project...
I have to run 4 different queries and send the results as the body of an email to defined recipients.
The problem is I will need to automate this process as I need to send the results every morning at 9am..., My initial thought was to just setup a job in SQL Server2000 and let that job to email the results, however this particular database is in SQL Server 2000...,
So then thought I could create a C# or Visual Basic program and use windows scheduler to run and email the reports, however I again come back to the fact that it is SQL Server 2000 and there is not a Stored Procedure to send mail.
I was able to find a Send Mail stored procedure online, but then couldn't figure out how to attach results to a parameter.
Any insight on how others would handle this would be greatly appreciated.
Thanks,
AJ
SQL 2000 does have jobs.
http://msdn.microsoft.com/en-us/library/aa215382(v=sql.80).aspx
How to create a job (Transact-SQL)
To create a job
1.Execute sp_add_job to create a job.
2.Execute sp_add_jobstep to create one or more job steps.
3.Execute sp_add_jobschedule to create a job schedule.
Email in SQL 2000 can be done via Outlook, but it's kind of a pain... Blat is free and does not need Outlook or POP3.
To avoid attachments and present a nice looking email, simply concat your row data into an HTML table and assign the result to the body. If you need more than 8000 characters, you'll have to use the text data type, but might be somewhat difficult
declare #result varchar(8000)
set #result = '<table>'
select #result = #result + '<tr><td>' + col1 + '</td><td>' + col2 + '</td></tr>'
from whereever
where something = something_else
order by 1
set #result = #result + '</table>'
http://www.blat.net/
Sample code: http://www.devx.com/dbzone/Article/42178
Exec stp_BlatMail 'ServerName', 'fhtapia#gmail.com',
'System Maintenance: Low Memory', 'D:\Data\TempFiles\MemoryRpt.txt'
CREATE PROCEDURE stp_BlatMail (#FromName AS VARCHAR (1000), #To AS VARCHAR(1000),
#Subject AS VARCHAR(1000), #BODY AS VARCHAR(8000),
#ATTACHMENTS AS VARCHAR(1024) = NULL) AS
-- By: Francisco H Tapia <fhtapia#gmail.com>
-- Date: 8/19/2003
-- Purpose: Provide Outlook free SMTP Mailing
SET NOCOUNT ON
DECLARE #CMD as VARCHAR(8000), #result as INT
IF #TO = ''
BEGIN
SELECT 'ERROR: NO TO Name'
END
ELSE
BEGIN
SELECT #CMD = ' D:\Data\Common\blat.exe - -subject "'+ #Subject
+ '" -t "' + #To
+ '" -sender "SystemUID#Domain.com" -from "'
+ #FromName
+'" -f "SQLMail" -ReplyTo "SystemUID#Domain.com" -org "My Company Name" -x "X-INFO: " -noh
-noh2 -server "ExchangeServerName" -port 25 -u EmailUID -pw Password -body "'
+ LTRIM(RTRIM(#Body)) + '" '
+ ' -q '
If #ATTACHMENTS <> ''
BEGIN
SELECT #CMD = #CMD + ' -attach "' + #ATTACHMENTS + '" '
END
ELSE IF #Attachments IS NOT NULL
BEGIN
SELECT 'NO ATTACHMENT FOUND'
END
EXEC #result = master..xp_cmdShell #CMD, NO_OUTPUT
END
SET NOCOUNT OFF
I have 10 identical databases.
I get the database names at runtime.
I want to store rows into a collection of objects.
I also only want one hit on the database server.
My current approach:-
In a query (no stored procedures for X reason) I get list of databases and store in a temporary table.
Then I iterate through each database and create a dynamic query and execute it.
DECLARE #MaxRownum int SET #MaxRownum = (SELECT MAX(RowNum) FROM #Databases)
DECLARE #Iter int SET #Iter = 1
WHILE #Iter <= #MaxRownum
BEGIN
DECLARE #Database varchar(255) SELECT #Database = Databases FROM #Databases
WHERE RowNum = #Iter
IF HAS_DBACCESS(#Database) > 0
BEGIN
//appending query
END
SET #Iter = #Iter + 1
END
EXEC(#Query)
Can I use Linq + entity framework with one hit to server, without dynamic query and without hampering the performance? Is there any better solution?
Having no idea what your query is (I asked but you did not supply it), and not sure that you understand it is going to be extremely difficult to supply database names as variables without a "dynamic query", here is a much simpler way to do it IMHO:
DECLARE #sql NVARCHAR(MAX);
SELECT #sql = N'';
SELECT #sql = #sql + CHAR(13) + CHAR(10) + 'UNION ALL'
--// you will need to fill in your "//appending query" stuff here:
+ ' SELECT ... FROM ' + QUOTENAME(Databases) + '.dbo.tablename'
FROM #Databases
WHERE HAS_DBACCESS(Databases) = 1;
SET #sql = STUFF(#sql, 1, 9, '');
EXEC sp_executesql #sql;