2013-04-23 39 views
9

Tôi có một biến trong R mà tôi muốn chuyển sang cơ sở dữ liệu. Tôi có thể sử dụng paste như nhiều đề xuất khi đọc kết quả của Google, nhưng điều đó là không an toàn do lỗ hổng SQL injection. Tôi thích nội dung như sau:Truy vấn tham số với RODBC

x <- 42 
sqlQuery(db, 'SELECT Id, Name FROM People WHERE Age > ?;', bind=c(x)) 

Có thể sử dụng truy vấn được tham số hóa với RODBC không? Nếu không, có thư viện thay thế nào hỗ trợ chúng không?

Tôi đang sử dụng SQL Server, RODBC 1.3-6 và R 3.0.0.

+0

Không phải là tôi biết. 'sprintf' là lựa chọn duy nhất khác để' dán' mà tôi biết, nhưng điều đó cũng không làm bất kỳ việc khử trùng nào. – joran

+0

Dường như được đề cập [ở đây] (http://stackoverflow.com/a/1645086/142019) là "trình giữ chỗ" nhưng tôi vẫn không thể tìm thấy cách sử dụng chúng. –

+1

sẽ làm sạch chuỗi trước khi dán nó không thực hiện điều tương tự? –

Trả lời

5

Đây là các tùy chọn mà tôi biết khi sử dụng RODBC. Tôi biết rằng RSQLite hỗ trợ ràng buộc tham số nguyên bản, nhưng đó là thường không phải là một tùy chọn cho hầu hết mọi người.

# Note that sprintf doesn't quote character values. The quotes need 
# to be already in the sql, or you have to add them yourself to the 
# parameter using paste(). 
q <- "select * from table where val1 = '%s' and val2 < %d and val3 >= %f" 
sprintf(q,"Hey!",10,3.141) 

# The gsub route means you can't easily use a single placeholder 
# value. 
q <- "select * from table where val1 = '?' and val2 < ? and val3 >= ?" 
gsub("?","Value!",q,fixed = TRUE) 

Tôi giải quyết nhiều truy vấn đóng hộp cho công việc yêu cầu các thông số khác nhau. Vì trong trường hợp của tôi, tôi chỉ có các đặc quyền SELECT và tôi là người duy nhất đang chạy mã số của mình, tôi thực sự không cần phải lo lắng về việc xác thực.

Vì vậy, về cơ bản, tôi đã đi theo tuyến đường gsub, để có thể lưu trữ tất cả các truy vấn của tôi trong các tệp .sql riêng biệt theo số . Điều này là do các truy vấn thường dài đủ để giữ chúng trong tệp .R của tôi chỉ trở nên khó sử dụng. Giữ chúng riêng biệt giúp tôi dễ dàng chỉnh sửa và duy trì chúng với định dạng và làm nổi bật rằng phù hợp hơn với SQL.

Vì vậy, tôi đã viết một số hàm nhỏ đọc truy vấn từ tệp .sql và ràng buộc bất kỳ tham số nào. Tôi viết truy vấn có tham số được biểu thị bằng dấu hai chấm, tức là :param1:, :param2:.

Sau đó, tôi sử dụng chức năng này để đọc file sql:

function (path, args = NULL) 
{ 
    stopifnot(file.exists(path)) 
    if (length(args) > 0) { 
     stopifnot(all(names(args) != "")) 
     sql <- readChar(path, nchar = file.info(path)$size) 
     p <- paste0(":", names(args), ":") 
     sql <- gsub_all(pattern = p, replacement = args, x = sql) 
     return(sql) 
    } else { 
     sql <- readChar(path, nchar = file.info(path)$size) 
     return(sql) 
    } 
} 

nơi gsub_all là về cơ bản chỉ là một wrapper cho một vòng lặp for qua các thông số và args là danh sách tên các giá trị tham số.

Đó là phạm vi tùy chọn mà tôi biết.

+1

Việc truyền tham số cho một truy vấn ngược lại với việc xây dựng chuỗi với thao tác chuỗi .Nó nguy hiểm (cho phép tấn công SQL injection), mong manh (ngắt do các vấn đề nội địa hóa khi định dạng các giá trị thành chuỗi) và chậm –

+0

@PanagiotisKanavos Việc sử dụng RODBC phổ biến nhất từ ​​R là để các nhà phân tích dữ liệu truy xuất dữ liệu từ db. Điều này đang thay đổi một chút với sự ra đời của những thứ như Shiny, nhưng phần lớn người dùng chỉ bao giờ gửi các truy vấn mà họ đã viết, với số lượng nhỏ. Vì vậy, không có vấn đề nào trong số đó có thể là mối quan ngại đối với nhiều người dùng RODBC. – joran

+0

Ngược lại, chúng * làm * ảnh hưởng đến những người cố gắng vượt qua ví dụ tham số ngày, hoặc quên sử dụng cú pháp thích hợp để truyền Unicode hoặc chuỗi ANSI hoặc gặp xung đột đối chiếu. Các nhà phân tích dữ liệu có nhiều khả năng phạm sai lầm như vậy. Trong mọi trường hợp, OP hỏi làm thế nào để sử dụng * truy vấn tham số *, không phải làm thế nào để xây dựng một chuỗi SQL –

9

Mateusz Zoltak viết RODBCext gói vào năm 2014 (dựa trên công trình của Brian Ripley và Michael Lapsley):

conn = odbcConnect('MyDataSource') 

sqlPrepare(conn, "SELECT * FROM myTable WHERE column = ?") 
sqlExecute(conn, 'myValue') 
sqlFetchMore(conn) 

Nguồn: http://cran.r-project.org/web/packages/RODBCext/vignettes/Parameterized_SQL_queries.html

+1

Có - đây là giải pháp tốt hơn. Tham số thích hợp làm giảm các vấn đề bảo mật (SQL Injection) và có thể cải thiện hiệu suất do các kế hoạch truy vấn được lưu trữ (chỉ quan trọng nếu truy vấn được thực hiện rất nhiều). Là một mô hình phần mềm Craftsman tốt hơn - tôi khuyên bạn nên sử dụng loại giải pháp này. – ripvlan

Các vấn đề liên quan