Truy vấn chia sẻ của người dùng: SQL động so với SQLCMD


15

Tôi phải cấu trúc lại và ghi lại một số foo.sqltruy vấn sẽ được chia sẻ bởi một nhóm hỗ trợ công nghệ DB (đối với cấu hình của khách hàng và những thứ tương tự). Có những loại vé xuất hiện thường xuyên trong đó mỗi khách hàng có máy chủ và cơ sở dữ liệu riêng, nhưng nếu không thì lược đồ là giống nhau trên bảng.

Thủ tục lưu trữ không phải là một lựa chọn tại thời điểm hiện tại. Tôi đang tranh luận về việc nên sử dụng động hay SQLCMD, tôi chưa sử dụng nhiều vì tôi hơi mới ở SQL Server.

Kịch bản SQLCMD tôi cảm thấy chắc chắn "trông" sạch hơn đối với tôi, và dễ đọc hơn và thực hiện các thay đổi nhỏ đối với các truy vấn khi cần, nhưng cũng buộc người dùng kích hoạt chế độ SQLCMD. Động khó hơn vì việc tô sáng cú pháp bị mất do truy vấn được viết bằng thao tác chuỗi.

Chúng đang được chỉnh sửa và chạy bằng Management Studio 2012, SQL phiên bản 2008R2. Một số ưu / nhược điểm của một trong hai phương thức hoặc một số "thực tiễn tốt nhất" của SQL Server đối với phương pháp này hay phương pháp khác là gì? Là một trong số họ "an toàn" hơn so với người khác?

Ví dụ động:

declare @ServerName varchar(50) = 'REDACTED';
declare @DatabaseName varchar(50) = 'REDACTED';
declare @OrderIdsSeparatedByCommas varchar(max) = '597336, 595764, 594594';

