Fehler auslösen, wenn der BCP-Befehl keine Daten in eine Datei kopiert

7

Mit dem BCP-Befehl generiere ich Dateien von SQL Server DB-Tabellen. Der BCP-Befehl erstellt eine leere Datei für jede Tabelle, wenn keine Daten in die Dateien ausgegeben werden können. Dies kann durch einen Fehler in der geschriebenen Abfrage verursacht werden oder eine leere Variable wird an den BCP-Befehl übergeben.

Gibt es eine Möglichkeit, diese Ereignisse als Fehler zu erfassen und einen Fehlercode zurückzugeben?

Ich führe dies von einer gespeicherten Prozedur aus. Gibt es eine Möglichkeit, wie ich in SP damit umgehen kann?

MySQL DBA
quelle
Können Sie bitte Ihre Frage aktualisieren, um genau anzugeben, wie Sie BCP in der gespeicherten Prozedur aufrufen?
Solomon Rutzky

Antworten:

8

Powershell ist dein Freund hier. Wenn Sie mit cmd-Befehlen in Powershell arbeiten, können Sie die $LASTEXITCODEVariable verwenden, um das Ergebnis des von Ihnen ausgeführten Befehls zu lesen.

Der folgende Code übergibt einen BCPBefehl an das Cmdlet Invoke-Expression und erfasst dessen Ausgabe.

$OutputPath = "C:\temp\Numbers-20151230.dat"

try
{
    $Command = "bcp dbo.Numbers out $OutputPath -T -n -S Localhost\JamesA_Test -d UtilityDB"
    $Output = Invoke-Expression -command $Command

    if ($LASTEXITCODE)
    { 
        throw $Output
    }
}
catch
{
    Write-Host "BCP command failed: $Output"   
}

Ich bin mir nicht sicher, wie Sie mit dem Fehler umgehen möchten, daher habe ich Write-Hostden Fehler nur für dieses Beispiel angezeigt. Sie können den Fehler im Ereignisprotokoll, in einer Datei, einer Tabelle in SQL usw. protokollieren.

James Anderson
quelle
Vielen Dank, James, aber ich führe dies über eine gespeicherte Prozedur aus. Gibt es eine Möglichkeit, wie ich damit in SP umgehen kann?
MySQL DBA
Mit dem Powershell-Code können Sie einen SQL Agent-Job erstellen, den Sie von Ihrem SP aus aufrufen. Dieser Link ist ebenfalls eine Option, aber ich würde dies nicht in der Produktion empfehlen. mssqltips.com/sqlservertip/2087/…
James Anderson
2

Die Frage ist etwas vage in Bezug darauf, wie BCP ausgeführt wird, außer dass es innerhalb einer gespeicherten Prozedur durchgeführt wird. Da aber , dass alles , was wir wirklich im Moment wissen, werde ich davon ausgehen , dass Sie anrufen BCP.EXEaus xp_cmdshell.

Wenn Sie einfach die tatsächlichen Fehler erfassen möchten, die von ausgelöst werden BCP, ist dies sehr einfach, da der ERRORLEVELWert als INTvon der xp_cmdshellgespeicherten Prozedur zurückgegeben wird:

DECLARE @ErrorLevel INT;
EXEC @ErrorLevel = xp_cmdshell
         N'BCP "SELECT * FROM sys.objects where 1=  " queryout C:\temp\BCPtest.txt -T -w ';
SELECT @ErrorLevel;

Kehrt zurück:

1

Wenn Sie jedoch Abfragen behandeln möchten, die erfolgreich abgeschlossen wurden und dennoch 0 Zeilen als "Fehler" -Zustand zurückgeben, ist dies ebenfalls möglich. Dies erfordert nur etwas mehr Aufwand:

DECLARE @ErrorLevel INT;
EXEC @ErrorLevel = xp_cmdshell
   N'BCP "SELECT * FROM sys.objects where 1= 0 " queryout C:\temp\BCPtest.txt -T -w && (FORFILES /P C:\TEMP\ /M BCPtest.txt /C "CMD /C IF @fsize LSS 3 DEL C:\TEMP\BCPtest.txt" & IF NOT EXIST C:\temp\BCPtest.txt EXIT -3)';
SELECT @ErrorLevel;

Kehrt zurück:

-3

Bitte beachten Sie, dass die lange Befehlszeile entweder als einzelne Zeile beibehalten oder in ein .CMDSkript eingefügt werden muss, damit sie ordnungsgemäß funktioniert.

