Tuesday 20 October 2020

Lazy writer in SQL Server

 

Lazy writer in SQL Server:-

  • Lazy writer check each page header and verify whether all transactions in the pages are committed then pages will be flushed to disk… [Page header contains an infusion of whether committed\uncommitted]
  • This is one of the background processes where works only from BUFFER Cache.
  • The lazy writer works only when the system is running with memory pressure or running out of memory.
  • User cannot see or create, manual lazy writer operation in SQL Server
  • We cannot able to capture lazy writer information into SQL Server logs.
  • Lazy writer allocating space for new pages in the buffer pool
  • Lazy writer Checking for memory pressure.
  • Lazy writer Only works with the buffer pool but not in the log file.

Note: The prime job of a lazy writer is it flush the pages from buffer to disk.

Difference Between Checkpoint And Lazy Writer

 

CHECKPOINT vs LAZY WRITER


CHECKPOINTLAZY WRITER
Checkpoint runs in an only transaction log fileThe lazy writer operates from a buffer pool
The checkpoint is the logged operation and writes to Tlog fileThe lazy writer  is the non-logged operation and did not write to Tlog file
Checkpoint can be controlled by user and SQL server engine as wellThe lazy writer only operate by the Sql server engine
The checkpoint is the background process which triggers every 3 secThe lazy writer does not have any fixed timeline and only occurs when there is memory pressure in the buffer pool
We can able to track checkpoint into Sql server logs by enabling the trace 3502Lazy writer information cannot be able to track into SQL Server logs.
In memory, free pages list not taken care and kept free listIn memory, free pages list  taken care and kept free list
We can fire a query to see checkpoint information by using Select * from:: fn_dblog (null, null) WHERE [Operation] like ‘%CKPT’

 

We don’t have any query to see lazy writer information
Command: Checkpoint  [time in a sec]No command available

Thursday 1 October 2020

PowerShell Scripts


PSScript 1:

The below mentioned PowerShell script is used to load all CSV files into SQL Server at a time. 

Note: Before executing this script need to check the SQL Server Instance name, Database name, and finally path of the CSV files.

 

function CSVUpload 

