我希望在我的select请求中使用EXIST,根据文章是否在一组年份的一本书中发表,我希望有一个“已发表”的列。
所以我做以下几件事
p params[:years] # "2002"
p params[:years].is_a?(String) # true
Article.select(<<~SQL.squish, years: params[:years])
articles.*, EXISTS(
SELECT 1
FROM books
WHERE books.year IN (:years)
) AS published
SQL但是它给了我一个错误
不支持的参数类型:哈希。构造一个Arel节点
如果我像这样尝试
p params[:years] # "2002"
p params[:years].is_a?(String) # true
Article.select(<<~SQL.squish)
articles.*, EXISTS(
SELECT 1
FROM books
WHERE books.year IN (#{params[:years]})
) AS published
SQL未定义函数:错误:运算符不存在:字符变化= bigint,其中books.year IN (2022年)
但是帕拉姆:年数是一根线,我不知道我怎么能做我想做的事,我也不明白这两种错误。
编辑:如果我像这样使用单引号
p params[:years] # "2002"
p params[:years].is_a?(String) # true
Article.select(<<~SQL.squish)
articles.*, EXISTS(
SELECT 1
FROM books
WHERE books.year IN ('#{params[:years]}')
) AS published
SQL它只适用于一个值,但如果是params:年份是一个数组,它可以工作,但是输出是不正确的,如果我在2003年的一本书中有文章,它将设置为false。
p params[:years] # ["2002", "2003"]
p params[:years].is_a?(String) # false
Article.select(<<~SQL.squish)
articles.*, EXISTS(
SELECT 1
FROM books
WHERE books.year IN ('#{params[:years]}')
) AS published
SQL发布于 2021-05-25 16:21:45
通过将用户输入插入到SQL查询中,您将为SQL注入攻击留下很大的空间。
通过使用Arel来构造查询,这很容易防止:
Article.select(
Article.arel_table[Arel.star],
Book.select(1)
.where(year: params[:years])
.arel
.exists
.as('published')
)SELECT
"articles".*,
EXISTS (SELECT 1 FROM "books" WHERE "books"."year" IN (?, ?, ?)) AS published
FROM "articles"发布于 2021-05-25 16:33:36
Object.select方法在ActiveRecord中不像.where那样支持清理后的占位符插入。这就是为什么您要得到所得到的错误- select需要一个字段列表,但是您的一个参数是Hash。
获得您要寻找的功能的一种方法是,正如错误消息所建议的那样,使用Arel (如果您不熟悉它,请将Arel看作是ActiveRecord内部用来构造其SQL查询的“构建块”)。马克斯的回答是将查询转换为Arel的好方法。
或者,您也可以像ActiveRecord的其他部分一样对自己的SQL进行净化。为了清楚起见,将其分为SQL解析和select语句:
sql = Article.sanitize_sql([<<~SQL, year: params[:years]])
articles.*, EXISTS(
SELECT 1
FROM books
WHERE books.year IN (:years)
) AS published
SQL
Article.select(sql)我想说的是,手工清洁您的SQL将帮助您克服目前的绊脚石,但如果您要在整个商店进行类似的操作,您将受益于了解Arel做什么和如何使用它。
https://stackoverflow.com/questions/67691586
复制相似问题