我有一个名为Coupon
的表。
该表有一个名为query
的列,其中包含一个字符串。
query
字符串中有一些为where
语句格式化的逻辑条件。例如:
coupon1.query
=> " '/hats' = :url "
coupon2.query
=> " '/pants' = :url OR '/shoes' = :url "
我想编写一个存储过程,它以两个参数作为输入:一个Coupon
id列表和一个变量(在本例中,是当前URL(。
我希望该过程从每个Coupon
中查找query
列的值。然后,它应该在where
语句中运行该字符串,插入我的另一个参数(当前url(,然后返回任何匹配的Coupon
id。
以下是我在上面两张优惠券的情况下期望程序的表现。
Example 1:
* Call procedure with ids for coupon1 and coupon2, with @url = '/hats'
* Expect coupon1 to be returned.
Example 2:
* Call procedure with ids for coupon1 and coupon2, with @url = '/pants'
* Expect coupon2 to be returned.
Example 3:
* Call procedure with ids for coupon1 and coupon2, with @url = '/shirts'
* Expect no ids returned. URL does not match '/hats' for coupon1, and doesn't match '/pants or /shoes' for coupon2.
在ActiveRecord中测试这些很容易。这里只是示例1。
@url = '/hats'
@query = coupon1.query
# "'/hats' = :url"
Coupon.where(@query, url: @url).count
=> 2
# count is non-zero number because the query matches the url parameter.
# Coupon1 passes, its id would be returned from the stored procedure.
'/hats' == '/hats'
@query = coupon2.query
# " '/pants' = :url OR '/shoes' = :url "
Coupon.where(@query, url: @url).count
=> 0
# count is 0 because the query does not match the url parameter.
# Coupon2 does not pass, its id would not be returned from the stored procedure.
'/pants' != '/hats', '/shoes' != '/hats'
你可以把它写成一个循环(我用activerecord在ruby on rails中(,但我需要一些性能更好的东西——我可能有很多优惠券,所以我不能直接用循环检查每个优惠券。查询包含复杂的AND/OR逻辑,所以我也不能只与url列表进行比较。但这里有一些循环的代码,它本质上就是我试图翻译成存储过程的内容。
# assume coupon1 has id 1, coupon2 has id 2
@coupons = [coupon1, coupon2]
@url = '/hats'
@coupons.map do |coupon|
if Coupon.where(coupon.query, url: @url).count > 0
coupon.id
else
nil
end
end
=> [1, nil]
好吧,我一直在思考这个问题。
总体情况:
A。您有一个想要搜索的@url
,以便在许多潜在的Coupons
中找到匹配项
B。coupon
的URL可能与@url
匹配
如果这是问题的真实程度,我认为你真的把事情搞得太复杂了。
coupon1.query
=> ["/hats"]
coupon2.query
=> ["/pants", "/shoes"]
@url = '/hats'
Coupon.where('FIND_IN_SET(:url, query) <> 0')
或者类似的事情,我自己不是mySQL用户。
然而,这是非常有可能实现的,甚至可能有更好的ActiveRecord
查询方式。
更新
好吧,我错过了什么。我实际上无法在控制台中复制这个。
@url = '/hats'
@query = coupon1.query
# "'/hats' = :url"
Coupon.where(@query, url: @url).count
> SELECT * FROM 'coupons' WHERE ( '/hats' = '/hats' )
正如您从select语句中看到的,这将始终返回所有记录。与编写SELECT * FROM 'coupons' WHERE ( true )
相同
您实际上是如何执行有效查询的?
很抱歉在我的回答中发布了这个,我想要良好的格式。
如果我这里有什么问题,也许我们需要把它转移到聊天室。
我想你的名声刚好够我邀请你去一个房间。
更新2
由于您必须分别将@query
与每条记录进行比较,我认为您必须循环。
但是,我认为您不需要使用Coupon.where
来实现这一点,因为您一次只比较一条记录。
@coupons.map do |coupon|
# don't bother putting nil in the array
next unless coupon.query == @url
coupon.id
end
然而,你最初的问题是关于缩放时的性能,你知道你不会用循环来解决这个问题。
也许是JSONB而不是String,这样您就可以实际执行一些SQL了。
但是,即使有了JSONB,这仍然很复杂,因为需要对您的条件进行适当的评估。
{
"url": {
"AND": ["/hats", "/shoes"],
"OR": ["/pants"]
},
"logged_in": true,
"is_gold_member": false
}
{
"logged_in": false,
"url": "/hats"
}
{
"url": {
"OR": ["/pants", "/shoes"]
}
}
最终,我认为您使用query
属性所做的工作将继续成为您的绊脚石。这很聪明,但并不简单。
如果它是我的应用程序,我想我会重新考虑我的用例,并尝试找到一种不同的策略,以更符合实际的方式将特定的coupons
映射到特定的参数。