Showing posts with label backup. Show all posts
Showing posts with label backup. Show all posts

2019-02-03

Installing Ola Hallengren Maintenance Solution


Ola Hallengren Maintenance Solution (OHMS) is with good reason a well-known and widely used solution for SQL Server database backups and maintenance of indexes and statistics together with database integrity check. The solution is recommended by many great DBAs and SQL Server MVPs. Personally I have used the solution with great success in several financial organisations.

Installing OHMS is quite simple. In this text I will look into some details when installing OHMS in a scalable SQL Server infrastructure. The installation is here done i five steps:
  1. Create DBA database
  2. Configure OHMS installation
  3. Run OHMS installation
  4. Configure OHMS
  5. Test OHMS
With dbatools (dbatools.io) there is the PowerShell CmdLet Install-DbaMaintenanceSolution, that install or update OHMS in a given database. Check the details on the code in GitHub. I have not personally used dbatools, but they look fine and I might take a deeper look later.

Create DBA Database

Create seperate userdatabase on each instance for SQL Server administration, e.g. [sqladmin] with a prefix of the organisation. User underscore for space in the name like [nielsgrove_sqladmin].

This is a general solution that can be used for other database administration solutions.
This database does not require OHMS, but you should still do backups and maintain indexes.

Configure installation

Put OHMS in seperate filegroup to use database for other purposes now and in the future.

ALTER DATABASE [sqladmin] ADD FILEGROUP [OHMS];
GO
ALTER DATABASE [sqladmin] ADD FILE ( NAME = N'ohms_data0', FILENAME = N'C:\Data\sqladmin_ohms_data0.ndf' , SIZE = 8MB , FILEGROWTH = 64MB ) TO FILEGROUP [OHMS];
GO
ALTER DATABASE [sqladmin] MODIFY FILEGROUP [OHMS] DEFAULT;
GO


The filegroup [OHMS] is marked as default during the OHMS installation.

OHMS is using the schema [dbo] by default. You could change this, but this would require a rather detailed rewrite of the code. As the code in the scriptfile ManitenanceSolution.sql is more than 8000 lines I would not recomment changing the schema.

Download script from ola.hallengren.com. Define database name from above in the USE-statement around line 22.
Create cmd- or PowerShell-file to implement configuration. The simple solution used here is a one-line cmd-script as in the next section. You should always script even simple things, as you then are prepared to scale out with the same quality.
If you are building a automatic installation and upgrade you should get the precise URLs from the general .com-URL. That might require a more complex configuration.

If you want a longer or shorter history on backuphistory, jobhistory or CommandLog from the default 30 days you can edit the file MaintenanceSolution.sql by searching for DATEADD(dd,-30,GETDATE()) and change the value. You should change the history length before running the OHMS installation as it is more simple than alter the jobs afterwards. I like to have 42 days of history...

Run OHMS installation

sqlcmd command-line using configuration above. If you create a more complex configuration, the execution itself will have to be considered in detail.

C:\>sqlcmd.exe -S "(local)\SQLSERVER" -E -d "sqladmin" -i "C:\SysAdmin\MaintenanceSolution.sql"
Changed database context to 'sqladmin'.


And change the default filegroup back to [PRIMARY]
ALTER DATABASE [sqladmin] MODIFY FILEGROUP [PRIMARY] DEFAULT;
GO


Configure OHMS

The sql-script dowes create SQL Agent jobs, but the jobs has no schedule. Add a schedule to each job by the stored procedure [msdb].[dbo].[sp_add_jobschedule]. This is a quick example:

DECLARE @schedule_id int;
EXECUTE [msdb].[dbo].[sp_add_jobschedule]
  @job_name=N'CommandLog Cleanup',
  @name=N'OHMS CommandLog Cleanup',
  @enabled=1,
  @freq_type=8,
  @freq_interval=1,
  @freq_subday_type=1,
  @freq_subday_interval=0,
  @freq_relative_interval=0,
  @freq_recurrence_factor=1,
  @active_start_date=20190203,
  @active_end_date=99991231,
  @active_start_time=190000,
  @active_end_time=235959, @schedule_id = @schedule_id OUTPUT;
