So sichern Sie eine gesamte Datenbank in Textdateien

7

Wenn es dumm ist und funktioniert, ist es immer noch dumm.

Das Hullabaloo über GDPR brachte mich zum Nachdenken - wenn Sie in Ihre Backups gehen und Daten ändern / löschen müssten, wie würden Sie das tun? Das Öffnen einer .bak-Datei klingt für mich schwierig. Aber es ist ziemlich einfach, eine TXT-Datei zu öffnen.

Könnte ich stattdessen meine Datenbank in einer Textdatei sichern, um das Löschen des Verlaufs aus den Sicherungen zu vereinfachen?

James
quelle
2
Ich höre, JSON ist 1000 Nanosekunden schneller.
Erik Darling
2
Wie auch immer, ich mag diese Frage, aber ich werde scramen, bevor daraus ein PowerShell-Wettbewerb wird. Viel Glück!
Erik Darling
lol! Vielen Dank. Ich habe dies für eine Weile in Google Drive gehabt, wollte es teilen.
James
1) Und was genau ist Ihre Frage? 2) "Solange Sie keine Funktionen, gespeicherten Prozeduren, Ansichten, Einschränkungen oder Indizes haben." Das sieht für mich nicht nach einem guten DB-Backup aus ...
Patrick Mevzek
4
@ James - wenn du deine eigene Frage beantworten willst, ist es völlig in Ordnung - es wird sogar empfohlen. Teilen Sie die Frage einfach auf und beantworten Sie sie in zwei Teile. Ich habe Ihre "Antwort" in den Antwortbereich verschoben.
Brent Ozar

Antworten:

2

Dies ist zwar keine gute Wahl für tatsächliche Sicherungen, es gibt jedoch einige Anwendungsfälle für das Speichern von Tabellen in Textdateien. In diesem Zusammenhang besteht ein alternativer Ansatz zum xp_cmdshellAufrufen von BCP.EXE darin, die gespeicherte Prozedur DB_BulkExport SQLCLR zu verwenden, die in SQL # (das ich geschrieben habe) verfügbar ist . Sie können dann File_GZip verwenden , um die Dateien zu komprimieren.

Die folgenden Vor- und Nachteile der Verwendung von DB_BulkExport im Vergleich zu BCP.EXE sind :

Vorteile:

  1. Keine Notwendigkeit zu aktivieren xp_cmdshell
  2. Fügt Spaltenüberschriften hinzu
  3. Text qualifiziert entweder keine Felder, alle Felder oder nur Felder, die dies erfordern (dh nicht numerische / nicht binäre Felder). Textqualifizierende Zeichenfolgenfelder bedeuten, dass Sie anstelle eines Zeichens oder einer Folge von Zeichen, von denen Sie hoffen, dass sie dies nicht sind, ein Standardfeldtrennzeichen (z. B. ein Komma oder eine Registerkarte) und ein Standardzeilentrennzeichen (z. B. CRLF oder LF / NL) verwenden können in beliebigen Zeichenfolgenfeldern vorhanden. Das Textqualifikationsmerkmal ist benutzerdefiniert (normalerweise in doppelten Anführungszeichen), und die Escape-Sequenz für eingebettete Textqualifizierer kann beliebig sein (normalerweise auch in doppelten Anführungszeichen, kann aber auch ein Schrägstrich sein).
  4. Kontrolle über die BITDarstellung: 1/0, T / F oder Wahr / Falsch.
  5. (bald) Die Kontrolle über die SMALLDATETIME, DATETIME, DATE, TIME, DATETIME2, und DATETIMEOFFSETFormat.

Nachteile:

  1. Nicht kostenlos ( DB_BulkExport und File_GZip sind nur in der Vollversion verfügbar)

Bitte beachten Sie, dass DB_BulkExport nur Daten exportiert. Die Struktur der Ergebnismenge wird nicht aufgezeichnet. Dies unterscheidet sich weder von BCP noch von der in der Antwort des Haupt-Community-Wikis bereitgestellten Lösung, da die dortige Lösung Kollatierungen, berechnete Spalten, die meisten Spaltenoptionen usw. ignoriert.

