2016-01-13 8 views
5

Ich versuche, einige sqlcmd über T-SQL auf SQL Server 2008 auszuführen. Es gibt einen Teil meines Codes, wo ich eine Datendateigröße und ob diese Dateigröße überprüfen ist nicht gleich 0, dann starten Sie die spezifische Tabelle zu löschen, damit ich BCP in neuen Daten kann.Kombinieren Sie die Befehlszeile und T-SQL - SQL Server

Unten ist mein Code, der nicht ausgeführt wird:

SET @myVariable = ' 
SETLOCAL 
FOR %%R IN (X:\Main Folder\Data\'[email protected]+'_'[email protected]+'.dat) DO SET size=%%~zR 
IF %size% NEQ 0 (
     SQLCMD -E -S my-server-name -Q "DELETE FROM '[email protected]+'.'[email protected]+'.'[email protected]+';" >> X:\Main Folder\Log\Log.txt 
)' 

EXEC master..xp_cmdshell @myVariable 

Aus irgendeinem Grund, wenn ich meine gespeicherte Prozedur ausführen, scheint der Code oben zu überspringen, weil es keine Fehlermeldungen nicht zurückschießen.

EDIT: Nach der Anpassung der Abstand und mein Code, @ myVariable, wird jetzt ausgeführt. Es funktioniert jedoch immer noch nicht in Bezug darauf, dass es immer noch die Tabelle löscht, obwohl die Datendateigröße = 0 ist. Wenn ich es jedoch innerhalb einer Batch-Datei fest programmiere, funktioniert es einwandfrei. Irgendwelche Ideen?

+0

Warum Sie nicht über eine gespeicherte Prozedur zur Datenbank hinzufügen, dass Ihr Scheck tut und löschen oder kürzen. Dann führe dies über die Befehlszeile aus? –

+0

Zitat um den Dateinamen? Sowohl in der 'for' als auch in der umgeleiteten Ausgabe. – shawnt00

+0

Abhängig von Ihrer Konfiguration und der Versionsnummer von OS fehlt a/i nach IF. Probieren Sie IF/i. – Christian4145

Antwort

1

Ich erkannte, dass ich die Tabelle Zählung mit dieser Methode anstelle einer Datendateigröße überprüfen:

SET @sqlCheck = 'SQLCMD -E -S ServerA -Q "IF (SELECT COUNT(*) FROM '[email protected]aseName+'.'[email protected]+'.'[email protected]+') > 0 BEGIN DELETE FROM ServerB.'[email protected]+'.'[email protected]+'.'[email protected]+' END;"' 
       EXEC MASTER..xp_cmdshell @sqlcheck 
+1

Es ist besser, diesen Code in eine gespeicherte Prozedur wie in meinem Kommentar erwähnt auf deine Frage. Wenn Ihr Code irgendeine Art von Benutzereingabe benötigt, können Sie sich selbst für SQL-Injection-Angriffe öffnen. –

+0

Der Code befindet sich in einer gespeicherten Prozedur. Danke – AznDevil92

+0

Warum sollten Sie vor dem Löschen überprüfen, ob die Tabelle leer ist oder nicht? Das Zählen dauert eine Weile und hat keinen zusätzlichen Nutzen. Führen Sie einfach den Befehl DELETE aus; Unabhängig von der Anzahl der Datensätze, mit denen Sie begonnen haben (null oder tausend), ist das Endergebnis identisch: eine leere Tabelle. – deroby

1

Sie verwenden X:\ in Ihrem Code. Der Code wird jedoch unter dem Dienstkonto für SQL Server ausgeführt. Für dieses Konto ist möglicherweise x: nicht verfügbar.

Ich würde vorschlagen, ein UNC anstelle eines zugeordneten Laufwerks zu verwenden. Stellen Sie außerdem sicher, dass Ihr Dienst unter einem Domänenkonto ausgeführt wird und das Domänenkonto über alle erforderlichen Berechtigungen für die UNC verfügt.

+0

Ich habe gerade versucht, UNC wie \\ Servername \ X $ \ .... aber es hat nicht funktioniert.Und ich glaube nicht, dass es ein Berechtigungsproblem ist, da alle meine anderen xp_cmdshell-Befehle ausgeführt werden. – AznDevil92

2

Ich kann nicht mit Ihrem DOS-Befehl sprechen. Ich kann jedoch vorschlagen, Ole Automation Procedures zu verwenden, um die Dateigröße zu erhalten. Auf diese Weise müssen Sie nicht auf Batch-Befehle angewiesen sein.