{

param(

    [parameter(Mandatory)]

    [ValidateNotNullOrEmpty()]

    [string] $serverName,


    [parameter(Mandatory)]

    [ValidateNotNullOrEmpty()]

    [string] $dbCheck,


    [parameter(Mandatory)]

    [ValidateNotNullOrEmpty()]

    [string] $pth

)


$strQuery = ""


$fls = Get-ChildItem -Path 'C:\\PSScript\\RINDLA\\' 

#$flsDifrectories =  $fls  | where {$_.mode -eq 'd----' } | select Name

#$flsDifrectories | Out-Host 

$csvAll=$fls | where {$_.name.EndsWith('.csv') } | select Name

foreach ($tree in $csvAll) {

$fileName='C:\\PSScript\\RINDLA\\'+$tree.Name 

if((Get-Content $fileName))

{

 $file = Import-Csv $fileName | Get-Member -MemberType NoteProperty

   #Check for table exists or not 

    $cn2 = new-object system.data.SqlClient.SQLConnection("Data Source="+'RAJ-PC\RINDLA'+";Integrated Security=SSPI;Initial Catalog="+'Sales_Info'+"");

     $strQueryTest= "select name from sys.tables where name = '"+$tree.Name.Replace(".csv","").Replace(".","_")+"'"

    $cmd = new-object system.data.sqlclient.sqlcommand( $strQueryTest, $cn2);

    $cn2.Open();

    $movedUserDetails= $cmd.ExecuteReader()

   try

   {

     

     if(! $movedUserDetails.HasRows )

     {

     $isExists="false"

     

     }

     if( $movedUserDetails.HasRows)

     {

    

        while ($movedUserDetails.Read())

         {

           if($movedUserDetails.GetValue(0) -eq $tree.Name.Replace(".csv","").Replace(".","_"))

           {

            $isExists="true"      

           }

         }

         }

     }

     catch

     {

       #log error

       throw "Error reading file "+$tree.Name

     }

     finally

     {

       $cn2.Close() 

  

     }


     #create table for new file 

      if($isExists -eq "false")

         {

         

         $strQuery = "Create Table ["+ $tree.Name.Replace(".csv","").Replace(".","_") +"]" 

        $strQueryCol="" 

         foreach ($col in $file) {

          $strQueryCol= $strQueryCol+ ",["+$col.Name + "] varchar (max)"

        }

        if($strQueryCol.Length -gt 0)

        {

         $isExists ="true";

        $strQueryCol = $strQueryCol.Substring(1,$strQueryCol.Length-1)

        if( $strQueryCol -ne "")

        {

         $strQuery =  $strQuery + " ( " + $strQueryCol + " )"

        }

          $cmdNew = new-object system.data.sqlclient.sqlcommand( $strQuery, $cn2);

           $cn2.Open();

            $cmdNew.ExecuteNonQuery();

            $cn2.Close() 

        }

         

       }


         #Insert data in table 

         $QueryInsert=""

         $data =Import-Csv  $fileName

        $fileName

        $i=0;

         if(!$data.length)

      {

      foreach ($dt in $data[0])

        {

        $Name=""

        $Values=""

             $dt.psobject.Properties | foreach { 

             if($Name -eq "")

             {

              $Name =$Name +"["+$_.Name +"]"

             }

            else

            {

             $Name =$Name +","+"["+$_.Name +"]"

            }

   

            if($Values -eq "")

             {

              $Values =$Values +"'"+$_.Value.Replace("'","''")+"'" 

             }

            else

            {

             $Values =$Values +",'"+$_.Value.Replace("'","''")+"'"

            }

   

            }

            

            $QueryInsert = "Insert into ["+$tree.Name.Replace(".csv","").Replace(".","_")+"] "

            $QueryInsert=$QueryInsert + "( "+ $Name + " )";

            $QueryInsert=$QueryInsert +" Values ("+ $Values +")";

         

           $cmdNewInsert = new-object system.data.sqlclient.sqlcommand( $QueryInsert, $cn2);

           $cn2.Open();

            $cmdNewInsert.ExecuteNonQuery();

            $cn2.Close() 

        }

      }

        while( $i -lt $data.length)

    {

    foreach ($dt in $data[$i])

        {

        $Name=""

        $Values=""

             $dt.psobject.Properties | foreach { 

             if($Name -eq "")

             {

              $Name =$Name +"["+$_.Name +"]"

             }

            else

            {

             $Name =$Name +","+"["+$_.Name +"]"

            }


            if($Values -eq "")

             {

              $Values =$Values +"'"+$_.Value.Replace("'","''")+"'" 

             }

            else

            {

             $Values =$Values +",'"+$_.Value.Replace("'","''")+"'"

            }


            }

            

            $QueryInsert = "Insert into ["+$tree.Name.Replace(".csv","").Replace(".","_")+"] "

            $QueryInsert=$QueryInsert + "( "+ $Name + " )";

            $QueryInsert=$QueryInsert +" Values ("+ $Values +")";

          $QueryInsert

           $cmdNewInsert = new-object system.data.sqlclient.sqlcommand( $QueryInsert, $cn2);

           $cn2.Open();

            $cmdNewInsert.ExecuteNonQuery();

            $cn2.Close() 

        }

        $i =$i +1

    }

    

    }

    }

 


}


CSVUpload -serverName 'RAJ-PC\RINDLA' `

            -dbCheck 'Sales_Info' `

            -pth 'C:\\PSScript\\RINDLA\\'




==================================================================================================================================================

PSScript 2:

The below mentioned PowerShell script is used to load the same CSV file structure of all files into SQL Server at a time. 

Note: Before executing this script need to check the SQL Server Instance name, Database name, Table name, and finally path of the CSV files.

#Read CSV file 

#open connection to database using bulkcopy

#convert array to data table

#bulkload data into table

#note: column sequence and data types should match


function Get-Type 

    param($type) 

 

$types = @( 

'System.Boolean', 

'System.Byte[]', 

'System.Byte', 

'System.Char', 

'System.nvarchar',

'System.Datetime', 

'System.Decimal', 

'System.Double', 

'System.Guid', 

'System.Int16', 

'System.Int32', 

'System.Int64', 

'System.Single', 

'System.UInt16', 

'System.UInt32', 

'System.UInt64') 

 

    if ( $types -contains $type ) { 

        Write-Output "$type" 

    } 

    else { 

        Write-Output 'System.String' 

         

    } 

}