Eine weitere von Kin erwähnte Option für SQL Server ist mssql-scripter . Dies ist ein kostenloses / Open Source-Tool für mehrere Plattformen auf Python-Basis. Ich habe es nicht verwendet, aber es scheint DDL- und / oder DML-Anweisungen zu exportieren, die ausgeführt werden können, um die Daten und / oder das Schema (Tabellen, Einschränkungen usw.) neu zu erstellen. Es scheint, dass die Daten nur als INSERTAnweisungen anstatt als begrenzte Felder exportiert werden. Sieht ziemlich interessant aus, aber bitte überprüfen Sie die " Probleme ", um sicherzustellen, dass nichts Ihre Nutzung beeinträchtigt.


Auch wenn in dieser Frage (und in der Antwort des Haupt-Community-Wikis) SQL Server erwähnt wird, sind die Probleme im Zusammenhang mit GDPR nicht spezifisch für SQL Server. Ich dachte nur, ich würde erwähnen, dass die Möglichkeit zum Exportieren von Tabellen (und sogar von Schemata usw.) für MySQL in den folgenden zwei Dienstprogrammen verfügbar ist:

Dasselbe kann in PostgreSQL mit den folgenden zwei Dienstprogrammen durchgeführt werden:

Informationen zu Oracle finden Sie in den folgenden Ressourcen, von denen ich glaube, dass sie Sie zumindest sehr nahe bringen, wenn auch nicht vollständig gleichwertig (danke an Michael Kutz , der mich in die richtige Richtung gelenkt hat):

Ich bin nicht sicher, ob ähnliche Dienstprogramme mit DB2 geliefert werden.

Solomon Rutzky
quelle
Sie sollten mssql-cli
Kin Shah
@Kin Danke für den Vorschlag. Dieses Tool ist jedoch nur ein Abfragetool und scheint keine praktikable Option für den Export von Daten in CSV / TsV-Dateien zu sein. Es sei denn, es gibt einen Teil der Dokumentation, den ich nicht gesehen habe, aber soweit ich das beurteilen kann, handelt es sich nur um ein interaktives Abfragetool.
Solomon Rutzky
Mein schlechtes .. ich meinte mssql-scripter .. github.com/Microsoft/mssql-scripter .. genau wie mysqldump Dienstprogramm.
Kin Shah
@ Kin Ah, ok. Ich habe das Projekt überprüft und die Informationen zu meiner Antwort hinzugefügt. Danke, dass du es erwähnt hast :-)
Solomon Rutzky
Für Oracle wurde SQL*Plus12c + hinzugefügt SET MARKUP CSV. Ähnliches gilt für SQLcl. Ich weiß nur nicht, ob es die für jede Tabelle erforderliche Schleife ausführen kann. Laden ist SQL*Loader.
Michael Kutz
4

Das Folgende ist keine gute Backup-Lösung. Wenn Sie Ihren Job nicht kündigen und Ihrem Chef auf dem Weg zur Tür einen Streich spielen möchten, sollten Sie ihn nicht benutzen. Aber ich habe ein paar Dinge gelernt und hatte Spaß beim Schreiben, hoffentlich auch bei anderen. Wir werden einige ETL-Konzepte verwenden, die unter anderen Umständen tatsächlich nützlich sein könnten. Die vollständigen Skripte finden Sie unten. Folgendes werden wir tun:

Schreiben Sie das Schema in eine eigene Tabelle.
Schreiben Sie jede Tabelle in der Datenbank in eine eigene dynamisch benannte Textdatei.
Erstellen Sie das Schema in einer neuen Datenbank aus der Textdatei
neu. Erstellen Sie eine neue Tabelle und fügen Sie jede Tabelle aus ihrer Textdatei ein

Bevor wir beginnen: Um die gesamte Datenbank zu erstellen, benötigen Sie mehrere hundert GB Speicher. Wenn Sie zu pingelig sind, können Sie die Top 1000 im Skript eingeben, um die Größe der TXT-Dateien zu begrenzen. Dann müssen Sie Ihrem Chef nicht erklären, wie Sie C: \ ausgefüllt haben

Aktivieren Sie xp_cmdshell - hier erklärt:

https://blog.sqlauthority.com/2007/04/26/sql-server-enable-xp_cmdshell-using-sp_configure/

  1. Schreiben Sie das Schema in eine Textdatei

Wir werden prüfen, ob die Tabelle bereits vorhanden ist (wenn wir das Skript bereits ausgeführt haben). Es ist einfacher, den Tisch jedes Mal einfach fallen zu lassen.

    if exists (select name from sys.tables st with (Nolock) where name = 'HeaderTable')

    begin
    drop table HeaderTable
    end

    SELECT
        st.name, sc.name 'Column_Name', t.Name 'Data_type',sc.max_length 'Max_Length',
        sc.precision, sc.scale, sc.is_nullable
        into HeaderTable
        FROM    
        sys.tables st
        inner join sys.columns sc on sc.object_id = st.object_id
        INNER JOIN sys.types t ON sc.user_type_id = t.user_type_id

