diff --git a/Errors/README.md b/Errors/README.md
index f02a7d51..8c01d4f0 100644
--- a/Errors/README.md
+++ b/Errors/README.md
@@ -23,42 +23,42 @@ SELECT message_id, severity, text
Your `language_id` you can find in [`sys.syslanguages`](https://docs.microsoft.com/en-us/sql/relational-databases/system-compatibility-views/sys-syslanguages-transact-sql) system view, column `msglangid`:
-| langid | dateformat | datefirst | upgrade | name | alias | months | shortmonths | days | lcid | msglangid
-|--------|------------|-----------|---------|--------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|------|----------
-| 0 | mdy | 7 | 0 | us_english | English | January,February,March,April,May,June,July,August,September,October,November,December | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec | Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday | 1033 | 1033
-| 1 | dmy | 1 | 0 | Deutsch | German | Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember | Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez | Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag | 1031 | 1031
-| 2 | dmy | 1 | 0 | Français | French | janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre | janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc | lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche | 1036 | 1036
-| 3 | ymd | 7 | 0 | 日本語 | Japanese | 01,02,03,04,05,06,07,08,09,10,11,12 | 01,02,03,04,05,06,07,08,09,10,11,12 | 月曜日,火曜日,水曜日,木曜日,金曜日,土曜日,日曜日 | 1041 | 1041
-| 4 | dmy | 1 | 0 | Dansk | Danish | januar,februar,marts,april,maj,juni,juli,august,september,oktober,november,december | jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec | mandag,tirsdag,onsdag,torsdag,fredag,lørdag,søndag | 1030 | 1030
-| 5 | dmy | 1 | 0 | Español | Spanish | Enero,Febrero,Marzo,Abril,Mayo,Junio,Julio,Agosto,Septiembre,Octubre,Noviembre,Diciembre | Ene,Feb,Mar,Abr,May,Jun,Jul,Ago,Sep,Oct,Nov,Dic | Lunes,Martes,Miércoles,Jueves,Viernes,Sábado,Domingo | 3082 | 3082
-| 6 | dmy | 1 | 0 | Italiano | Italian | gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre | gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic | lunedì,martedì,mercoledì,giovedì,venerdì,sabato,domenica | 1040 | 1040
-| 7 | dmy | 1 | 0 | Nederlands | Dutch | januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december | jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec | maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag,zondag | 1043 | 1043
-| 8 | dmy | 1 | 0 | Norsk | Norwegian | januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember | jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,des | mandag,tirsdag,onsdag,torsdag,fredag,lørdag,søndag | 2068 | 2068
-| 9 | dmy | 7 | 0 | Português | Portuguese | janeiro,fevereiro,março,abril,maio,junho,julho,agosto,setembro,outubro,novembro,dezembro | jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez | segunda-feira,terça-feira,quarta-feira,quinta-feira,sexta-feira,sábado,domingo | 2070 | 2070
-| 10 | dmy | 1 | 0 | Suomi | Finnish | tammikuuta,helmikuuta,maaliskuuta,huhtikuuta,toukokuuta,kesäkuuta,heinäkuuta,elokuuta,syyskuuta,lokakuuta,marraskuuta,joulukuuta | tammi,helmi,maalis,huhti,touko,kesä,heinä,elo,syys,loka,marras,joulu | maanantai,tiistai,keskiviikko,torstai,perjantai,lauantai,sunnuntai | 1035 | 1035
-| 11 | ymd | 1 | 0 | Svenska | Swedish | januari,februari,mars,april,maj,juni,juli,augusti,september,oktober,november,december | jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec | måndag,tisdag,onsdag,torsdag,fredag,lördag,söndag | 1053 | 1053
-| 12 | dmy | 1 | 0 | čeština | Czech | leden,únor,březen,duben,květen,červen,červenec,srpen,září,říjen,listopad,prosinec | I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII | pondělí,úterý,středa,čtvrtek,pátek,sobota,neděle | 1029 | 1029
-| 13 | ymd | 1 | 0 | magyar | Hungarian | január,február,március,április,május,június,július,augusztus,szeptember,október,november,december | jan,febr,márc,ápr,máj,jún,júl,aug,szept,okt,nov,dec | hétfő,kedd,szerda,csütörtök,péntek,szombat,vasárnap | 1038 | 1038
-| 14 | dmy | 1 | 0 | polski | Polish | styczeń,luty,marzec,kwiecień,maj,czerwiec,lipiec,sierpień,wrzesień,październik,listopad,grudzień | I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII | poniedziałek,wtorek,środa,czwartek,piątek,sobota,niedziela | 1045 | 1045
-| 15 | dmy | 1 | 0 | română | Romanian | ianuarie,februarie,martie,aprilie,mai,iunie,iulie,august,septembrie,octombrie,noiembrie,decembrie | Ian,Feb,Mar,Apr,Mai,Iun,Iul,Aug,Sep,Oct,Nov,Dec | luni,marţi,miercuri,joi,vineri,sîmbătă,duminică | 1048 | 1048
-| 16 | ymd | 1 | 0 | hrvatski | Croatian | siječanj,veljača,ožujak,travanj,svibanj,lipanj,srpanj,kolovoz,rujan,listopad,studeni,prosinac | sij,vel,ožu,tra,svi,lip,srp,kol,ruj,lis,stu,pro | ponedjeljak,utorak,srijeda,četvrtak,petak,subota,nedjelja | 1050 | 1050
-| 17 | dmy | 1 | 0 | slovenčina | Slovak | január,február,marec,apríl,máj,jún,júl,august,september,október,november,december | I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII | pondelok,utorok,streda,štvrtok,piatok,sobota,nedeľa | 1051 | 1051
-| 18 | dmy | 1 | 0 | slovenski | Slovenian | januar,februar,marec,april,maj,junij,julij,avgust,september,oktober,november,december | jan,feb,mar,apr,maj,jun,jul,avg,sept,okt,nov,dec | ponedeljek,torek,sreda,četrtek,petek,sobota,nedelja | 1060 | 1060
-| 19 | dmy | 1 | 0 | ελληνικά | Greek | Ιανουαρίου,Φεβρουαρίου,Μαρτίου,Απριλίου,Μα_ου,Ιουνίου,Ιουλίου,Αυγούστου,Σεπτεμβρίου,Οκτωβρίου,Νοεμβρίου,Δεκεμβρίου | Ιαν,Φεβ,Μαρ,Απρ,Μαϊ,Ιουν,Ιουλ,Αυγ,Σεπ,Οκτ,Νοε,Δεκ | Δευτέρα,Τρίτη,Τετάρτη,Πέμπτη,Παρασκευή,Σάββατο,Κυριακή | 1032 | 1032
-| 20 | dmy | 1 | 0 | български | Bulgarian | януари,февруари,март,април,май,юни,юли,август,септември,октомври,ноември,декември | януари,февруари,март,април,май,юни,юли,август,септември,октомври,ноември,декември | понеделник,вторник,сряда,четвъртък,петък,събота,неделя | 1026 | 1026
-| 21 | dmy | 1 | 0 | русский | Russian | Январь,Февраль,Март,Апрель,Май,Июнь,Июль,Август,Сентябрь,Октябрь,Ноябрь,Декабрь | янв,фев,мар,апр,май,июн,июл,авг,сен,окт,ноя,дек | понедельник,вторник,среда,четверг,пятница,суббота,воскресенье | 1049 | 1049
-| 22 | dmy | 1 | 0 | Türkçe | Turkish | Ocak,Şubat,Mart,Nisan,Mayıs,Haziran,Temmuz,Ağustos,Eylül,Ekim,Kasım,Aralık | Oca,Şub,Mar,Nis,May,Haz,Tem,Ağu,Eyl,Eki,Kas,Ara | Pazartesi,Salı,Çarşamba,Perşembe,Cuma,Cumartesi,Pazar | 1055 | 1055
-| 23 | dmy | 1 | 0 | British | British English | January,February,March,April,May,June,July,August,September,October,November,December | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec | Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday | 2057 | 1033
-| 24 | dmy | 1 | 0 | eesti | Estonian | jaanuar,veebruar,märts,aprill,mai,juuni,juuli,august,september,oktoober,november,detsember | jaan,veebr,märts,apr,mai,juuni,juuli,aug,sept,okt,nov,dets | esmaspäev,teisipäev,kolmapäev,neljapäev,reede,laupäev,pühapäev | 1061 | 1061
-| 25 | ymd | 1 | 0 | latviešu | Latvian | janvāris,februāris,marts,aprīlis,maijs,jūnijs,jūlijs,augusts,septembris,oktobris,novembris,decembris | jan,feb,mar,apr,mai,jūn,jūl,aug,sep,okt,nov,dec | pirmdiena,otrdiena,trešdiena,ceturtdiena,piektdiena,sestdiena,svētdiena | 1062 | 1062
-| 26 | ymd | 1 | 0 | lietuvių | Lithuanian | sausis,vasaris,kovas,balandis,gegužė,birželis,liepa,rugpjūtis,rugsėjis,spalis,lapkritis,gruodis | sau,vas,kov,bal,geg,bir,lie,rgp,rgs,spl,lap,grd | pirmadienis,antradienis,trečiadienis,ketvirtadienis,penktadienis,šeštadienis,sekmadienis | 1063 | 1063
-| 27 | dmy | 7 | 0 | Português (Brasil) | Brazilian | Janeiro,Fevereiro,Março,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro | Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez | Segunda-Feira,Terça-Feira,Quarta-Feira,Quinta-Feira,Sexta-Feira,Sábado,Domingo | 1046 | 1046
-| 28 | ymd | 7 | 0 | 繁體中文 | Traditional Chinese | 一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月 | 01,02,03,04,05,06,07,08,09,10,11,12 | 星期一,星期二,星期三,星期四,星期五,星期六,星期日 | 1028 | 1028
-| 29 | ymd | 7 | 0 | 한국어 | Korean | 01,02,03,04,05,06,07,08,09,10,11,12 | 01,02,03,04,05,06,07,08,09,10,11,12 | 월요일,화요일,수요일,목요일,금요일,토요일,일요일 | 1042 | 1042
-| 30 | ymd | 7 | 0 | 简体中文 | Simplified Chinese | 01,02,03,04,05,06,07,08,09,10,11,12 | 01,02,03,04,05,06,07,08,09,10,11,12 | 星期一,星期二,星期三,星期四,星期五,星期六,星期日 | 2052 | 2052
-| 31 | dmy | 1 | 0 | Arabic | Arabic | Muharram,Safar,Rabie I,Rabie II,Jumada I,Jumada II,Rajab,Shaaban,Ramadan,Shawwal,Thou Alqadah,Thou Alhajja | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec | Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday | 1025 | 1025
-| 32 | dmy | 7 | 0 | ไทย | Thai | มกราคม,กุมภาพันธ์,มีนาคม,เมษายน,พฤษภาคม,มิถุนายน,กรกฎาคม,สิงหาคม,กันยายน,ตุลาคม,พฤศจิกายน,ธันวาคม | ม.ค.,ก.พ.,มี.ค.,เม.ย.,พ.ค.,มิ.ย.,ก.ค.,ส.ค.,ก.ย.,ต.ค.,พ.ย.,ธ.ค. | จันทร์,อังคาร,พุธ,พฤหัสบดี,ศุกร์,เสาร์,อาทิตย์ | 1054 | 1054
-| 33 | dmy | 1 | 0 | norsk (bokmål) | Bokmål | januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember | jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,des | mandag,tirsdag,onsdag,torsdag,fredag,lørdag,søndag | 1044 | 1044
+| langid | dateformat | datefirst | upgrade | name | alias | months | shortmonths | days | lcid | msglangid |
+|--------|------------|-----------|---------|--------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|------|-----------|
+| 0 | mdy | 7 | 0 | us_english | English | January,February,March,April,May,June,July,August,September,October,November,December | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec | Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday | 1033 | 1033 |
+| 1 | dmy | 1 | 0 | Deutsch | German | Januar,Februar,März,April,Mai,Juni,Juli,August,September,Oktober,November,Dezember | Jan,Feb,Mär,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez | Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag | 1031 | 1031 |
+| 2 | dmy | 1 | 0 | Français | French | janvier,février,mars,avril,mai,juin,juillet,août,septembre,octobre,novembre,décembre | janv,févr,mars,avr,mai,juin,juil,août,sept,oct,nov,déc | lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche | 1036 | 1036 |
+| 3 | ymd | 7 | 0 | 日本語 | Japanese | 01,02,03,04,05,06,07,08,09,10,11,12 | 01,02,03,04,05,06,07,08,09,10,11,12 | 月曜日,火曜日,水曜日,木曜日,金曜日,土曜日,日曜日 | 1041 | 1041 |
+| 4 | dmy | 1 | 0 | Dansk | Danish | januar,februar,marts,april,maj,juni,juli,august,september,oktober,november,december | jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec | mandag,tirsdag,onsdag,torsdag,fredag,lørdag,søndag | 1030 | 1030 |
+| 5 | dmy | 1 | 0 | Español | Spanish | Enero,Febrero,Marzo,Abril,Mayo,Junio,Julio,Agosto,Septiembre,Octubre,Noviembre,Diciembre | Ene,Feb,Mar,Abr,May,Jun,Jul,Ago,Sep,Oct,Nov,Dic | Lunes,Martes,Miércoles,Jueves,Viernes,Sábado,Domingo | 3082 | 3082 |
+| 6 | dmy | 1 | 0 | Italiano | Italian | gennaio,febbraio,marzo,aprile,maggio,giugno,luglio,agosto,settembre,ottobre,novembre,dicembre | gen,feb,mar,apr,mag,giu,lug,ago,set,ott,nov,dic | lunedì,martedì,mercoledì,giovedì,venerdì,sabato,domenica | 1040 | 1040 |
+| 7 | dmy | 1 | 0 | Nederlands | Dutch | januari,februari,maart,april,mei,juni,juli,augustus,september,oktober,november,december | jan,feb,mrt,apr,mei,jun,jul,aug,sep,okt,nov,dec | maandag,dinsdag,woensdag,donderdag,vrijdag,zaterdag,zondag | 1043 | 1043 |
+| 8 | dmy | 1 | 0 | Norsk | Norwegian | januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember | jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,des | mandag,tirsdag,onsdag,torsdag,fredag,lørdag,søndag | 2068 | 2068 |
+| 9 | dmy | 7 | 0 | Português | Portuguese | janeiro,fevereiro,março,abril,maio,junho,julho,agosto,setembro,outubro,novembro,dezembro | jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez | segunda-feira,terça-feira,quarta-feira,quinta-feira,sexta-feira,sábado,domingo | 2070 | 2070 |
+| 10 | dmy | 1 | 0 | Suomi | Finnish | tammikuuta,helmikuuta,maaliskuuta,huhtikuuta,toukokuuta,kesäkuuta,heinäkuuta,elokuuta,syyskuuta,lokakuuta,marraskuuta,joulukuuta | tammi,helmi,maalis,huhti,touko,kesä,heinä,elo,syys,loka,marras,joulu | maanantai,tiistai,keskiviikko,torstai,perjantai,lauantai,sunnuntai | 1035 | 1035 |
+| 11 | ymd | 1 | 0 | Svenska | Swedish | januari,februari,mars,april,maj,juni,juli,augusti,september,oktober,november,december | jan,feb,mar,apr,maj,jun,jul,aug,sep,okt,nov,dec | måndag,tisdag,onsdag,torsdag,fredag,lördag,söndag | 1053 | 1053 |
+| 12 | dmy | 1 | 0 | čeština | Czech | leden,únor,březen,duben,květen,červen,červenec,srpen,září,říjen,listopad,prosinec | I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII | pondělí,úterý,středa,čtvrtek,pátek,sobota,neděle | 1029 | 1029 |
+| 13 | ymd | 1 | 0 | magyar | Hungarian | január,február,március,április,május,június,július,augusztus,szeptember,október,november,december | jan,febr,márc,ápr,máj,jún,júl,aug,szept,okt,nov,dec | hétfő,kedd,szerda,csütörtök,péntek,szombat,vasárnap | 1038 | 1038 |
+| 14 | dmy | 1 | 0 | polski | Polish | styczeń,luty,marzec,kwiecień,maj,czerwiec,lipiec,sierpień,wrzesień,październik,listopad,grudzień | I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII | poniedziałek,wtorek,środa,czwartek,piątek,sobota,niedziela | 1045 | 1045 |
+| 15 | dmy | 1 | 0 | română | Romanian | ianuarie,februarie,martie,aprilie,mai,iunie,iulie,august,septembrie,octombrie,noiembrie,decembrie | Ian,Feb,Mar,Apr,Mai,Iun,Iul,Aug,Sep,Oct,Nov,Dec | luni,marţi,miercuri,joi,vineri,sîmbătă,duminică | 1048 | 1048 |
+| 16 | ymd | 1 | 0 | hrvatski | Croatian | siječanj,veljača,ožujak,travanj,svibanj,lipanj,srpanj,kolovoz,rujan,listopad,studeni,prosinac | sij,vel,ožu,tra,svi,lip,srp,kol,ruj,lis,stu,pro | ponedjeljak,utorak,srijeda,četvrtak,petak,subota,nedjelja | 1050 | 1050 |
+| 17 | dmy | 1 | 0 | slovenčina | Slovak | január,február,marec,apríl,máj,jún,júl,august,september,október,november,december | I,II,III,IV,V,VI,VII,VIII,IX,X,XI,XII | pondelok,utorok,streda,štvrtok,piatok,sobota,nedeľa | 1051 | 1051 |
+| 18 | dmy | 1 | 0 | slovenski | Slovenian | januar,februar,marec,april,maj,junij,julij,avgust,september,oktober,november,december | jan,feb,mar,apr,maj,jun,jul,avg,sept,okt,nov,dec | ponedeljek,torek,sreda,četrtek,petek,sobota,nedelja | 1060 | 1060 |
+| 19 | dmy | 1 | 0 | ελληνικά | Greek | Ιανουαρίου,Φεβρουαρίου,Μαρτίου,Απριλίου,Μα_ου,Ιουνίου,Ιουλίου,Αυγούστου,Σεπτεμβρίου,Οκτωβρίου,Νοεμβρίου,Δεκεμβρίου | Ιαν,Φεβ,Μαρ,Απρ,Μαϊ,Ιουν,Ιουλ,Αυγ,Σεπ,Οκτ,Νοε,Δεκ | Δευτέρα,Τρίτη,Τετάρτη,Πέμπτη,Παρασκευή,Σάββατο,Κυριακή | 1032 | 1032 |
+| 20 | dmy | 1 | 0 | български | Bulgarian | януари,февруари,март,април,май,юни,юли,август,септември,октомври,ноември,декември | януари,февруари,март,април,май,юни,юли,август,септември,октомври,ноември,декември | понеделник,вторник,сряда,четвъртък,петък,събота,неделя | 1026 | 1026 |
+| 21 | dmy | 1 | 0 | русский | Russian | Январь,Февраль,Март,Апрель,Май,Июнь,Июль,Август,Сентябрь,Октябрь,Ноябрь,Декабрь | янв,фев,мар,апр,май,июн,июл,авг,сен,окт,ноя,дек | понедельник,вторник,среда,четверг,пятница,суббота,воскресенье | 1049 | 1049 |
+| 22 | dmy | 1 | 0 | Türkçe | Turkish | Ocak,Şubat,Mart,Nisan,Mayıs,Haziran,Temmuz,Ağustos,Eylül,Ekim,Kasım,Aralık | Oca,Şub,Mar,Nis,May,Haz,Tem,Ağu,Eyl,Eki,Kas,Ara | Pazartesi,Salı,Çarşamba,Perşembe,Cuma,Cumartesi,Pazar | 1055 | 1055 |
+| 23 | dmy | 1 | 0 | British | British English | January,February,March,April,May,June,July,August,September,October,November,December | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec | Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday | 2057 | 1033 |
+| 24 | dmy | 1 | 0 | eesti | Estonian | jaanuar,veebruar,märts,aprill,mai,juuni,juuli,august,september,oktoober,november,detsember | jaan,veebr,märts,apr,mai,juuni,juuli,aug,sept,okt,nov,dets | esmaspäev,teisipäev,kolmapäev,neljapäev,reede,laupäev,pühapäev | 1061 | 1061 |
+| 25 | ymd | 1 | 0 | latviešu | Latvian | janvāris,februāris,marts,aprīlis,maijs,jūnijs,jūlijs,augusts,septembris,oktobris,novembris,decembris | jan,feb,mar,apr,mai,jūn,jūl,aug,sep,okt,nov,dec | pirmdiena,otrdiena,trešdiena,ceturtdiena,piektdiena,sestdiena,svētdiena | 1062 | 1062 |
+| 26 | ymd | 1 | 0 | lietuvių | Lithuanian | sausis,vasaris,kovas,balandis,gegužė,birželis,liepa,rugpjūtis,rugsėjis,spalis,lapkritis,gruodis | sau,vas,kov,bal,geg,bir,lie,rgp,rgs,spl,lap,grd | pirmadienis,antradienis,trečiadienis,ketvirtadienis,penktadienis,šeštadienis,sekmadienis | 1063 | 1063 |
+| 27 | dmy | 7 | 0 | Português (Brasil) | Brazilian | Janeiro,Fevereiro,Março,Abril,Maio,Junho,Julho,Agosto,Setembro,Outubro,Novembro,Dezembro | Jan,Fev,Mar,Abr,Mai,Jun,Jul,Ago,Set,Out,Nov,Dez | Segunda-Feira,Terça-Feira,Quarta-Feira,Quinta-Feira,Sexta-Feira,Sábado,Domingo | 1046 | 1046 |
+| 28 | ymd | 7 | 0 | 繁體中文 | Traditional Chinese | 一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月 | 01,02,03,04,05,06,07,08,09,10,11,12 | 星期一,星期二,星期三,星期四,星期五,星期六,星期日 | 1028 | 1028 |
+| 29 | ymd | 7 | 0 | 한국어 | Korean | 01,02,03,04,05,06,07,08,09,10,11,12 | 01,02,03,04,05,06,07,08,09,10,11,12 | 월요일,화요일,수요일,목요일,금요일,토요일,일요일 | 1042 | 1042 |
+| 30 | ymd | 7 | 0 | 简体中文 | Simplified Chinese | 01,02,03,04,05,06,07,08,09,10,11,12 | 01,02,03,04,05,06,07,08,09,10,11,12 | 星期一,星期二,星期三,星期四,星期五,星期六,星期日 | 2052 | 2052 |
+| 31 | dmy | 1 | 0 | Arabic | Arabic | Muharram,Safar,Rabie I,Rabie II,Jumada I,Jumada II,Rajab,Shaaban,Ramadan,Shawwal,Thou Alqadah,Thou Alhajja | Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec | Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday | 1025 | 1025 |
+| 32 | dmy | 7 | 0 | ไทย | Thai | มกราคม,กุมภาพันธ์,มีนาคม,เมษายน,พฤษภาคม,มิถุนายน,กรกฎาคม,สิงหาคม,กันยายน,ตุลาคม,พฤศจิกายน,ธันวาคม | ม.ค.,ก.พ.,มี.ค.,เม.ย.,พ.ค.,มิ.ย.,ก.ค.,ส.ค.,ก.ย.,ต.ค.,พ.ย.,ธ.ค. | จันทร์,อังคาร,พุธ,พฤหัสบดี,ศุกร์,เสาร์,อาทิตย์ | 1054 | 1054 |
+| 33 | dmy | 1 | 0 | norsk (bokmål) | Bokmål | januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember | jan,feb,mar,apr,mai,jun,jul,aug,sep,okt,nov,des | mandag,tirsdag,onsdag,torsdag,fredag,lørdag,søndag | 1044 | 1044 |
## Levels of Severity
@@ -115,6 +115,7 @@ Your `language_id` you can find in [`sys.syslanguages`](https://docs.microsoft.c
| 596 | Cannot continue execution because the session is in the kill state. | [596_link1],[596_link2][31],[596_link3][32] |
| 650 | You can only specify the READPAST lock in the READ COMMITTED or REPEATABLE READ isolation levels. | [650_link1] |
| 657 | Could not disable support for increased partitions in database … | [657_link1] |
+| 665 | The operating system returned error 665(The requested operation could not be completed due to a file … | [665_link1],[665_link2][52] |
| 666 | The maximum system-generated unique value for a duplicate group was exceeded for index with … | [666_link1] |
| 701 | There is insufficient system memory in resource pool '%ls' to run this query. … | [701_link1],[701_link2][11] |
| 824 | SQL Server detected a logical consistency-based I/O error … | [824_link1],[824_link2],[KB2152734],[824_link3][42] |
@@ -129,6 +130,7 @@ Your `language_id` you can find in [`sys.syslanguages`](https://docs.microsoft.c
| 1205 | Transaction (Process ID %d) was deadlocked on %.*ls resources with another process and has been chosen … | [1205_link1][46] |
| 1222 | Lock request time out period exceeded. | [1222_link1][46] |
| 1219 | Your session has been disconnected because of a high priority DDL operation. | [1219_link1][32] |
+| 1450 | The operating system returned error 1450 (Insufficient system resources exist to complete the … | [52] |
| 1480 | The %S_MSG database "%.*ls" is changing roles from "%ls" to "%ls" because the mirroring session or … | [1480_link1][48] |
| 1701 | Creating or altering table %ls failed because the minimum row size would be 8061, including 10 b … | [1701_link1] |
| 1807 | Could not obtain exclusive lock on database ‘model’. Retry the operation later. … | [1807_link1] |
@@ -301,6 +303,7 @@ Your `language_id` you can find in [`sys.syslanguages`](https://docs.microsoft.c
[49]:https://www.red-gate.com/simple-talk/sql/t-sql-programming/row-versioning-concurrency-in-sql-server/
[50]:https://sqlblog.org/2020/11/05/navigating-dbcc-checkdb-for-vldb
[51]:https://www.mssqltips.com/sqlservertip/6230/memoryoptimized-tempdb-metadata-in-sql-server-2019/
+[52]:https://learn.microsoft.com/troubleshoot/sql/database-engine/database-file-operations/1450-and-665-errors-running-dbcc-checkdb
[Who owns your availability groups?]:http://www.cjsommer.com/2016-10-20-who-owns-your-availability-groups/
[Cannot Connect to SQL Server]:https://dallasdbas.com/irl-cannot-connect-to-sql-server/
@@ -314,6 +317,7 @@ Your `language_id` you can find in [`sys.syslanguages`](https://docs.microsoft.c
[596_link1]:http://sql-sasquatch.blogspot.ru/2017/09/sqlserver-just-how-minimal-can-that.html
[650_link1]:https://sqlundercover.com/2019/02/07/alter-table-fails-on-replicated-tables-with-isolation-level-serializable-or-read-uncommitted-on-sql2012-and-earlier/
[657_link1]:https://blog.sqlauthority.com/2016/05/20/sql-server-disabling-15000-15k-partitions/
+[665_link1]:https://dba.stackexchange.com/a/334820
[666_link1]:https://blogs.msdn.microsoft.com/psssql/2018/02/16/uniqueifier-considerations-and-error-666/
[701_link1]:https://blogs.msdn.microsoft.com/psssql/2017/02/22/be-aware-of-701-error-if-you-use-memory-optimized-table-variable-in-a-loop/
[824_link1]:http://www.sqlservercentral.com/blogs/sql-server-citation-sql-blog-by-hemantgiri-s-goswami-sql-mvp/2016/08/23/resolve-microsoft-sql-server-error-code-824/
@@ -383,6 +387,6 @@ Your `language_id` you can find in [`sys.syslanguages`](https://docs.microsoft.c
[25713_link2]:https://sqlquantumleap.com/2018/01/30/server-audit-mystery-filtering-action_id-gets-error-msg-25713/
[33111_link1]:https://sqlundercover.com/2018/04/04/encrypting-sql-server-database-backups/
[35250_link1]:https://blog.sqlauthority.com/2017/05/18/sql-server-fix-msg-35250-level-16-state-7-connection-primary-replica-not-active-command-cannot-processed/
-[35217_link1] :https://www.seangallardy.com/error-35217-and-availability-groups-smh/
+[35217_link1]:https://www.seangallardy.com/error-35217-and-availability-groups-smh/
[41121_link1]:https://joshthecoder.com/2020/06/22/a-bug-with-ag-health-checks.html
[41922_link1]:http://timradney.com/2020/05/23/updated-default-settings-for-azure-sql-managed-instance-databases/
diff --git a/README.md b/README.md
index db3dc914..49364d6f 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,7 @@ Useful links, scripts, tools and best practice for Microsoft SQL Server Database
- [Idera SQL Server Blog](https://community.idera.com/tags/SQL%2bServer)
- [SQL Server Science Blog](https://www.sqlserverscience.com/) (by Max Vernon)
- [Kohera SQL Server Blog](https://kohera.be/blog/category/sql-server/)
+ - [SQL Queries Cheat Sheet](https://helpercodes.com/sql-query-cheatsheet-tutorial/)
- Security (great thanks to [Troy Hunt](https://www.troyhunt.com/troys-ultimate-list-of-security-links/))
- SQL injection
- [sqlmap - The tool for mounting SQL injection attacks tests against a running site](http://sqlmap.org/)
@@ -239,6 +240,8 @@ Useful links, scripts, tools and best practice for Microsoft SQL Server Database
- [SQL Murder Mystery](https://github.com/NUKnightLab/sql-mysteries) (by NUKnightLab)
- [SQL Murder Mystery with answers](https://github.com/erika-e/sql-mysteries) (by Erika Pullum)
- [SQL Problems and solutions](http://www.sql-tutorial.ru/en) (by S. I. Moiseenko)
+ - [Advanced T-SQL Puzzles](https://github.com/smpetersgithub/AdvancedSQLPuzzles) (by Scott Peters)
+ - [Leetcode SQL Problems](https://leetcode.com/problemset/database/)
- Paid
- [Lynda Courses](http://www.lynda.com/SQL-Server-training-tutorials/456-0.html)
- [Veeam Free Courses](https://go.veeam.com/microsoft-sql-series-webinars.html)
@@ -426,7 +429,8 @@ Useful links, scripts, tools and best practice for Microsoft SQL Server Database
- [sp_Develop - can be used by database developers, software developers and for performing database code (smell) reviews.s](https://github.com/EmergentSoftware/SQL-Server-Development-Assessment) (by Kevin Martin)
- [DBA Dash - Performance monitoring tool for SQL Server](https://github.com/trimble-oss/dba-dash) (by Trimble)
- [lowlydba.sqlserver - A cross-platform Ansible collection using PowerShell to configure and maintain SQL Server](https://github.com/lowlydba/lowlydba.sqlserver) - (by John McCall)
- - [SQLWatch - SQLWATCH is an Open Source and completely free SQL Server Monitoring project](https://sqlwatch.io/)
+ - [SQLWatch - SQLWATCH is an Open Source and completely free SQL Server Monitoring project](https://sqlwatch.io/) (by Marcin Gminski)
+ - [Azure_Synapse_Toolbox - Repository of tools/queries for managing and monitoring Azure Synapse](https://github.com/microsoft/Azure_Synapse_Toolbox) (by Microsoft)
- Other
- [SQL# SQLCLR functions](https://sqlsharp.com/) (by Sql Quantum Lift)
- [SQL Server Latch Classes Library](https://www.sqlskills.com/help/latches/) (by Paul S. Randal)
@@ -485,8 +489,10 @@ Useful links, scripts, tools and best practice for Microsoft SQL Server Database
- [SQL Assessment API rules in .csv format](https://github.com/microsoft/sql-server-samples/blob/master/samples/manage/sql-assessment-api/DefaultRuleset.csv) (by Microsoft)
- [SQL cheat sheet for PostgreSQL and Oracle](https://www.pcwdld.com/sql-cheat-sheet) (by Marc Wilson)
- [Bllitz Excel UI](https://1pro.bi/blitz-excel-ui/) (by Alex)
- - [Industry-specific Data Models - cover Subject Areas and are used to create Enterprise Data Models](http://www.databaseanswers.org/data_models/)
+ - [Industry-specific Data Models - cover Subject Areas and are used to create Enterprise Data Models](https://web.archive.org/web/20220330034214/http://databaseanswers.org/data_models/)
+ - [Library of Database Schemas](https://dbschemalibrary.com/)
- [SQLFacts - A powerful suite of FREE TSQL tools for SQL Server database professionals](https://www.sqlfacts.com)
+ - [sql-log-shipping-service - provides a solution for automatically restoring SQL Server transaction log backups](https://github.com/trimble-oss/sql-log-shipping-service) (by Trimble Online Source Store)
**[⬆ back to top](#table-of-contents)**
@@ -502,6 +508,7 @@ BIML Resources
- [Stairway to Biml](http://www.sqlservercentral.com/stairway/100550/)
- [Biml User Group at LinkedIn](https://www.linkedin.com/groups/4640985)
- [Building Blocks of Biml (Pluralsight course by Stacia Misner Varga)](https://app.pluralsight.com/library/courses/building-blocks-biml/table-of-contents)
+- [SQL Reserved Words](https://modern-sql.com/reserved-words-empirical-list) (by Markus Winand)
BIML Bloggers
- [Ben Weissman](https://www.solisyon.de/biml-blog-de/)
diff --git a/SQL Server Drivers.md b/SQL Server Drivers.md
index 72b2f906..43dec2e7 100644
--- a/SQL Server Drivers.md
+++ b/SQL Server Drivers.md
@@ -26,6 +26,12 @@ This driver is developed, tested, and supported by Microsoft.
[Microsoft ADO.NET for SQL Server](https://docs.microsoft.com/en-us/sql/connect/ado-net/microsoft-ado-net-for-sql-server) | [Download .Net Driver](http://www.microsoft.com/net/download/)
+### Ballerina
+The Ballerina MSSQL Connector provides seamless integration with Microsoft SQL Server, allowing developers to work with databases efficiently in cloud-native and REST-based applications. This connector is an open-source project which is actively developed, tested, and supported by the Ballerina team.
+
+[ballerinax/mssql connector for SQL Server](https://central.ballerina.io/ballerinax/mssql/latest) | [GitHub](https://github.com/ballerina-platform/module-ballerinax-mssql)
+
+
### JDBC
The JDBC SQL driver is a Java implementation of the TDS protocol, which is supported by all modern versions of SQL Server. This driver is developed, tested, and supported by Microsoft.
diff --git a/SQL Server Name Convention and T-SQL Programming Style.md b/SQL Server Name Convention and T-SQL Programming Style.md
index 5f01ff49..2e63ae48 100644
--- a/SQL Server Name Convention and T-SQL Programming Style.md
+++ b/SQL Server Name Convention and T-SQL Programming Style.md
@@ -325,7 +325,7 @@ SQL Server T-SQL Coding Conventions, Best Practices, and Programming Guidelines.
4. All [Service Broker statements](https://docs.microsoft.com/sql/t-sql/statements/send-transact-sql):
> If the SEND statement isn't the first statement in a batch or stored procedure, the preceding statement must be terminated with a semicolon (;).
- - All script files should end with `GO` and line break. This is neccesary for batching scripts run throw `sqlcmd` or another tools.
+ - All script files should end with `GO` and line break. This is necessary for batching scripts run through `sqlcmd` or another tools.
- Keywords should be in **UPPERCASE**: `SELECT`, `FROM`, `GROUP BY` etc. This increases the readability of the code.
- Data types declaration should be in **lowercase**: `varchar(30)`, `int`, `real`, `nvarchar(max)` etc.
More details [here](https://www.sentryone.com/blog/aaronbertrand/backtobasics-lower-case-data-types).
diff --git a/SQL Server Trace Flag.md b/SQL Server Trace Flag.md
index 197ffdc8..452038d8 100644
--- a/SQL Server Trace Flag.md
+++ b/SQL Server Trace Flag.md
@@ -45,7 +45,7 @@ Source links:
**Great thanks to:**
- Aaron Morelli ([b](https://sqlcrossjoin.wordpress.com) | [@sqlcrossjoin](https://twitter.com/sqlcrossjoin))
- Steinar Andersen ([b](http://www.sqlservice.se/) | [@SQLSteinar](https://twitter.com/SQLSteinar))
-- Brent Ozar ([b](https://www.brentozar.com/) | [@BrentO](https://twitter.com/BrentO))
+- Brent Ozar ([b](https://www.brentozar.com/))
- Yusuf Anis
- Lars Utterström
- Martin Höglund
@@ -83,6 +83,8 @@ Source links:
- Wilfred van Dijk
- Tracy Boggiano ([b](http://databasesuperhero.com) | [t](https://twitter.com/TracyBoggiano))
- Danilo Zocco (https://github.com/CrazySwimmer)
+- Michael Abair (https://github.com/abair34)
+- Mikulas Mraz (https://github.com/Prohiller)
@@ -234,11 +236,11 @@ If you know behavior some of them please open an issue or contact me (taranov.pr
- [Trace Flag 3427](#3427) (for SQL Server 2016)
- [Trace Flag 3449](#3449) (for versions SQL Server 2012 SP3 CU3 or later or SQL Server 2014 SP1 CU7 or later)
- [Trace Flag 6534](#6534) (for versions SQL Server 2012, 2014, 2016) (if use [spatial data types](https://docs.microsoft.com/sql/relational-databases/spatial/spatial-data-sql-server))
- - [Trace Flag 7412](#7412) (for versions >= SQL Server 2016)
+ - [Trace Flag 7412](#7412) (for versions >= SQL Server 2016 and < SQL Server 2019)
- [Trace Flag 7745](#7745) (for versions >= SQL Server 2016 and Query Store enabled)
- [Trace Flag 7752](#7752) (for versions >= SQL Server 2016 and < 2019 and Query Store enabled)
- [Trace Flag 7806](#7806) (for SQL Server Express Edition)
- - [Trace Flag 8099](#8099) (for versions >= 2019 CU2)
+ - [Trace Flag 8099](#8099) (for versions 2019 CU2 and 2019 CU3 only)
**Trace Flag 272** prevents identity gap after restarting SQL Server 2012 instance, critical for columns with identity and `tinyint` and `smallint` data types.
(Demo for repeating this issue [here](https://github.com/ktaranov/sqlserver-kit/Errors/Identity_gap_sql_server_2012.sql))
@@ -285,7 +287,7 @@ Use this trace flag if SQL Server is experiencing high number of [QDS_LOADDB](ht
**Trace Flag: 7806** enables a dedicated administrator connection ([DAC]) on SQL Server Express.
-**Trace Flag: 8099** enables a spinlock contention fix for high-end systems running SQL Server 2019 (15.x) serving many concurrent users.
+**Trace Flag: 8099** enables a spinlock contention fix for high-end systems running SQL Server 2019 (15.x) serving many concurrent users. Starting with SQL 2019 CU 4 this fix is enabled by default.
@@ -2819,9 +2821,9 @@ Link: https://support.microsoft.com/kb/3003760
#### Trace Flag: 3895
**Undocumented trace flag**
-Function: In SQL Server 2019, when you enable the [Memory-Optimized TempDB Metadata](https://docs.microsoft.com/en-us/sql/relational-databases/databases/tempdb-database) feature, this trace flag is automatically enabled after the next restart.
-When you disable that feature, the trace flag is automatically removed after the next restart.
-Also if you add race flag 3895 to startup parameters after restarting SQL Server option `IsTempdbMetadataMemoryOptimized` will be enabled.
+Function: In SQL Server 2019, when you enable the [Memory-Optimized TempDB Metadata](https://docs.microsoft.com/en-us/sql/relational-databases/databases/tempdb-database) feature, this trace flag is automatically enabled as a global session flag after the next restart.
+When you disable that feature, the trace flag is automatically removed as a global session flag after the next restart.
+Also, if you add trace flag 3895 to startup parameters after restarting SQL Server option `IsTempdbMetadataMemoryOptimized` will be enabled. NOTE: It is NOT recommended to add trace flag 3895 as a startup trace flag to enable TempDB Memory-Optimized Metadata. Although this technically enables the option, you will no longer be able to disable it using sp_configure or ALTER SERVER CONFIGURATION. The only way you'll be able to disable this feature is to manually remove this trace flag from startup parameters in the SQL Server Configuration Manager startup properties
Link: https://github.com/ktaranov/sqlserver-kit/blob/master/Scripts/Trace_Flag/Trace_Flag_3895.sql
Scope: global only
@@ -3341,6 +3343,21 @@ Function: SQL 9 – After 4610 & 4618 you can still customize the quota for Toke
Link: https://support.microsoft.com/kb/959823
+
+#### Trace Flag: 4631
+Function: Disables SHA2_256/AES256 for hashing passwords that generate encryption keys. Starting in SQL Server 2017 (14.x), SHA2 is used instead of SHA1. This means extra steps might be necessary to have your SQL Server 2017 (14.x) installation decrypt items that were encrypted by SQL Server 2016 (13.x)
+Link: [Encryption Changes in SQL Server 2017 CU2](https://learn.microsoft.com/en-us/sql/relational-databases/security/encryption/create-identical-symmetric-keys-on-two-servers?view=sql-server-ver17#encryption-changes-in-sql-server-2017-cu2)
+Link: [KB 4053407: SQL Server 2017 cannot decrypt data encrypted by earlier versions of SQL Server using the same symmetric key](https://support.microsoft.com/kb/4053407)
+Scope: global only
+
+
+
+#### Trace Flag: 4675
+Function: Enable checks on create credential for managed identity on a SQL Server on Azure VM if Microsoft Entra authentication is enabled. Enables diagnostics for the CREATE CREDENTAIL WITH IDENTITY = 'Managed Identity' statement. The trace flag provides information about the primary managed identity and its setting for SQL Server on Azure VM.
+Link: [DBCC TRACEON Trace Flags](https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql?view=sql-server-ver17)
+Scope: global or session
+
+
#### Trace Flag: 5004
Function: Pauses TDE encryption scan and causes encryption scan worker to exit without doing any work.
@@ -3465,7 +3482,14 @@ Link: https://support.microsoft.com/kb/4517771
Scope: global only
SQL Server Version: >= 2019 CU1, >= 2017 CU18, >= 2016 SP1 CU10
-
+
+
+#### Trace Flag: 6773
+Function: Enables a fix to address an out of memory issue that occurs when bulk copying XML data where the sum of all XML attributes are > 2MB. This flag can help in circumstances where a client is running bulk copy of XML data and it receives sql error 6303 "XML parsing: Document parsing required too much memory".
+Link: https://support.microsoft.com/en-us/topic/kb4019125-fix-system-center-configuration-manager-replication-process-by-using-bcp-apis-fails-when-there-is-a-large-value-in-an-xml-column-53a969d5-801c-63ab-a557-bc89fe264394
+SQL Server Version: >= 2016 CU7, >= 2014 SP2 CU6, >= 2014 SP1 CU13, >= 2012 SP3 CU9
+
+
#### Trace Flag: 6950
**Undocumented trace flag**
@@ -3484,6 +3508,13 @@ Scope: global only
SQL Server Version: >= 2022
+
+#### Trace Flag: 6981
+Function: Handle large objects (LOBs) when rebuilding the index that has an assertion issue (Location: blobbase.cpp:345; Expression: IS_ON (BLB_TI_END, m_status)). After turning on this trace flag and rebuilding the affected index, the assertion dump shouldn't be generated again. Added in SQL Server 2019 CU29.
+Link: [SQL Server 2019 CU29 Documentation](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2019/cumulativeupdate29#3370476)
+SQL Server Version: >= 2019 CU29
+
+
#### Trace Flag: 7103
**Undocumented trace flag**
@@ -3575,7 +3606,8 @@ Scope: global or session or query
Function: Enables the lightweight query execution statistics profiling infrastructure.
unless your server is already CPU bound, like you’re running all the time with 95% CPU, unless you are at that point, turn on this trace flag at any server you have.
This would be my advice here because this enables that lightweight profiling infrastructure there and then you’ll see in a few minutes what it unleashes here.
-So one thing that happens when I enable the lightweight profiling is that the sys.dm_exec_query_profiles DMV, which is something that actually populates the live query stats ability or feature of SSMS, now also is also populated with this lightweight profiling, which means that for all essence, we are now able to run a live query stats on all fashions at any given point in time, and this is extremely useful for let’s say a production DBA that someone calls and says, “Hey, you have a problem. To tap into running system and look at what it’s doing.”
+So one thing that happens when I enable the lightweight profiling is that the sys.dm_exec_query_profiles DMV, which is something that actually populates the live query stats ability or feature of SSMS, now also is also populated with this lightweight profiling, which means that for all essence, we are now able to run a live query stats on all fashions at any given point in time, and this is extremely useful for let’s say a production DBA that someone calls and says, “Hey, you have a problem. To tap into running system and look at what it’s doing.”
+**Applies to: SQL Server 2016 (13.x) Service Pack 1 and later versions. Starting with SQL Server 2019 (15.x), this trace flag has no effect because lightweight profiling is enabled by default.**
Link: [Docs Trace Flags]
Link: https://support.microsoft.com/kb/3170113
Link: https://www.brentozar.com/archive/2017/10/get-live-query-plans-sp_blitzwho/
@@ -3667,6 +3699,12 @@ Function: SQL 9 - Full-text index population for the indexed view is very slow
+#### Trace Flag: 7617
+Function: Skip dropping full-text index fragments marked as deletion during a database recovery process. Added in SQL Server 2019 CU29.
+Link: [SQL Server 2019 CU29 Documentation](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2019/cumulativeupdate29#3540450)
+
+
#### Trace Flag: 7646
Function: SQL 10 - Avoids blocking when using full text indexing.
@@ -4064,6 +4102,12 @@ Function: Writes detailed information about Ms-DTC context & state changes to th
Link: None
+
+#### Trace Flag: 8531
+Function: Fixes a contention issue with high KTM_RECOVERY_MANAGER wait times that you might encounter when running XA distributed transactions. Note: You need to turn on trace flag 8531 as a startup trace flag.
+Link: [SQL Server 2019 CU29 Documentation](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2019/cumulativeupdate29#3417392)
+
+
#### Trace Flag: 8599
Function: Allows you to use a save-point within a distributed transaction
@@ -4787,6 +4831,15 @@ Link: [New Undocumented Trace Flags]
Scope: ?
+
+#### Trace Flag: 9265
+**Undocumented trace flag**
+Function: MS engineering team has decided to generate a DUMP from SQL 2019+ every time a “PLAN cannot be generated”. Once a Dump freezes connections during the generation, the cluster will probably lost the connectivity to the cluster and the AG can be down/failover if it has automatic failover configured. The mitigation for this issue (avoid DUMP generation) is to enable TF Trace Flag 9265 on Startup.
+Once TF 9265 is enabled, SQL Server stops generating DUMP on the “PLAN cannot be generated” error but only writes a message to ERRORLOG file (Internal Query Processor Error: The query processor could not produce a query plan. For more information, contact Customer Support Services).
+
+Link: None
+
+
#### Trace Flag: 9268
Function: SQL 8 - When SQL Server runs a parameterized query that contains several IN clauses, each with a large number of values, SQL Server may return the following error message after a minute or more of high CPU utilization: KB 325658. Server: Msg 8623, Level 16, State 1. Internal Query Processor Error: The query processor could not produce a query plan. Contact your primary support provider for more information.
@@ -5517,6 +5570,14 @@ Scope: global or session
SQL Server Version: >= 2019 CU9, >= 2017 CU21
+
+#### Trace Flag: 12310
+Function: Availability groups are designed with flow control gates on the primary replica to avoid excessive resource consumption, such as network and memory resources, on all availability replicas. SQL Server 2022 (16.x) increases the limits to the number of messages that each gate allows. By using trace flag 12310, the increased limit is also available to the following versions of SQL Server: SQL Server 2019 (15.x) CU9, SQL Server 2017 (14.x) CU18, SQL Server 2016 (13.x) SP1 CU16.
+Link: https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/monitor-performance-for-always-on-availability-groups?tabs=new-limits#flow-control-gates
+Scope: global
+SQL Server Version: >= 2019 CU9, >= 2017 CU18, >= 2016 SP1 CU16
+
+
#### Trace Flag: 12311
Function: After TF [12306](#12306) is enabled, you can further enable 12311 trace flag that represent maximum group commit times of **1ms**. This TF is additive whith [12312](#12312), [12314](#12314), and [12318](#12318). The maximum group commit time setting is capped at 10ms. However, these should not be repeated. For example, do not specify the same TF more than one time.
@@ -5565,6 +5626,51 @@ Scope: global or session
SQL Server Version: >= 2019 CU9, >= 2017 CU21
+### Trace Flag: 12502
+Function: Disables external authorization policies for on-premises SQL Server instances.
+Use this flag when you see increased PREEMPTIVE_OS_QUERYREGISTRY waits to occur. Due to a bug a SQL Server might lookup Windows Registry values even for a simple queries which do not work with operating system at all.
+Link: [Docs Trace Flags](https://docs.microsoft.com/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql)
+Link: [SQL Server 2022 CU5 Documentation](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2022/cumulativeupdate5#2351584)
+Scope: global
+SQL Server Version: >= 2022 CU5
+
+
+Note: In my case vast majority of queries executed against SQL Server started looking up non-existing registry value at:
+`HKLM\Software\Microsoft\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQLServer\PurviewConfig`
+so this buggy behavior might be triggered only after other software gets integrated with SQL Server 2022.
+
+
+
+#### Trace Flag: 12606
+Function: Enables Query Store for secondary replicas.
+Link: https://learn.microsoft.com/en-us/sql/relational-databases/performance/query-store-for-secondary-replicas?view=sql-server-ver16
+Scope: global
+SQL Server Version: >= 2022
+
+
+
+#### Trace Flag: 12618
+Function: Introduces a new plan regression detection model for Automatic Plan Correction that includes multiple consecutive checks.
+Link: [SQL Server 2022 CU4 Documentation](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2022/cumulativeupdate4#2344871)
+Scope: global
+SQL Server Version: >= 2022 CU4
+
+
+
+#### Trace Flag: 12656
+Function: For Automatic Plan Correction, introduces the ability to use a time-based plan regression check that will occur five minutes after a plan change is discovered, which avoids biasing the regression checks by queries that execute quickly.
+Link: [SQL Server 2022 CU4 Documentation](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2022/cumulativeupdate4#2344871)
+Scope: global
+SQL Server Version: >= 2022 CU4
+
+
+#### Trace Flag: 15915
+Function: Fixes a performance issue that you might encounter only when sp_lock is called frequently from multiple connections, which might cause a memory leak. The memory isn't cleaned up until you restart the SQL Server service. Added in SQL Server 2019 CU29.
+Link: [SQL Server 2019 CU29 Documentation](https://learn.microsoft.com/en-us/troubleshoot/sql/releases/sqlserver-2019/cumulativeupdate29#3180085)
+SQL Server Version: >= 2019 CU29
+
+
+
[Docs Trace Flags]:https://docs.microsoft.com/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql
[Query Store Trace Flags]:https://www.sqlskills.com/blogs/erin/query-store-trace-flags/
[DBCC TRACEON]:https://docs.microsoft.com/sql/t-sql/database-console-commands/dbcc-traceon-transact-sql
diff --git a/SSMS/SSMS_Addins.md b/SSMS/SSMS_Addins.md
index eb39238d..985fc505 100644
--- a/SSMS/SSMS_Addins.md
+++ b/SSMS/SSMS_Addins.md
@@ -1,9 +1,10 @@
# SQL Server Management Studio add-ins
-Complete list of useful and must have add-ins for SQL Server Management Studio - **36** SSMS add-ins
+Complete list of useful and must have add-ins for SQL Server Management Studio - **38** SSMS add-ins
| Name | Download page | Release Date | Support SSMS Version | Developer | Free version | Price |
|-------------------------------------------------------|-------------------------------|--------------|:---------------------|----------------------|--------------|------:|
| [SSMSBoost](#ssmsboost) | [SSMSBoost] | 2019-08-19 | 2008-2018 | Solutions Crew GmbH | Yes | $150 |
+| [SQL Refactor Studio](#sql-refactor-studio) | [SQL Refactor Studio] | 2013-09-01 | 2013-2016 | SQL Refactor Studio Team | Yes | $17-25 |
| [SqlSmash](#sqlsmash) | [SqlSmash] | 2017-06-10 | 2008-2017 | Smashing Jedis LLC | Yes | $99 |
| [SQL Search](#sql-search) | [SQL Search] | 2017-02-27 | 2008-2017 | Red Gate | Yes | No |
| [Red Gate SQL Test](#red-gate-sql-test) | [Red Gate SQL Test] | 2017-03-21 | 2008-2017 | Red Gate | No | $369 |
@@ -34,12 +35,15 @@ Complete list of useful and must have add-ins for SQL Server Management Studio -
| [Tabs Studio](#tabs-studio) | [Tabs Studio] | 2017-08-24 | 2012-2017 | Vlasov Studio | No | $49 |
| [Workload Addin](#workload-addin) | [Workload Addin] | 2017-02-07 | 2008-2012 | Tomáš Bauer | Yes | No |
| [SQL Server Diagnostics](#sql-server-diagnostics) | [SQL Server Diagnostics] | 2017-06-22 | 2016-2017 | Microsoft | Yes | No |
-| [VersionSQL](#versionsql) | [VersionSQL] | 2017-02-16 | 2012-2017 | VersionSQL | Yes | $149 |
+| [VersionSQL](#versionsql) | [VersionSQL] | 2017-02-16 | 2012-2017 | Wrism Innovations | Yes | $199 |
| [Spotlight Tuning Pack](#spotlight-tuning-pack) | [Spotlight Tuning Pack] | 2018-06-01 | 2012-2017 | Quest Software Inc | Yes | $180 |
| [Michel Max - SSMS Tools](#michel-max) | [Michel Max - SSMS Tools] | 2018-11-16 | 2012-2018 | Michel Max | Yes | No |
| [SSMS Schema Folders](#ssms-schema-folders) | [SSMS Schema Folders] | 2018-10-06 | 2012-2018 | Nicholas Ross | Yes | No |
| [Statistics Reporter](#statistics-reporter) | [Statistics Reporter] | 2019-04-17 | 2014-2018 | Analytics Bar | Yes | No |
| [SSMS Lizard](#ssms-lizard) | [SSMS Lizard] | 2020-06-15 | 2018-2018 | Lizard Labs Software | Yes | No |
+| [SSMS Object Explorer Menu](#ssms-menu) | [SSMS Object Explorer Menu] | 2023-07-10 | 2018-2022 | Daniel Brink | Yes | No |
+| [SQL Shades dark mode](#sql-shades) | [SQL Shades] | 2022-08-03 | 2018-2022 | Wrism Innovations | Yes | No |
+| [Axial SQL Tools](#axial-sql-tools) | [Axial SQL Tools] | 2025-01-26 | 2022-2025 | Alex Bochkov | Yes | No |
@@ -70,6 +74,39 @@ Licensing options: after 30 day trial period register and get free community lic
[Features list / SSMSBoost version comparison](http://www.ssmsboost.com/VersionCompare)
+
+## SQL Refactor Studio
+Download page: [SQL Refactor Studio]
+Release date: 2013-09-01
+Support Version: 2013-2016
+Developer: SQL Refactor Studio Team
+Free version: Yes
+Price: $17-25
+
+SQL Refactor Studio is a SQL Server Management Studio addin. It contains a lot of useful tools that help SQL Server developers to perform their daily routines.
+
+Key features of SQL Refactor Studio:
+- Group Databases and Database Objects
+- SQL Query History
+- Generate C# classes
+- Refactoring «Rename»
+- View Dependencies
+- Find Code
+- Displaying calculations on the SSMS status bar
+- Refactoring «Add CRUD Methods»
+- Refactoring «Introduce Trigger for History»
+- Refactoring «Add Lookup Table»
+- Refactoring «Move Columns»
+- Find Data
+- Generate Select Statement
+- Script Table Data
+- Change collation of table columns
+- Highlight all occurrences of selected word
+- Scripting an object as ALTER
+- Get row count
+- Get Top(N) rows
+
+
## SqlSmash
Download page: [SqlSmash]
@@ -656,7 +693,56 @@ SSMS Lizard extends SQL Server Management Studio with a number of new features n
- Quickly attach the query results to an e-mail, FTP or even Google Drive with a single click of a button; and more.
+
+## SSMS Object Explorer Menu
+Download page: [SSMS Object Explorer Menu]
+Release date: 2023-07-10
+Support Version: 2018-2023
+Developer: Daniel Brink
+Free version: Yes
+Price: No
+
+SSMS extension for adding custom menu items to the Object Explorer's right-click context menu.
+
+
+## SQL Shades
+Download page: [SQL Shades]
+Release date: 2022-08-03
+Support Version: 18+
+Developer: Wrism Innovations
+Free version: Yes
+Price: No
+
+Adds a true dark theme to SSMS.
+
+
+## Axial SQL Tools
+Download page: [Axial SQL Tools]
+Release date: 2025-01-26
+Support Version: 20+
+Developer: Alex Bochkov
+Free version: Yes
+Price: No
+
+Axial SQL Tools is a productivity add-in for SQL Server Management Studio 20/21, designed to streamline your workflow and address common limitations in SSMS. Built from the personal experience of a seasoned SQL Server engineer, this tool incorporates community feedback to continuously improve and simplify routine tasks.
+
+Main Features:
+- Transaction Warning: Instantly see if you’ve left any transactions open.
+- Precise Execution Time: Monitor query execution times down to the millisecond on the status bar.
+- Format Any TSQL Code: Validate and format your TSQL code using the Microsoft TSQL parser for improved readability.
+- Query Templates and Snippets: Access a library of query templates for common tasks, saving time on routine queries.
+- Export Grid to Excel: Quickly export grid results directly into an Excel file.
+- Export Grid to Email: Export grid results and send the file via email directly from SSMS.
+- Export Grid as Temp Table: Convert grid results into temporary tables using generated INSERT statements.
+- Script Selected Object Definition: Easily generate scripts for the definitions of selected objects from your query.
+- Server Health Dashboard: Get a quick overview of the server’s key metrics.
+- Right Alignment for Numeric Values in Grid: Automatically align numeric values to the right in the grid for better readability.
+- BULK Data Transfer Between Two Open Connections: Seamlessly transfer bulk data between two open connections with a single click.
+- Query History: Maintain a detailed log of executed queries for auditing and easy retrieval.
+
+
[SSMSBoost]:http://www.ssmsboost.com/
+[SQL Refactor Studio]: https://sqlrefactorstudio.com/
[SqlSmash]:http://www.sqlsmash.com/
[Red Gate SQL Code Guard]:https://www.red-gate.com/products/sql-development/sql-code-guard/
[SQL Search]:http://www.red-gate.com/products/sql-development/sql-search/
@@ -694,8 +780,11 @@ SSMS Lizard extends SQL Server Management Studio with a number of new features n
[Michel Max - SSMS Tools]:https://sourceforge.net/projects/michelmaxssmstools2017/
[SSMS Schema Folders]:https://github.com/nicholas-ross/SSMS-Schema-Folders
[Statistics Reporter]:https://analyticsbar.com/blog/statistics-reporter-ssms-extension/
+[SSMS Object Explorer Menu]:https://github.com/brink-daniel/ssms-object-explorer-menu
[SQL_Search Download]:https://download.red-gate.com/SQL_Search.exe
[Apex SQL Search Download]:https://www.apexsql.com/zips/ApexSQLSearch.exe
[DbForge Search Download]:https://www.devart.com/dbforge/sql/search/searchsql22std.exe
[SSMS Lizard]:https://www.lizard-labs.com/sql_server_management_studio_ssms_extesnsion_lizard.aspx
+[SQL Shades]:https://www.sqlshades.com/
+[Axial SQL Tools]:(https://github.com/Axial-SQL/AxialSqlTools)
diff --git a/Scripts/Adding_Trace_Flags_To_Startup_Parameters.sql b/Scripts/Adding_Trace_Flags_To_Startup_Parameters.sql
index b3f5af8d..9872dd1a 100644
--- a/Scripts/Adding_Trace_Flags_To_Startup_Parameters.sql
+++ b/Scripts/Adding_Trace_Flags_To_Startup_Parameters.sql
@@ -2,152 +2,108 @@
Source link: https://blog.waynesheffield.com/wayne/archive/2017/09/registry-sql-server-startup-parameters/
Author: Wayne Sheffield
-Globally enable / disable the specified trace flags.
-Use DBCC TRACEON/TRACEOFF to enable disable globally trace flags, then adjust
-the SQL Server instance startup parameters for these trace flags.
-
+Description: Globally enable or disable the specified trace flags.
+Use DBCC TRACEON/TRACEOFF to enable or disable trace flags globally,
+then adjust the SQL Server instance startup parameters for these trace flags.
+
SQL Server startup parameters are stored in the registry at:
-HKLM\Software\Microsoft\Microsoft SQL Server\MSSQL12.SQL2014\MSSQLServer\Parameters
-
+HKLM\Software\Microsoft\MSSQLSERVER\MSSQLServer\Parameters
+
To use the xp_instance_reg... XPs, use:
HKLM\Software\Microsoft\MSSQLSERVER\MSSQLServer\Parameters.
-
-To use:
+
+Usage:
1. Add the Trace Flags that you want modified to the @TraceFlags table variable.
2. Set the @DebugLevel variable to 1 to see what will happen on your system first.
-3. When satisified what will happen, set @DebugLevel to 0 to actually execute the statements.
+3. When satisfied with the output, set @DebugLevel to 0 to actually execute the statements.
********************************************************************************
- MODIFICATION LOG
+MODIFICATION LOG
********************************************************************************
2016-08-03 WGS Initial Creation.
*******************************************************************************/
SET NOCOUNT ON;
--- Declare and initialize variables.
--- To use with SQL 2005, cannot set the variables in the declare statement.
-DECLARE @MaxValue INTEGER,
- @SQLCMD VARCHAR(MAX),
- @RegHive VARCHAR(50),
- @RegKey VARCHAR(100),
- @DebugLevel TINYINT;
-
-SET @RegHive = 'HKEY_LOCAL_MACHINE';
-SET @RegKey = 'Software\Microsoft\MSSQLSERVER\MSSQLServer\Parameters';
-SET @DebugLevel = 0; -- only makes changes if set to zero!
-
--- Add the trace flags that you want changed here.
--- If enable = 1, DBCC TRACEON will be run; if enable = 0 then DBCC TRACEOFF will be run.
--- If enable_on_startup = 1, then this TF will be added to start up on service restart;
--- If enable_on_startup - 0, then this TF will be removed from starting up service restart
+
+DECLARE @MaxValue INT,
+ @SQLCMD VARCHAR(MAX),
+ @RegHive VARCHAR(50) = 'HKEY_LOCAL_MACHINE',
+ @RegKey VARCHAR(100) = 'Software\Microsoft\MSSQLSERVER\MSSQLServer\Parameters',
+ @DebugLevel TINYINT = 1;
+
DECLARE @TraceFlags TABLE (
- TF INTEGER,
- enable BIT,
- enable_on_startup BIT,
- TF2 AS '-T' + CONVERT(VARCHAR(15), TF)
+ TF INT,
+ enable BIT,
+ enable_on_startup BIT,
+ TF2 AS '-T' + CONVERT(VARCHAR(15), TF)
);
INSERT INTO @TraceFlags (TF, enable, enable_on_startup)
--- To work with SQL 2005, cannot use a table value constructor.
--- So, use SELECT statements with UNION ALL for each TF to modify.
-SELECT 1117, 1, 1 UNION ALL
-SELECT 1118, 1, 1 UNION ALL
-SELECT 1204, 0, 0 UNION ALL
-SELECT 1222, 0, 0;
+VALUES (1117, 1, 1),
+ (1118, 1, 1),
+ (1204, 0, 0),
+ (1222, 0, 0);
-
--- Get all of the arguments / parameters when starting up the service.
DECLARE @SQLArgs TABLE (
- Value VARCHAR(50),
- Data VARCHAR(500),
- ArgNum AS CONVERT(INTEGER, REPLACE(Value, 'SQLArg', '')));
+ Value VARCHAR(50),
+ Data VARCHAR(500),
+ ArgNum AS CONVERT(INT, REPLACE(Value, 'SQLArg', ''))
+);
INSERT INTO @SQLArgs
- EXECUTE master.sys.xp_instance_regenumvalues @RegHive, @RegKey;
+EXEC master.sys.xp_instance_regenumvalues @RegHive, @RegKey;
+SELECT @MaxValue = MAX(ArgNum) FROM @SQLArgs;
+PRINT 'MaxValue: ' + CAST(@MaxValue AS VARCHAR);
--- Get the highest argument number that is currently set
-SELECT @MaxValue = MAX(ArgNum)
-FROM @SQLArgs;
-RAISERROR('MaxValue: %i', 10, 1, @MaxValue) WITH NOWAIT;
-
--- Disable specified trace flags
-SELECT @SQLCMD = 'DBCC TRACEOFF(' +
- STUFF((SELECT ',' + CONVERT(VARCHAR(15), TF)
- FROM @TraceFlags
- WHERE enable = 0
- ORDER BY TF
- FOR XML PATH(''), TYPE).value('.','varchar(max)')
- ,1,1,'') + ', -1);'
+SELECT @SQLCMD = 'DBCC TRACEOFF(' +
+ STUFF((SELECT ',' + CONVERT(VARCHAR(15), TF)
+ FROM @TraceFlags
+ WHERE enable = 0
+ ORDER BY TF
+ FOR XML PATH(''), TYPE).value('.','varchar(max)'), 1, 1, '') + ', -1);';
+IF @DebugLevel = 0 EXEC (@SQLCMD);
+PRINT 'Disable TFs Command: "' + @SQLCMD + '"';
-IF @DebugLevel = 0 EXECUTE (@SQLCMD);
-RAISERROR('Disable TFs Command: "%s"', 10, 1, @SQLCMD) WITH NOWAIT;
-
--- Enable specified trace flags
-SELECT @SQLCMD = 'DBCC TRACEON(' +
- STUFF((SELECT ',' + CONVERT(VARCHAR(15), TF)
- FROM @TraceFlags
- WHERE enable = 1
- ORDER BY TF
- FOR XML PATH(''), TYPE).value('.','varchar(max)')
- ,1,1,'') + ', -1);'
-
-IF @DebugLevel = 0 EXECUTE (@SQLCMD);
-RAISERROR('Enable TFs Command: "%s"', 10, 1, @SQLCMD) WITH NOWAIT;
+SELECT @SQLCMD = 'DBCC TRACEON(' +
+ STUFF((SELECT ',' + CONVERT(VARCHAR(15), TF)
+ FROM @TraceFlags
+ WHERE enable = 1
+ ORDER BY TF
+ FOR XML PATH(''), TYPE).value('.','varchar(max)'), 1, 1, '') + ', -1);';
+IF @DebugLevel = 0 EXEC (@SQLCMD);
+PRINT 'Enable TFs Command: "' + @SQLCMD + '"';
DECLARE cSQLParams CURSOR LOCAL FAST_FORWARD FOR
-WITH cte AS
-(
- -- Current arguments, with new TFs added at the end. Get a row number to sort by.
- SELECT *,
- ROW_NUMBER() OVER (ORDER BY ISNULL(ArgNum, 999999999), TF) - 1 AS RN
- FROM @SQLArgs arg
+WITH cte AS (
+ SELECT * ,
+ ROW_NUMBER() OVER (ORDER BY ISNULL(ArgNum, 999999999), TF) - 1 AS RN
+ FROM @SQLArgs arg
FULL OUTER JOIN @TraceFlags tf ON arg.Data = tf.TF2
-), cte2 AS
-(
- -- Use the row number to calc the SQLArg# for new TFs.
- -- Use the original Value (SQLArg#) and Data for all rows if possible,
- -- Otherwise use the calculated SQLArg# and the calculated TF2 column.
- -- Only get the original non-TF-matched parameters, and the TFs set to be enabled
- -- (existing startup TFs not in @TraceFlags are left alone).
- SELECT ca.Value,
- ca.Data
- -- in case any TFs are removed, calculate new row numbers in order
- -- to renumber the SQLArg values
- , ROW_NUMBER() OVER (ORDER BY RN) - 1 AS RN2
- FROM cte
- -- Again, for SQL 2005, use SELECT statement instead of VALUES.
- CROSS APPLY (SELECT ISNULL(Value, 'SQLArg' + CONVERT(VARCHAR(15), RN)),
- ISNULL(Data, TF2) ) ca(Value, Data)
- WHERE ISNULL(enable_on_startup, 1) = 1 -- ISNULL handles non-TF parameters
+), cte2 AS (
+ SELECT ca.Value, ca.Data,
+ ROW_NUMBER() OVER (ORDER BY RN) - 1 AS RN2
+ FROM cte
+ CROSS APPLY (SELECT ISNULL(Value, 'SQLArg' + CONVERT(VARCHAR(15), RN)), ISNULL(Data, TF2)) ca(Value, Data)
+ WHERE ISNULL(enable_on_startup, 1) = 1
)
--- The first three parameters are the location of the errorlog directory,
--- and the master database file locations. Ignore these.
--- This returns the remaining parameters that should be set.
--- Also return the highest number of parameters, so can determine if any need to be deleted.
-SELECT 'SQLArg' + CONVERT(VARCHAR(15), RN2) AS Value,
- Data,
- MAX(RN2) OVER () AS MaxRN2
-FROM cte2
-WHERE RN2 > 2
+SELECT 'SQLArg' + CONVERT(VARCHAR(15), RN2) AS Value, Data, MAX(RN2) OVER () AS MaxRN2
+FROM cte2
+WHERE RN2 > 2
ORDER BY RN2;
-
-DECLARE @Value VARCHAR(50),
- @Data VARCHAR(500),
- @MaxRN2 INTEGER;
+
+DECLARE @Value VARCHAR(50), @Data VARCHAR(500), @MaxRN2 INT;
OPEN cSQLParams;
FETCH NEXT FROM cSQLParams INTO @Value, @Data, @MaxRN2;
WHILE @@FETCH_STATUS = 0
BEGIN
- IF @DebugLevel = 0 EXECUTE master.sys.xp_instance_regwrite @RegHive, @RegKey, @Value, 'REG_SZ', @Data;
- RAISERROR('EXECUTE master.sys.xp_instance_regwrite ''%s'', ''%s'', ''%s'', ''REG_SZ'', ''%s''', 10, 1, @RegHive, @RegKey, @Value, @Data) WITH NOWAIT;
+ IF @DebugLevel = 0 EXEC master.sys.xp_instance_regwrite @RegHive, @RegKey, @Value, 'REG_SZ', @Data;
+ PRINT 'EXEC master.sys.xp_instance_regwrite ''' + @RegHive + ''', ''' + @RegKey + ''', ''' + @Value + ''', ''REG_SZ'', ''' + @Data + '''';
FETCH NEXT FROM cSQLParams INTO @Value, @Data, @MaxRN2;
END;
CLOSE cSQLParams;
DEALLOCATE cSQLParams;
--- In case deleting more TFs than added, there may be extra SQLArg values left behind.
--- Need to delete the extras now.
WHILE @MaxValue > @MaxRN2
BEGIN
- SET @Value = 'SQLArg' + CONVERT(VARCHAR(15), @MaxValue);
- IF @DebugLevel = 0 EXECUTE master.sys.xp_instance_regdeletevalue @RegHive, @RegKey, @Value;
- RAISERROR('EXECUTE master.sys.xp_instance_regdeletevalue ''%s'', ''%s'', ''%s''', 10, 1, @RegHive, @RegKey, @Value) WITH NOWAIT;
- SET @MaxValue = @MaxValue - 1;
+ SET @Value = 'SQLArg' + CONVERT(VARCHAR(15), @MaxValue);
+ IF @DebugLevel = 0 EXEC master.sys.xp_instance_regdeletevalue @RegHive, @RegKey, @Value;
+ PRINT 'EXEC master.sys.xp_instance_regdeletevalue ''' + @RegHive + ''', ''' + @RegKey + ''', ''' + @Value + '''';
+ SET @MaxValue = @MaxValue - 1;
END;
diff --git "a/Scripts/Backup_Restore_What\342\200\231s_My_Status.sql" "b/Scripts/Backup_Restore_What\342\200\231s_My_Status.sql"
index 9d760042..c7560aa7 100644
--- "a/Scripts/Backup_Restore_What\342\200\231s_My_Status.sql"
+++ "b/Scripts/Backup_Restore_What\342\200\231s_My_Status.sql"
@@ -28,4 +28,6 @@ FROM sys.dm_exec_requests r
WHERE command IN (
'RESTORE DATABASE'
, 'BACKUP DATABASE'
+ ,'RESTORE LOG'
+ ,'BACKUP LOG'
);
diff --git a/Scripts/Disable_Enable_All_Triggers_In_Database.sql b/Scripts/Disable_Enable_All_Triggers_In_Database.sql
index fe9feb6f..5801e967 100644
--- a/Scripts/Disable_Enable_All_Triggers_In_Database.sql
+++ b/Scripts/Disable_Enable_All_Triggers_In_Database.sql
@@ -21,7 +21,7 @@ INNER JOIN sys.tables ta ON ta.object_id = tr.parent_id
INNER JOIN sys.schemas sc ON sc.schema_id = ta.schema_id
WHERE tr.is_disabled = 0;
--- Perpare Variable Script for Execution
+-- Prepare Variable Script for Execution
DECLARE @Disable_Statement nvarchar(max) = (
SELECT tmp.DisableTriggerStatement + CHAR(10) AS "data()"
FROM #Triggers tmp
@@ -31,9 +31,9 @@ DECLARE @Disable_Statement nvarchar(max) = (
-- Execute SQL
EXEC sys.sp_executesql @Disable_Statement;
--- Perpare Variable Script for Execution
+-- Prepare Variable Script for Execution
DECLARE @Enable_Statement nvarchar(max) = (
- SELECT tmp.DisableTriggerStatement + CHAR(10) AS "data()"
+ SELECT tmp.EnableTriggerStatement + CHAR(10) AS data
FROM #Triggers tmp
FOR XML PATH('')
);
diff --git a/Stored_Procedure/dbo.sp_PrintString.sql b/Stored_Procedure/dbo.sp_PrintString.sql
index 7fe8ccae..6633031c 100644
--- a/Stored_Procedure/dbo.sp_PrintString.sql
+++ b/Stored_Procedure/dbo.sp_PrintString.sql
@@ -2,93 +2,69 @@ IF OBJECT_ID('dbo.sp_PrintString', 'P') IS NULL
EXEC ('CREATE PROCEDURE dbo.sp_PrintString AS SELECT 1');
GO
-ALTER PROCEDURE dbo.sp_PrintString(
- @str NVARCHAR(MAX)
+ALTER PROCEDURE dbo.sp_PrintString (
+ @str NVARCHAR(MAX)
)
AS
-/*
-DECLARE @longStr NVARCHAR(MAX) = REPLICATE(N'R', 5000) + CAST(CHAR(13) AS NVARCHAR(MAX)) + REPLICATE(N'Я', 5000) + CAST(CHAR(13) + N'ё' AS NVARCHAR(MAX));
-PRINT('Microsoft PRINT with string truncating:');
-PRINT(@longStr);
-PRINT('Right PRINT using dbo.sp_PrintString without string truncating:');
-EXEC dbo.sp_PrintString @str = @longStr;
-*/
BEGIN
- DECLARE @line NVARCHAR(MAX)
- , @startLocation INT
- , @length INT
- , @totalLength INT
- , @current INT;
-
- SELECT @startLocation = 1
- ,@totalLength = DATALENGTH(@str)
- ,@current = 1
- ,@length = 0
+ DECLARE @line NVARCHAR(MAX),
+ @startLocation INT = 1,
+ @length INT = 0,
+ @totalLength INT = DATALENGTH(@str),
+ @current INT = 1;
DECLARE @PrintLine NVARCHAR(MAX) =
- N'DECLARE @pos int = 1
- WHILE @pos <= LEN(@line)
- BEGIN
- PRINT SUBSTRING(@line, @pos, 4000);
- SET @pos = @pos + 4000;
- END;'
+ N'
+ DECLARE @pos INT = 1
+ WHILE @pos <= LEN(@line)
+ BEGIN
+ PRINT SUBSTRING(@line, @pos, 4000);
+ SET @pos = @pos + 4000;
+ END;';
WHILE @current <= @totalLength
BEGIN
- IF (
- SUBSTRING(@str, @current, 2) IN (
- CHAR(0x0D) + CHAR(0x0A)
- ,CHAR(0x0A) + CHAR(0x0D)
- )
- )
+ IF SUBSTRING(@str, @current, 2) IN (
+ CHAR(0x0D) + CHAR(0x0A),
+ CHAR(0x0A) + CHAR(0x0D)
+ )
BEGIN
IF @length <= 0
PRINT '';
ELSE
- BEGIN -- line
+ BEGIN
SELECT @line = SUBSTRING(@str, @startLocation, @length);
-
- EXEC sp_executesql @PrintLine
- ,N'@line NVARCHAR(max)'
- ,@line;
+ EXEC sp_executesql @PrintLine, N'@line NVARCHAR(MAX)', @line;
END
- SELECT @startLocation = @current + 2
- , @current = @current + 2
- , @length = 0;
+ SELECT @startLocation = @current + 2,
+ @current = @current + 2,
+ @length = 0;
CONTINUE;
END
- ELSE IF (
- SUBSTRING(@str, @current, 1) IN (
- CHAR(0x0D)
- ,CHAR(0x0A)
- )
- )
+ ELSE IF SUBSTRING(@str, @current, 1) IN (CHAR(0x0D), CHAR(0x0A))
BEGIN
IF @length <= 0
PRINT '';
ELSE
BEGIN
SELECT @line = SUBSTRING(@str, @startLocation, @length);
-
- EXEC sp_executesql @PrintLine
- ,N'@line NVARCHAR(max)'
- ,@line;
+ EXEC sp_executesql @PrintLine, N'@line NVARCHAR(MAX)', @line;
END
- SELECT @startLocation = @current + 1
- ,@current = @current + 1
- ,@length = 0;
+ SELECT @startLocation = @current + 1,
+ @current = @current + 1,
+ @length = 0;
CONTINUE;
END
- SELECT @current = @current + 1
- , @length = @length + 1;
+ SELECT @current = @current + 1,
+ @length = @length + 1;
END
- IF (@startLocation <= DATALENGTH(@str))
+ IF @startLocation <= DATALENGTH(@str)
PRINT SUBSTRING(@str, @startLocation, DATALENGTH(@str));
END;
GO
diff --git a/Stored_Procedure/dbo.sp_RestoreGene.sql b/Stored_Procedure/dbo.sp_RestoreGene.sql
index 7bea9b5d..f9a3051e 100644
--- a/Stored_Procedure/dbo.sp_RestoreGene.sql
+++ b/Stored_Procedure/dbo.sp_RestoreGene.sql
@@ -1,26 +1,61 @@
USE [master]
GO
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME = 'sp_RestoreGene')
-EXEC ('CREATE PROC dbo.sp_RestoreGene AS SELECT 1')
+EXEC ('CREATE PROC dbo.sp_RestoreGene AS SELECT ''stub version, to be replaced''')
GO
-
+
/*********************************************************************************************
-Restore Generator v4.3 (2014-10-03)
+Restore Generator v8.3 (2022-04-11)
(C) 2012, Paul Brewer
-
+
Feedback: paulbrewer@yahoo.co.uk
-Updates: https://paulbrewer.wordpress.com/
-
+User Guide: https://restoregene.com
+
This procedure queries msdb database backup history and database file details in master.
-It builds and returns RESTORE DATABASE commands as it's result set, it does not execute the commands.
-
+It builds and returns RESTORE DATABASE commands as its result set, it does not execute the commands.
+
+----------------------------------------------------------------------------------------------
+PARAMETERS:
+
+@Database - Default NULL, Restore a specific database or a list of comma seperated database names, NULL which restores all user databases.
+@TargetDatabase - Defaults to NULL, override restored database name, only possible if restoring one specific database, if supplied will rename database files and log file too.
+@WithMoveDataFiles - Defaults to the actual data file paths, overrides WITH MOVE for data file folder
+@WithMoveLogFile - Defaults to the actual log file path, overrides WITH MOVE for log file folder
+@WithMoveFileStreamFile - Defaults to the actual file stream file path, overrides WITH MOVE for file stream file folder
+@FromFileFullUNC - Defaults to actual backup file drive and folder, UNC path to full backup file
+@FromFileDiffUNC - Defaults to actual drive and folder, UNC path to differential backup file
+@FromFileLogUNC - Defaults to actual drive and folder, UNC path to log backup files
+@StopAt - Defaults to current (datetime2), Stop At a specific date time
+@StandbyMode - Defaults to 0, Leave database in standby mode
+@IncludeSystemDBs - Defaults to 0, include master, model and msdb database restores
+@WithRecovery - Defaults to 0, Recover database(s) after restore
+@WithCHECKDB - Defaults to 0, Include a CHECKDB after recovery
+@WithReplace - Defaults to 0, replace existing database when no tail log backup exists
+@LogShippingStartTime - Only used by ps_RestoreGene
+@LogShippingLastLSN - Only used by ps_RestoreGene
+@BlobCredential - Defaults to NULL, SQL Credential for Azure Blog Storage Account
+@RestoreScriptReplaceThis - Defaults to NULL, Restore Script String Find
+@RestoreScriptWithThis - Defaults to NULL, Restore Script String Replace
+@SuppressWithMove - Default 1, Exclude WITH MOVE statements from the Restore Script
+@PivotWithMove - Default 0, If SuppressWithMove = 0, pivot secondary files to new rows in the result set
+@RestoreScriptOnly - Default 0, Return just the RESTORE DATABASE commands
+@DropDatabaseAfterRestore - Default 0, Drops the database after the restore, use with check db
+@SetSingleUser - Default 0, Adds ALTER DATABASE @database SET SINGLE_USER WITH ROLLBACK IMMEDIATE
+@ExcludeDiffAndLogBackups - Default 0, 0=full+diff+ log, 1=full only, 2-full+diff, 3=full+log, 4=diff+log
+@IncludeDeviceType7 - Defaults to 1, Include device type 7 backups in restore script
+@IncludeDeviceType102 - Defaults to 1, Include device type 102 backups in restore script
+@IncludeDeviceType2 - Defaults to 1, Include device type 2 backups in restore script
+@IncludeDeviceType9 - Defaults to 1, Include device type 9 backups in restore script
+
+----------------------------------------------------------------------------------------------
CHANGE LOG:
+
December 23, 2012 - V1.01 - Release
-January 4,2013 - V1.02 - LSN Checks + Bug fix to STOPAT date format
-January 11,2013 - V1.03 - SQL Server 2005 compatibility (backup compression problem) & @StandbyMode
+January 4, 2013 - V1.02 - LSN Checks + Bug fix to STOPAT date format
+January 11, 2013 - V1.03 - SQL Server 2005 compatibility (backup compression problem) & @StandbyMode
January 14, 2013 - V1.04 - WITH COPY and up to 10 striped backup files
January 15, 2013 - V1.05 - Format of constructed restore script, enclose database name in [ ]
-February 7, 2013 - V1.06 - Modified WHERE device_type IN (7,102,2)
+February 7, 2013 - V1.06 - Modified WHERE device_type IN (7,102,2,9)
May 26, 2013 - V1.07 - Various changes for PoSh Driver Script compatibility
October 14, 2013 - V1.08 - Rename parameters, more meaningful names
October 15, 2013 - V2.00 - Add 2nd CTE for striped backup files and remove repeating calls to CTE
@@ -28,11 +63,11 @@ November 5, 2013 - V2.01 - Secondary .ndf files only included if @WithMoveDat
- Extended artificial LSN used for sequencing WITH RECOVERY/CHECKDB to 21 characters
- Include database name in results for logging in PoSh
May 23, 2014 - V3.0 - Improved logging and error handling, add 'LogShippingVariableDeclare' parameter
- Default is Yes when called from query analysier, set to No when called by PoSh
+ Default is Yes when called from query analyser, set to No when called by PoSh
May 30, 2014 - V3.1 - Restructured CTE, all predicates moved to the CTE
June 6, 2014 - V3.2 - Add 'LogShippingStartTime' parameter for PowerShell script.
To skip subsequent full and diff backups when restoring Logs in PoSh log shipping mode
- - Add 'Target Restore Database Name' parameter, Manuj Bahl suggestion
+ - Add 'Target Restore Database Name' parameter
If restoring a specific database, allows overriding restored name
Probably used in conjunction with a @WithMove override of data and log files to a different folder.
- Add LogShippingLastLSN parameter, used to filter results sent to ps_LogShippingLight
@@ -45,24 +80,94 @@ August 5, 2014 - V3.4 - Remove TRY CATCH blocks from log restores and chec
- Remove CTE's,use #temp tables, suggested by S.Stresser
August 25, 2014 - V3.5 - goestock suggestion - Include device type 7 for AWS EC2 backups
August 29th, 2014 - V3.6 - Richard found and fixed a problem with variable declare where database name has a space or full stop.
-July 28, 2015 - V3.7 - Luke Sarro, modifications for case sensitivity & parameter sniffing
+July 28, 2015 - V3.7 - Luke Sarro, modifications for case sensitivity & parameter sniffing, a massive amount of work done.
August 28, 2015 - V3.8 - Luke Sarro modifications, recovery fork management. supports forking in recovery plans when constructing a restore script.
Only tested where a single fork exists, IE a restore was done to a historic point in time, with recovery, then subsequent log backups are taken.
When a fork exists the restore script will follow the new path for subsequent differential and log backups taken after the forking point.
If no rbackup forks exist, the procedure works as before.
-September 02 2015 - V3.9 - SQLMongo modification to replace hyphens in database name
-September 04, 2015 - V4.0 - Domingo modification, check for default backup path = ''
+September 02 2015 - V3.9 - SQLMongo modification to replace hyphens in database name
+September 04, 2015 - V4.0 - Domingo modification, check for default backup path = ''
September 29, 2015 - V4.2 - Fixes bug identifying full backups taken after a recovery fork, has introduced code duplication to be removed later but fixes problem
-October 3, 2015 - V4.3 - Remove code duplication introduced in V4.2
+October 3, 2015 - V4.3 - Remove code duplication introduced in V4.2
+*
+June 3rd, 2016 - V5.0 - Feedback from Brent Ozar related viewing spike:
+ - V5.0 - Reintroduce SQL Server 2005 compatibility - Bob McLaren - Declaring and assigning variables on same line doesn't work with SQL Server 2005.
+ - V5.0 - Override data and log file paths if overriding restored database name
+
+June 11th, 2016 - V5.1 - Azure http url restore support, mick@sqlfairy.com
+ - V5.1 - Restore Script find and replacement variables, mick@sqlfairy.com
+
+June 17th, 2016 - V5.2 - Removed TRY CATCH block in log restore commands
+June 26th, 2016 - V5.3 - Include FILESTREAM files in WITH MOVE, jgkiran
+July 3rd, 2016 - V5.4 - New parameter for FILESTREAM files, for WITH MOVE parameter, jgkiran
+July 14rd, 2016 - V5.5 - Rename rollback files when standby = 1 to allow multiple concurrent ps_RestoreGene invocations, jgkiran
+
+August 8th, 2016 - V5.6 - Accepts a comma separated variable list of databases to restore, suggested Stephen T
+ - Got permission to reuse the 'SELECT DATABASES' code snippet from ola hallengren - https://ola.hallengren.com/
+August 8th, 2016 - V5.7 - Make restore to new database, secondary file names, more meaningful
+August 9th, 2016 - V5.8 - Include the WITH MOVE statements on the same results line as the corresponding RESTORE DATABASE statement, flatten the result set to simplify the PoSh driver
+August 9th, 2016 - V5.9 - New Parameter, suppress WITH MOVE statements is default, parameter allows override to include them in the script.
+
+August 13th, 2016 - V6.0
+ - Add parameter option to pivot WITH MOVE secondary files to a new result set line. Not compatible with PoSh driver, useful with large, complex databases.
+ - Add a parameter option to include just the restore script in the result set. Not compatible with PoSh driver, useful as SQL Agent Job step output.
+
+August 14th, 2016 - V6.1 - Remove parameter @UseDefaultDatabaseBackupPath , no longer used
+August 15th, 2016 - V6.2 - @TargetDatabase parameter, file rename tidy
+August 17th, 2016 - V6.3 - Edge case fix to fork point LSN
+August 19th, 2016 - V6.4 - Publish SQL Server Central
+September 4th, 2016 - V6.41 - PoSh Driver Last LSN minor fix. - Published SQL Server Central
+September 9th, 2016 - V6.42 - Fix for multiple log files with move, J.Peablles
+September 9th, 2016 - V6.43 - Fix for mirrored backups, C.Richardson
+September 9th, 2016 - V6.44 - Mod to log file LSN selection, include if matches diff, R.Devries
+September 30th, 2016 - V6.45 - Improvements to the output generated when @RestoreScriptOnly = 1, John Lee
+September 30th, 2016 - V6.5 - New 'Drop Database After Restore' parameter for use with CHECKDB and automated backup verification, John Lee
+
+November 5th, 2016 - V6.51 - Increase length of @Database parameter to allow for more comma seperated database names
+November 5th, 2016 - V6.52 - Edge case log exclusion criteria mod, R.Devries
+November 5th, 2016 - V6.53 - New parameter option to include ALTER DATABASE @database SET SINGLE_USER WITH ROLLBACK IMMEDIATE, Hakan Ekman
+November 5th, 2016 - V6.54 - New parameter option to exclude differential and log backups (presumaby for development environment refreshes), Mehedi Amin
+
+November 19th, 2016 - V6.55 - Remove semi colon from start of T-SQL restore command strings, serves no purpose
+November 19th, 2016 - V6.56 - Remove variable declare, log reference and RAISERROR, serves no purpose and generates an error in edge cases ( my_database and my-database ), Lars Rasmussen
+November 19th, 2016 - V6.57 - Parameters options for device types to include, Lars Rasmussen
+
+December 3rd, 2016 - V6.58 - Remove TIME function from STANDBY mode file name construct, for SQL Server 2005 compatibility, Erwin Louwerse
+March 26th, 2017 - V6.59 - Significant performance improvements where an instance has many databases, Simone Bizzotto
+
+June 24th, 2017 - V6.70 - Case Sensitive Collation Database Compatibility - M.Pollock
+September 9th, 2017 - V6.71 - Device Type filter fix, LostInEurope01
+November 11th, 2017 - V6.72 - Mod to log file selection, R.Devries
+ - Increase @Databases variable length, Bob
+Febuary 14th, 2018 - V6.73 - Add escape characters to database name in SET SINGLE_USER, Rod
+Febuary 16th, 2018 - V7.0 - Increase support for stripped backup files from 10 to 15, Mehedi
+Febuary 17th, 2018 - V8.0 - Extensive changes to the backup fork handling, Mike
+March 30th, 2018 - V8.04 - Code refactor
+May 25th, 2018 - V8.05 - Missing exclusion of copy only backups reintroduced - Andy
+August 25th, 2018 - V8.10 - Enhancements to the @ExcludeDiffAndLogBackups parameters, Bob and Chris F
+ - Change from parameter data type from bit to int
+ - 0 (default) = full, diff and logs, 1 = full only, 2 = full and diff, 3 = full and logs
+ - Existing BIT data type functionality retained and extended by new values 2 and 3.
+November 8th, 2018 - V8.11 - Bug fix to last log file - Mike
+December 6th, 2018 - V8.12 - Optimisation and environment setup fix - Adam
+January 12th, 2019 - V8.13 - Sequence of FROM DISK = 'Stripe' files in RESTORE command
+January 3rd, 2021 - V8.14 - Restore from Azure blog storage using FROM DISK instead of FROM URL for backup stripes - Aaron
+January 9rd, 2021 - V8.15 - Change parameter @ExcludeDiffAndLogBackups, new option 4 to exclude full restore, return incremental diff & log restores - Mehedi Amin
+July 11th, 2021 - V8.16 - Add support for an additional 5 stripes bring the total supported to 20, Steven Dannen
+April 5th, 2023 - V8.2 - Masood : Bug fix restoring striped backups
+April 11th, 2023 - V8.3 - Aaron : Result Set Columns fix
+
+
+*
********************************************************************************************/
-
-
-ALTER PROC dbo.sp_RestoreGene
+
+ALTER PROC [dbo].[sp_RestoreGene]
(
- @Database SYSNAME = NULL,
+ @Database VARCHAR(MAX) = NULL,
@TargetDatabase SYSNAME = NULL,
@WithMoveDataFiles VARCHAR(2000) = NULL,
@WithMoveLogFile VARCHAR(2000) = NULL,
+ @WithMoveFileStreamFile VARCHAR(2000) = NULL,
@FromFileFullUNC VARCHAR(2000) = NULL,
@FromFileDiffUNC VARCHAR(2000) = NULL,
@FromFileLogUNC VARCHAR(2000) = NULL,
@@ -72,934 +177,1556 @@ ALTER PROC dbo.sp_RestoreGene
@WithRecovery BIT = 0,
@WithCHECKDB BIT = 0,
@WithReplace BIT = 0,
- @UseDefaultDatabaseBackupPath BIT = 0,
- @Log_Reference VARCHAR (250) = NULL,
- @LogShippingVariableDeclare BIT = 1,
+
+ -- Parameters for PowerShell script use only
@LogShippingStartTime DATETIME = NULL,
- @LogShippingLastLSN VARCHAR(25) = NULL
+ @LogShippingLastLSN VARCHAR(25) = NULL,
+ -- Parameters for PowerShell script use only
+
+ @BlobCredential VARCHAR(255) = NULL, --Credential used for Azure blob access
+ @RestoreScriptReplaceThis NVARCHAR(255) = NULL,
+ @RestoreScriptWithThis NVARCHAR(255) = NULL,
+ @SuppressWithMove BIT = 1,
+ @PivotWithMove BIT = 0,
+ @RestoreScriptOnly BIT = 0,
+ @DropDatabaseAfterRestore BIT = 0,
+ @SetSingleUser BIT = 0,
+ @ExcludeDiffAndLogBackups INT = 0,
+
+ -- Options to exclude device types
+ @IncludeDeviceType7 BIT = 0, --virtual device
+ @IncludeDeviceType102 BIT = 1,
+ @IncludeDeviceType2 BIT = 1,
+ @IncludeDeviceType9 BIT = 1
)
AS
BEGIN
-
-SET NOCOUNT ON;
-
--- Avoid Parameter Sniffing Problems
-DECLARE @Database_ SYSNAME = @Database,
- @TargetDatabase_ SYSNAME = @TargetDatabase,
- @WithMoveDataFiles_ VARCHAR(2000) = @WithMoveDataFiles,
- @WithMoveLogFile_ VARCHAR(2000) = @WithMoveLogFile,
- @FromFileFullUNC_ VARCHAR(2000) = @FromFileFullUNC,
- @FromFileDiffUNC_ VARCHAR(2000) = @FromFileDiffUNC,
- @FromFileLogUNC_ VARCHAR(2000) = @FromFileLogUNC,
- @StopAt_ DATETIME = @StopAt,
- @StandbyMode_ BIT = @StandbyMode,
- @IncludeSystemDBs_ BIT = @IncludeSystemDBs,
- @WithRecovery_ BIT = @WithRecovery,
- @WithCHECKDB_ BIT = @WithCHECKDB,
- @WithReplace_ BIT = @WithReplace,
- @UseDefaultDatabaseBackupPath_ BIT = @UseDefaultDatabaseBackupPath,
- @Log_Reference_ VARCHAR (250) = @Log_Reference,
- @LogShippingVariableDeclare_ BIT = @LogShippingVariableDeclare,
- @LogShippingStartTime_ DATETIME = @LogShippingStartTime,
- @LogShippingLastLSN_ VARCHAR(25) = @LogShippingLastLSN
-
--- Defaults Recovery Point Times
-IF ISNULL(@StopAt_,'') = ''
- SET @StopAt_ = GETDATE();
-
-IF ISNULL(@LogShippingStartTime_,'') = ''
- SET @LogShippingStartTime_ = @StopAt_;
-
--- Default written to the SQL Server Error Log
-IF ISNULL(@Log_Reference_,'') = ''
- SET @Log_Reference_ = 'sp_RestoreGene - Recovery in Progress';
-
--- Allow override of restored database name only if working with a specific database
-IF @TargetDatabase_ IS NOT NULL AND @Database_ IS NULL
- SET @TargetDatabase_ = NULL;
-
--- ps_LogShippingLight - Filtered Results
-IF ISNULL(@LogShippingLastLSN_,'') = ''
- SET @LogShippingLastLSN_ = '-1';
-
--- Backup file locations defaulted to '' by ps_RestoreGene
-IF @FromFileFullUNC_ = ''
- SET @FromFileFullUNC_ = NULL;
-
-IF @FromFileDiffUNC_ = ''
- SET @FromFileDiffUNC_ = NULL;
-
-IF @FromFileLogUNC_ = ''
- SET @FromFileLogUNC_ = NULL;
-
--- Environment Preparation
-IF OBJECT_ID('tempdb..#CTE') IS NOT NULL
- DROP TABLE #CTE;
-
-IF OBJECT_ID('tempdb..#Stripes') IS NOT NULL
- DROP TABLE #Stripes;
-
--- Backup forking
-IF OBJECT_ID('tempdb..#BackupFork') IS NOT NULL
- DROP TABLE #BackupFork;
-
-SELECT rf1.database_name, MAX(database_backup_lsn) database_backup_lsn, MAX(fork_point_lsn) fork_point_lsn, MAX(last_lsn) last_lsn
-INTO #BackupFork
-FROM msdb.dbo.backupset rf1
-INNER JOIN
-(
- SELECT database_name, MAX(backup_finish_date) AS backup_finish_date
- FROM msdb.dbo.backupset
- WHERE backup_start_date <= @LogShippingStartTime_
- AND fork_point_lsn IS NULL
- GROUP BY database_name
-) rf2
- ON rf1.database_name = rf2.database_name
-
-WHERE fork_point_lsn IS NOT NULL
-AND rf1.backup_start_date <= @LogShippingStartTime_
-AND rf1.backup_finish_date > rf2.backup_finish_date
-GROUP BY rf1.database_name;
-
-CREATE INDEX IDX_BackupFork ON #BackupFork(database_name);
-
-
---------------------------------------------------------------
--- CTE1 Full Backup UNION Differential Backup UNION Log Backup
---------------------------------------------------------------
-WITH CTE
-(
- database_name
- ,current_compatibility_level
- ,last_lsn
- ,current_is_read_only
- ,current_state_desc
- ,current_recovery_model_desc
- ,has_backup_checksums
- ,backup_size
- ,[type]
- ,backupmediasetid
- ,family_sequence_number
- ,backupstartdate
- ,physical_device_name
- ,position
-)
-AS
-(
-
---------------------------------------------------------------
--- Full backup - Most current immediately before @LogShippingStartTime_
-SELECT
- bs.database_name
- ,d.[compatibility_level] AS current_compatibility_level
- ,bs.last_lsn
- ,d.[is_read_only] AS current_is_read_only
- ,d.[state_desc] AS current_state_desc
- ,d.[recovery_model_desc] current_recovery_model_desc
- ,bs.has_backup_checksums
- ,bs.backup_size AS backup_size
- ,'D' AS [type]
- ,bs.media_set_id AS backupmediasetid
- ,mf.family_sequence_number
- ,x.backup_start_date AS backupstartdate
- ,mf.physical_device_name
- ,bs.position
-FROM msdb.dbo.backupset bs
-
-INNER JOIN sys.databases d
- ON bs.database_name = d.name
-
-INNER JOIN
-(
+
+ SET NOCOUNT ON;
+
+ -- Avoid Parameter Sniffing Problems
+ DECLARE @Database_ VARCHAR(MAX);
+ SET @Database_ = @Database;
+ DECLARE @TargetDatabase_ SYSNAME;
+ SET @TargetDatabase_ = @TargetDatabase;
+
+ -- Special handling for PoSh Driver
+ DECLARE @WithMoveDataFiles_ VARCHAR(2000);
+ IF @WithMoveDataFiles <> '' SET @WithMoveDataFiles_= @WithMoveDataFiles;
+ DECLARE @WithMoveLogFile_ VARCHAR(2000);
+ IF @WithMoveLogFile <> '' SET @WithMoveLogFile_= @WithMoveLogFile;
+ DECLARE @WithMoveFileStreamFile_ VARCHAR(2000);
+ IF @WithMoveFileStreamFile <> '' SET @WithMoveFileStreamFile_= @WithMoveFileStreamFile;
+
+ DECLARE @FromFileFullUNC_ VARCHAR(2000);
+ SET @FromFileFullUNC_ = @FromFileFullUNC;
+ DECLARE @FromFileDiffUNC_ VARCHAR(2000);
+ SET @FromFileDiffUNC_ = @FromFileDiffUNC;
+ DECLARE @FromFileLogUNC_ VARCHAR(2000);
+ SET @FromFileLogUNC_ = @FromFileLogUNC;
+ DECLARE @StopAt_ DATETIME;
+ SET @StopAt_ = @StopAt;
+ DECLARE @StandbyMode_ BIT;
+ SET @StandbyMode_ = @StandbyMode;
+ DECLARE @IncludeSystemDBs_ BIT;
+ SET @IncludeSystemDBs_ = @IncludeSystemDBs;
+ DECLARE @WithRecovery_ BIT;
+ SET @WithRecovery_ = @WithRecovery;
+ DECLARE @WithCHECKDB_ BIT;
+ SET @WithCHECKDB_ = @WithCHECKDB;
+ DECLARE @WithReplace_ BIT;
+ SET @WithReplace_ = @WithReplace;
+ DECLARE @LogShippingStartTime_ DATETIME;
+ SET @LogShippingStartTime_ = @LogShippingStartTime;
+ DECLARE @LogShippingLastLSN_ VARCHAR(25);
+ SET @LogShippingLastLSN_ = @LogShippingLastLSN;
+ DECLARE @BlobCredential_ VARCHAR(255);
+ SET @BlobCredential_ = @BlobCredential;
+ DECLARE @RestoreScriptReplaceThis_ NVARCHAR(255);
+ SET @RestoreScriptReplaceThis_ = @RestoreScriptReplaceThis;
+ DECLARE @RestoreScriptWithThis_ NVARCHAR(255);
+ SET @RestoreScriptWithThis_ = @RestoreScriptWithThis;
+ DECLARE @SuppressWithMove_ BIT;
+ SET @SuppressWithMove_ = @SuppressWithMove;
+ DECLARE @PivotWithMove_ BIT
+ SET @PivotWithMove_ = @PivotWithMove;
+ DECLARE @RestoreScriptOnly_ BIT;
+ SET @RestoreScriptOnly_ = @RestoreScriptOnly;
+ DECLARE @DropDatabaseAfterRestore_ BIT;
+ SET @DropDatabaseAfterRestore_ = @DropDatabaseAfterRestore;
+ DECLARE @SetSingleUser_ BIT;
+ SET @SetSingleUser_ = @SetSingleUser;
+ DECLARE @ExcludeDiffAndLogBackups_ INT;
+ SET @ExcludeDiffAndLogBackups_ = @ExcludeDiffAndLogBackups;
+
+ -- Defaults Recovery Point Times
+ IF ISNULL(@StopAt_,'') = ''
+ SET @StopAt_ = GETDATE();
+
+ IF ISNULL(@LogShippingStartTime_,'') = ''
+ SET @LogShippingStartTime_ = @StopAt_;
+
+ -- Allow override of restored database name only if working with a specific database
+ IF @TargetDatabase_ IS NOT NULL AND @Database_ IS NULL
+ SET @TargetDatabase_ = NULL;
+
+ IF CHARINDEX(',',@Database_, 1) > 0
+ SET @TargetDatabase_ = NULL;
+
+ -- ps_LogShippingLight - Filtered Results
+ IF ISNULL(@LogShippingLastLSN_,'') = ''
+ SET @LogShippingLastLSN_ = '-1';
+
+ -- Present WITH MOVE if override paths are supplied
+ IF ISNULL(@WithMoveDataFiles,'') <> '' OR ISNULL(@WithMoveLogFile_,'') <> '' OR ISNULL(@WithMoveFileStreamFile_,'') <> '' OR ISNULL(@TargetDatabase_,'') <> ''
+ SET @SuppressWithMove_ = 0
+
+ -- Backup file locations defaulted to '' by ps_RestoreGene
+ IF @FromFileFullUNC_ = ''
+ SET @FromFileFullUNC_ = NULL;
+
+ IF @FromFileDiffUNC_ = ''
+ SET @FromFileDiffUNC_ = NULL;
+
+ IF @FromFileLogUNC_ = ''
+ SET @FromFileLogUNC_ = NULL;
+
+ -- Environment Preparation
+ IF OBJECT_ID('tempdb..#CTE') IS NOT NULL
+ DROP TABLE #CTE;
+
+ IF OBJECT_ID('tempdb..#Stripes') IS NOT NULL
+ DROP TABLE #Stripes;
+
+ IF OBJECT_ID('tempdb..#BackupFork') IS NOT NULL
+ DROP TABLE #BackupFork;
+
+ IF OBJECT_ID('tempdb..#ForkPointsCount') IS NOT NULL
+ DROP TABLE #ForkPointsCount;
+
+ IF OBJECT_ID('tempdb..#RestoreGeneResults') IS NOT NULL
+ DROP TABLE #RestoreGeneResults;
+
+ IF OBJECT_ID('tempdb..#tmpDatabases') IS NOT NULL
+ DROP TABLE #tmpDatabases;
+
+ IF OBJECT_ID('tempdb..#DeviceTypes') IS NOT NULL
+ DROP TABLE #DeviceTypes;
+
+ IF OBJECT_ID('tempdb..#FullBackups') IS NOT NULL
+ DROP TABLE #FullBackups;
+
+ CREATE TABLE #DeviceTypes (device_type tinyint);
+ IF @IncludeDeviceType7 = 1 INSERT INTO #DeviceTypes (device_type) SELECT 7;
+ IF @IncludeDeviceType102 = 1 INSERT INTO #DeviceTypes (device_type) SELECT 102;
+ IF @IncludeDeviceType2 = 1 INSERT INTO #DeviceTypes (device_type) SELECT 2;
+ IF @IncludeDeviceType9 = 1 INSERT INTO #DeviceTypes (device_type) SELECT 9;
+
+ --=======================================================================================================================================
+ -- 'SELECT DATABASES' code snippet used below with permission from the ola hallengren - https://ola.hallengren.com/
+ -----------------------------------------------------------------------------------------------------------------------------------------
+ CREATE TABLE #tmpDatabases
+ (
+ ID int IDENTITY,
+ DatabaseName nvarchar(450),
+ DatabaseNameFS nvarchar(max),
+ DatabaseType nvarchar(max),
+ Selected bit,
+ Completed bit
+ );
+ CREATE CLUSTERED INDEX IDX_tmpDatabases ON #tmpDatabases(DatabaseName);
+
+ IF CHARINDEX(',',@Database_,1) > 0
+ BEGIN
+
+ DECLARE @SelectedDatabases TABLE
+ (
+ DatabaseName nvarchar(max),
+ DatabaseType nvarchar(max),
+ Selected bit
+ );
+
+ DECLARE @ErrorMessage nvarchar(max);
+ DECLARE @Error int;
+
+ ----------------------------------------------------------------------------------------------------
+ --// Select databases //--
+ ----------------------------------------------------------------------------------------------------
+
+ SET @Database_ = REPLACE(@Database_, ', ', ',');
+
+ WITH Databases1 (StartPosition, EndPosition, DatabaseItem) AS
+ (
+ SELECT 1 AS StartPosition,
+ ISNULL(NULLIF(CHARINDEX(',', @Database_, 1), 0), LEN(@Database_) + 1) AS EndPosition,
+ SUBSTRING(@Database_, 1, ISNULL(NULLIF(CHARINDEX(',', @Database_, 1), 0), LEN(@Database_) + 1) - 1) AS DatabaseItem
+ WHERE @Database_ IS NOT NULL
+ UNION ALL
+ SELECT CAST(EndPosition AS int) + 1 AS StartPosition,
+ ISNULL(NULLIF(CHARINDEX(',', @Database_, EndPosition + 1), 0), LEN(@Database_) + 1) AS EndPosition,
+ SUBSTRING(@Database_, EndPosition + 1, ISNULL(NULLIF(CHARINDEX(',', @Database_, EndPosition + 1), 0), LEN(@Database_) + 1) - EndPosition - 1) AS DatabaseItem
+ FROM Databases1
+ WHERE EndPosition < LEN(@Database_) + 1
+ ),
+ Databases2 (DatabaseItem, Selected) AS
+ (
+ SELECT CASE WHEN DatabaseItem LIKE '-%' THEN RIGHT(DatabaseItem,LEN(DatabaseItem) - 1) ELSE DatabaseItem END AS DatabaseItem,
+ CASE WHEN DatabaseItem LIKE '-%' THEN 0 ELSE 1 END AS Selected
+ FROM Databases1
+ ),
+ Databases3 (DatabaseItem, DatabaseType, Selected) AS
+ (
+ SELECT CASE WHEN DatabaseItem IN('ALL_DATABASES','SYSTEM_DATABASES','USER_DATABASES') THEN '%' ELSE DatabaseItem END AS DatabaseItem,
+ CASE WHEN DatabaseItem = 'SYSTEM_DATABASES' THEN 'S' WHEN DatabaseItem = 'USER_DATABASES' THEN 'U' ELSE NULL END AS DatabaseType,
+ Selected
+ FROM Databases2
+ ),
+ Databases4 (DatabaseName, DatabaseType, Selected) AS
+ (
+ SELECT CASE WHEN LEFT(DatabaseItem,1) = '[' AND RIGHT(DatabaseItem,1) = ']' THEN PARSENAME(DatabaseItem,1) ELSE DatabaseItem END AS DatabaseItem,
+ DatabaseType,
+ Selected
+ FROM Databases3
+ )
+ INSERT INTO @SelectedDatabases (DatabaseName, DatabaseType, Selected)
+ SELECT DatabaseName,
+ DatabaseType,
+ Selected
+ FROM Databases4
+ OPTION (MAXRECURSION 0)
+
+ INSERT INTO #tmpDatabases (DatabaseName, DatabaseNameFS, DatabaseType, Selected, Completed)
+ SELECT [name] AS DatabaseName,
+ REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([name],'\',''),'/',''),':',''),'*',''),'?',''),'"',''),'<',''),'>',''),'|',''),' ','') AS DatabaseNameFS,
+ CASE WHEN name IN('master','msdb','model') THEN 'S' ELSE 'U' END AS DatabaseType,
+ 0 AS Selected,
+ 0 AS Completed
+ FROM sys.databases
+ WHERE [name] <> 'tempdb'
+ AND source_database_id IS NULL
+ ORDER BY [name] ASC
+
+ UPDATE tmpDatabases
+ SET tmpDatabases.Selected = SelectedDatabases.Selected
+ FROM #tmpDatabases tmpDatabases
+ INNER JOIN @SelectedDatabases SelectedDatabases
+ ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]')
+ AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL)
+ WHERE SelectedDatabases.Selected = 1
+
+ UPDATE tmpDatabases
+ SET tmpDatabases.Selected = SelectedDatabases.Selected
+ FROM #tmpDatabases tmpDatabases
+ INNER JOIN @SelectedDatabases SelectedDatabases
+ ON tmpDatabases.DatabaseName LIKE REPLACE(SelectedDatabases.DatabaseName,'_','[_]')
+ AND (tmpDatabases.DatabaseType = SelectedDatabases.DatabaseType OR SelectedDatabases.DatabaseType IS NULL)
+ WHERE SelectedDatabases.Selected = 0
+
+ IF @Database_ IS NULL OR NOT EXISTS(SELECT * FROM @SelectedDatabases) OR EXISTS(SELECT * FROM @SelectedDatabases WHERE DatabaseName IS NULL OR DatabaseName = '')
+ BEGIN
+ SET @ErrorMessage = 'The value for the parameter @Databases is not supported.' + CHAR(13) + CHAR(10) + ' '
+ RAISERROR(@ErrorMessage,16,1) WITH NOWAIT
+ SET @Error = @@ERROR
+ END
+
+ END
+
+ --=======================================================================================================================================
+ -- Full Backups - Last before stop at for each database
SELECT
- a.database_name
- ,MAX(a.backup_start_date) backup_start_date
+ a.database_name
+ ,MAX(a.backup_finish_date) AS backup_finish_date
+ INTO #FullBackups
FROM msdb.dbo.backupset a
JOIN msdb.dbo.backupmediafamily b
- ON a.media_set_id = b.media_set_id
-
- LEFT OUTER JOIN #BackupFork c
- ON c.database_name = a.database_name
-
+ ON a.media_set_id = b.media_set_id
+
WHERE a.[type] = 'D'
- AND device_type IN (7,102,2)
+ AND (a.database_name IN (SELECT DatabaseName FROM #tmpDatabases WHERE Selected = 1) OR @Database_ IS NULL OR a.database_name = @Database_)
+ AND device_type IN (SELECT device_type FROM #DeviceTypes)
AND a.is_copy_only = 0
+ AND b.mirror = 0
AND a.backup_start_date <= @LogShippingStartTime_
-
- -- Recovery Forking
- AND ISNULL(c.fork_point_lsn,a.database_backup_lsn) >= a.database_backup_lsn
- AND ((c.fork_point_lsn IS NOT NULL AND a.database_backup_lsn < c.database_backup_lsn) OR c.fork_point_lsn IS NULL)
-
- GROUP BY a.database_name
-
-) x
- ON x.database_name = bs.database_name
- AND x.backup_start_date = bs.backup_start_date
-
-JOIN msdb.dbo.backupmediafamily mf
- ON mf.media_set_id = bs.media_set_id
- AND mf.family_sequence_number Between bs.first_family_number And bs.last_family_number
-
-WHERE bs.type = 'D'
-AND mf.physical_device_name NOT IN ('Nul', 'Nul:')
-
-UNION
-
---------------------------------------------------------------
--- Differential backup, most current immediately before @StopAt_
-SELECT
- bs.database_name
- ,d.[compatibility_level] AS current_compatibility_level
- ,bs.last_lsn
- ,d.[is_read_only] AS current_is_read_only
- ,d.[state_desc] AS current_state_desc
- ,d.[recovery_model_desc] current_recovery_model_desc
- ,bs.has_backup_checksums
- ,bs.backup_size AS backup_size
- ,'I' AS [type]
- ,bs.media_set_id AS backupmediasetid
- ,mf.family_sequence_number
- ,x.backup_start_date AS backupstartdate
- ,mf.physical_device_name
- ,bs.position
-FROM msdb.dbo.backupset bs
-
-INNER JOIN sys.databases d
- ON bs.database_name = d.name
-
+ GROUP BY a.database_name;
-INNER JOIN -- Last Full in recovery fork before STOPAT
-(
---------------------------------------------
- SELECT
- a.database_name
- ,MAX(a.backup_start_date) backup_start_date
- FROM msdb.dbo.backupset a
+ CREATE INDEX IDX_FullBackups ON #FullBackups(database_name);
+
+ --=======================================================================================================================================
+ -- Backup forking (suppress differential, log file select criteria)
+ SELECT bs.database_name, first_recovery_fork_guid, MIN(fork_point_lsn) AS fork_point_lsn
+ INTO #BackupFork
+ FROM msdb.dbo.backupset bs
JOIN msdb.dbo.backupmediafamily b
- ON a.media_set_id = b.media_set_id
-
- LEFT OUTER JOIN #BackupFork c
- ON c.database_name = a.database_name
-
- WHERE a.[type] = 'D'
- AND device_type IN (7,102,2)
- AND a.is_copy_only = 0
- AND a.backup_start_date <= @LogShippingStartTime_
-
- -- Recovery Forking
- AND ISNULL(c.fork_point_lsn,a.database_backup_lsn) >= a.database_backup_lsn
- AND ((c.fork_point_lsn IS NOT NULL AND a.database_backup_lsn < c.database_backup_lsn) OR c.fork_point_lsn IS NULL)
-
- GROUP BY a.database_name
---------------------------------------------
-) y
- ON y.database_name = bs.database_name
+ ON bs.media_set_id = b.media_set_id
+ JOIN #FullBackups fb
+ ON fb.database_name = bs.database_name
+ WHERE fb.backup_finish_date < bs.backup_start_date
+ AND bs.backup_finish_date < @LogShippingStartTime_
+ AND bs.first_recovery_fork_guid <> bs.last_recovery_fork_guid
+ GROUP BY bs.database_name, first_recovery_fork_guid;
+
+ -- Only one fork point between the selected full backup and the stop at time supported
+ SELECT database_name, COUNT(*) AS ForkPoints
+ INTO #ForkPointsCount
+ FROM #BackupFork rh
+ GROUP BY database_name;
+
+ --=======================================================================================================================================
+ --------------------------------------------------------------
+ -- CTE1 Full Backup
+ --------------------------------------------------------------
+ WITH CTE
+ (
+ database_name
+ ,current_compatibility_level
+ ,first_lsn
+ ,last_lsn
+ ,fork_point_lsn
+ ,current_is_read_only
+ ,current_state_desc
+ ,current_recovery_model_desc
+ ,has_backup_checksums
+ ,backup_size
+ ,[type]
+ ,backupmediasetid
+ ,family_sequence_number
+ ,backupfinishdate
+ ,physical_device_name
+ ,position
+ ,first_recovery_fork_guid
+ ,last_recovery_fork_guid
+ )
+ AS
+ (
+ --------------------------------------------------------------
+ -- Full backup - Most recent before @LogShippingStartTime_
+ SELECT
+ bs.database_name
+ ,d.[compatibility_level] AS current_compatibility_level
+ ,bs.first_lsn
+ ,bs.last_lsn
+ ,bs.fork_point_lsn
+ ,d.[is_read_only] AS current_is_read_only
+ ,d.[state_desc] AS current_state_desc
+ ,d.[recovery_model_desc] current_recovery_model_desc
+ ,bs.has_backup_checksums
+ ,bs.backup_size AS backup_size
+ ,'D' AS [type]
+ ,bs.media_set_id AS backupmediasetid
+ ,mf.family_sequence_number
+ ,bs.backup_finish_date AS backupfinishdate
+ ,mf.physical_device_name
+ ,bs.position
+ ,bs.first_recovery_fork_guid
+ ,bs.last_recovery_fork_guid
+
+ FROM msdb.dbo.backupset bs
+
+ INNER JOIN sys.databases d
+ ON bs.database_name = d.name
+
+ INNER JOIN #FullBackups fb
+ ON fb.database_name = bs.database_name
+ AND fb.backup_finish_date = bs.backup_finish_date
+
+ JOIN msdb.dbo.backupmediafamily mf
+ ON mf.media_set_id = bs.media_set_id
+ AND mf.family_sequence_number Between bs.first_family_number And bs.last_family_number
+
+ WHERE bs.type = 'D'
+ AND mf.mirror = 0
+ AND mf.physical_device_name NOT IN ('Nul', 'Nul:')
+ AND bs.is_copy_only = 0
+
+ UNION
+
+ --------------------------------------------------------------
+ -- Differential backup, most current immediately before @StopAt_, suppress if backup fork points exist
+ SELECT
+ bs.database_name
+ ,d.[compatibility_level] AS current_compatibility_level
+ ,bs.first_lsn
+ ,bs.last_lsn
+ ,bs.fork_point_lsn
+ ,d.[is_read_only] AS current_is_read_only
+ ,d.[state_desc] AS current_state_desc
+ ,d.[recovery_model_desc] current_recovery_model_desc
+ ,bs.has_backup_checksums
+ ,bs.backup_size AS backup_size
+ ,'I' AS [type]
+ ,bs.media_set_id AS backupmediasetid
+ ,mf.family_sequence_number
+ ,bs.backup_finish_date AS backupfinishdate
+ ,mf.physical_device_name
+ ,bs.position
+ ,bs.first_recovery_fork_guid
+ ,bs.last_recovery_fork_guid
+ FROM msdb.dbo.backupset bs
+
+ INNER JOIN sys.databases d
+ ON bs.database_name = d.name
+
+ INNER JOIN #FullBackups fb
+ ON fb.database_name = bs.database_name
+ AND fb.backup_finish_date < bs.backup_finish_date
+
+ INNER JOIN -- Last Diff before STOPAT / Suppress if backup forkpoints exist
+ (
+ SELECT
+ a.database_name
+ ,MAX(backup_start_date) backup_start_date
+ FROM msdb.dbo.backupset a
+ JOIN msdb.dbo.backupmediafamily b
+ ON a.media_set_id = b.media_set_id
+
+ WHERE a.[type] = 'I'
+ AND (a.database_name IN (SELECT DatabaseName FROM #tmpDatabases WHERE Selected = 1) OR @Database_ IS NULL OR a.database_name = @Database_)
+ AND device_type IN (SELECT device_type FROM #DeviceTypes)
+ AND a.is_copy_only = 0
+ AND b.mirror = 0
+ AND a.backup_start_date <= ISNULL(@StopAt_,GETDATE())
+ GROUP BY a.database_name
+ ) x
+ ON x.database_name = bs.database_name
+ AND x.backup_start_date = bs.backup_start_date
+
+ INNER JOIN msdb.dbo.backupmediafamily mf
+ ON mf.media_set_id = bs.media_set_id
+ AND mf.family_sequence_number Between bs.first_family_number And bs.last_family_number
+
+ LEFT OUTER JOIN #BackupFork bf
+ ON bf.database_name = bs.database_name
+
+ WHERE bs.type = 'I'
+ AND mf.physical_device_name NOT IN ('Nul', 'Nul:')
+ AND mf.mirror = 0
+ AND @StopAt_ = @LogShippingStartTime_
+ AND bf.fork_point_lsn IS NULL
+
+ UNION
+
+ --------------------------------------------------------------
+ -- Log file backups - after 1st full / before @StopAt_
+ SELECT
+ bs.database_name
+ ,d.[compatibility_level] AS current_compatibility_level
+ ,bs.first_lsn
+ ,bs.last_lsn
+ ,bs.fork_point_lsn
+ ,d.[is_read_only] AS current_is_read_only
+ ,d.[state_desc] AS current_state_desc
+ ,d.[recovery_model_desc] current_recovery_model_desc
+ ,bs.has_backup_checksums
+ ,bs.backup_size AS backup_size
+ ,'L' AS [type]
+ ,bs.media_set_id AS backupmediasetid
+ ,mf.family_sequence_number
+ ,bs.backup_finish_date as backupfinishdate
+ ,mf.physical_device_name
+ ,bs.position
+ ,bs.first_recovery_fork_guid
+ ,bs.last_recovery_fork_guid
+ FROM msdb.dbo.backupset bs
+
+ INNER JOIN sys.databases d
+ ON bs.database_name = d.name
+
+ INNER JOIN msdb.dbo.backupmediafamily mf
+ ON mf.media_set_id = bs.media_set_id
+ AND mf.family_sequence_number Between bs.first_family_number And bs.last_family_number
+
+ INNER JOIN #FullBackups fb
+ ON bs.database_name = fb.database_name
+
+ LEFT OUTER JOIN -- Select the first log file after STOPAT
+ (
+ SELECT DISTINCT x.database_name, CASE WHEN y.last_Log_After_StopAt IS NULL THEN CONVERT(datetime, '31 Dec 2050') ELSE y.last_Log_After_StopAt END AS last_Log_After_StopAt
+ FROM msdb.dbo.backupset x
+ LEFT JOIN
+ (
+ SELECT
+ database_name
+ ,MIN(backup_start_date) last_Log_After_StopAt
+ FROM msdb.dbo.backupset a
+ JOIN msdb.dbo.backupmediafamily b
+ ON a.media_set_id = b.media_set_id
+ WHERE a.[type] = 'L'
+ AND (a.database_name IN (SELECT DatabaseName FROM #tmpDatabases WHERE Selected = 1) OR @Database_ IS NULL OR a.database_name = @Database_)
+ AND b.mirror = 0
+ AND device_type IN (SELECT device_type FROM #DeviceTypes)
+ AND a.backup_start_date > ISNULL(@StopAt_,'1 Jan, 1900')
+ GROUP BY database_name
+ ) y
+ ON x.database_name = y.database_name
+ ) x
+ ON bs.database_name = x.database_name
+
+ LEFT OUTER JOIN #BackupFork bf
+ ON bf.database_name = bs.database_name
+ AND bf.first_recovery_fork_guid = bs.first_recovery_fork_guid
+
+ WHERE bs.backup_start_date <= x.last_Log_After_StopAt -- Include 1st log after stop at
+ AND bs.backup_start_date >= fb.backup_finish_date -- Full backup finish
+ --AND bs.backup_finish_date <= @StopAt_
+ AND (bs.database_name IN (SELECT DatabaseName FROM #tmpDatabases WHERE Selected = 1) OR @Database_ IS NULL OR bs.database_name = @Database_)
+ AND mf.physical_device_name NOT IN ('Nul', 'Nul:')
+ AND mf.mirror = 0
+ AND bs.type = 'L'
+ AND device_type IN (SELECT device_type FROM #DeviceTypes)
+ AND (bf.fork_point_lsn > bs.first_lsn OR bf.first_recovery_fork_guid IS NULL OR bs.first_recovery_fork_guid <> bs.last_recovery_fork_guid)
+ )
+
+ SELECT * INTO #CTE FROM CTE;
+ CREATE INDEX IDX_CTE ON #CTE(database_name);
+
+ --------------------------------------------------------------
+ -- CTE2 Optionally, striped backup file details
+ --------------------------------------------------------------
+ WITH Stripes
+ (
+ database_name,
+ backupmediasetid,
+ family_sequence_number,
+ last_lsn,
+ S2_pdn,
+ S3_pdn,
+ S4_pdn,
+ S5_pdn,
+ S6_pdn,
+ S7_pdn,
+ S8_pdn,
+ S9_pdn,
+ S10_pdn,
+ S11_pdn,
+ S12_pdn,
+ S13_pdn,
+ S14_pdn,
+ S15_pdn,
+ --************************
+ S16_pdn,
+ S17_pdn,
+ S18_pdn,
+ S19_pdn,
+ S20_pdn
+ --************************
+ )
+ AS
+ (
+ SELECT
+ Stripe1.database_name,
+ Stripe1.backupmediasetid,
+ Stripe1.family_sequence_number,
+ Stripe1.last_lsn,
+ Stripe2.physical_device_name AS S2_pdn,
+ Stripe3.physical_device_name AS S3_pdn,
+ Stripe4.physical_device_name AS S4_pdn,
+ Stripe5.physical_device_name AS S5_pdn,
+ Stripe6.physical_device_name AS S6_pdn,
+ Stripe7.physical_device_name AS S7_pdn,
+ Stripe8.physical_device_name AS S8_pdn,
+ Stripe9.physical_device_name AS S9_pdn,
+ Stripe10.physical_device_name AS S10_pdn,
+ Stripe11.physical_device_name AS S11_pdn,
+ Stripe12.physical_device_name AS S12_pdn,
+ Stripe13.physical_device_name AS S13_pdn,
+ Stripe14.physical_device_name AS S14_pdn,
+ Stripe15.physical_device_name AS S15_pdn
+ --************************
+ ,Stripe16.physical_device_name AS S16_pdn
+ ,Stripe17.physical_device_name AS S17_pdn
+ ,Stripe18.physical_device_name AS S18_pdn
+ ,Stripe19.physical_device_name AS S19_pdn
+ ,Stripe20.physical_device_name AS S20_pdn
+ --************************
+ FROM #CTE AS Stripe1
+
+ LEFT OUTER JOIN #CTE AS Stripe2
+ ON Stripe2.database_name = Stripe1.database_name
+ AND Stripe2.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe2.family_sequence_number = 2
+
+ LEFT OUTER JOIN #CTE AS Stripe3
+ ON Stripe3.database_name = Stripe1.database_name
+ AND Stripe3.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe3.family_sequence_number = 3
+
+ LEFT OUTER JOIN #CTE AS Stripe4
+ ON Stripe4.database_name = Stripe1.database_name
+ AND Stripe4.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe4.family_sequence_number = 4
+
+ LEFT OUTER JOIN #CTE AS Stripe5
+ ON Stripe5.database_name = Stripe1.database_name
+ AND Stripe5.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe5.family_sequence_number = 5
+
+ LEFT OUTER JOIN #CTE AS Stripe6
+ ON Stripe6.database_name = Stripe1.database_name
+ AND Stripe6.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe6.family_sequence_number = 6
+
+ LEFT OUTER JOIN #CTE AS Stripe7
+ ON Stripe7.database_name = Stripe1.database_name
+ AND Stripe7.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe7.family_sequence_number = 7
+
+ LEFT OUTER JOIN #CTE AS Stripe8
+ ON Stripe8.database_name = Stripe1.database_name
+ AND Stripe8.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe8.family_sequence_number = 8
+
+ LEFT OUTER JOIN #CTE AS Stripe9
+ ON Stripe9.database_name = Stripe1.database_name
+ AND Stripe9.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe9.family_sequence_number = 9
+
+ LEFT OUTER JOIN #CTE AS Stripe10
+ ON Stripe10.database_name = Stripe1.database_name
+ AND Stripe10.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe10.family_sequence_number = 10
+
+ LEFT OUTER JOIN #CTE AS Stripe11
+ ON Stripe11.database_name = Stripe1.database_name
+ AND Stripe11.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe11.family_sequence_number = 11
+
+ LEFT OUTER JOIN #CTE AS Stripe12
+ ON Stripe12.database_name = Stripe1.database_name
+ AND Stripe12.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe12.family_sequence_number = 12
+
+ LEFT OUTER JOIN #CTE AS Stripe13
+ ON Stripe13.database_name = Stripe1.database_name
+ AND Stripe13.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe13.family_sequence_number = 13
+
+ LEFT OUTER JOIN #CTE AS Stripe14
+ ON Stripe14.database_name = Stripe1.database_name
+ AND Stripe14.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe14.family_sequence_number = 14
+
+ LEFT OUTER JOIN #CTE AS Stripe15
+ ON Stripe15.database_name = Stripe1.database_name
+ AND Stripe15.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe15.family_sequence_number = 15
-INNER JOIN -- Last Diff before STOPAT
-(
+ --************************
+ LEFT OUTER JOIN #CTE AS Stripe16
+ ON Stripe16.database_name = Stripe1.database_name
+ AND Stripe16.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe16.family_sequence_number = 16
+
+ LEFT OUTER JOIN #CTE AS Stripe17
+ ON Stripe17.database_name = Stripe1.database_name
+ AND Stripe17.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe17.family_sequence_number = 17
+
+ LEFT OUTER JOIN #CTE AS Stripe18
+ ON Stripe18.database_name = Stripe1.database_name
+ AND Stripe18.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe18.family_sequence_number = 18
+
+ LEFT OUTER JOIN #CTE AS Stripe19
+ ON Stripe19.database_name = Stripe1.database_name
+ AND Stripe19.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe19.family_sequence_number = 19
+
+ LEFT OUTER JOIN #CTE AS Stripe20
+ ON Stripe20.database_name = Stripe1.database_name
+ AND Stripe20.backupmediasetid = Stripe1.backupmediasetid
+ AND Stripe20.family_sequence_number = 20
+-- ************************
+
+ )
+
+ SELECT * INTO #Stripes FROM Stripes;
+
+ CREATE INDEX IDX_Stripes ON #Stripes(database_name);
+
+ --------------------------------------------------------------
+ -- Results, T-SQL RESTORE commands, below are based on CTE's above
+ --------------------------------------------------------------
+
SELECT
- a.database_name
- ,MAX(backup_start_date) backup_start_date
- FROM msdb.dbo.backupset a
- JOIN msdb.dbo.backupmediafamily b
- ON a.media_set_id = b.media_set_id
-
- LEFT OUTER JOIN #BackupFork c
- ON c.database_name = a.database_name
-
- WHERE a.[type] = 'I'
- AND device_type IN (7,102,2)
- AND a.is_copy_only = 0
- AND a.backup_start_date <= ISNULL(@StopAt_,GETDATE())
- AND a.database_backup_lsn = ISNULL(c.database_backup_lsn,a.database_backup_lsn )
- AND a.last_lsn <= ISNULL(c.fork_point_lsn, a.last_lsn)
- GROUP BY a.database_name
-
-) x
- ON x.database_name = bs.database_name
- AND x.backup_start_date = bs.backup_start_date
-
-
-INNER JOIN msdb.dbo.backupmediafamily mf
- ON mf.media_set_id = bs.media_set_id
- AND mf.family_sequence_number Between bs.first_family_number And bs.last_family_number
-
-WHERE bs.type = 'I'
-AND mf.physical_device_name NOT IN ('Nul', 'Nul:')
-AND x.backup_start_date > y.backup_start_date
-AND @StopAt_ = @LogShippingStartTime_
-
-UNION
-
---------------------------------------------------------------
--- Log file backups - after 1st full / before @StopAt_
-SELECT
- bs.database_name
- ,d.[compatibility_level] AS current_compatibility_level
- ,bs.last_lsn
- ,d.[is_read_only] AS current_is_read_only
- ,d.[state_desc] AS current_state_desc
- ,d.[recovery_model_desc] current_recovery_model_desc
- ,bs.has_backup_checksums
- ,bs.backup_size AS backup_size
- ,'L' AS [type]
- ,bs.media_set_id AS backupmediasetid
- ,mf.family_sequence_number
- ,bs.backup_start_date as backupstartdate
- ,mf.physical_device_name
- ,bs.position
-
-FROM msdb.dbo.backupset bs
-
-INNER JOIN sys.databases d
- ON bs.database_name = d.name
-
-INNER JOIN msdb.dbo.backupmediafamily mf
- ON mf.media_set_id = bs.media_set_id
- AND mf.family_sequence_number Between bs.first_family_number And bs.last_family_number
-
-INNER JOIN -- After the selected full backup
-(
+ CASE WHEN @RestoreScriptReplaceThis_ IS NOT NULL AND @RestoreScriptWithThis_ IS NOT NULL THEN REPLACE(a.Command, @RestoreScriptReplaceThis_, @RestoreScriptWithThis_) ELSE a.Command END AS TSQL, --Allow substitution of a path
+ CONVERT(nvarchar(30), a.backupfinishdate, 126)
+ AS BackupDate,
+ a.BackupDevice,
+ a.first_lsn,
+ a.last_lsn,
+ a.fork_point_lsn,
+ a.first_recovery_fork_guid,
+ a.last_recovery_fork_guid,
+ a.database_name ,
+ ROW_NUMBER() OVER(ORDER BY database_name, Sequence, a.backupfinishdate) AS SortSequence
+ INTO #RestoreGeneResults
+ FROM
+ (
+
+ --------------------------------------------------------------
+ -- Most recent full backup
+ --------------------------------------------------------------
+
SELECT
- a.database_name
- ,MAX(a.backup_start_date) backup_start_date
- FROM msdb.dbo.backupset a
- JOIN msdb.dbo.backupmediafamily b
- ON a.media_set_id = b.media_set_id
-
- LEFT OUTER JOIN #BackupFork c
- ON c.database_name = a.database_name
-
- WHERE a.[type] = 'D'
- AND device_type IN (7,102,2)
- AND a.is_copy_only = 0
- AND a.backup_start_date <= @LogShippingStartTime_
-
- -- Recovery Forking
- AND ISNULL(c.fork_point_lsn,a.database_backup_lsn) >= a.database_backup_lsn
- AND ((c.fork_point_lsn IS NOT NULL AND a.database_backup_lsn < c.database_backup_lsn) OR c.fork_point_lsn IS NULL)
-
- GROUP BY a.database_name
-) y
- ON bs.database_name = y.database_name
-
-LEFT OUTER JOIN #BackupFork r
- ON r.database_name = bs.database_name
+
+ 'RESTORE DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) + CHAR(13) +
+
+ CASE WHEN #CTE.physical_device_name LIKE 'http%' THEN ' FROM URL = N' ELSE ' FROM DISK = N' END + '''' + --Support restore from blob storage in Azure
+
+ CASE ISNULL(@FromFileFullUNC_,'Actual')
+ WHEN 'Actual' THEN #CTE.physical_device_name
+ ELSE @FromFileFullUNC_ + SUBSTRING(#CTE.physical_device_name,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 2,CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 1)
+ END + '''' + SPACE(1) + CHAR(13) +
+
+ -- Striped backup files
+ CASE ISNULL(#Stripes.S2_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S2_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S2_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S2_pdn,LEN(#Stripes.S2_pdn) - CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S3_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S3_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S3_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S3_pdn,LEN(#Stripes.S3_pdn) - CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S4_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S4_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S4_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S4_pdn,LEN(#Stripes.S4_pdn) - CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S5_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S5_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S5_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S5_pdn,LEN(#Stripes.S5_pdn) - CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S6_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S6_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S6_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S6_pdn,LEN(#Stripes.S6_pdn) - CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S7_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S7_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S7_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S7_pdn,LEN(#Stripes.S7_pdn) - CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S8_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S8_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S8_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S8_pdn,LEN(#Stripes.S8_pdn) - CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S9_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S9_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S9_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S9_pdn,LEN(#Stripes.S9_pdn) - CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S10_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S10_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S10_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S10_pdn,LEN(#Stripes.S10_pdn) - CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S11_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S11_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S11_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S11_pdn,LEN(#Stripes.S11_pdn) - CHARINDEX('\',REVERSE(#Stripes.S11_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S11_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S12_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S12_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S12_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S12_pdn,LEN(#Stripes.S12_pdn) - CHARINDEX('\',REVERSE(#Stripes.S12_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S12_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S13_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S13_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S13_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S13_pdn,LEN(#Stripes.S13_pdn) - CHARINDEX('\',REVERSE(#Stripes.S13_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S13_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S14_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S14_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S14_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S14_pdn,LEN(#Stripes.S14_pdn) - CHARINDEX('\',REVERSE(#Stripes.S14_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S14_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S15_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S15_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S15_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S15_pdn,LEN(#Stripes.S15_pdn) - CHARINDEX('\',REVERSE(#Stripes.S15_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S15_pdn),1) + 1) END + ''''
+ END +
+
+ --*******
+
+ CASE ISNULL(#Stripes.S16_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S16_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S16_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S16_pdn,LEN(#Stripes.S16_pdn) - CHARINDEX('\',REVERSE(#Stripes.S16_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S16_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S17_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S17_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S17_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S17_pdn,LEN(#Stripes.S17_pdn) - CHARINDEX('\',REVERSE(#Stripes.S17_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S17_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S18_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S18_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S18_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S18_pdn,LEN(#Stripes.S18_pdn) - CHARINDEX('\',REVERSE(#Stripes.S18_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S18_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S19_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S19_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S19_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S19_pdn,LEN(#Stripes.S19_pdn) - CHARINDEX('\',REVERSE(#Stripes.S19_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S19_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S20_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S20_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S20_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S20_pdn,LEN(#Stripes.S20_pdn) - CHARINDEX('\',REVERSE(#Stripes.S20_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S20_pdn),1) + 1) END + ''''
+ END +
-LEFT OUTER JOIN -- Select the first log file after STOPAT
-(
- SELECT DISTINCT x.database_name, database_backup_lsn,
- CASE WHEN y.last_Log_After_StopAt IS NULL THEN CONVERT(datetime, '31 Dec 2050') ELSE y.last_Log_After_StopAt END AS last_Log_After_StopAt
- FROM msdb.dbo.backupset x
- LEFT JOIN
+ --******
+
+ ' WITH ' +
+
+ CASE WHEN #CTE.physical_device_name LIKE 'http%' AND @BlobCredential_ IS NOT NULL THEN ' CREDENTIAL = ''' + @BlobCredential_ + ''', ' ELSE '' END +
+
+ CASE ISNULL(@WithReplace_,0) WHEN 1 THEN 'REPLACE, ' ELSE '' END + 'FILE = ' + CAST(#CTE.position AS VARCHAR(5)) + ',' +
+ CASE #CTE.has_backup_checksums WHEN 1 THEN 'CHECKSUM, ' ELSE ' ' END +
+
+ CASE @StandbyMode_ WHEN 0 THEN 'NORECOVERY,' ELSE 'STANDBY =N' + '''' + ISNULL(@FromFileFullUNC_,SUBSTRING(#CTE.physical_device_name,1,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name)))) + '\' + d.name + '_' + REPLACE(REPLACE(SUBSTRING(CONVERT(VARCHAR(24),GETDATE(),127),12,12),':',''),'.','') + '_ROLLBACK_UNDO.bak ' + '''' + ',' END + SPACE(1) +
+
+ 'STATS=10' + SPACE(1) +
+
+ CASE @SuppressWithMove_ WHEN 0 THEN
+
+ ', MOVE N' + '''' + x.LogicalName + '''' + ' TO ' +
+ '''' +
+ CASE ISNULL(@WithMoveDataFiles_,'Actual')
+ WHEN 'Actual' THEN x.PhysicalName
+ ELSE @WithMoveDataFiles_ + SUBSTRING(x.PhysicalName,LEN(x.PhysicalName) - CHARINDEX('\',REVERSE(x.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(x.PhysicalName),1) + 1)
+ END + '''' + SPACE(1) +
+
+ ', MOVE N' + '''' + y.LogicalName + '''' + ' TO ' +
+ '''' +
+ CASE ISNULL(@WithMoveLogFile_ ,'Actual')
+ WHEN 'Actual' THEN y.PhysicalName
+ ELSE @WithMoveLogFile_ + SUBSTRING(y.PhysicalName,LEN(y.PhysicalName) - CHARINDEX('\',REVERSE(y.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(y.PhysicalName),1) + 1)
+ END + ''''
+ ELSE ' '
+ END
+
+ AS Command,
+ 1 AS Sequence,
+ d.name AS database_name,
+ #CTE.physical_device_name AS BackupDevice,
+ #CTE.backupfinishdate,
+ #CTE.backup_size,
+ #CTE.first_lsn,
+ #CTE.last_lsn,
+ #CTE.fork_point_lsn,
+ #CTE.first_recovery_fork_guid,
+ #CTE.last_recovery_fork_guid
+
+ FROM sys.databases d
+
+ INNER JOIN
(
SELECT
- database_name
- ,MIN(backup_start_date) last_Log_After_StopAt
- FROM msdb.dbo.backupset a
- JOIN msdb.dbo.backupmediafamily b
- ON a.media_set_id = b.media_set_id
- WHERE a.[type] = 'L'
- AND device_type IN (7,102,2)
- AND a.backup_start_date > ISNULL(@StopAt_,'1 Jan, 1900')
- GROUP BY database_name
+ DB_NAME(mf.database_id) AS name
+ ,CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN mf.Physical_Name ELSE SUBSTRING(mf.Physical_Name,1,CASE CHARINDEX('.',mf.Physical_Name) WHEN 0 THEN LEN(mf.Physical_Name) ELSE CHARINDEX('.',mf.Physical_Name) - 1 END) + '_' + @TargetDatabase_ + '.mdf' END AS PhysicalName
+ ,mf.Name AS LogicalName
+ FROM sys.master_files mf
+ WHERE type_desc = 'ROWS'
+ AND mf.file_id = 1
+ ) x
+ ON d.name = x.name
+
+ INNER JOIN
+ (
+ SELECT
+ DB_NAME(mf.database_id) AS name, type_desc
+ ,CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN mf.Physical_Name ELSE SUBSTRING(mf.Physical_Name,1,CASE CHARINDEX('.',mf.Physical_Name) WHEN 0 THEN LEN(mf.Physical_Name) ELSE CHARINDEX('.',mf.Physical_Name) - 1 END) + '_' + @TargetDatabase_ + '.ldf' END AS PhysicalName
+ ,mf.Name AS LogicalName
+ FROM sys.master_files mf
+
+ -- Fix for multiple log files/with moves
+ INNER JOIN
+ (
+ SELECT database_id, MIN(file_id) AS logfile_id
+ FROM sys.master_files
+ WHERE type_desc = 'LOG'
+ GROUP BY database_id
+ ) logfileid
+ on logfileid.database_id = mf.database_id
+ AND logfileid.logfile_id = mf.file_id
+ WHERE type_desc = 'LOG'
) y
- ON x.database_name = y.database_name
-) x
- ON bs.database_name = x.database_name
- AND bs.database_backup_lsn = ISNULL(r.database_backup_lsn,bs.database_backup_lsn)
-
-WHERE bs.backup_start_date <= x.last_Log_After_StopAt -- Include 1st log after stop at
-AND bs.backup_start_date >= y.backup_start_date -- After last full backup start date
-AND bs.database_backup_lsn = ISNULL(r.database_backup_lsn,bs.database_backup_lsn) -- Recovery Fork
-AND NOT (bs.first_lsn < ISNULL(r.fork_point_lsn,'99999999999999999') AND bs.last_lsn > ISNULL(r.fork_point_lsn,'00000000000000000') AND bs.last_lsn <> ISNULL(r.last_lsn,bs.last_lsn))
-
+ ON d.name = y.name
+
+ LEFT OUTER JOIN #CTE
+ ON #CTE.database_name = d.name
+ AND #CTE.family_sequence_number = 1
+
+ INNER JOIN #Stripes
+ ON #Stripes.database_name = d.name
+ AND #Stripes.backupmediasetid = #CTE.backupmediasetid
+ AND #Stripes.last_lsn = #CTE.last_lsn
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'D'
+ AND #CTE.family_sequence_number = 1
+ AND ISNULL(fpc.ForkPoints,0) < 2
+ AND @ExcludeDiffAndLogBackups_ IN (0,1,2,3)
+
+ --------------------------------------------------------------
+ -- Most recent differential backup
+ --------------------------------------------------------------
+ UNION
+
+ SELECT
+
+ 'RESTORE DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) + CHAR(13) +
+ CASE WHEN #CTE.physical_device_name LIKE 'http%' THEN ' FROM URL = N' ELSE ' FROM DISK = N' END + '''' +
+
+ CASE ISNULL(@FromFileDiffUNC_,'Actual')
+ WHEN 'Actual' THEN #CTE.physical_device_name
+ ELSE @FromFileDiffUNC_ + SUBSTRING(#CTE.physical_device_name,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 2,CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 1)
+ END + '''' + SPACE(1) + CHAR(13) +
+
+ -- Striped backup files
+ CASE ISNULL(#Stripes.S2_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S2_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S2_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S2_pdn,LEN(#Stripes.S2_pdn) - CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S3_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S3_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S3_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S3_pdn,LEN(#Stripes.S3_pdn) - CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S4_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S4_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S4_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S4_pdn,LEN(#Stripes.S4_pdn) - CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S5_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S5_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S5_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S5_pdn,LEN(#Stripes.S5_pdn) - CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S6_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S6_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S6_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S6_pdn,LEN(#Stripes.S6_pdn) - CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S7_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S7_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S7_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S7_pdn,LEN(#Stripes.S7_pdn) - CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S8_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S8_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S8_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S8_pdn,LEN(#Stripes.S8_pdn) - CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S9_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S9_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S9_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S9_pdn,LEN(#Stripes.S9_pdn) - CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S10_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S10_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S10_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S10_pdn,LEN(#Stripes.S10_pdn) - CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S11_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S11_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S11_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S11_pdn,LEN(#Stripes.S11_pdn) - CHARINDEX('\',REVERSE(#Stripes.S11_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S11_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S12_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S12_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S12_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S12_pdn,LEN(#Stripes.S12_pdn) - CHARINDEX('\',REVERSE(#Stripes.S12_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S12_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S13_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S13_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S13_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S13_pdn,LEN(#Stripes.S13_pdn) - CHARINDEX('\',REVERSE(#Stripes.S13_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S13_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S14_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S14_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S14_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S14_pdn,LEN(#Stripes.S14_pdn) - CHARINDEX('\',REVERSE(#Stripes.S14_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S14_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S15_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S15_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S15_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S15_pdn,LEN(#Stripes.S15_pdn) - CHARINDEX('\',REVERSE(#Stripes.S15_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S15_pdn),1) + 1) END + ''''
+ END +
+
+ --**************************
+
+ CASE ISNULL(#Stripes.S16_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S16_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S16_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S16_pdn,LEN(#Stripes.S16_pdn) - CHARINDEX('\',REVERSE(#Stripes.S16_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S16_pdn),1) + 1) END + ''''
+ END +
+
+
+ CASE ISNULL(#Stripes.S17_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S17_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S17_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S17_pdn,LEN(#Stripes.S17_pdn) - CHARINDEX('\',REVERSE(#Stripes.S17_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S17_pdn),1) + 1) END + ''''
+ END +
+
+
+ CASE ISNULL(#Stripes.S18_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S18_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S18_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S18_pdn,LEN(#Stripes.S18_pdn) - CHARINDEX('\',REVERSE(#Stripes.S18_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S18_pdn),1) + 1) END + ''''
+ END +
+
+
+ CASE ISNULL(#Stripes.S19_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S19_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S19_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S19_pdn,LEN(#Stripes.S19_pdn) - CHARINDEX('\',REVERSE(#Stripes.S19_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S19_pdn),1) + 1) END + ''''
+ END +
+
+
+ CASE ISNULL(#Stripes.S20_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S20_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S20_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S20_pdn,LEN(#Stripes.S20_pdn) - CHARINDEX('\',REVERSE(#Stripes.S20_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S20_pdn),1) + 1) END + ''''
+ END +
+
+ --**************************
+
+ ' WITH FILE = ' + CAST(#CTE.position AS VARCHAR(5)) + ',' +
+ CASE #CTE.has_backup_checksums WHEN 1 THEN 'CHECKSUM, ' ELSE ' ' END +
+
+ CASE WHEN #CTE.physical_device_name LIKE 'http%' AND @BlobCredential_ IS NOT NULL THEN ' CREDENTIAL = ''' + @BlobCredential_ + ''', ' ELSE '' END +
+
+ CASE @StandbyMode_ WHEN 0 THEN 'NORECOVERY,' ELSE 'STANDBY =N' + '''' + ISNULL(@FromFileFullUNC_,SUBSTRING(#CTE.physical_device_name,1,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name)))) + '\' + d.name + '_' + REPLACE(REPLACE(SUBSTRING(CONVERT(VARCHAR(24),GETDATE(),127),12,12),':',''),'.','') + '_ROLLBACK_UNDO.bak ' + '''' + ',' END + SPACE(1) +
+
+ 'STATS=10' + SPACE(1)
+
+ AS Command,
+ 750000 AS Sequence,
+ d.name AS database_name,
+ #CTE.physical_device_name AS BackupDevice,
+ #CTE.backupfinishdate,
+ #CTE.backup_size,
+ #CTE.first_lsn,
+ #CTE.last_lsn,
+ #CTE.fork_point_lsn,
+ #CTE.first_recovery_fork_guid,
+ #CTE.last_recovery_fork_guid
+
+
+ FROM sys.databases d
+
+ INNER JOIN #CTE
+ ON #CTE.database_name = d.name
+ AND #CTE.family_sequence_number = 1
+
+ LEFT OUTER JOIN #Stripes
+ ON #Stripes.database_name = d.name
+ AND #Stripes.backupmediasetid = #CTE.backupmediasetid
+ AND #Stripes.last_lsn = #CTE.last_lsn
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'I'
+ AND #CTE.family_sequence_number = 1
+ AND @ExcludeDiffAndLogBackups_ IN (0,2,4)
+ AND ISNULL(fpc.ForkPoints,0) < 2
+
+ --------------------------------------------------------------
+ UNION -- Log backups taken since most recent full or diff
+ --------------------------------------------------------------
+
+ SELECT
+
+ 'RESTORE LOG [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) + CHAR(13) +
+
+ CASE WHEN #CTE.physical_device_name LIKE 'http%' THEN ' FROM URL = N' ELSE ' FROM DISK = N' END + '''' +
+
+ CASE ISNULL(@FromFileLogUNC_,'Actual')
+ WHEN 'Actual' THEN #CTE.physical_device_name
+ ELSE @FromFileLogUNC_ + SUBSTRING(#CTE.physical_device_name,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 2,CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 1)
+ END + '''' + CHAR(13) +
+
+ -- Striped backup files
+ CASE ISNULL(#Stripes.S2_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S2_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S2_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S2_pdn,LEN(#Stripes.S2_pdn) - CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S3_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S3_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S3_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S3_pdn,LEN(#Stripes.S3_pdn) - CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S4_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S4_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S4_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S4_pdn,LEN(#Stripes.S4_pdn) - CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S5_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S5_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S5_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S5_pdn,LEN(#Stripes.S5_pdn) - CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S6_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S6_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S6_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S6_pdn,LEN(#Stripes.S6_pdn) - CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S7_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S7_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S7_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S7_pdn,LEN(#Stripes.S7_pdn) - CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S8_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S8_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S8_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S8_pdn,LEN(#Stripes.S8_pdn) - CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S9_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S9_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S9_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S9_pdn,LEN(#Stripes.S9_pdn) - CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S10_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S10_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S10_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S10_pdn,LEN(#Stripes.S10_pdn) - CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S11_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S11_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S11_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S11_pdn,LEN(#Stripes.S10_pdn) - CHARINDEX('\',REVERSE(#Stripes.S11_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S11_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S12_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S12_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S12_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S12_pdn,LEN(#Stripes.S12_pdn) - CHARINDEX('\',REVERSE(#Stripes.S12_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S12_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S13_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S13_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S13_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S13_pdn,LEN(#Stripes.S13_pdn) - CHARINDEX('\',REVERSE(#Stripes.S13_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S13_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S14_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S14_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S14_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S14_pdn,LEN(#Stripes.S14_pdn) - CHARINDEX('\',REVERSE(#Stripes.S14_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S14_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S15_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S15_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S15_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S15_pdn,LEN(#Stripes.S15_pdn) - CHARINDEX('\',REVERSE(#Stripes.S15_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S15_pdn),1) + 1) END + ''''
+ END +
-AND mf.physical_device_name NOT IN ('Nul', 'Nul:')
-AND bs.type = 'L'
-AND device_type IN (7,102,2)
-)
-
-SELECT * INTO #CTE FROM CTE;
-CREATE INDEX IDX_CTE ON #CTE(database_name);
-
---------------------------------------------------------------
--- CTE2 Optionally, striped backup file details
---------------------------------------------------------------
-WITH Stripes
-(
- database_name,
- backupmediasetid,
- family_sequence_number,
- last_lsn,
- S2_pdn,
- S3_pdn,
- S4_pdn,
- S5_pdn,
- S6_pdn,
- S7_pdn,
- S8_pdn,
- S9_pdn,
- S10_pdn
-)
-AS
-(
-SELECT
- Stripe1.database_name,
- Stripe1.backupmediasetid,
- Stripe1.family_sequence_number,
- Stripe1.last_lsn,
- Stripe2.physical_device_name AS S2_pdn,
- Stripe3.physical_device_name AS S3_pdn,
- Stripe4.physical_device_name AS S4_pdn,
- Stripe5.physical_device_name AS S5_pdn,
- Stripe6.physical_device_name AS S6_pdn,
- Stripe7.physical_device_name AS S7_pdn,
- Stripe8.physical_device_name AS S8_pdn,
- Stripe9.physical_device_name AS S9_pdn,
- Stripe10.physical_device_name AS S10_pdn
-FROM #CTE AS Stripe1
-
-LEFT OUTER JOIN #CTE AS Stripe2
- ON Stripe2.database_name = Stripe1.database_name
- AND Stripe2.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe2.family_sequence_number = 2
-
-LEFT OUTER JOIN #CTE AS Stripe3
- ON Stripe3.database_name = Stripe1.database_name
- AND Stripe3.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe3.family_sequence_number = 3
-
-LEFT OUTER JOIN #CTE AS Stripe4
- ON Stripe4.database_name = Stripe1.database_name
- AND Stripe4.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe4.family_sequence_number = 4
-
-LEFT OUTER JOIN #CTE AS Stripe5
- ON Stripe5.database_name = Stripe1.database_name
- AND Stripe5.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe5.family_sequence_number = 5
-
-LEFT OUTER JOIN #CTE AS Stripe6
- ON Stripe6.database_name = Stripe1.database_name
- AND Stripe6.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe6.family_sequence_number = 6
-
-LEFT OUTER JOIN #CTE AS Stripe7
- ON Stripe7.database_name = Stripe1.database_name
- AND Stripe7.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe7.family_sequence_number = 7
-
-LEFT OUTER JOIN #CTE AS Stripe8
- ON Stripe8.database_name = Stripe1.database_name
- AND Stripe8.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe8.family_sequence_number = 8
-
-LEFT OUTER JOIN #CTE AS Stripe9
- ON Stripe9.database_name = Stripe1.database_name
- AND Stripe9.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe9.family_sequence_number = 9
-
-LEFT OUTER JOIN #CTE AS Stripe10
- ON Stripe10.database_name = Stripe1.database_name
- AND Stripe10.backupmediasetid = Stripe1.backupmediasetid
- AND Stripe10.family_sequence_number = 10
-)
-
-SELECT * INTO #Stripes FROM Stripes;
+ --***************
+ CASE ISNULL(#Stripes.S16_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S16_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S16_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S16_pdn,LEN(#Stripes.S16_pdn) - CHARINDEX('\',REVERSE(#Stripes.S16_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S16_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S17_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S17_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S17_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S17_pdn,LEN(#Stripes.S17_pdn) - CHARINDEX('\',REVERSE(#Stripes.S17_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S17_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S18_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S18_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S18_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S18_pdn,LEN(#Stripes.S18_pdn) - CHARINDEX('\',REVERSE(#Stripes.S18_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S18_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S19_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S19_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S19_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S19_pdn,LEN(#Stripes.S19_pdn) - CHARINDEX('\',REVERSE(#Stripes.S19_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S19_pdn),1) + 1) END + ''''
+ END +
+
+ CASE ISNULL(#Stripes.S20_pdn,'')
+ WHEN '' THEN ''
+ ELSE ',' + CASE WHEN #Stripes.S20_pdn LIKE 'http%' THEN ' URL = N' ELSE ' DISK = N' END + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S20_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S20_pdn,LEN(#Stripes.S20_pdn) - CHARINDEX('\',REVERSE(#Stripes.S20_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S20_pdn),1) + 1) END + ''''
+ END +
-CREATE INDEX IDX_Stripes ON #Stripes(database_name);
-
---------------------------------------------------------------
--- Results, T-SQL RESTORE commands, below are based on CTE's above
---------------------------------------------------------------
-
-SELECT
- a.Command AS TSQL,
- CONVERT(nvarchar(30), a.backupstartdate, 126)
- AS BackupDate,
- a.BackupDevice,
- a.last_lsn,
- a.database_name ,
- --ROW_NUMBER() OVER(ORDER BY database_name, Sequence, last_lsn) AS SortSequence
- ROW_NUMBER() OVER(ORDER BY database_name, Sequence, a.backupstartdate) AS SortSequence
-FROM
-(
-
---------------------------------------------------------------
--- Most recent full backup
---------------------------------------------------------------
-
-SELECT
- ';RESTORE DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) +
- 'FROM DISK = N' + '''' +
- CASE ISNULL(@FromFileFullUNC_,'Actual')
- WHEN 'Actual' THEN #CTE.physical_device_name
- ELSE @FromFileFullUNC_ + SUBSTRING(#CTE.physical_device_name,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 2,CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 1)
- END + '''' + SPACE(1) +
-
- -- Striped backup files
- CASE ISNULL(#Stripes.S2_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S2_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S2_pdn,LEN(#Stripes.S2_pdn) - CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S3_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S3_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S3_pdn,LEN(#Stripes.S3_pdn) - CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S4_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S4_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S4_pdn,LEN(#Stripes.S4_pdn) - CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S5_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S5_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S5_pdn,LEN(#Stripes.S5_pdn) - CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S6_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S6_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S6_pdn,LEN(#Stripes.S6_pdn) - CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S7_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S7_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S7_pdn,LEN(#Stripes.S7_pdn) - CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S8_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S8_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S8_pdn,LEN(#Stripes.S8_pdn) - CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S9_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S9_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S9_pdn,LEN(#Stripes.S9_pdn) - CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S10_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileFullUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S10_pdn ELSE @FromFileFullUNC_ + SUBSTRING(#Stripes.S10_pdn,LEN(#Stripes.S10_pdn) - CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 1) END + ''''
- END +
-
- ' WITH ' + CASE ISNULL(@WithReplace_,0) WHEN 1 THEN 'REPLACE, ' ELSE '' END + 'FILE = ' + CAST(#CTE.position AS VARCHAR(5)) + ',' +
- CASE #CTE.has_backup_checksums WHEN 1 THEN 'CHECKSUM, ' ELSE ' ' END +
-
- CASE @StandbyMode_ WHEN 0 THEN 'NORECOVERY,' ELSE 'STANDBY =N' + '''' + ISNULL(@FromFileFullUNC_,SUBSTRING(#CTE.physical_device_name,1,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name)))) + '\' + d.name + '_ROLLBACK_UNDO.bak ' + '''' + ',' END + SPACE(1) +
-
- 'STATS=10,' + SPACE(1) +
- 'MOVE N' + '''' + x.LogicalName + '''' + ' TO ' +
- '''' +
- CASE ISNULL(@WithMoveDataFiles_,'Actual')
- WHEN 'Actual' THEN x.PhysicalName
- ELSE @WithMoveDataFiles_ + SUBSTRING(x.PhysicalName,LEN(x.PhysicalName) - CHARINDEX('\',REVERSE(x.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(x.PhysicalName),1) + 1)
- END + '''' + ',' + SPACE(1) +
-
- 'MOVE N' + '''' + y.LogicalName + '''' + ' TO ' +
- '''' +
- CASE ISNULL(@WithMoveLogFile_ ,'Actual')
- WHEN 'Actual' THEN y.PhysicalName
- ELSE @WithMoveLogFile_ + SUBSTRING(y.PhysicalName,LEN(y.PhysicalName) - CHARINDEX('\',REVERSE(y.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(y.PhysicalName),1) + 1)
- END + '''' AS Command,
- 1 AS Sequence,
- d.name AS database_name,
- #CTE.physical_device_name AS BackupDevice,
- #CTE.backupstartdate,
- #CTE.backup_size,
- #CTE.last_lsn
-
-FROM sys.databases d
-
-JOIN
-(
+ --*****************
+
+ CASE @StandbyMode_ WHEN 0 THEN ' WITH NORECOVERY,' ELSE ' WITH STANDBY =N' + '''' + ISNULL(@FromFileFullUNC_,SUBSTRING(#CTE.physical_device_name,1,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name)))) + '\' + d.name + '_' + REPLACE(REPLACE(SUBSTRING(CONVERT(VARCHAR(24),GETDATE(),127),12,12),':',''),'.','') + '_ROLLBACK_UNDO.bak ' + '''' + ',' END + SPACE(1) +
+
+ CASE WHEN #CTE.physical_device_name LIKE 'http%' AND @BlobCredential_ IS NOT NULL THEN ' CREDENTIAL = ''' + @BlobCredential_ + ''', ' ELSE '' END +
+
+ CASE #CTE.has_backup_checksums WHEN 1 THEN ' CHECKSUM, ' ELSE ' ' END +
+
+ + 'FILE = ' + CAST(#CTE.position AS VARCHAR(5)) +
+ ' ,STOPAT = ' + '''' + CONVERT(VARCHAR(21),@StopAt_,120) + ''''
+
+ AS Command,
+ 1000000 AS Sequence,
+ d.name AS database_name,
+ #CTE.physical_device_name AS BackupDevice,
+ #CTE.backupfinishdate,
+ #CTE.backup_size,
+ #CTE.first_lsn,
+ #CTE.last_lsn,
+ #CTE.fork_point_lsn,
+ #CTE.first_recovery_fork_guid,
+ #CTE.last_recovery_fork_guid
+
+
+ FROM sys.databases d
+
+ INNER JOIN #CTE
+ ON #CTE.database_name = d.name
+ AND #CTE.family_sequence_number = 1
+
+ LEFT OUTER JOIN #Stripes
+ ON #Stripes.database_name = d.name
+ AND #Stripes.backupmediasetid = #CTE.backupmediasetid
+ AND #Stripes.last_lsn = #CTE.last_lsn
+
+ LEFT OUTER JOIN
+ (
+ SELECT database_name, MAX(last_lsn) last_lsn
+ FROM #CTE
+ WHERE [type] = 'I'
+
+ GROUP BY database_name
+ ) after_diff
+ ON after_diff.database_name = #CTE.database_name
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'L'
+ AND #CTE.family_sequence_number = 1
+ AND #CTE.last_lsn >= CASE WHEN @ExcludeDiffAndLogBackups_ IN(0,4) THEN ISNULL(after_diff.last_lsn,'0') ELSE 0 END
+ AND @ExcludeDiffAndLogBackups_ IN (0,3,4)
+
+ AND ISNULL(fpc.ForkPoints,0) < 2
+
+ --------------------------------------------------------------
+ UNION -- SET SINGLE USER WITH ROLLBACK IMMEDIATE
+ --------------------------------------------------------------
SELECT
- DB_NAME(mf.database_id) AS name
- ,mf.Physical_Name AS PhysicalName
- ,mf.Name AS LogicalName
- FROM sys.master_files mf
- WHERE type_desc = 'ROWS'
- AND mf.file_id = 1
-) x
- ON d.name = x.name
-
-JOIN
-(
+
+ 'BEGIN TRY ALTER DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE END TRY BEGIN CATCH PRINT' + '''' + 'SET SINGLE USER FAILED' + '''' + ' END CATCH' AS Command,
+ 0 AS Sequence,
+ d.name AS database_name,
+ '' AS BackupDevice,
+ GETDATE() AS backupfinishdate,
+ #CTE.backup_size,
+ '0' AS first_lsn,
+ '0' AS last_lsn,
+ '0' AS fork_point_lsn ,
+ '00000000-0000-0000-0000-000000000000' AS first_recovery_fork_guid,
+ '00000000-0000-0000-0000-000000000000' AS last_recovery_fork_guid
+
+ FROM sys.databases d
+
+ INNER JOIN #CTE
+ ON #CTE.database_name = d.name
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'D'
+ AND @SetSingleUser_ = 1
+ AND ISNULL(fpc.ForkPoints,0) < 2
+
+ --------------------------------------------------------------
+ UNION -- Restore WITH RECOVERY
+ --------------------------------------------------------------
SELECT
- DB_NAME(mf.database_id) AS name, type_desc
- ,mf.Physical_Name PhysicalName
- ,mf.Name AS LogicalName
- FROM sys.master_files mf
- WHERE type_desc = 'LOG'
-) y
- ON d.name = y.name
-
-LEFT OUTER JOIN #CTE
- ON #CTE.database_name = d.name
- AND #CTE.family_sequence_number = 1
-
-JOIN #Stripes
- ON #Stripes.database_name = d.name
- AND #Stripes.backupmediasetid = #CTE.backupmediasetid
- AND #Stripes.last_lsn = #CTE.last_lsn
-
-WHERE #CTE.[type] = 'D'
-AND #CTE.family_sequence_number = 1
-
---------------------------------------------------------------
--- Most recent differential backup
---------------------------------------------------------------
-UNION
-
-SELECT
- ';RESTORE DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) +
- 'FROM DISK = N' + '''' +
- CASE ISNULL(@FromFileDiffUNC_,'Actual')
- WHEN 'Actual' THEN #CTE.physical_device_name
- ELSE @FromFileDiffUNC_ + SUBSTRING(#CTE.physical_device_name,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 2,CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 1)
- END + '''' + SPACE(1) +
-
- -- Striped backup files
- CASE ISNULL(#Stripes.S2_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S2_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S2_pdn,LEN(#Stripes.S2_pdn) - CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S3_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S3_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S3_pdn,LEN(#Stripes.S3_pdn) - CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S4_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S4_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S4_pdn,LEN(#Stripes.S4_pdn) - CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S5_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S5_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S5_pdn,LEN(#Stripes.S5_pdn) - CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S6_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S6_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S6_pdn,LEN(#Stripes.S6_pdn) - CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S7_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S7_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S7_pdn,LEN(#Stripes.S7_pdn) - CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S8_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S8_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S8_pdn,LEN(#Stripes.S8_pdn) - CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S9_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S9_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S9_pdn,LEN(#Stripes.S9_pdn) - CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S10_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileDiffUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S10_pdn ELSE @FromFileDiffUNC_ + SUBSTRING(#Stripes.S10_pdn,LEN(#Stripes.S10_pdn) - CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 1) END + ''''
- END +
-
- ' WITH FILE = ' + CAST(#CTE.position AS VARCHAR(5)) + ',' +
- CASE #CTE.has_backup_checksums WHEN 1 THEN 'CHECKSUM, ' ELSE ' ' END +
-
- CASE @StandbyMode_ WHEN 0 THEN 'NORECOVERY,' ELSE 'STANDBY =N' + '''' + ISNULL(@FromFileFullUNC_,SUBSTRING(#CTE.physical_device_name,1,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name)))) + '\' + d.name + '_ROLLBACK_UNDO.bak ' + '''' + ',' END + SPACE(1) +
-
- 'STATS=10,' + SPACE(1) +
- 'MOVE N' + '''' + x.LogicalName + '''' + ' TO ' +
- '''' +
- CASE ISNULL(@WithMoveDataFiles_,'Actual')
- WHEN 'Actual' THEN x.PhysicalName
- ELSE @WithMoveDataFiles_ + SUBSTRING(x.PhysicalName,LEN(x.PhysicalName) - CHARINDEX('\',REVERSE(x.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(x.PhysicalName),1) + 1)
- END + '''' + ',' + SPACE(1) +
-
- 'MOVE N' + '''' + y.LogicalName + '''' + ' TO ' +
- '''' +
- CASE ISNULL(@WithMoveLogFile_ ,'Actual')
- WHEN 'Actual' THEN y.PhysicalName
- ELSE @WithMoveLogFile_ + SUBSTRING(y.PhysicalName,LEN(y.PhysicalName) - CHARINDEX('\',REVERSE(y.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(y.PhysicalName),1) + 1)
- END + '''' AS Command,
- 32769/2 AS Sequence,
- d.name AS database_name,
- #CTE.physical_device_name AS BackupDevice,
- #CTE.backupstartdate,
- #CTE.backup_size,
- #CTE.last_lsn
-
-FROM sys.databases d
-
-JOIN #CTE
-ON #CTE.database_name = d.name
-AND #CTE.family_sequence_number = 1
-
-LEFT OUTER JOIN #Stripes
-ON #Stripes.database_name = d.name
-AND #Stripes.backupmediasetid = #CTE.backupmediasetid
-AND #Stripes.last_lsn = #CTE.last_lsn
-
-INNER JOIN
-(
+
+ 'RESTORE DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) + 'WITH RECOVERY' AS Command,
+ 1000001 AS Sequence,
+ d.name AS database_name,
+ '' AS BackupDevice,
+ GETDATE() AS backupfinishdate,
+ #CTE.backup_size,
+ #CTE.first_lsn,
+ '999999999999999999997' AS last_lsn,
+ #CTE.fork_point_lsn ,
+ '00000000-0000-0000-0000-000000000000' AS first_recovery_fork_guid,
+ '00000000-0000-0000-0000-000000000000' AS last_recovery_fork_guid
+
+ FROM sys.databases d
+
+ INNER JOIN #CTE
+ ON #CTE.database_name = d.name
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'D'
+ AND @WithRecovery_ = 1
+ AND ISNULL(fpc.ForkPoints,0) < 2
+
+ --------------------------------------------------------------
+ UNION -- CHECKDB
+ --------------------------------------------------------------
SELECT
- DB_NAME(mf.database_id) AS name
- ,mf.Physical_Name AS PhysicalName
- ,mf.Name AS LogicalName
- FROM sys.master_files mf
- WHERE type_desc = 'ROWS'
- AND mf.file_id = 1
-) x
- ON d.name = x.name
-
-JOIN
-(
+
+ 'DBCC CHECKDB(' + '''' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + '''' + ') WITH NO_INFOMSGS, ALL_ERRORMSGS' AS Command,
+ 1000002 AS Sequence,
+ d.name AS database_name,
+ '' AS BackupDevice,
+ DATEADD(minute,1,GETDATE()) AS backupfinishdate,
+ #CTE.backup_size,
+ #CTE.first_lsn,
+ '999999999999999999998' AS last_lsn,
+ #CTE.fork_point_lsn,
+ '00000000-0000-0000-0000-000000000000' AS first_recovery_fork_guid,
+ '00000000-0000-0000-0000-000000000000' AS last_recovery_fork_guid
+
+ FROM sys.databases d
+
+ INNER JOIN #CTE
+ ON #CTE.database_name = d.name
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'D'
+ AND @WithCHECKDB_ = 1
+ AND @WithRecovery_ = 1
+ AND ISNULL(fpc.ForkPoints,0) < 2
+
+ --------------------------------------------------------------
+ UNION -- Drop database after restore
+ --------------------------------------------------------------
SELECT
- DB_NAME(mf.database_id) AS name, type_desc
- ,mf.Physical_Name PhysicalName
- ,mf.Name AS LogicalName
- FROM sys.master_files mf
- WHERE type_desc = 'LOG'
-) y
- ON d.name = y.name
-
-WHERE #CTE.[type] = 'I'
-AND #CTE.family_sequence_number = 1
-
---------------------------------------------------------------
-UNION -- Log backups taken since most recent full or diff
---------------------------------------------------------------
-
-SELECT
- ';BEGIN TRY RESTORE LOG [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) +
- 'FROM DISK = N' + '''' + --#CTE.physical_device_name + '''' + SPACE(1) +
- CASE ISNULL(@FromFileLogUNC_,'Actual')
- WHEN 'Actual' THEN #CTE.physical_device_name
- ELSE @FromFileLogUNC_ + SUBSTRING(#CTE.physical_device_name,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 2,CHARINDEX('\',REVERSE(#CTE.physical_device_name),1) + 1)
- END + '''' +
-
- -- Striped backup files
- CASE ISNULL(#Stripes.S2_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S2_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S2_pdn,LEN(#Stripes.S2_pdn) - CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S2_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S3_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S3_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S3_pdn,LEN(#Stripes.S3_pdn) - CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S3_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S4_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S4_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S4_pdn,LEN(#Stripes.S4_pdn) - CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S4_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S5_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S5_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S5_pdn,LEN(#Stripes.S5_pdn) - CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S5_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S6_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S6_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S6_pdn,LEN(#Stripes.S6_pdn) - CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S6_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S7_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S7_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S7_pdn,LEN(#Stripes.S7_pdn) - CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S7_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S8_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S8_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S8_pdn,LEN(#Stripes.S8_pdn) - CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S8_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S9_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S9_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S9_pdn,LEN(#Stripes.S9_pdn) - CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S9_pdn),1) + 1) END + ''''
- END +
-
- CASE ISNULL(#Stripes.S10_pdn,'')
- WHEN '' THEN ''
- ELSE ', DISK = N' + '''' + CASE ISNULL(@FromFileLogUNC_,'Actual') WHEN 'Actual' THEN #Stripes.S10_pdn ELSE @FromFileLogUNC_ + SUBSTRING(#Stripes.S10_pdn,LEN(#Stripes.S10_pdn) - CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 2,CHARINDEX('\',REVERSE(#Stripes.S10_pdn),1) + 1) END + ''''
- END +
-
- CASE @StandbyMode_ WHEN 0 THEN ' WITH NORECOVERY,' ELSE ' WITH STANDBY =N' + '''' + ISNULL(@FromFileFullUNC_,SUBSTRING(#CTE.physical_device_name,1,LEN(#CTE.physical_device_name) - CHARINDEX('\',REVERSE(#CTE.physical_device_name)))) + '\' + d.name + '_ROLLBACK_UNDO.bak ' + '''' + ',' END + SPACE(1) +
-
- CASE #CTE.has_backup_checksums WHEN 1 THEN ' CHECKSUM, ' ELSE ' ' END +
-
- + 'FILE = ' + CAST(#CTE.position AS VARCHAR(5)) +
- ' ,STOPAT = ' + '''' + CONVERT(VARCHAR(21),@StopAt_,120) + '''' +
- ' ,MOVE N' + '''' + x2.LogicalName + '''' + ' TO ' +
- '''' +
- CASE ISNULL(@WithMoveDataFiles_,'Actual')
- WHEN 'Actual' THEN x2.PhysicalName
- ELSE @WithMoveDataFiles_ + SUBSTRING(x2.PhysicalName,LEN(x2.PhysicalName) - CHARINDEX('\',REVERSE(x2.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(x2.PhysicalName),1) + 1)
- END + '''' + ',' + SPACE(1) +
-
- ' MOVE N' + '''' + y1.LogicalName + '''' + ' TO ' +
- '''' +
- CASE ISNULL(@WithMoveLogFile_ ,'Actual')
- WHEN 'Actual' THEN y1.PhysicalName
- ELSE @WithMoveLogFile_ + SUBSTRING(y1.PhysicalName,LEN(y1.PhysicalName) - CHARINDEX('\',REVERSE(y1.PhysicalName),1) + 2,CHARINDEX('\',REVERSE(y1.PhysicalName),1) + 1)
- END + '''' + 'END TRY BEGIN CATCH PRINT ' + '''' + 'Transaction Log File Restore Exclusion - Check Recovery Sequence.' + '''' + ' END CATCH;' AS Command,
- 32769 AS Sequence,
- d.name AS database_name,
- #CTE.physical_device_name AS BackupDevice,
- #CTE.backupstartdate,
- #CTE.backup_size,
- #CTE.last_lsn
-
-FROM sys.databases d
-
-INNER JOIN
-(
+
+ -- Comment out all commands if multiple forking points exist
+ --CASE WHEN @CommentOut = 1 THEN ' -- Multipe backup fork points detected, command suppressed -- ' ELSE '' END +
+
+ 'DROP DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) AS Command,
+ 1000003 AS Sequence,
+ d.name AS database_name,
+ '' AS BackupDevice,
+ GETDATE() AS backupfinishdate,
+ #CTE.backup_size,
+ #CTE.first_lsn,
+ '999999999999999999999' AS last_lsn,
+ #CTE.fork_point_lsn,
+ '00000000-0000-0000-0000-000000000000' AS first_recovery_fork_guid,
+ '00000000-0000-0000-0000-000000000000' AS last_recovery_fork_guid
+
+
+
+ FROM sys.databases d
+
+ INNER JOIN #CTE
+ ON #CTE.database_name = d.name
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'D'
+ AND @DropDatabaseAfterRestore_ = 1
+ AND ISNULL(fpc.ForkPoints,0) < 2
+
+ --------------------------------------------------------------
+ UNION -- RAISERROR suppress restore when multiple forkpoints exist
+ --------------------------------------------------------------
SELECT
- DB_NAME(mf.database_id) AS name
- ,mf.Physical_Name AS PhysicalName
- ,mf.Name AS LogicalName
- FROM sys.master_files mf
- WHERE type_desc = 'ROWS'
- AND mf.file_id = 1
-) x2
-ON d.name = x2.name
-
-INNER JOIN
-(
+
+ 'RAISERROR (' + '''' + 'Multiple restores per performed between the selected full backup and stop at time for database ' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ' - Restore Gene processing aborted.' + '''' + ',0,0);' + + SPACE(1) AS Command,
+ 1 AS Sequence,
+ d.name AS database_name,
+ '' AS BackupDevice,
+ GETDATE() AS backupfinishdate,
+ #CTE.backup_size,
+ '000000000000000000000' AS first_lsn,
+ '999999999999999999999' AS last_lsn,
+ #CTE.fork_point_lsn,
+ '00000000-0000-0000-0000-000000000000' AS first_recovery_fork_guid,
+ '00000000-0000-0000-0000-000000000000' AS last_recovery_fork_guid
+
+ FROM sys.databases d
+
+ INNER JOIN #CTE
+ ON #CTE.database_name = d.name
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'D'
+ AND ISNULL(fpc.ForkPoints,0) > 1
+
+ --------------------------------------------------------------
+ UNION -- WITH MOVE secondary data files, allows for up to 32769/2 file groups
+ --------------------------------------------------------------
+
SELECT
- DB_NAME(mf.database_id) AS name, type_desc
- ,mf.Physical_Name PhysicalName
- ,mf.Name AS LogicalName
- FROM sys.master_files mf
- WHERE type_desc = 'LOG'
-) y1
- ON d.name = y1.name
-
-INNER JOIN #CTE
- ON #CTE.database_name = d.name
- AND #CTE.family_sequence_number = 1
-
-LEFT OUTER JOIN #Stripes
- ON #Stripes.database_name = d.name
- AND #Stripes.backupmediasetid = #CTE.backupmediasetid
- AND #Stripes.last_lsn = #CTE.last_lsn
-
-LEFT OUTER JOIN
-(
- SELECT database_name, MAX(last_lsn) last_lsn
- FROM #CTE
- WHERE [type] = 'I'
- GROUP BY database_name
-) after_diff
- ON after_diff.database_name = #CTE.database_name
-
-WHERE #CTE.[type] = 'L'
-AND #CTE.family_sequence_number = 1
-AND #CTE.last_lsn > ISNULL(after_diff.last_lsn,'0')
-
---------------------------------------------------------------
-UNION -- Declare @msg_ variable
---------------------------------------------------------------
-SELECT
--- '; DECLARE @msg_' + d.name + ' VARCHAR(1000)' AS Command,
- ';DECLARE @msg_' + REPLACE(REPLACE(REPLACE(d.name,' ','_'),'.','_'),'-','_') + ' VARCHAR(1000)' AS Command,
- 0 AS Sequence,
- d.name AS database_name,
- '' AS BackupDevice,
- GETDATE() AS backupstartdate,
- #CTE.backup_size,
- '0' AS last_lsn
-
-FROM sys.databases d
-
-JOIN #CTE
-ON #CTE.database_name = d.name
-
-WHERE #CTE.[type] = 'D'
-AND @LogShippingVariableDeclare_ = 1
-
---------------------------------------------------------------
-UNION -- Restore WITH RECOVERY
---------------------------------------------------------------
-SELECT
- ';SET @msg_' + REPLACE(REPLACE(REPLACE(d.name,' ','_'),'.','_'),'-','_') + ' = ' + '''' + @Log_Reference_ + '''' + '; RAISERROR (@msg_' + REPLACE(REPLACE(REPLACE(d.name,' ','_'),'.','_'),'-','_') + ',0,0) WITH LOG' + ';RESTORE DATABASE [' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + ']' + SPACE(1) + 'WITH RECOVERY' AS Command,
- 32771 AS Sequence,
- d.name AS database_name,
- '' AS BackupDevice,
- GETDATE() AS backupstartdate,
- #CTE.backup_size,
- '999999999999999999998' AS last_lsn
-
-FROM sys.databases d
-
-JOIN #CTE
-ON #CTE.database_name = d.name
-
-WHERE #CTE.[type] = 'D'
-AND @WithRecovery_ = 1
-
---------------------------------------------------------------
-UNION -- CHECKDB
---------------------------------------------------------------
-SELECT
- ';SET @msg_' + REPLACE(REPLACE(REPLACE(d.name,' ','_'),'.','_'),'-','_') + ' = ' + '''' + @Log_Reference_ + '''' + '; RAISERROR (@msg_' + REPLACE(REPLACE(REPLACE(d.name,' ','_'),'.','_'),'-','_') + ',0,0) WITH LOG' + ';DBCC CHECKDB(' + '''' + CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN d.[name] ELSE @TargetDatabase_ END + '''' + ') WITH NO_INFOMSGS, ALL_ERRORMSGS' AS Command,
- 32772 AS Sequence,
- d.name AS database_name,
- '' AS BackupDevice,
- DATEADD(minute,1,GETDATE()) AS backupstartdate,
- #CTE.backup_size,
- '999999999999999999999' AS last_lsn
-
-FROM sys.databases d
-
-JOIN #CTE
-ON #CTE.database_name = d.name
-
-WHERE #CTE.[type] = 'D'
-AND @WithCHECKDB_ = 1
-AND @WithRecovery_ = 1
-
---------------------------------------------------------------
-UNION -- WITH MOVE secondary data files, allows for up to 32769/2 file groups
---------------------------------------------------------------
-SELECT
- ', MOVE N' + '''' + b.name + '''' + ' TO N' + '''' +
- CASE ISNULL(@WithMoveDataFiles_,'Actual')
- WHEN 'Actual' THEN b.physical_name
- ELSE @WithMoveDataFiles_ + SUBSTRING(b.Physical_Name,LEN(b.Physical_Name) - CHARINDEX('\',REVERSE(b.Physical_Name),1) + 2,CHARINDEX('\',REVERSE(b.Physical_Name),1) + 1)
- END + '''',
- b.file_id AS Sequence,
- DB_NAME(b.database_id) AS database_name,
- 'SECONDARY FULL' AS BackupDevice,
- #CTE.backupstartdate,
- #CTE.backup_size,
- #CTE.last_lsn
-
-FROM sys.master_files b
-INNER JOIN #CTE
-ON #CTE.database_name = DB_NAME(b.database_id)
-
-WHERE #CTE.[type] = 'D'
-AND b.type_desc = 'ROWS'
-AND b.file_id > 2
-AND @WithMoveDataFiles_ IS NOT NULL
---------------------------------------------------------------
-) a
---------------------------------------------------------------
-
-WHERE a.database_name = ISNULL(@Database_,a.database_name)
-AND (@IncludeSystemDBs_ = 1 OR a.database_name NOT IN('master','model','msdb'))
-AND a.last_lsn > @LogShippingLastLSN_
-
-ORDER BY
- database_name,
- Sequence,
- --last_lsn
- backupstartdate
-
-END;
+ -- Comment out all commands if multiple forking points exist
+ --CASE WHEN @CommentOut = 1 THEN ' -- Multipe backup fork points detected, command suppressed -- ' ELSE '' END +
+
+ ' ,MOVE N' + '''' + b.name + '''' + ' TO N' + '''' +
+
+ CASE b.type_desc
+
+ WHEN 'ROWS' THEN
+ CASE ISNULL(@WithMoveDataFiles_,'Actual')
+ WHEN 'Actual' THEN CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN b.Physical_Name ELSE SUBSTRING(b.Physical_Name,1,CASE CHARINDEX('.',b.Physical_Name) WHEN 0 THEN LEN(b.Physical_Name) ELSE CHARINDEX('.',b.Physical_Name) - 1 END) + '_' + @TargetDatabase_ + '.ndf' END
+ ELSE CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN @WithMoveDataFiles_ + SUBSTRING(b.Physical_Name,LEN(b.Physical_Name) - CHARINDEX('\',REVERSE(b.Physical_Name),1) + 2,CHARINDEX('\',REVERSE(b.Physical_Name),1) + 1)
+ ELSE @WithMoveDataFiles_ + SUBSTRING (b.Physical_Name, (LEN(b.Physical_Name)-CHARINDEX('\',REVERSE(b.Physical_Name)) + 2),CHARINDEX('\',REVERSE(b.Physical_Name)) - 1 - CHARINDEX('.',REVERSE(b.Physical_Name))) + '_' + @TargetDatabase_ + '.ndf' END
+ END
+
+ WHEN 'FILESTREAM' THEN
+ CASE ISNULL(@WithMoveFileStreamFile_,'Actual')
+ WHEN 'Actual' THEN CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN b.Physical_Name ELSE SUBSTRING(b.Physical_Name,1,CASE CHARINDEX('.',b.Physical_Name) WHEN 0 THEN LEN(b.Physical_Name) ELSE CHARINDEX('.',b.Physical_Name) - 1 END) + '_' + @TargetDatabase_ END
+ ELSE CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN @WithMoveFileStreamFile_ + SUBSTRING(b.Physical_Name,LEN(b.Physical_Name) - CHARINDEX('\',REVERSE(b.Physical_Name),1) + 2,CHARINDEX('\',REVERSE(b.Physical_Name),1) + 1)
+ ELSE @WithMoveFileStreamFile_ + SUBSTRING (b.Physical_Name, (LEN(b.Physical_Name)-CHARINDEX('\',REVERSE(b.Physical_Name)) + 2),CHARINDEX('\',REVERSE(b.Physical_Name)) - 1 - CHARINDEX('.',REVERSE(b.Physical_Name))) + '_' + @TargetDatabase_ END
+ END
+
+ WHEN 'LOG' THEN
+ CASE ISNULL(@WithMoveLogFile_,'Actual')
+ WHEN 'Actual' THEN CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN b.Physical_Name ELSE SUBSTRING(b.Physical_Name,1,CASE CHARINDEX('.',b.Physical_Name) WHEN 0 THEN LEN(b.Physical_Name) ELSE CHARINDEX('.',b.Physical_Name) - 1 END) + '_' + @TargetDatabase_ + '.ldf' END
+ ELSE CASE ISNULL(@TargetDatabase_,'') WHEN '' THEN @WithMoveLogFile_ + SUBSTRING(b.Physical_Name,LEN(b.Physical_Name) - CHARINDEX('\',REVERSE(b.Physical_Name),1) + 2,CHARINDEX('\',REVERSE(b.Physical_Name),1) + 1)
+ ELSE @WithMoveLogFile_ + SUBSTRING (b.Physical_Name, (LEN(b.Physical_Name)-CHARINDEX('\',REVERSE(b.Physical_Name)) + 2),CHARINDEX('\',REVERSE(b.Physical_Name)) - 1 - CHARINDEX('.',REVERSE(b.Physical_Name))) + '_' + @TargetDatabase_ + '.ldf' END
+
+ END
+
+ END
+
+ + '''',
+ b.file_id AS Sequence,
+ DB_NAME(b.database_id) AS database_name,
+ 'SECONDARY FULL' AS BackupDevice,
+ #CTE.backupfinishdate,
+ #CTE.backup_size,
+ #CTE.first_lsn,
+ #CTE.last_lsn,
+ #CTE.fork_point_lsn,
+ #CTE.first_recovery_fork_guid,
+ #CTE.last_recovery_fork_guid
+
+ FROM sys.master_files b
+ INNER JOIN #CTE
+ ON #CTE.database_name = DB_NAME(b.database_id)
+
+ LEFT OUTER JOIN #ForkPointsCount fpc
+ ON fpc.database_name = #CTE.database_name
+
+ WHERE #CTE.[type] = 'D'
+ AND b.type_desc IN ('ROWS','FILESTREAM','LOG')
+ AND b.file_id > 2
+ AND ISNULL(fpc.ForkPoints,0) < 2
+
+ --------------------------------------------------------------
+ ) a
+ LEFT OUTER JOIN #tmpDatabases b
+ ON a.database_name = b.DatabaseName
+ --------------------------------------------------------------
+
+ WHERE (@Database_ IS NULL OR b.Selected = 1 OR @Database_ = a.database_name)
+ AND (@IncludeSystemDBs_ = 1 OR a.database_name NOT IN('master','model','msdb'))
+ AND a.last_lsn >= @LogShippingLastLSN_;
+
+ CREATE INDEX IDX_RestoreGeneResults ON #RestoreGeneResults (database_name,SortSequence,BackupDate);
+
+ --------------------------------------------------------------
+ -- Result Set
+ IF @SuppressWithMove_ = 1
+ BEGIN
+ IF @RestoreScriptOnly_ = 0
+ BEGIN
+
+ SELECT x4.TSQL, x4.BackupDate, x4.BackupDevice, x4.first_lsn, x4.last_lsn, x4.fork_point_lsn, x4.first_recovery_fork_guid, x4.last_recovery_fork_guid, x4.database_name, x4.SortSequence
+ FROM #RestoreGeneResults x4
+ WHERE ISNULL(x4.BackupDevice,'') <> 'SECONDARY FULL'
+ ORDER BY
+ x4.database_name,
+ x4.SortSequence,
+ x4.BackupDate
+ END
+ ELSE
+ BEGIN
+ SELECT x4.TSQL AS [--TSQL]
+ FROM #RestoreGeneResults x4
+ WHERE ISNULL(x4.BackupDevice,'') <> 'SECONDARY FULL'
+ ORDER BY
+ x4.database_name,
+ x4.SortSequence,
+ x4.BackupDate
+ END
+ END
+ ELSE
+ BEGIN
+ IF @PivotWithMove_ = 1
+ BEGIN
+
+ IF @RestoreScriptOnly_ = 0
+ BEGIN
+ SELECT
+ x4.TSQL, x4.BackupDate, x4.BackupDevice, x4.first_lsn, x4.last_lsn, x4.fork_point_lsn, x4.first_recovery_fork_guid, x4.last_recovery_fork_guid, x4.database_name, x4.SortSequence
+ FROM #RestoreGeneResults x4
+ ORDER BY
+ x4.database_name,
+ x4.SortSequence,
+ x4.BackupDate
+ END
+ ELSE
+ BEGIN
+ SELECT
+ x4.TSQL AS [--TSQL]
+ FROM #RestoreGeneResults x4
+ ORDER BY
+ x4.database_name,
+ x4.SortSequence,
+ x4.BackupDate
+ END
+
+ END
+ ELSE
+ BEGIN
+ IF @RestoreScriptOnly_ = 0
+ BEGIN
+ WITH WithMoves AS
+ (
+ SELECT
+ last_lsn,
+ STUFF((SELECT ' ' + TSQL FROM #RestoreGeneResults x3 WHERE x3.last_lsn = x2.last_lsn AND ISNULL(x3.BackupDevice,'') = 'SECONDARY FULL' ORDER BY x3.SortSequence FOR XML PATH, TYPE).value('.[1]', 'nvarchar(max)'), 1,1,'') AS WithoveCmds
+ FROM #RestoreGeneResults x2
+ GROUP BY last_lsn
+ )
+
+ SELECT
+ CASE @SuppressWithMove_ WHEN 0 THEN CASE ISNULL(x5.WithoveCmds,'') WHEN '' THEN x4.TSQL ELSE x4.TSQL + ' ' + x5.WithoveCmds END
+ ELSE x4.TSQL
+ END AS TSQL,
+ x4.BackupDate, x4.BackupDevice, x4.first_lsn, x4.last_lsn, x4.fork_point_lsn, x4.first_recovery_fork_guid, x4.last_recovery_fork_guid, x4.database_name, x4.SortSequence
+ FROM #RestoreGeneResults x4
+ LEFT OUTER JOIN WithMoves x5
+ ON x4.last_lsn = x5.last_lsn
+ WHERE ISNULL(x4.BackupDevice,'') <> 'SECONDARY FULL'
+ ORDER BY
+ x4.database_name,
+ x4.SortSequence,
+ x4.BackupDate
+ END
+ ELSE
+ BEGIN
+ WITH WithMoves AS
+ (
+ SELECT
+ last_lsn,
+ STUFF((SELECT ' ' + TSQL FROM #RestoreGeneResults x3 WHERE x3.last_lsn = x2.last_lsn AND ISNULL(x3.BackupDevice,'') = 'SECONDARY FULL' ORDER BY x3.SortSequence FOR XML PATH, TYPE).value('.[1]', 'nvarchar(max)'), 1,1,'') AS WithoveCmds
+ FROM #RestoreGeneResults x2
+ GROUP BY last_lsn
+ )
+
+ SELECT
+ CASE @SuppressWithMove_ WHEN 0 THEN CASE ISNULL(x5.WithoveCmds,'') WHEN '' THEN x4.TSQL ELSE x4.TSQL + ' ' + x5.WithoveCmds END
+ ELSE x4.TSQL
+ END AS [--TSQL]
+ FROM #RestoreGeneResults x4
+ LEFT OUTER JOIN WithMoves x5
+ ON x4.last_lsn = x5.last_lsn
+ WHERE ISNULL(x4.BackupDevice,'') <> 'SECONDARY FULL'
+ ORDER BY
+ x4.database_name,
+ x4.SortSequence,
+ x4.BackupDate
+ END
+ END
+ END
+END
GO
diff --git a/Utilities/README.md b/Utilities/README.md
index a293961e..628e4570 100644
--- a/Utilities/README.md
+++ b/Utilities/README.md
@@ -217,6 +217,7 @@ Utility types (main purpose), in braces `{}` current counts:
| [EMS Data Comparer](#ems-data-comparer) | No | No | [DC] | [EMS Data Comparer] | 2012-11-16 | EMS | | No | $47 |
| [NitroAccelerator](#nitroaccelerator) | No | No | [?] | [NitroAccelerator] | 2016-07-10 | Nitrosphere | | No | $1265 |
| [ERwin Data Modeler](#erwin-data-modeler) | Oracle,MySQL,PostgreSQL,Other | No | [DA] | [ERwin Data Modeler] | 2016-06-03 | ERwin | | No | $810 |
+| [Luna Modeler](#luna-modeler) | Oracle,MySQL,PostgreSQL,Other | Linux,Mac | [DA] | [Luna Modeler] | 2022-10-05 | Ideamerit | | No | $99 |
| [Toad Data Point](#toad-data-point) | Oracle,MySQL,PostgreSQL,Other | No | [?] | [Toad Data Point] | 2016-06-16 | Quest Software | | No | ? |
| [SQL Power Architect](#sql-power-architect) | Oracle,MySQL,PostgreSQL,Other | Linux,Mac | [DA] | [SQL Power Architect] | 2016-02-11 | SQLPower | | Yes | $995 |
| [DbWrench](#dbwrench) | Oracle,MySQL,PostgreSQL,Other | Linux,Mac | [DA] | [DbWrench] | 2019-09-17 | Nizana Systems | | No | $99 |
@@ -3112,6 +3113,18 @@ Price: $810
ERwin Data Modeler is an industry-leading data modeling solution that provides a simple, visual interface to manage your complex data environment
+## Luna Modeler
+
+Download page: [Luna Modeler]
+Release date: 2022-10-05
+Support Version: 2019/2022
+Author: Ideamerit
+Free version: No
+Price: $99
+
+Luna Modeler is a database design and data modeling tool for relational databases. With Luna Modeler, you can create ER diagrams, design and visualize database structures, generate SQL scripts, and document your data models.
+
+
## Toad Data Point
Download page: [Toad Data Point]
@@ -6396,6 +6409,7 @@ SQLWATCH is an Open Source (MIT License) Real-Time monitoring solution for SQL S
[EMS Data Comparer]:http://www.sqlmanager.net/en/products/mssql/datacomparer
[NitroAccelerator]:http://nitrosphere.com/nitroaccelerator/
[ERwin Data Modeler]:http://erwin.com/products/data-modeler
+[Luna Modeler]:https://www.datensen.com/luna-modeler-for-relational-databases.html
[Toad Data Point]:https://www.quest.com/products/toad-data-point/
[SQL Power Architect]:http://www.sqlpower.ca/page/architect
[DbWrench]:http://www.dbwrench.com/