2015-07-01 17 views
5

Có một câu hỏi tương tự ở đây nhưng nó không thực sự trả lời câu hỏi.Có thể sử dụng mệnh đề IN trong sql đơn giản Slick cho số nguyên không?

Is it possible to use IN clause in plain sql Slick?

Lưu ý rằng điều này thực sự là một phần của một truy vấn lớn hơn và phức tạp hơn, vì vậy tôi cần phải sử dụng sql đơn giản thay vì trơn của nâng nhúng. Một cái gì đó như sau sẽ được tốt:

val ids = List(2,4,9) 
sql"SELECT * FROM coffee WHERE id IN ($ids)" 

Trả lời

11

Tiền tố sql mở ra một StringContext nơi bạn có thể thiết lập các thông số SQL. Không có tham số SQL cho một danh sách, vì vậy bạn có thể dễ dàng kết thúc việc tự mở SQL injection ở đây nếu bạn không cẩn thận. Có một số gợi ý tốt (và một số nguy hiểm) về xử lý vấn đề này với SQLServer trên this question. Bạn có một vài lựa chọn:

Tốt nhất có lẽ là sử dụng các nhà điều hành #$ cùng với mkString để suy SQL động:

val sql = sql"""SELECT * FROM coffee WHERE id IN (#${ids.mkString(",")})""" 

này không sử dụng đúng các thông số và do đó có thể mở cửa cho SQL- tiêm và các vấn đề khác.

Một lựa chọn khác là sử dụng chuỗi suy thường xuyên và mkString để xây dựng báo cáo kết quả:

val query = s"""SELECT * FROM coffee WHERE id IN (${ids.mkString(",")})""" 
StaticQuery.queryNA[Coffee](query) 

Đây thực chất là phương pháp tương tự như sử dụng #$, nhưng có thể linh hoạt hơn trong trường hợp tổng quát.

Nếu lỗ hổng SQL injection là mối quan tâm chính (ví dụ: nếu các thành phần của ids là người dùng được cung cấp), bạn có thể tạo truy vấn có tham số cho từng phần tử ids. Sau đó, bạn sẽ cần phải cung cấp một ví dụ tùy chỉnh SetParameter để trơn có thể biến List vào các thông số:

implicit val setStringListParameter = new SetParameter[List[String]]{ 
    def apply(v1: List[String], v2: PositionedParameters): Unit = { 
     v1.foreach(v2.setString) 
    } 
} 

val idsInClause = List.fill(ids.length)("?").mkString("(", ",", ")") 
val query = s"""SELECT * FROM coffee WHERE id IN ($idsInClause)""" 
Q.query[List[String], String](query).apply(ids).list(s) 

Kể từ khi bạn idsInts, điều này có lẽ ít hơn của một mối quan tâm, nhưng nếu bạn thích phương pháp này, bạn sẽ chỉ cần phải thay đổi setStringListParameter sử dụng Int thay vì String:

+3

Nếu 'id' có loại' Danh sách [Int] 'Tôi không thể xem cách tiêm sql là có thể ngay cả khi họ được người dùng cung cấp. – Daenyth

+1

@Daenyth Nó chắc chắn ít quan tâm (mặc dù đôi khi số nguyên SQL injection có thể là một vấn đề, do nguyên nhân chia cho số không hoặc ngoại lệ khác, và sau đó khai thác trạng thái không thành công - Google "sql injection integer"). Nhưng tôi muốn nói cách tốt nhất là sử dụng thông số để tránh các sự cố trên đường (ví dụ: nếu nhà phát triển khác thay đổi loại thành 'Chuỗi' xuống để chứa các loại ID mới bao gồm một số ký tự). Tôi đã thực sự chỉ bao gồm các trường hợp khi nó là một 'String' ở đây. –

+0

Cảm ơn câu trả lời Ben! Rất thông tin về các giải pháp có lỗ hổng có thể xảy ra. Tuy nhiên, tôi đồng ý với @Daenyth rằng bạn không thể sql tiêm với một loại số nguyên rõ ràng. –

3
val ids = List(610113193610210035L, 220702198208189710L) 

    implicit object SetListLong extends SetParameter[List[Long]] { 
    def apply(vList: List[Long], pp: PositionedParameters) { 
     vList.foreach(pp.setLong) 
    } 
    } 

    val select = sql""" 
     select idnum from idnum_0 
     where idnum in ($ids#${",?" * (ids.size - 1)}) 
    """.as[Long] 

@Ben Reich là đúng. đây là một mã mẫu, thử nghiệm trên slick 3.1.0.

($ids#${",?" * (ids.size - 1)})

1

Mặc dù đây không phải là câu trả lời phổ biến và có thể không được những gì tác giả muốn, tôi vẫn muốn chỉ này ra để bất cứ ai xem câu hỏi này.

Một số chương trình phụ trợ DB hỗ trợ các loại mảng và có các tiện ích mở rộng cho Slick cho phép thiết lập các loại mảng này trong nội suy.

Ví dụ, Postgres có cú pháp where column = any(array), và với slick-pg bạn có thể sử dụng cú pháp này như sau:

def query(ids: Seq[Long]) = db.run(sql"select * from table where ids = any($ids)".as[Long]) 

này mang lại một cú pháp sạch hơn nhiều, đó là thân thiện hơn với bộ nhớ cache trình biên dịch tuyên bố và cũng an toàn từ SQL injection và nguy cơ tổng thể của việc tạo ra một SQL không đúng định dạng với cú pháp nội suy #$var.

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