首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Rails SQL "select in“跨几个列: where (code1,code2) in ("A",1),(”A“,3),("Q",9))

Rails SQL "select in“跨几个列: where (code1,code2) in ("A",1),(”A“,3),("Q",9))
EN

Stack Overflow用户
提问于 2018-05-03 18:21:07
回答 2查看 103关注 0票数 2

我有一个业务要求根据一个表中的两个字段选择记录: code1和code2。选择是复杂和硬编码,没有可编码的押韵或理由,并包括大约十几对,在表中实际存在的100对。

  • C,1
  • C,2
  • J,9
  • Z,0

请注意,表中还有其他"C“代码,如(C,3)。没有组合字段将它们都捕获为值,例如"C3“。

SQL支持这样的查询:Two columns in subquery in where clause

代码语言:javascript
代码运行次数:0
运行
复制
SELECT * from rejection_codes
  where (code1, code2) in (("A", 1), ("A", 3), ("Q", 9))

有什么方法可以在Rails和ActiveRecord的ORM中做到这一点,而无需使用原始SQL呢?

如果重要的话,我正在使用Postgres运行Rails 4.2.9。

*你为什么不..。*

添加一个字段:我无法控制数据库模式。如果是这样的话,我会为这个组添加一个新的列作为标志。或将值连接到字符串中的计算列。或者别的什么..。可是,我不会呀。

使用原始SQL:是的.如果我不能通过ORM完成的话,我可能会这么做。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-05-03 19:36:38

如果你想要的正是这样的结构,那么你可以这样做:

代码语言:javascript
代码运行次数:0
运行
复制
pairs = [['A', 1], ['A', 3], ['Q', 9]]
RejectionCode.where('(code1, code2) in ((?), (?), (?))', *pairs)

当然,pairs.length不一定总是三岁,所以您可以这样说:

代码语言:javascript
代码运行次数:0
运行
复制
pairs = [['A', 1], ['A', 3], ['Q', 9]]
placeholders = (%w[(?)] * pairs.length).join(', ')
RejectionCode.where("(code1, code2) in (#{placeholders})", *pairs)

是的,这是使用字符串内插来构建SQL片段,但是在这种情况下它是完全安全的,因为您正在构建所有的字符串,并且您确切地知道其中包含了什么。如果你把它放到一个范围里,那么至少丑陋会被隐藏起来,你可以很容易地用你的测试套件来覆盖它。

或者,你也可以利用一些等价的。in是一种奇特的or,因此它们所做的事情大致相同:

代码语言:javascript
代码运行次数:0
运行
复制
c in (x, y, z)
c = x or c = y or c = z

而且记录(甚至匿名记录)是逐列比较的,因此它们是等价的:

代码语言:javascript
代码运行次数:0
运行
复制
(a, b) = (x, y)
a = x and b = y

这意味着像这样的事情:

代码语言:javascript
代码运行次数:0
运行
复制
pairs = [['A', 1], ['A', 3], ['Q', 9]]
and_pair = ->(a) { RejectionCode.where('code1 = ? and code2 = ?', *a) }
and_pair[pairs[0]].or(and_pair[pairs[1]]).or(and_pair[pairs[2]])

会给你同样的结果。或更广泛地说:

代码语言:javascript
代码运行次数:0
运行
复制
pairs = [['A', 1], ['A', 3], ['Q', 9], ... ]
and_pair = ->(a) { RejectionCode.where('code1 = ? and code2 = ?', *a) }
query = pairs[1..-1].inject(and_pair[pairs.first]) { |q, a| q.or(and_pair[a]) }

再说一次,你会想把这个丑陋隐藏在一个范围里。

票数 1
EN

Stack Overflow用户

发布于 2018-05-03 19:34:03

*这是一个不错的解决办法,但并不能完全解决ORM问题*

在ActiveRecord中找不到正确的方法来做到这一点,我只是猜到了,希望是最好的:

代码语言:javascript
代码运行次数:0
运行
复制
class ApprovalCode < ActiveRecord::Base

  REJECTION_CODES = [
    ['A', '0'],
    ['R', '1'],
    ['R', '5'],
    ['R', '6'],
    ['X', 'F'],
    ['X', 'G']
  ]

  scope :rejection_allowed, -> { where([:code, :sub_code], REJECTION_CODES) }  # This didn't work.

end

那不起作用。因此,我在作用域中使用了原始SQL,这确实有效:

代码语言:javascript
代码运行次数:0
运行
复制
  scope :rejection_allowed, -> { where("(code, sub_code) in (#{rejection_list})") }

  def self.rejection_list
    REJECTION_CODES
      .map{|code, sub_code| "('#{code}', '#{sub_code}')"}
      .join(', ')
  end

我仍然希望在ORM中找到如何做到这一点,或者阅读关于完全不同的方法来解决这个问题的建议。因为它都封装在一个作用域和一个常量中,以后重构就很简单了,将常量和作用域分开将允许进行无痛测试。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50161583

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档