function Out-DataTable 

    [CmdletBinding()] 

    param([Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [PSObject[]]$InputObject) 

 

    Begin 

    { 

        $dt = new-object Data.datatable   

        $First = $true  

    } 

    Process 

    { 

        foreach ($object in $InputObject) 

        { 

            $DR = $DT.NewRow()   

            foreach($property in $object.PsObject.get_properties()) 

            {   

                if ($first) 

                {   

                    $Col =  new-object Data.DataColumn   

                    $Col.ColumnName = $property.Name.ToString()   

                    if ($property.value) 

                    { 

                        if ($property.value -isnot [System.DBNull]) { 

                            $Col.DataType = [System.Type]::GetType("$(Get-Type $property.TypeNameOfValue)") 

                         } 

                    } 

                    $DT.Columns.Add($Col) 

                }   

                if ($property.Gettype().IsArray) { 

                    $DR.Item($property.Name) =$property.value | ConvertTo-XML -AS String -NoTypeInformation -Depth 1 

                }   

               else { 

                    $DR.Item($property.Name) = $property.value 

                } 

            }   

            $DT.Rows.Add($DR)   

            $First = $false 

        } 

    }  

      

    End 

    { 

        Write-Output @(,($dt)) 

    } 

 

} #Out-DataTable

$fileExtension = '*csv*.*'#you can use wildcards here for name and for extension

$searchinfolder = 'C:\PSScript\RINDLA'

$FileNames=Get-ChildItem -Path $searchinfolder -Filter $fileExtension -Recurse | %{$_.FullName}

$fileName =  [System.IO.Path]::GetFileNameWithoutExtension($FileNames)

foreach($Files in $FileNames)

{

$file=$Files


$cn = new-object system.data.SqlClient.SQLConnection("Data Source="+'RAJ-PC\RINDLA'+";Integrated Security=SSPI;Initial Catalog="+'Estimation_Details'+"");

$cn.Open()

$bc = new-object ("System.Data.SqlClient.SqlBulkCopy") $cn

$bc.BatchSize = 10000;

$bc.BulkCopyTimeout = 1000

$bc.DestinationTableName = "All_Region_Sales"

#$bc.DestinationTableName = $fileName


$data = Import-Csv $file | Out-DataTable


$bc.WriteToServer($data)


}



==================================================================================================================================================

PSScript 3:

The below mentioned PowerShell script is used to Extract the query result from SQL Server to CSV files

Note: Before executing this script need to check the SQL Server Instance name, Database name, Query, and finally path of the CSV files.


[string] $Server= "RAJ-PC\RINDLA"

[string] $Database = "AdventureWorks"

[string] $UserSqlQuery= $("SELECT * FROM sys.tables")



# declaration not necessary, but good practice

$resultsDataTable = New-Object System.Data.DataTable

$resultsDataTable = ExecuteSqlQuery $Server $Database $UserSqlQuery 


# executes a query and populates the $datatable with the data

function ExecuteSqlQuery ($Server, $Database, $SQLQuery) {

    $Datatable = New-Object System.Data.DataTable

    

    $Connection = New-Object System.Data.SQLClient.SQLConnection

    $Connection.ConnectionString = "server='$Server';database='$Database';trusted_connection=true;"

    $Connection.Open()

    $Command = New-Object System.Data.SQLClient.SQLCommand

    $Command.Connection = $Connection

    $Command.CommandText = $SQLQuery

    $Reader = $Command.ExecuteReader()

    $Datatable.Load($Reader)

    $Connection.Close()

    

    return $Datatable

}


#validate we got data

Write-Host ("The table contains: " + $resultsDataTable.Rows.Count + " rows")

$resultsDataTable | export-Csv -Path "D:\RAJ\Queryoutput.csv"


==================================================================================================================================================

PSScript 4:

The below mentioned PowerShell script is used to Extract the query result from multiple SQL Server Instances to CSV files

Note: Before executing this script need to check the SQL Server Instance name list, SQL Scripts path, and finally output path of the CSV files.


Push-location

import-module sqlps

Pop-Location


$OutputPath = "D:\RAJ\Output"

$SQLScriptsPath = 'D:\RAJ\SE'

$ServerList = "D:\RAJ\ServerNames.txt"


Get-ChildItem $SqlScriptsPath -Filter *.sql | Sort-Object Name | ForEach-Object {

  $File = $_

  Get-Content $ServerList | ForEach-Object {

    $Server = $_


    # A directory and file based on the server name from the text file

    New-Item "$OutputPath\$Server" -ItemType Directory -Force | Out-Null

    $OutputFile = "$OutputPath\$Server\$($File.BaseName).csv"

    $OutputFile 

    try {

      Invoke-SqlCmd –ServerInstance $Server -QueryTimeout 0 -InputFile $File.FullName |

        Export-Csv $OutputFile -NoTypeInformation

    } catch {

      Write-Host $_.Exception.Message -ForegroundColor Red

    }

  }

}