SELECT @schedule_id;
GO


OHMS create these 11 jobs in SQL Agent:
  • CommandLog Cleanup
  • DatabaseBackup - SYSTEM_DATABASES - FULL
  • DatabaseBackup - USER_DATABASES - DIFF
  • DatabaseBackup - USER_DATABASES - FULL
  • DatabaseBackup - USER_DATABASES - LOG
  • DatabaseIntegrityCheck - SYSTEM_DATABASES
  • DatabaseIntegrityCheck - USER_DATABASES
  • IndexOptimize - USER_DATABASES
  • Output File Cleanup
  • sp_delete_backuphistory
  • sp_purge_jobhistory
You can get a list of the jobs from the table [msdb].[dbo].[sysjobs]:
SELECT * FROM msdb.dbo.sysjobs
WHERE [description]=N'Source: https://ola.hallengren.com'
ORDER BY [name];


Glenn Berry has written a script that creates the jobs with a good reference schedule. The script is introduced in the blog post "Creating SQL Server Agent Job Schedules for Ola Hallengren’s Maintenance Solution".

All OHMS jobs are placed in the SQL Agent job category Database Maintenance. You could change the category with the stored procedure [msdb].[dbo].[sp_update_job], but I think that the benefits will be rather limited.

Log files are placed in SQL Server ErrorLogPath where the SQL Server ErrorLog and default traces are placed by default. If you want the OHMS log files another place, then change the job steps with the stored procedure [msdb].[dbo].[sp_update_jobstep]. You should only do this if you are forced to by stupid policies.

Put configuration in script file(-s) and make it idempotent. Also put everything in version control, with the documentation.