Wenn Sie jetzt eine schnelle Auswahl * aus HeaderTable vornehmen, sehen Sie jede Tabelle, jede Spalte, ihren Datentyp und ihre maximale Länge - alles, was wir zum erneuten Erstellen benötigen.

  1. Schreiben Sie jede Tabelle in der Datenbank in eine eigene dynamisch benannte Textdatei

Hier fängt es an, cool zu werden. Wir werden einen Cursor verwenden, um sys.tables zu durchlaufen und jeden in seine eigene .txt zu kopieren.

Wir werden eine Reihe von Variablen verwenden:

@table geht in den Cursor. Es wird die Namen jeder Tabelle enthalten, während wir gehen.

@Database, @filepath, @filename, @filetype werden alle zum Erstellen einer Reihe dynamischer SQL-Anweisungen verwendet.

@sql wird unsere letzten SQL-Befehle enthalten, um sie in sp_executesql zu setzen.

Mit den Trennzeichen und Zeilenabschlusszeichen wird es etwas schwierig. Wenn Sie die Standardeinstellungen von | verwenden und / r, Sie werden es mit der Kommentartabelle wirklich schwer haben. Wir müssen etwas verwenden, von dem wir wissen, dass es nirgendwo in der StackOverflow-Datenbank verwendet wird. Sie könnten newid (), Raketenschiffe und Kulleraugen verwenden oder Sie könnten Ihren Lieblingskinderreim verwenden. Alles, solange es noch nicht auf StackOverflow ist.