==================================================================================================================================================

PSScript 4:

The below mentioned PowerShell script is used to save the result of SQL Query output into CSV files.

Note: Before executing this script need to check the SQL Server Instance name list, Output path(CSV), and SQL Scripts path.


Push-location

import-module sqlps

Pop-Location


$OutputPath = "E:\DBScripts\Raj\Output"

$SQLScriptsPath = 'E:\DBScripts\Raj\Scripts'

$ServerList = "E:\DBScripts\Raj\ServerNames.txt"


Get-ChildItem $SqlScriptsPath -Filter *.sql | Sort-Object Name | ForEach-Object {

  $File = $_

  Get-Content $ServerList | ForEach-Object {

    $Server = $_


    # A directory and file based on the server name from the text file

    New-Item "$OutputPath\$Server" -ItemType Directory -Force | Out-Null

    $OutputFile = "$OutputPath\$Server\$($File.BaseName).csv"

    $OutputFile 

    try {

      Invoke-SqlCmd –ServerInstance $Server -InputFile $File.FullName |

        Export-Csv $OutputFile -NoTypeInformation

    } catch {

      Write-Host $_.Exception.Message -ForegroundColor Red

    }

  }

}


==================================================================================================================================================


Tuesday 21 April 2020

How to I get all the information of database by using T-SQL script ?


SELECT @@SERVERNAME As Server_Name,Database_Id,CONVERT(VARCHAR(25), DB.name) AS DBName,CONVERT(VARCHAR(10),
DATABASEPROPERTYEX(name, 'status')) AS [Status],State_Desc,
(SELECT COUNT(1) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'rows') AS Data_Files,
(SELECT SUM((size*8)/1024) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'rows') AS [Data_In_MB],
(SELECT COUNT(1) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'log') AS LogFiles,
(SELECT SUM((size*8)/1024) FROM sys.master_files WHERE DB_NAME(database_id) = DB.name AND type_desc = 'log') AS [Log_In_MB],
user_access_desc AS [User_Access],recovery_model_desc AS [Recovery_Model],
CASE compatibility_level
WHEN 60 THEN '60 (SQL Server 6.0)'
WHEN 65 THEN '65 (SQL Server 6.5)'
WHEN 70 THEN '70 (SQL Server 7.0)'
WHEN 80 THEN '80 (SQL Server 2000)'
WHEN 90 THEN '90 (SQL Server 2005)'
WHEN 100 THEN '100 (SQL Server 2008/R2)'
WHEN 110 THEN '110 (SQL Server 2012)'
WHEN 120 THEN '120 (SQL Server 2014)'
WHEN 130 THEN '130 (SQL Server 2016)'
WHEN 140 THEN '140 (SQL Server 2017)'
WHEN 150 THEN '150 (SQL Server 2019)'
END AS [Compatibility_Level],
CONVERT(VARCHAR(20), create_date, 103) + ' ' + CONVERT(VARCHAR(20), create_date, 108) AS [Creation_Date],
-- last backup
ISNULL
(
(
SELECT TOP 1 CASE TYPE WHEN 'D' THEN 'Full' WHEN 'I' THEN 'Differential' WHEN 'L' THEN 'Transaction log' END + ' – ' +
LTRIM(ISNULL(STR(ABS(DATEDIFF(DAY, GETDATE(),Backup_finish_date))) + ' days ago', 'NEVER')) + ' – ' +
CONVERT(VARCHAR(20), backup_start_date, 103) + ' ' + CONVERT(VARCHAR(20), backup_start_date, 108) + ' – ' +
CONVERT(VARCHAR(20), backup_finish_date, 103) + ' ' + CONVERT(VARCHAR(20), backup_finish_date, 108) +
' (' + CAST(DATEDIFF(second, BK.backup_start_date,
BK.backup_finish_date) AS VARCHAR(4)) + ' '
+ 'seconds)'
FROM msdb..backupset BK WHERE BK.database_name = DB.name ORDER BY backup_set_id DESC
)
,'-'
) AS [Last_Backup],
CASE WHEN is_fulltext_enabled = 1 THEN 'Fulltext enabled' ELSE '' END AS [Full_Text],
CASE WHEN is_auto_close_on = 1 THEN 'autoclose' ELSE '' END AS [Auto_Close],page_verify_option_desc AS [Page_Verify_Option],
CASE WHEN is_read_only = 1 THEN 'read only' ELSE '' END AS [Read_Only],
CASE WHEN is_auto_shrink_on = 1 THEN 'autoshrink' ELSE '' END AS [Auto_Shrink],
CASE WHEN is_auto_create_stats_on = 1 THEN 'auto create statistics' ELSE '' END AS [Aut_Create_Statistics],
CASE WHEN is_auto_update_stats_on = 1 THEN 'auto update statistics' ELSE '' END AS [Auto_Update_Statistics],
CASE WHEN is_in_standby = 1 THEN 'standby' ELSE '' END AS [Standby],
CASE WHEN is_cleanly_shutdown = 1 THEN 'cleanly shutdown' ELSE '' END AS [Cleanly_Shutdown]
FROM sys.databases DB
ORDER BY DBName, [Last_backup] DESC, NAME

