Tăng lỗi nếu lệnh BCP không thể đổ dữ liệu vào một tệp


7

Sử dụng lệnh BCP Tôi đang tạo các tệp của bảng SQL Server DB. Lệnh BCP tạo một tệp trống cho mỗi bảng khi không thể kết xuất dữ liệu vào các tệp. Điều này có thể được gây ra bởi một lỗi trong truy vấn được viết hoặc một biến trống được truyền cho lệnh BCP.

Có cách nào chúng ta có thể nắm bắt các sự kiện này là lỗi và làm cho nó trả về một số mã lỗi không?

Tôi đang thực hiện điều này từ một thủ tục được lưu trữ. Có cách nào tôi có thể xử lý việc này trong SP không?


Bạn có thể vui lòng cập nhật câu hỏi của mình để đưa vào chính xác cách bạn gọi BCP trong Quy trình được lưu trữ không?
Solomon Rutzky

Câu trả lời:


8

Powershell là bạn của bạn ở đây. Khi làm việc với các lệnh cmd trong Powershell, bạn có thể sử dụng $LASTEXITCODEbiến để đọc kết quả của lệnh bạn đã thực hiện.

Đoạn mã dưới đây chuyển một BCPlệnh vào lệnh ghép ngắn Invoke-Expression và bắt đầu ra của nó.

$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"   
}

Tôi không chắc bạn muốn xử lý lỗi như thế nào nên tôi chỉ sử dụng Write-Hostđể hiển thị lỗi cho ví dụ này. Bạn có thể ghi lỗi vào nhật ký sự kiện, tệp, bảng trong SQL, v.v.


Cảm ơn James nhưng tôi đang thực hiện điều này từ một thủ tục được lưu trữ. Có cách nào tôi có thể xử lý việc này trong SP không?
MySQL DBA

Bạn có thể sử dụng mã Powershell để tạo Công việc Tác nhân SQL mà bạn gọi từ SP của mình. Liên kết này cũng là một tùy chọn nhưng tôi không khuyên bạn nên làm điều này trong sản xuất. mssqltips.com/sqlservertip/2087/ Lần
James Anderson

2

Câu hỏi hơi mơ hồ liên quan đến cách BCP được thực thi, bên ngoài nó được thực hiện trong Quy trình được lưu trữ. Nhưng vì đó là tất cả những gì chúng tôi thực sự biết vào lúc này, tôi sẽ cho rằng bạn đang gọi BCP.EXEtừ xp_cmdshell.

Nếu bạn chỉ muốn ghi lại các lỗi thực tế đang bị ném BCP, thì điều đó rất dễ thực hiện vì ERRORLEVELgiá trị được trả về dưới dạng INTtừ xp_cmdshellthủ tục được lưu trữ:

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

Trả về:

1

Tuy nhiên, nếu bạn muốn xử lý các truy vấn hoàn thành thành công nhưng trả về 0 hàng là điều kiện "lỗi", thì điều đó cũng có thể được thực hiện, nó chỉ cần nỗ lực hơn một chút:

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;

Trả về:

-3

Xin lưu ý rằng dòng lệnh dài cần phải được giữ dưới dạng một dòng đơn hoặc được đặt vào một .CMDtập lệnh để nó hoạt động chính xác.

Logic bổ sung, ở định dạng dễ đọc hơn, là:

&& (
    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
   )

Giải trình:

  • &&Toán tử này sẽ chạy lệnh ở phía bên phải chỉ khi lệnh ở phía bên trái hoàn thành thành công. Lý do sử dụng toán tử này là để cho phép BCPđặt ERRORLEVELgiá trị nếu nó gặp lỗi; các lệnh ở phía bên phải chỉ cần thiết nếu BCPkhông không chạy vào một lỗi chưa trả về 0 hàng.
  • (nhóm ngoặc đơn các lệnh bên trong chúng với nhau. Điều này cho phép chúng tôi chạy các lệnh FORFILESIFchỉ khi BCPhoàn thành thành công. Mặt khác, không có lệnh nào trong ()được thực thi.
  • FORFILESquay vòng qua danh sách các tệp, được chỉ định bởi các công tắc nhất định và thực thi lệnh cho mỗi tệp phù hợp với tiêu chí (tương tự findlệnh trong Unix).

    • /Plà con đường bắt đầu. Nó phải là một đường dẫn và không thể chứa tên tệp.
    • /M là bộ lọc tên tệp hoặc "mặt nạ".
    • /Clà lệnh để chạy cho mỗi tệp. Nó khá nhiều nhu cầu để bắt đầu CMD /C. Các IFlệnh kiểm tra kích thước của tập tin được tìm thấy qua các @fsizebiến được thay thế bằng FORFILESvà nếu nó là ít hơn 3 (ví dụ LSS 3), sau đó nó sẽ chỉ xóa các tập tin. Trong thử nghiệm của tôi, tôi thấy rằng việc sử dụng một trong hai -choặc không có gì BCPđể chỉ ra đầu ra ASCII / VARCHAR sẽ dẫn đến một tệp trống 0 byte. Nhưng việc sử dụng -wđể chỉ ra kết quả đầu ra Unicode / NVARCAR trong các tệp trống 2 byte (phải là Dấu thứ tự byte). Do đó, kiểm tra "kích thước <3" bao gồm cả hai kịch bản.

      Lý do để xóa tệp là vì nó là thứ có thể được kiểm tra trong quy trình cha. Vì CMD /Cđang được sử dụng để chạy lệnh trên các tệp được tìm thấy FORFILES, nên nó là một biến phụ và các biến môi trường sẽ không tồn tại (tương tự như tạo bảng tạm thời cục bộ trong Dynamic SQL) và thoát khỏi mã lỗi chỉ đơn giản là trả về cho cha quá trình như nó đã xảy ra Tạo một tập tin trống làm chỉ báo là một tùy chọn, nhưng sau đó nó cần phải được dọn sạch hoặc lộn xộn. Và nếu quy trình đang được coi là lỗi vì không trả lại bất kỳ hàng nào, thì dù sao chúng tôi cũng không muốn tệp.

  • & Toán tử này thực thi lệnh ở phía bên phải bất kể trạng thái thành công hay thất bại của lệnh ở phía bên trái.
  • IF đây là một thử nghiệm đơn giản cho sự tồn tại của tệp được chỉ định và nếu tệp đó không tồn tại, thì nó sẽ chạy lệnh được chỉ định.
    • EXIT -3quá trình này thoát khỏi quy trình hiện tại (là quy trình HĐH cấp cao nhất được bắt đầu bởi xp_cmdshell) trong khi đặt ERRORLEVELgiá trị thành -3. Bạn có thể thay đổi thành -3bất kỳ giá trị nào bạn thích, chỉ cần chú ý không sử dụng các giá trị đã được sử dụng BCPđể bạn có thể phân biệt giữa chúng. Các EXITchỉ là cần thiết để được sử dụng một cách rõ ràng nếu bạn muốn thiết lập ERRORLEVEL(tương tự như một trong hai không quy định cụ thể RETURNở phần cuối của một thủ tục lưu trữ, hoặc cần phải xác định nó để vượt qua trở lại một không 0giá trị).
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.