Zuerst müssen Sie Ole Automation Procedures auf dem SQL-Server-Instanz ermöglichen, wie folgt:

sp_configure 'show advanced options', 1; 
GO 
RECONFIGURE; 
GO 
sp_configure 'Ole Automation Procedures', 1; 
GO 
RECONFIGURE; 
GO 

Sie müssen dies nur einmal tun.


Als nächstes ist ein Skript, das die Dateigröße erhält. In diesem Beispiel wird davon ausgegangen, dass es sich um eine Datei namens C:\Temp\testfile.txt handelt. Das Skript wählt die Größe aus, wenn die Datei vorhanden ist, oder wählt 0, wenn dies nicht der Fall ist. Sie können dieses Skript als Beispiel nehmen, um basierend auf der Größe zu tun, was Sie wollen.

Hier geht:

DECLARE @hr INT; 
DECLARE @size INT; 
DECLARE @obj_file INT; 
DECLARE @obj_file_system INT; 
DECLARE @file_name VARCHAR(100)='C:\Temp\testfile.txt'; 

-- Create a FileSystemObject. Create this once for all subsequent file manipulation. Don't forget to destroy this object once you're done with file manipulation (cf cleanup) 
EXEC @hr = sp_OACreate 'Scripting.FileSystemObject', @obj_file_system OUT; 
IF @hr<>0 GOTO __cleanup; 

-- Get a handle for the file. Don't forget to release the handle for each file you get a handle for (see cleanup). The return will be different from 0 if the file doesn't exist 
EXEC @hr = sp_OAMethod @obj_file_system, 'GetFile', @obj_file out, @file_name; 
IF @hr<>0 GOTO __print_file_size; 

-- Retrieve the file size. 
EXEC sp_OAGetProperty @obj_file, 'size', @size OUT; 

__print_file_size: 
SELECT ISNULL(@size,0) AS file_size; 

__cleanup: 
EXEC sp_OADestroy @obj_file_system; 
EXEC sp_OADestroy @obj_file; 
3

Sie müssen einzelne % in Ihrer for Schleife verwenden, wie Sie den Code nicht in einer Batch-Datei ausführen, sind (das erfordert %%) finden Sie in diesem post für einige weitere Klärung. So sollte Ihre for-Schleife sein:

2

Ich denke, das Problem ist, dass Sie keine Anführungszeichen um Ihre Dateinamen verwenden. Dieses oberste Verzeichnis enthält ein Leerzeichen.

Jacos Antwort sieht korrekt aus und ich bin sicher, dass es ein Teil des Problems war. Sie sollten wahrscheinlich initialisieren size nur um sicher zu sein:

SET @myVariable = ' 
SETLOCAL 
SET size=0 
FOR %R IN ("X:\Main Folder\Data\'[email protected]+'_'[email protected]+'.dat") DO SET size=%~zR 
IF %size% NEQ 0 (
     SQLCMD -E -S my-server-name -Q "DELETE FROM '[email protected]+'.'[email protected]+'.'[email protected]+';" >> "X:\Main Folder\Log\Log.txt" 
)' 

EXEC master..xp_cmdshell @myVariable 

ohne die Anführungszeichen der for Schleife behandeln wird, dass seine „Set“ als zwei Elemente durch den Raum getrennt (die Terminologie in for /? verwendet).Wenn Ihr aktuelles Verzeichnis X:\Main Folder\Data\ ist, würde es trotzdem funktionieren, da es den letzten als relativen Pfad zur .dat Datei und dann immer noch set s den richtigen Wert auf dem letzten Durchlauf sieht.

+0

Ich habe Anführungszeichen und doppelte einfache Anführungszeichen hinzugefügt und das zweite Prozent entfernt. Immer noch kein Glück – AznDevil92

+0

Haben Sie bereits eine Größe in Ihrer Umgebung? Haben Sie bestätigt, dass es tatsächlich die Schleife macht und den Wert erreicht? – shawnt00

+0

Ein anderer Gedanke: Ist es möglich, dass Sie keine Befehlserweiterungen aktiviert haben? Ich glaube, das könnte dazu führen, dass es auf dem 'if ... neq' fehlschlägt und dann bedingungslos den Löschvorgang ausführt. – shawnt00

2