Hier ist das Skript:

    declare @table varchar(255),
    @Database varchar(255),
    @filepath varchar(255),
    @filename varchar(255),
    @filetype varchar(255),
    @sql nvarchar(max),
    @delimiter varchar(255),
    @rowterminator varchar(255)

    set @Database = 'StackOverflow'
    set @filepath = 'C:\Data\' -- fix pretty-print mishandling of not-truly escaped ' 
    set @filetype = '.txt'
    set @delimiter = 'WhimmyWhammyWozzle'
    set @rowterminator = 'WubaLubaDubDub'

    declare c cursor local for

    select name from sys.tables with (Nolock)

    open c

    fetch from c into @table

    while @@FETCH_STATUS = 0
    begin

    SET @filename = @table

    --output to txt
    set @sql = N'declare @bcp varchar(4000)
    set @bcp = ''bcp " select top 10000 * from ' + @table + ' " queryout '
    + @filepath +  @filename + @filetype + ' -t "' + @delimiter + '" -r "'
     + @rowterminator + '" -c -T -d ' + @Database + '''
    print @bcp

    EXECUTE master.dbo.xp_cmdshell @BCP'

    print @sql
    --exec sp_executesql @sql

    fetch next from c into @table
    end

    close c

    deallocate c

Beachten Sie, dass ich die Sicherheit aktiviert habe, falls Sie sie gerade eingefügt und F5 gedrückt haben. Nicht, dass das jemals jemand tun würde, oder? exec sp_executesql @sql wird erst ausgeführt, wenn Sie die Kommentatoren entfernen. Ich habe auch eine Top 10000 aufgenommen.

Gehen Sie zu Ihrem Dateipfad, und Sie sollten eine Reihe von Textdateien sehen.

Öffnen Sie eine und ändern Sie einige Daten. Wenn Sie der Meinung sind, dass das manuelle Öffnen für Bauern bestimmt ist, können Sie mit Fart.exe alle Textdateien suchen und ersetzen.

  1. Schreiben Sie den Header neu

Erstellen Sie eine neue Datenbank.

Wir werden die Neuerstellung der Header-Tabelle hart codieren und sie verwenden, um den Rest neu zu erstellen.

Header wiederherstellen:

    if exists (select name from sys.tables where name = 'HeaderTable')
    begin
    drop table HeaderTable
    end

    create table HeaderTable
    (Table_Name varchar(255),
    Column_Name  varchar(255),
    Data_type  varchar(255),
    Max_Length  varchar(255),
    precision  varchar(255),
    scale varchar(255),
    is_nullable  varchar(255))

Und jetzt werden wir unser Schema in Massen in HeaderTable einfügen:

    set @sql = 'BULK INSERT HeaderTable FROM ''' + @filepath + 'HeaderTable'+ @filetype + ''' WITH (FIELDTERMINATOR = '''
     + @delimiter + ''', ROWTERMINATOR = ''' + @rowterminator + ''')'
    print @sql
    --exec sp_executesql @sql

    We will have to tidy it up a bit, to make the next steps easier:

    update HeaderTable
    set Max_Length = 'max'
    where Max_length = -1

    update HeaderTable
    set Max_Length = '(' + Max_Length + ')'

    update HeaderTable
    set Max_Length = ''
    where Data_type in ( 'int', 'bigint', 'smallint', 'tinyint',
    'date','datetime', 'uniqueidentifier', 'sysname', 'bit')
  1. Neu erstellen & Bulk Fügen Sie jede Tabelle aus ihrer Textdatei ein

Und hier wird es wieder cool. Wir werden HeaderTable durchlaufen und jede Tabelle neu erstellen, wobei die Create-Anweisung mit STUFF () verkettet wird. Fragen Sie mich nicht, wie stuff () funktioniert - ein alter Mitarbeiter (Mike Ignatoski) hat mir das vor Jahren gegeben. Zuverlässige Quellen sagen, er habe es ursprünglich von einem Typen namens Solomon bekommen.

    declare @table varchar(255),
    @column_string nvarchar(max),
    @sql nvarchar(max),
    @string nvarchar(max),
    @filepath varchar(255),
    @filename varchar(255),
    @filetype varchar(255),
    @sql nvarchar(max),
    @delimiter varchar(255),
    @rowterminator varchar(255)


    set @filepath = 'C:\Data\' -- fix pretty-print mishandling of not-truly escaped '
    set @filetype = '.txt'
    set @delimiter = 'WhimmyWhammyWozzle'
    set @rowterminator = 'WubaLubaDubDub'

    declare c cursor local for

    select distinct Table_Name from HeaderTable
    where Table_Name != 'HeaderTable'

    open c
    fetch from c into @table

    while @@FETCH_STATUS = 0
    begin

    set @string = null

    set @string = (select stuff( (
    select ', ' + Column_Name + ' ' + Data_type  + Max_Length from HeaderTable
    where Table_Name = @table
    for xml path ('')),1,2,''))

    print @string

    set @sql =  ' if not exists (select top 1 name from sys.tables where name = ''' + @table + ''') begin
    create table ' + @table + ' (' + @string + ') end'

    print @sql
    exec sp_executesql @sql

    --populate the table
    set @sql = 'BULK INSERT ' + @table + ' FROM ''' + @filepath + @table + '.txt'' WITH (FIELDTERMINATOR = '''
    + @delimiter  + ''', ROWTERMINATOR = ''' + @rowterminator + ''' )'

    print @sql
    exec sp_executesql @sql

    fetch next from c into @table

    end

    close c

    deallocate c

Und da haben Sie es - Ihre Datenbank wurde aus Textdateien wiederhergestellt. Sie können die .bak-Datei auf den Müllhaufen der Geschichte legen! Solange Sie keine Funktionen, gespeicherten Prozeduren, Ansichten, Einschränkungen oder Indizes haben.

Hier sind die vollständigen Skripte: Bad Idea Jeans Backup:

    declare @table varchar(255),
    @Database varchar(255),
    @filepath varchar(255),
    @filename varchar(255),
    @filetype varchar(255),
    @sql nvarchar(max),
    @delimiter varchar(255),
    @rowterminator varchar(255)

    set @Database = 'StackOverflow'
    set @filepath = 'C:\Data\' -- fix pretty-print mishandling of not-truly escaped '
    set @filetype = '.txt'
    set @delimiter = 'WhimmyWhammyWozzle'
    set @rowterminator = 'WubaLubaDubDub'

    --create database header

    if exists (select name from sys.tables st with (Nolock) where name = 'HeaderTable')

    begin
    drop table HeaderTable
    end

    SELECT
        st.name, sc.name 'Column_Name', t.Name 'Data_type',sc.max_length 'Max_Length',
        sc.precision, sc.scale, sc.is_nullable
        into HeaderTable
        FROM    
        sys.tables st
        inner join sys.columns sc on sc.object_id = st.object_id
        INNER JOIN sys.types t ON sc.user_type_id = t.user_type_id

        select * from HeaderTable

    declare c cursor local for

    select name from sys.tables so with (Nolock)

    open c

    fetch from c into @table

    while @@FETCH_STATUS = 0
    begin

    SET @filename = @table


    --output to txt
    set @sql = N'declare @bcp varchar(4000)
    set @bcp = ''bcp " select top 10000 * from ' + @table + ' " queryout '
    + @filepath +  @filename + @filetype + ' -t "' + @delimiter + '" -r "'
     + @rowterminator + '" -c -T -d ' + @Database + '''
    print @bcp

    EXECUTE master.dbo.xp_cmdshell @BCP'

    print @sql
     exec sp_executesql @sql

    fetch next from c into @table
    end

    close c

    deallocate c