declare @sql_OrderCheckQuery varchar(max) = ('
use {@DatabaseName};
select 
    -- stuff
from 
    {@ServerName}.{@DatabaseName}.[dbo].[client_orders]
        as "Order"
    inner join {@ServerName}.{@DatabaseName}.[dbo].[vendor_client_orders]
        as "VendOrder" on "Order".o_id = "VendOrder".vco_oid
where "VendOrder".vco_oid in ({@OrderIdsSeparatedByCommas});
');
set @sql_OrderCheckQuery = replace( @sql_OrderCheckQuery, '{@ServerName}',   quotename(@ServerName)   );
set @sql_OrderCheckQuery = replace( @sql_OrderCheckQuery, '{@DatabaseName}', quotename(@DatabaseName) );
set @sql_OrderCheckQuery = replace( @sql_OrderCheckQuery, '{@OrderIdsSeparatedByCommas}', @OrderIdsSeparatedByCommas );
print   (@sql_OrderCheckQuery); -- For debugging purposes.
execute (@sql_OrderCheckQuery);

Ví dụ SQLCMD:

:setvar ServerName "[REDACTED]";
:setvar DatabaseName "[REDACTED]";
:setvar OrderIdsSeparatedByCommas "597336, 595764, 594594"

use $(DatabaseName)
select 
    --stuff
from 
    $(ServerName).$(DatabaseName).[dbo].[client_orders]
        as "Order"
    inner join $(ServerName).$(DatabaseName).[dbo].[vendor_client_orders]
        as "VendOrder" on "Order".o_id = "VendOrder".vco_oid
where "VendOrder".vco_oid in ($(OrderIdsSeparatedByCommas));

Mục đích của use ...kịch bản của bạn là gì? Có quan trọng đối với việc thực hiện đúng của truy vấn tiếp theo không? Tôi hỏi vì nếu thay đổi cơ sở dữ liệu hiện tại là một trong những kết quả mong đợi của truy vấn của bạn, phiên bản SQL động sẽ chỉ thay đổi trong phạm vi của truy vấn động, không phải trong phạm vi bên ngoài, không giống như biến thể SQLCMD (mà, của Tất nhiên, chỉ có một phạm vi).
Andriy M

Các usetuyên bố có thể có thể được bỏ qua, như phạm vi sẽ không được thay đổi trong kịch bản này đặc biệt anyways. Tôi có một số ít trường hợp sử dụng trong đó sẽ có các tìm kiếm trên máy chủ chéo nhưng điều đó có thể vượt quá phạm vi của bài đăng này.
Phrancis

Câu trả lời:


13

Chỉ cần đưa những thứ này ra khỏi đường đi:

  • Về mặt kỹ thuật, cả hai tùy chọn này đều là các truy vấn "động" / ad hoc không được phân tích cú pháp / xác thực cho đến khi chúng được gửi. Và cả hai đều dễ bị SQL Injection vì họ không được tham số hóa (mặc dù với kịch bản SQLCMD, nếu bạn đang đi trong một biến từ một kịch bản CMD sau đó bạn có cơ hội để thay thế 'với '', mà có thể hoặc không làm việc có thể tùy thuộc vào nơi các biến đang được sử dụng).

  • Có những ưu và nhược điểm đối với từng phương pháp:

    • Các tập lệnh SQL trong SSMS có thể dễ dàng được chỉnh sửa (rất tốt nếu đó là một yêu cầu) và làm việc với kết quả dễ dàng hơn so với đầu ra từ SQLCMD. Về mặt trái, người dùng đang ở trong một IDE nên rất dễ gây rối cho SQL và IDE giúp dễ dàng thực hiện nhiều thay đổi lớn mà không cần biết SQL để thực hiện.
    • Chạy các tập lệnh thông qua SQLCMD.EXE không cho phép người dùng dễ dàng thực hiện các thay đổi (không cần chỉnh sửa tập lệnh trong trình chỉnh sửa và sau đó lưu tập lệnh trước). Điều này thật tuyệt nếu người dùng không cần phải thay đổi tập lệnh. Phương pháp này cũng cho phép đăng nhập mỗi lần thực hiện nó. Về mặt trái, nếu có nhu cầu thường xuyên chỉnh sửa các tập lệnh thì nó sẽ khá cồng kềnh. Hoặc, nếu người dùng cần quét qua 100 nghìn hàng của tập kết quả và / hoặc sao chép các kết quả đó vào Excel hoặc một cái gì đó, thì điều đó cũng khó khăn trong phương pháp này.

Nếu những người hỗ trợ của bạn không thực hiện các truy vấn đặc biệt và chỉ điền vào các biến đó, thì họ không cần phải ở SSMS nơi họ có thể chỉnh sửa các tập lệnh đó và thực hiện các thay đổi không mong muốn.

Tôi sẽ tạo các tập lệnh CMD để nhắc người dùng về các giá trị biến mong muốn và sau đó gọi SQLCMD.EXE với các giá trị đó. Tập lệnh CMD thậm chí có thể ghi nhật ký thực thi vào một tệp, hoàn thành với dấu thời gian và các giá trị biến được gửi.

Tạo một tập lệnh CMD trên mỗi tập lệnh SQL và đặt vào thư mục dùng chung được nối mạng. Một người dùng nhấp đúp vào tập lệnh CMD và nó chỉ hoạt động.

Đây là một ví dụ:

  • nhắc người dùng cho tên máy chủ (chưa có lỗi kiểm tra)
  • nhắc người dùng cho tên cơ sở dữ liệu
    • nếu để trống, nó sẽ liệt kê các cơ sở dữ liệu trên máy chủ được chỉ định và nhắc lại
    • nếu tên cơ sở dữ liệu không hợp lệ, người dùng sẽ được nhắc lại
  • nhắc người dùng về OrderIDsSpayatedByCommas
    • nếu trống, nhắc người dùng một lần nữa
  • chạy tập lệnh SQL, chuyển vào giá trị %OrderIDsSeparatedByCommas%dưới dạng biến SQLCMD$(OrderIDsSeparatedByCommas)
  • ghi nhật ký ngày, thời gian, ServerName, DatabaseName và OrderIDsSpayatedByCommas vào một tệp nhật ký có tên cho Đăng nhập Windows chạy tập lệnh (theo cách này, nếu thư mục nhật ký là mạng và có nhiều người sử dụng thì sẽ không có ghi sự tranh chấp về tệp nhật ký như có thể xảy ra nếu USERNAME được đăng nhập vào tệp cho mỗi mục nhập)
    • nếu thư mục tệp nhật ký không tồn tại, nó sẽ được tạo

Kiểm tra tập lệnh SQL (có tên: FixPro HiệuX.sql ):

SELECT  *
FROM    sys.objects
WHERE   [schema_id] IN ($(OrderIdsSeparatedByCommas));

Tập lệnh CMD (có tên: FixPro HiệuX.cmd ):

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

SET ScriptLogPath=\\server\share\RunSqlCmdScripts\LogFiles

CLS

SET /P ScriptServerName=Please enter in a Server Name (leave blank to exit): 

IF "%ScriptServerName%" == "" GOTO :ThisIsTheEnd

REM echo %ScriptServerName%

:RequestDatabaseName
ECHO.
SET /P ScriptDatabaseName=Please enter in a Database Name (leave blank to list DBs on %ScriptServerName%): 

IF "%ScriptDatabaseName%" == "" GOTO :GetDatabaseNames

SQLCMD -b -E -W -h-1 -r0 -S %ScriptServerName% -Q "SET NOCOUNT ON; IF (NOT EXISTS(SELECT [name] FROM sys.databases WHERE [name] = N'%ScriptDatabaseName%')) RAISERROR('Invalid DB name!', 16, 1);" 2> nul

IF !ERRORLEVEL! GTR 0 (
    ECHO.
    ECHO That Database Name is invalid. Please try again.

    SET ScriptDatabaseName=
    GOTO :RequestDatabaseName
)

:RequestOrderIDs
ECHO.
SET /P OrderIdsSeparatedByCommas=Please enter in the OrderIDs (separate multiple IDs with commas): 

IF "%OrderIdsSeparatedByCommas%" == "" (

    ECHO.
    ECHO Don't play me like that. You gots ta enter in at least ONE lousy OrderID, right??
    GOTO :RequestOrderIDs
)


REM Finally run SQLCMD!!
SQLCMD -E -W -S %ScriptServerName% -d %ScriptDatabaseName% -i FixProblemX.sql -v OrderIdsSeparatedByCommas=%OrderIdsSeparatedByCommas%

REM Log this execution
SET ScriptLogFile=%ScriptLogPath%\%~n0_%USERNAME%.log
REM echo %ScriptLogFile%

IF NOT EXIST %ScriptLogPath% MKDIR %ScriptLogPath%

ECHO %DATE% %TIME% ServerName=%ScriptServerName%    DatabaseName=[%ScriptDatabaseName%] OrderIdsSeparatedByCommas=%OrderIdsSeparatedByCommas%   >> %ScriptLogFile%

GOTO :ThisIsTheEnd

:GetDatabaseNames
ECHO.
SQLCMD -E -W -h-1 -S %ScriptServerName% -Q "SET NOCOUNT ON; SELECT [name] FROM sys.databases ORDER BY [name];"
ECHO.
GOTO :RequestDatabaseName

:ThisIsTheEnd
PAUSE

Hãy chắc chắn để chỉnh sửa ScriptLogPathbiến về phía trên cùng của tập lệnh.

Ngoài ra, các tập lệnh SQL (được chỉ định bởi -icông tắc dòng lệnh cho SQLCMD.EXE ) có thể được hưởng lợi từ việc có một đường dẫn đủ điều kiện, nhưng không hoàn toàn chắc chắn.

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.