nicht sicher, ob dies eine Option für Sie ist aber da Sie auf SQL 2008 sind, sollten Sie in der Lage sein, stattdessen Powershell-Befehl zu verwenden:

DECLARE @File varchar(100), @outputFile varchar(100) 
DECLARE @cmd varchar(1000) 
SELECT @File = 'path_to_file' 
SELECT @outputFile = 'path_to_output_file' 

SELECT @cmd = 'powershell.exe -command "if((Get-Item '''[email protected]+''').length -gt 0) {&sqlcmd.exe -E -S SERVERNAME -Q ''SELECT name FROM master.sys.databases ;'' -o '[email protected]+'}"' 


SELECT @cmd 

exec master..xp_cmdshell @cmd 

ich eingecheckt haben und es scheint, auf die zusammen zu arbeiten, je Dateigröße.

2

Warum würden Sie gehen ‚down‘ auf der Kommandozeile überhaupt? (Es gibt Gründe, warum xp_cmdshell standardmäßig deaktiviert ist)

Könnten Sie nicht einfach Schleife über Ihre Tabellen (sys.tables WHERE name LIKE ...) und dann

  • eine Schattenkopie der Tabelle erstellen (SELECT INTO)
  • BULK INSERT aus die (erwartete) Datei in die Schattentabelle (in einer TRY..CATCH, um Situationen zu behandeln, in denen die Datei nicht existiert, oder leer ist, oder beschädigt ist.)
  • Wenn es Daten in der Schattentabelle gibt, dann DELETE die tatsächliche Tabellensätze und verschieben die Daten über
  • Wenn es keine Daten in der Schattentabelle gibt dann DELETE die tatsächlichen Tabelle Datensätze (oder lassen Sie sie in, wenn Sie eine fehlende oder leere BCP-Datei bedeutet, dass es später ankommen wird und Sie sind mit der aktuellen Version stecken jetzt) ​​
  • DROP der Schattentabelle wieder
1

Sie die Namen der Datenbanken und Tabellen bereits zu kennen scheint, so dass Sie die folgende verwenden können, die im Grunde eine dIR für die Datei macht Sie suchen und prüft, ob es '0 bytes' ist, wenn ja, dann macht es was auch immer du willst. Dinge zu beachten:

  • STRING VORLAGEN - Wenn Strings Gebäude, Ich mag eine ‚Vorlage‘ bauen und dann innerhalb der Zeichenfolge zu ersetzen. Dies ist ein guter Weg, um sicherzustellen, dass Sie die richtige Anzahl von Anführungszeichen, Klammern usw. haben. Ich habe es hier zweimal gemacht, einmal um den DIR-Befehl zu erstellen und dann um den TRUNCATE-Befehl zu erstellen.

  • TRUNCATE - obwohl nicht Teil, wenn Ihre Frage, möchten Sie möglicherweise eine TRUNCATE anstelle von DELETE FROM verwenden. Wenn Sie eine Million Zeilen in Ihrer Tabelle hatten, kann DELETE FROM 2 Minuten dauern, während TRUNCATE immer 0-Sekunden dauert, um zu laufen.

Ihre Antwort:

SET NOCOUNT ON; 

DECLARE @DatabaseName VARCHAR(50) = 'database1' 
DECLARE @TableName VARCHAR(50) = 'table1' 

DECLARE @PathTemplate VARCHAR(50) = 'dir c:\temp\{@DatabaseName}_{@TableName}.txt' 

SET @PathTemplate = REPLACE(@PathTemplate, '{@DatabaseName}', @DatabaseName); 
SET @PathTemplate = REPLACE(@PathTemplate, '{@TableName}', @TableName); 

DECLARE @FileNames AS TABLE (FileNames VARCHAR(100)) 

INSERT @FileNames (FileNames) 
exec xp_cmdshell @PathTemplate 

IF EXISTS (SELECT 1 FROM @FileNames WHERE FileNames LIKE '%0 bytes') 
BEGIN 
    PRINT 'No Content/Missing File' 
END 
ELSE BEGIN 

    DECLARE @SqlExc VARCHAR(500) = 'TRUNCATE TABLE [{@DatabaseName}].[dbo].[{@TableName}]' 
    SET @SqlExc = REPLACE(@SqlExc, '{@DatabaseName}', @DatabaseName); 
    SET @SqlExc = REPLACE(@SqlExc, '{@TableName}', @TableName); 

    PRINT @SqlExc 
    -- sp_executesql @SqlExc <-- Do this in production 

END 
Verwandte Themen