How to I get all objects count in all databases by using T-SQL script?


Declare @Qry_Results nvarchar(max)
select @Qry_Results = coalesce(@Qry_Results + char(13) + char(10) + ' UNION ALL ','') +
'
SELECT ' + QUOTENAME([Name],'''') + ' as DBName, [AGGREGATE_FUNCTION], [CHECK_CONSTRAINT],[DEFAULT_CONSTRAINT],[FOREIGN_KEY_CONSTRAINT],
[SQL_SCALAR_FUNCTION],[CLR_SCALAR_FUNCTION],[CLR_TABLE_VALUED_FUNCTION],[SQL_INLINE_TABLE_VALUED_FUNCTION],[INTERNAL_TABLE],
[SQL_STORED_PROCEDURE],[CLR_STORED_PROCEDURE],[PLAN_GUIDE],[PRIMARY_KEY_CONSTRAINT],[RULE],[REPLICATION_FILTER_PROCEDURE],
[SYNONYM],[SERVICE_QUEUE],[CLR_TRIGGER],[SQL_TABLE_VALUED_FUNCTION],[SQL_TRIGGER],[TABLE_TYPE],[USER_TABLE],[UNIQUE_CONSTRAINT],
[VIEW],[EXTENDED_STORED_PROCEDURE]
from
(
select [Name], type_Desc
from ' + quotename([Name]) + '.sys.objects where is_ms_shipped = 0) src
PIVOT (
count([Name])
FOR type_desc in ([AGGREGATE_FUNCTION], [CHECK_CONSTRAINT],[DEFAULT_CONSTRAINT],[FOREIGN_KEY_CONSTRAINT],[SQL_SCALAR_FUNCTION],
[CLR_SCALAR_FUNCTION],[CLR_TABLE_VALUED_FUNCTION],[SQL_INLINE_TABLE_VALUED_FUNCTION],[INTERNAL_TABLE],[SQL_STORED_PROCEDURE],
[CLR_STORED_PROCEDURE],[PLAN_GUIDE],[PRIMARY_KEY_CONSTRAINT],[RULE],[REPLICATION_FILTER_PROCEDURE],[SYNONYM],[SERVICE_QUEUE],
[CLR_TRIGGER],[SQL_TABLE_VALUED_FUNCTION],[SQL_TRIGGER],[TABLE_TYPE],[USER_TABLE],[UNIQUE_CONSTRAINT],[VIEW],[EXTENDED_STORED_PROCEDURE]
)
) pvt
'
from sys.databases
where [name] NOT IN ('master','tempdb','model','msdb') order by [Name]

execute(@Qry_Results)

How to I get all object names along with schema name details in all databases by using T-SQL Script?

USE MASTER;
DECLARE @name sysname;
DECLARE @sql nvarchar(max) = '
SELECT
DB_NAME() AS [database_name],
OBJECT_SCHEMA_NAME(object_id) AS [schema_name],
name AS ObjectName,
type,
type_desc
FROM sys.objects Where type in (''FN'',''U'',''V'',''IT'',''P '')
';
DECLARE @theSQL nvarchar(max);
DECLARE @results TABLE (
[database_name] sysname,
[schema_name] sysname,
[ObjectName] sysname,
[type] sysname,
[type_desc] sysname
);

DECLARE dbs CURSOR STATIC LOCAL FORWARD_ONLY READ_ONLY
FOR
SELECT name
FROM sys.databases
-- you may want to exclude system databases here
WHERE name NOT IN ('master', 'model', 'msdb', 'tempdb', 'distribution')
And state_desc!='OFFLINE'
OPEN dbs;
FETCH NEXT FROM dbs INTO @name;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @theSQL = 'EXEC ' + QUOTENAME(@name) + '.sys.sp_executesql @sql';
INSERT @results
EXEC sys.sp_executesql @theSQL, N'@sql nvarchar(max)', @sql
FETCH NEXT FROM dbs INTO @name;
END
CLOSE dbs;
DEALLOCATE dbs;

SELECT *
FROM @results;

How do I get all table count in all databases from the SQL Server Instance by using T-SQL Script ?



Declare @sql nvarchar(max);
Select @sql =
    (select ' UNION ALL
        select @@ServerName As ServerName ,' +  + quotename(name,'''') + ' as database_name,                              Count( t.name)  as table_name
       FROM '+ quotename(name) + '.sys.tables t
         JOIN '+ quotename(name) + '.sys.schemas s   on s.schema_id = t.schema_id'
    from sys.databases
    where state=0 and database_id>4