If you want to remove the OHMS jobs then this query on the table [msdb].[dbo].[sysjobs] can generate the statements:
SELECT N'EXECUTE [msdb].[dbo].[sp_delete_job] @job_name=N''' + [name] + N''', @delete_unused_schedule=1;'
FROM [msdb].[dbo].[sysjobs]
WHERE [description]=N'Source: https://ola.hallengren.com';


Test OHMS

Run each job starting with database integrity checks, then full data backup moving to differential backup and closing with transaction log backup spiced up with index and statistics maintenance jobs.
Check output of each job and check SQL Server errorlog after each SQL Server Agent job execution.

You can generate the statements th start the OHMS jobs refering the stored procedure [msdb].[dbo].[sp_start_job] in a query on the table [msdb].[dbo].[sysjobs] like above:
SELECT N'EXECUTE [msdb].[dbo].[sp_start_job] @job_name=N''' + [name] + N''';'
FROM [msdb].[dbo].[sysjobs]
WHERE [description]=N'Source: https://ola.hallengren.com';


Improvements

  • The installation script handles both installation and update. Some identification on which version is installed and the version about to be deployed.
  • Integrate installation with your Definitive Software Library (DSL). Put the complete installations set with Ola's and you own scripts in a combined installation set.
  • More robust errorhandling. Especially in the tests.

History

2019-02-03 : First text. 2021-06-15 : Section on changed history added.

2018-10-09

TFS Backup

Backup share is required by the TFS backup wizard. Actually the wizard checks if the share is on the local host. To be sure that the rights are correct for a SQL Server backup the SQL Server service account should be owner of the folder that is shared. Subfolders should be created with the (undocumented) extended stored procedure xp_create_subdir to ensure that the SQL Server service account has the correct rights on the given folder.

Backup by TFS Job Tasks defined in TFS Administration Console (TfsMgmt.exe). Please be avare of that files are written in two contexts - the TFS Service (TFSService) and the SQL Server service:
  • Recovery Model on all TFS and SSRS databases is set to Full in the context of the administrator.
  • Backup configuration is written in xml-file in TFS backup folder (BackupSettings.xml) in the context of the TFS Service.
  • Backup of Reporting Services encryption keys are done by the wizard - not by a TFS backup job. This is why there only is one snk-file on a key backup in the TFS backup share.
Actual process in backup job:
  1. Verify installation, running status and capacity.
  2. Prepare databases for synchronized backup
  3. Grant permissions to backup path
  4. Full database backup of TFS databases; Configuration, Collection(-s) and Warehouse. Also SSRS databases; ReportServer and ReportServerTempDB. Backup files are created and written inthe context of the SQL Server service - like a normal SQL Server backup.
  5. Marking TFS databases using stored procedure prc_SetTransactionMark in each database; EXEC prc_SetTransactionMark TfsMarkTfpt
  6. Backup transaction log on TFS and SSRS databases. Backup files are created and written in the context of the SQL Server service.
Log files on the TFS job task are in the folder "%ProgramData%\Microsoft\Team Foundation\Server Configuration\Logs".
Also configuration and log files on each TFS backup run in the TFS backup folder.
  • Transactional_<number>.log; TFS job log on transaction log backup.
  • <database name>_<number>L.trn; SQL Server transaction log backup file.
  • Delete_<number>.log; TFS job log on deletion of old files.
  • Full_<number>.log; TFS job log on full database backup.
  • <database name>_number>F.bak; SQL Server full database backup file.
  • BackupSets.xml; TFS backup history on existing backup files.
  • RSKey<number>.snk; SQL Server Reporting Services encryption key backup file.
The log file BackupSets.xml holds information about the the existing backup files and their history.
Existing backup files is controlled by the TFS Delete job that deletes backup files on the given retention period when the recommended minimum value is seven days.

TFS Scheduled Backups does not
    • back up system databases; master, msdb, model. You could use Ola's SQL Server Maintenance Solution (GitHub).
    • back up Service Master Key (SMK).
    • back up Database Master Key (DMK) on master.
    • verify backup files.

    2013-08-22

    Backup failed - nonrecoverable I/O

    On a SQL 2008 SP2 (10.0.4000) I take database backup with a Maintenance Plan. This morning I had a failed database backup:
    Executing the query "BACKUP DATABASE [thedatabase] TO  DISK = N'T:\\Backup\\th..." failed with the following error: "A nonrecoverable I/O error occurred on file "T:\\Backup\\thedatabase_backup_2011_01_18_221400_5137140.bak:" 112(failed to retrieve text for this error. Reason: 15105).
    No nice :-(

    When I looked in the log file generated by the Maintenance Plan, the error message was:
    Task start: 2011-01-18T22:14:00.
    Task end: 2011-01-18T22:14:01.
    Failed:(-1073548784) Executing the query "BACKUP DATABASE [thedatabase] TO  DISK = N'T:\\Backup\\th..." failed with the following error: "A nonrecoverable I/O error occurred on file "T:\\Backup\\thedatabase_backup_2011_01_18_221400_5137140.bak:" 112(failed to retrieve text for this error. Reason: 15105).
    BACKUP DATABASE is terminating abnormally.". Possible failure reasons: Problems with the query, "ResultSet" property not set correctly, parameters not set correctly, or connection not established correctly.


    When I looked in the Windows System Log, there was no relevant entries.
    But looking at the drive and the free space I saw that there was not space enough for the next backup file.
    After a cleanup and manual execution of the Maintenance Plan everything was green.

    The amount of free space can be looked up with PowerShell like this:
    Get-WmiObject -Query "SELECT FreeSpace, Size FROM Win32_LogicalDisk WHERE DeviceID = 'T:'" -ComputerName SANDY.sqladmin.lan |
    Format-Table @{Name='Freespace (GB)';Expression={"{0:N1}" -f($_.FreeSpace/1gb)}}, @{Name='Size (GB)';Expression={"{0:N1}" -f($_.Size/1gb)}} -AutoSize

    The value of the parameter ComputerName should be changed to the actual databaseserver, also value in the WQL WHERE clause should be changed to the drive indicated in the error message.

    The follow up is to order some additional storage.

    I find the error message somewhat misleading. It looks like the ResultSet part is from the connectivity and not from the root error.

    History

    2011-01-19 : This is postede for the first time.
    2013-08-22 : The PowerShell script is added.

    2012-10-16

    Check NetBackup file backup


    I have been looking in ways to ensure that a SQL Server backup file is backed up by NetBackup, so that the backup file can be deleted from the local disk. I have not found a API for the local NetBackup client, but there are a lot of command-line tools.
    In this case our backup vendor has pointed out the tool "bplist" (bplist.exe). Usually bplist is located in the folder „%ProgramFiles\Veritea\NetBackup\bin\“.
    A bplist answer example looks like this:
    -rwx------ root;SQL1 root;Doma      485888 Jul 29 20:00:01 E:\MSSQL\Backup\ReportServer_backup_2012_07_28_221142_2861771.bak
    if bplist is called with the parameters „-l -b -Listseconds“.

    WARNING!!!
    No year on NetBackup backup.
    This is also discussed in the forum thread "bplist does not display the year of a backup?".
    It looks like there is an issue on the age of the backup, if it is more than six months old or not.
    Also when I look in the documentation, there are some differences on the answer from the NetBackup server if it is Windows or Linux. Please notice that it is the NetBackup server, not the client operating system that gives the answer.

    bplist can be executed in PowerShell with the invoke operator (&):
    & "<NetBackup folder>\bplist.exe" -l -b "X:\MSSQL\Backup\sqladmin_repository_backup_2012_12_24_172249_7006410.bak"
    The full path must be provided for the file to examine in NetBackup.

    To look at how to handle bplist and its answer in a automated way, I made this PowerShell spike script:
    param (
      [Parameter()]
      [ValidateScript({Get-ChildItem -Path $_})]
      [string]$NetBackup_Folder = 'C:\Program Files\Veritas\NetBackup\bin'
    )

    Set-StrictMode -Version 2.0

    function Get-NetBackup_bplist {
    [CmdletBinding()]
    param(
      [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
      [System.IO.FileInfo]$File
    )
      BEGIN { Write-Verbose "OS Version = $([System.Environment]::OSVersion.VersionString)" }
      PROCESS {
        Write-Verbose "File name = '$($File.FullName)'."

        $BpList = New-Object -TypeName PSObject
        $BpList = $File
        $BpList.PSObject.TypeNames.Insert(0,'SqlAdmin.NetBackup.BpList')

        # Get file backup status from NetBackup
        $bplist_answer = $(& "$NetBackup_Folder\bplist.exe" -l -b -Listseconds "$($File.FullName)") 2>&1 # Redirect bplist.exe error to $bplist_answer
        if ($bplist_answer.GetType().IsArray) { # Multiple backups in NetBackup
          Write-Verbose " $($bplist_answer.Length) backups found in NetBackup. Will continue on last backup."
          $_bplist = $bplist_answer[0] # Get last backup in NetBackup
        }
        else {
          $_bplist = $bplist_answer
        }
        Write-Verbose " $($_bplist)"

        # Evaluate file backup status from NetBackup. Add -PassThru to last added member.
        if ($_bplist.ToString() -ceq 'EXIT STATUS 227: no entity was found') { # File not in NetBackup
          Write-Verbose " --- NO backup in NetBackup ($($File.Name))."
          Add-Member -InputObject $BpList -MemberType NoteProperty -Name HasBackup -Value $false
          Add-Member -InputObject $BpList -MemberType NoteProperty -Name BackupLength -Value $null
          Add-Member -InputObject $BpList -MemberType NoteProperty -Name BackupTime -Value $null
        }
        else {
          Write-Verbose " +++ Backup is in NetBackup ($($File.Name))."
          Add-Member -InputObject $BpList -MemberType NoteProperty -Name HasBackup -Value $true

          # Get backup details
          $regex = [regex]"\w+"
          $Backup = $($regex.matches($_bplist)) # Returns [System.Text.RegularExpressions.Match]

          if ($Backup[5].Value.SubString($Backup[5].Length-1) -eq 'K') { # Is the most right char 'K'?
            $Backup_Size = [int]$Backup[5].Value.SubString(0,$Backup[5].Length-1) * 1024 # Convert from KB to Bytes
          }
          else {
            $Backup_Size = [int]$Backup[5].Value
          }
          Add-Member -InputObject $BpList -MemberType NoteProperty -Name BackupLength -Value $Backup_Size
          $DateParse = "$($Backup[7].Value) $($Backup[6].Value) $([System.DateTime]::Now.Year) $($Backup[8].Value):$($Backup[9].Value):$($Backup[10].Value)"
          Add-Member -InputObject $BpList -MemberType NoteProperty -Name BackupTime -Value $([System.DateTime]::Parse($DateParse))
        }
        Write-Output $BpList
      }
      END {}
    }


    ### INVOCATION ###
    switch -casesensitive ($ComputerName) {
      'TRACY.SQLADMIN.LAN' {
      Get-ChildItem -Path 'D:\MSSQL_Backup' |
      Sort-Object -Property Length -Descending |
      Sort-Object -Property LastWriteTime -Descending |
      Select-Object -First 100 |
      Get-NetBackup_bplist | #-Verbose |
      Where-Object { $_.HasBackup -eq $true } |
      #Format-Table Name,Length,HasBackup,BackupTime,BackupLength -AutoSize
      ForEach-Object { Remove-Item $_.FullName -Confirm:$true }
      break
      }
      default {
      Get-ChildItem -Path 'X:\MSSQL\Backup' |
      Sort-Object -Property Length -Descending |
      Select-Object -First 20 |
      Get-NetBackup_bplist |
      #Format-Table Name,Length,HasBackup,BackupTime -AutoSize
      ForEach-Object { Remove-Item $_.FullName -Confirm:$true }
      break
      }
    }


    Again – a spike…

    The general motivation to look into this is to ensure that the restore chain is complete, also in the secondary backup on NetBackup.
    The complete restore chain is necessary to ensure complete recovery.

    (This is a running update on a post from 2012-08-27)

    2012-04-09

    Get Backup Directory with ADO.NET

    About 1½ year ago I made a entry on this blog on how to get the SQL Server Backup Directory by using the undocumented stored procedure "[master].[sys].[xp_instance_regread]".
    Today I needed the path name in a PowerShell script, and I also wanted to call the procedure correct.
    This I have done by calling the procedure through ADO.NET as a stored procedure, not in a EXECUTE statement as dynamic SQL.
    $ServerName = '(local)'
    $cnnStr = "Data Source=$ServerName;Integrated Security=SSPI;Application Name=SqlBackupFolder"
    $Cnn = New-Object System.Data.SqlClient.SqlConnection $cnnStr
    $Cmd = New-Object System.Data.SqlClient.SqlCommand
    $Cmd.Connection = $Cnn
    $Cmd.CommandText = '[master].[sys].[xp_instance_regread]'
    $Cmd.CommandType = [System.Data.CommandType]::StoredProcedure
    $Cmd.Parameters.Add("@rootkey", [System.Data.SqlDbType]::NVarChar, 128) | Out-Null
    $Cmd.Parameters['@rootkey'].Direction = [System.Data.ParameterDirection]::Input
    $Cmd.Parameters['@rootkey'].Value = 'HKEY_LOCAL_MACHINE'
    $Cmd.Parameters.Add("@key", [System.Data.SqlDbType]::NVarChar, 128) | Out-Null
    $Cmd.Parameters['@key'].Direction = [System.Data.ParameterDirection]::Input
    $Cmd.Parameters['@key'].Value = 'SOFTWARE\Microsoft\MSSQLSERVER\MSSQLSERVER'
    $Cmd.Parameters.Add("@value_name", [System.Data.SqlDbType]::NVarChar, 128) | Out-Null
    $Cmd.Parameters['@value_name'].Direction = [System.Data.ParameterDirection]::Input
    $Cmd.Parameters['@value_name'].Value = 'BackupDirectory'
    $Cmd.Parameters.Add("@value", [System.Data.SqlDbType]::NVarChar, 128) | Out-Null
    $Cmd.Parameters['@value'].Direction = [System.Data.ParameterDirection]::Output
    $Cnn.Open()
    $_RowCount = $Cmd.ExecuteNonQuery()
    $Cnn.Close()
    $SqlBackupFolder = $Cmd.Parameters['@value'].Value
    ":: SQL Server Backup Folder = '$SqlBackupFolder'."

    The answer from the script is like
    :: SQL Server Backup Folder = 'C:\MSSQL\Backup'.

    It is possible to reduce the number of lines in the script, but this way I can use the call of the procedure for other information than the Backup Directory.

    If you plan to reuse the Command object, you should consider to remove the Parameters ($Cmd.Parameters.Clear()) and reset the CommandType ($Cmd.CommandType = [System.Data.CommandType]::Text).

    2011-03-09

    "BACKUP DATABASE is terminating abnormally."

    This morning I had a error on a SQL 2000/Windows Server 2000 installation in a backup by a Maintenance Plan.
    In the SQL Agent job the error was shown by
    Executed as user: *****. sqlmaint.exe failed. [SQLSTATE 42000] (Error 22029).  The step failed.

    The Maintenance plan log said:
    [11] Database ServicedeskDB: Database Backup...
        Destination: [********201103082203.BAK]
    [Microsoft SQL-DMO (ODBC SQLState: 42000)] Error 3202: [Microsoft][ODBC SQL Server Driver][SQL Server]Write on '********201103082203.BAK' failed, status = 112. See the SQL Server error log for more details.
    [Microsoft][ODBC SQL Server Driver][SQL Server]BACKUP DATABASE is terminating abnormally.


    In the SQL Error Log There were these entries.
    2011-03-08 22:04:11.52 spid66    BackupMedium::ReportIoError: write failure on backup device '********201103082203.BAK'. Operating system error 112(error not found).
    2011-03-08 22:04:11.53 spid66    Internal I/O request 0x6B514990: Op: Write, pBuffer: 0x06350000, Size: 983040, Position: 4470543872, UMS: Internal: 0x103, InternalHigh: 0x0, Offset: 0xA771600, OffsetHigh: 0x1, m_buf: 0x06350000, m_len: 983040, m_actualBytes: 0, m_errcode: 112, BackupFile: ********201103082203.BAK
    2011-03-08 22:04:11.53 backup    BACKUP failed to complete the command BACKUP DATABASE [*****] TO  DISK = N'********201103082203.BAK' WITH  INIT ,  NOUNLOAD ,  NOSKIP ,  STATS = 10,  NOFORMAT


    By the SQL Error Log it could look like a storage failure :-(
    Not a nice way to start the day.

    When I looked at the Windows System Log there were no entries on storage failure.
    But looking at the storage I saw that there were 4.15 GB available, and that the last backup took 4.16 GB.

    After a quick cleanup and a manual execution of the job - with success - the conclusion is that the error indicates a lack of available storage.

    2011-01-18

    Share backup files

    When I have to establish a database mirror or restore a database in another environment, I usually restore direct from the original backup file without a copy or move before. The restore is done on a UNC reference to the backup file.
    This can not be done in the Management Studio GUI, but must be done by T-SQL as the GUI fails on a UNC reference to the backup file.
    The UNC reference is on a share I create on the backup disk. I prefer to name the share "SQLBackup".
    I have looked around for Best Practices on file shares and security. The best description I found is by Derek Melber (see Reference), but I have some issues...

    It makes sense in general to control the details in access and rights i NTFS, and make a general access on the share. Derek suggests to grant access on the share to Authenticated Users in the domain and give Full Control rights. This should be done while you keep in mind that a Windows default right is that Everyone can Read.
    When we are dealing with database backup files, that contain sensible data, I find this access a little too "broad" as is grant access to too many accounts.

    Instead I have given specific Read access to the share, and depends on the general Read access to Everyone.
    If Everyone is removed, and this I have seen in several organisations, you have to take care of the file access and rights also.

    And again: Keep in mind that the backup files are to be handled as sensitive data.

    Reference

    Derek Melber: "Share Permissions".

    2010-08-23

    Backup Directory

    With SQL Server 2005 we were able to define a default backup directory. This is a nice thing to maintain, so that a restore can be done quickly and correct.
    The directory path is stored in the Windows Registry, and is available with a (undocumented) stored procedure.
    DECLARE @reg_key_data NVARCHAR(255);
    EXECUTE [master].[dbo].[xp_instance_regread]
      N'HKEY_LOCAL_MACHINE'
     ,N'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQLServer'
     ,N'BackupDirectory'
     ,@reg_key_data OUTPUT;
    SELECT @reg_key_data AS N'backup_directory';

    (Fig. 1) SQL Server Management Studio, Facets

    The script works on SQL server 2005 and 2008.
    You could set the backup directory path by the (undocumented) stored procedure master.dbo.xp_instance_regwrite, but I prefer to change the value at the installation or manually using SQL Server Management Studio.
    The name of the backup directory is available for read and write in SQL Server Management Studio as a facet to the database instance. Right-click on the instance in Object Explorer and select Facets. In the facet "Server" it is the second item.

    Reference

    Dinesh Asanka: „Working with the Registry“ (SQLServerCentral).

    2009-08-18

    msdb.dbo.backupset

    This morning I wanted to have a quick look at the backup history of a given database. This I got by this statement

    SELECT [backup_start_date] AS 'Start'
    ,([backup_finish_date]-[backup_start_date]) AS 'Duration'
    ,(([backup_size]/1024)/1024)/1024 AS 'Size [GB]'
    FROM [msdb].[dbo].[backupset]
    WHERE [database_name]='my_database' AND [type]='D'
    ORDER BY [Start] DESC;


    The multiple division by 1024 is to get a clear number.

    2009-01-06

    Differential database backup using SMO

    A differential database backup of a single database can be done like this using SMO and PowerShell:

    [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO')
    $SsdbName = 'Sandbox\Ssdb0'
    $BackupPath = 'Z:\Backup\Ssdb0'
    $Server = New-Object 'Microsoft.SqlServer.Management.Smo.Server' $SsdbName
    $Database = $Server.Databases['msdb']
    $Backup = New-Object 'Microsoft.SqlServer.Management.Smo.Backup'
    $Backup.Checksum = $true
    $Backup.Database = $Database.Name
    $Backup.Incremental = $true
    $Backup.BackupSetDescription = 'Differential backup of the database [' + $Database.Name + '].'
    $BackupFileName = $Database.Name + '_Diff.bak'
    $BackupDevice = New-Object 'Microsoft.SqlServer.Management.Smo.BackupDeviceItem'
    $BackupDevice.DeviceType = 'File'
    $BackupDevice.Name = [System.IO.Path]::Combine($BackupPath, $BackupFileName)
    $Backup.Devices.Add($BackupDevice)
    $Backup.BackupSetName = $Database.Name + ' ' + $BackupType + ' backup'
    $Backup.SqlBackup($Server)
    $Backup.Wait()
    "[{0}] is backed up to the file '{1}'." -f $Database.Name, $BackupDevice.Name


    The generated backup file is named "msdb_Diff.bak". A more unique filename is preferred, but this is just a simple example.

    The output of the script is:
    [msdb] is backed up to the file 'T:\Backup\Ssdb0\msdb_Diff.bak'.

    A more detailed description of SMO backup using PowerShell is done by Muthusamy Anantha Kumar in a article at Database Journal (link).