Die zusätzliche Logik in einem besser lesbaren Format lautet:

&& (
    FORFILES /P C:\TEMP\
             /M BCPtest.txt
             /C "CMD /C IF @fsize LSS 3 DEL C:\TEMP\BCPtest.txt"
    & IF NOT EXIST C:\temp\BCPtest.txt EXIT -3
   )

Erläuterung:

  • &&Dieser Operator führt den Befehl auf der rechten Seite nur aus, wenn der Befehl auf der linken Seite erfolgreich ausgeführt wurde. Der Grund für die Verwendung dieses Operators besteht darin BCP, den ERRORLEVELWert festlegen zu können, wenn ein Fehler auftritt. die Befehle auf der rechten Seite werden nur benötigt , wenn BCPsie nicht in einen Fehler läuft noch zurück 0 Zeilen.
  • (Die Klammern gruppieren die darin enthaltenen Befehle. Dadurch können wir die Befehle FORFILESund IFnur ausführen, wenn BCPsie erfolgreich abgeschlossen wurden. Andernfalls wird keiner der Befehle innerhalb von (und )ausgeführt.
  • FORFILESDurchläuft eine Liste von Dateien, die von bestimmten Schaltern angegeben werden, und führt für jede Datei einen Befehl aus, der den Kriterien entspricht (ähnlich dem findBefehl in Unix).

    • /Pist der Startpfad. Es muss ein Pfad sein und darf den Dateinamen nicht enthalten.
    • /M ist der Dateinamenfilter oder "Maske".
    • /Cist der Befehl, der für jede Datei ausgeführt werden soll. Es muss so ziemlich anfangen CMD /C. Der IFBefehl testet die Größe der Datei, die über die @fsizeVariable gefunden wird, die durch ersetzt wird. FORFILESWenn sie kleiner als 3 ist (dh LSS 3), wird die Datei einfach gelöscht. Bei meinen Tests stellte ich fest, dass die Verwendung von entweder -coder nichts mit BCPzur Angabe der ASCII / VARCHAR-Ausgabe zu einer leeren Datei mit 0 Byte führen würde. Die Verwendung der -wUnicode / NVARCAR-Ausgabe führt jedoch zu leeren Dateien mit 2 Bytes (dies sollte das Byte Order Mark sein). Daher deckt das Testen auf "Größe <3" beide Szenarien ab.

      Der Grund für das Löschen der Datei ist, dass sie im übergeordneten Prozess getestet werden kann. Da CMD /Cder Befehl zum Ausführen des Befehls für Dateien verwendet wird, die von gefunden werden FORFILES, handelt es sich um einen Unterprozess, und Umgebungsvariablen bleiben nicht bestehen (ähnlich wie beim Erstellen einer lokalen temporären Tabelle in Dynamic SQL), und das Beenden mit einem Fehlercode wird einfach an das übergeordnete Element zurückgegeben Prozess wie es schon gehen würde. Das Erstellen einer leeren Datei als Indikator ist eine Option, muss dann aber entweder bereinigt werden oder ist unübersichtlich. Und wenn der Prozess als Fehler angesehen wird, weil keine Zeilen zurückgegeben werden, möchten wir die Datei sowieso nicht.

  • & Dieser Operator führt den Befehl auf der rechten Seite aus, unabhängig vom Erfolgs- oder Fehlerstatus des Befehls auf der linken Seite.
  • IF Dies führt einen einfachen Test auf das Vorhandensein der angegebenen Datei durch. Wenn diese Datei nicht vorhanden ist, wird der angegebene Befehl ausgeführt.
    • EXIT -3Dadurch wird der aktuelle Prozess (der Betriebssystemprozess der obersten Ebene, von dem gestartet wurde xp_cmdshell) beendet, während der ERRORLEVELWert auf gesetzt wird -3. Sie können den -3Wert in einen beliebigen Wert ändern. Achten Sie jedoch darauf, keine Werte zu verwenden, die bereits von verwendet werden, BCPdamit Sie zwischen diesen unterscheiden können. Das muss EXITnur explizit verwendet werden, wenn Sie festlegen möchten ERRORLEVEL(ähnlich wie entweder keine Angabe RETURNam Ende einer gespeicherten Prozedur oder Angabe, um einen Nichtwert zurückzugeben 0).
Solomon Rutzky
quelle