--Group by name
    order by [name] for xml path(''), type).value('.', 'nvarchar(max)');
set @sql = stuff(@sql, 1, 12, '') + ' order by database_name,table_name';
execute (@sql)

Monday 23 March 2020

SQL Script For List Of All Database Size Details

IF OBJECT_ID('tempdb.dbo.#space') IS NOT NULL
    DROP TABLE #space

CREATE TABLE #space (
      database_id INT PRIMARY KEY
    , data_used_size DECIMAL(18,2)
    , log_used_size DECIMAL(18,2)
)

DECLARE @SQL NVARCHAR(MAX)

SELECT @SQL = STUFF((
    SELECT '
    USE [' + d.name + ']
    INSERT INTO #space (database_id, data_used_size, log_used_size)
    SELECT
          DB_ID()
        , SUM(CASE WHEN [type] = 0 THEN space_used END)
        , SUM(CASE WHEN [type] = 1 THEN space_used END)
    FROM (
        SELECT s.[type], space_used = SUM(FILEPROPERTY(s.name, ''SpaceUsed'') * 8. / 1024)
        FROM sys.database_files s
        GROUP BY s.[type]
    ) t;'
    FROM sys.databases d
    WHERE d.[state] = 0
    FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '')

EXEC sys.sp_executesql @SQL

SELECT
      d.database_id
    , d.name
    , d.state_desc
    , d.recovery_model_desc
    , t.total_size
    , t.data_size
    , s.data_used_size
    , t.log_size
    , s.log_used_size
    , bu.full_last_date
    , bu.full_size
    , bu.log_last_date
    , bu.log_size
FROM (
    SELECT
          database_id
        , log_size = CAST(SUM(CASE WHEN [type] = 1 THEN size END) * 8. / 1024 AS DECIMAL(18,2))
        , data_size = CAST(SUM(CASE WHEN [type] = 0 THEN size END) * 8. / 1024 AS DECIMAL(18,2))
        , total_size = CAST(SUM(size) * 8. / 1024 AS DECIMAL(18,2))
    FROM sys.master_files
    GROUP BY database_id
) t
JOIN sys.databases d ON d.database_id = t.database_id
LEFT JOIN #space s ON d.database_id = s.database_id
LEFT JOIN (
    SELECT
          database_name
        , full_last_date = MAX(CASE WHEN [type] = 'D' THEN backup_finish_date END)
        , full_size = MAX(CASE WHEN [type] = 'D' THEN backup_size END)
        , log_last_date = MAX(CASE WHEN [type] = 'L' THEN backup_finish_date END)
        , log_size = MAX(CASE WHEN [type] = 'L' THEN backup_size END)
    FROM (
        SELECT
              s.database_name
            , s.[type]
            , s.backup_finish_date
            , backup_size =
                        CAST(CASE WHEN s.backup_size = s.compressed_backup_size
                                    THEN s.backup_size
                                    ELSE s.compressed_backup_size
                        END / 1048576.0 AS DECIMAL(18,2))
            , RowNum = ROW_NUMBER() OVER (PARTITION BY s.database_name, s.[type] ORDER BY s.backup_finish_date DESC)
        FROM msdb.dbo.backupset s
        WHERE s.[type] IN ('D', 'L')
    ) f
    WHERE f.RowNum = 1
    GROUP BY f.database_name
) bu ON d.name = bu.database_name
ORDER BY t.total_size DESC