Bad Idea Jeans Restore:

declare @table varchar(255),
    @column_string nvarchar(max),
    @sql nvarchar(max),
    @string nvarchar(max),
    @filepath varchar(255),
    @filename varchar(255),
    @filetype varchar(255),
    @delimiter varchar(255),
    @rowterminator varchar(255)


    set @filepath = 'C:\Data\' -- fix pretty-print mishandling of not-truly escaped '
    set @filetype = '.txt'
    set @delimiter = 'WhimmyWhammyWozzle'
    set @rowterminator = 'WubaLubaDubDub'

    --restore header
    if exists (select name from sys.tables where name = 'HeaderTable')
    begin
    drop table HeaderTable
    end

    create table HeaderTable
    (Table_Name varchar(255),
    Column_Name  varchar(255),
    Data_type  varchar(255),
    Max_Length  varchar(255),
    precision  varchar(255),
    scale varchar(255),
    is_nullable  varchar(255))

    set @sql = 'BULK INSERT HeaderTable FROM ''' + @filepath + 'HeaderTable'+ @filetype + ''' WITH (FIELDTERMINATOR = '''
     + @delimiter + ''', ROWTERMINATOR = ''' + @rowterminator + ''')'
    print @sql
    exec sp_executesql @sql

    --make some changes so that we can concatenate our create tables more easily
    update HeaderTable
    set Max_Length = 'max'
    where Max_length = -1

    update HeaderTable
    set Max_Length = '(' + Max_Length + ')'

    update HeaderTable
    set Max_Length = ''
    where Data_type in ( 'int', 'bigint', 'smallint', 'tinyint',
    'date','datetime', 'uniqueidentifier', 'sysname', 'bit')

    select * from HeaderTable

    --restore DB

    declare c cursor local for

    select distinct name from sys.columns
    where name != 'HeaderTable'

    open c
    fetch from c into @table

    while @@FETCH_STATUS = 0
    begin

    set @string = null

    set @string = (select stuff( (
    select ', ' + Column_Name + ' ' + Data_type  + Max_Length from HeaderTable
    where name = @table
    for xml path ('')),1,2,''))

    print @string

    set @sql =  ' if not exists (select top 1 name from sys.tables where name = ''' + @table + ''') begin
    create table ' + @table + ' (' + @string + ') end'

    print @sql
    --exec sp_executesql @sql

    set @sql = 'BULK INSERT ' + @table + ' FROM ' + '' + @filepath + @table + '.txt'' WITH (FIELDTERMINATOR = '''
    + @delimiter  + ''', ROWTERMINATOR = ''' + @rowterminator + ''' )'

    print @sql
    --exec sp_executesql @sql

    fetch next from c into @table

    end

    close c

    deallocate c
Brent Ozar
quelle
1
Dies beginnt zwar mit "Dies ist keine gute Lösung", es ist jedoch eine weitere Warnung erforderlich, falls jemand aufgrund der folgenden Probleme versucht, diesen Code zu verwenden: 1) Viele Datentypen werden nicht ordnungsgemäß verarbeitet: DECIMAL, SQL_VARIANT, XML, CLR , usw; 2) behandelt keine Schemata; 3) setzt keine Sortierung; 4) verarbeitet keine berechneten Spalten; 5) behandelt keine Spaltenoptionen: ROWGUID, SPARSE, IDENTITY usw.; 6) möglicher Verlust von Zeichenfolgendaten durch Erzwingen aller Codepages und von Unicode in die Standardcodepage des Systems; 8) Möglicher Hauptfehler: Tabellen können mit Spalten in einer anderen Reihenfolge als in der Originaltabelle neu erstellt werden. 9) mehr?
Solomon Rutzky