【问题标题】:How to group combined AND with OR statements using the Sequel GEM in Ruby?如何使用 Ruby 中的 Sequel GEM 对组合 AND 和 OR 语句进行分组?
【发布时间】:2014-07-25 22:17:50
【问题描述】:

我相当擅长 PHP(OOP 和平面)。在过去一年左右的时间里,我一直负责维护 Ruby 代码库。一项我仍在学习的技能。我不太清楚如何使用 Sequel 正确构建过滤链,以便可以正确包含 AND 以及 OR 语句。

这是我想要的 MySQL 查询结构:

SELECT * FROM `some_objects`
WHERE (
  (
    ((`datebegin` >= 1950) AND (`datebegin` <= 1959)) OR ((`dateend` >= 1950) AND (`dateend` <= 1959))
  )
  OR
  ((`datebegin` <= 1950) AND (`dateend` >= 1959))
  AND
  (NOT `datebegin` = 0) AND (NOT `dateend` = 0)
)
;

这是我正在使用的 Sequel 代码片段:

some_objects = where{((datebegin >= start_year) & (datebegin <= end_year)) | ((dateend >= start_year) & (dateend <= end_year))}.
               or{(datebegin <= start_year) & (dateend >= end_year)}.
               where(~:datebegin => 0, ~:dateend => 0)

这就是我实际得到的:

SELECT * FROM `some_objects`
WHERE (
  (
    ((`datebegin` >= 1950) AND (`datebegin` <= 1959)) OR ((`dateend` >= 1950) AND (`dateend` <= 1959))
    OR
    ((`datebegin` <= 1950) AND (`dateend` >= 1959))
  )
  AND
  (NOT `datebegin` = 0) AND (NOT `dateend` = 0)
)
;

我还尝试了相同 Sequel 代码的不同变体,例如:

some_objects = where(:datebegin => start_year..end_year).
               or(:dateend => start_year..end_year).
               or{|o|(o.datebegin <= start_year) & (o.dateend >= end_year)}.
               where(~:datebegin => 0, ~:dateend => 0)

还有这个:

some_objects = where(:datebegin => start_year..end_year).
               or(:dateend => start_year..end_year).
               or{(datebegin <= start_year) & (dateend >= end_year)}.
               where(~:datebegin => 0, ~:dateend => 0)

但我仍然得到第一个 SQL 结构,其中整个块基本上是 ((AND OR AND OR))

(
  ((`datebegin` >= 1950) AND (`datebegin` <= 1959)) OR ((`dateend` >= 1950) AND (`dateend` <= 1959))
  OR
  ((`datebegin` <= 1950) AND (`dateend` >= 1959))
)

当我想要((AND OR AND) OR)时:

(
  ((`datebegin` >= 1950) AND (`datebegin` <= 1959)) OR ((`dateend` >= 1950) AND (`dateend` <= 1959))
)
OR
((`datebegin` <= 1950) AND (`dateend` >= 1959))

【问题讨论】:

  • 使用BETWEEN 操作符会更容易吗?这减少了AND 案例的数量。
  • @tadman 我同意。但我不是这样设置的。但是您正在查看 Sequel 在 Ruby 中生成的 SQL;我输出 MySQL 输出来得到它。我正在处理的是 Ruby Sequel GEM 处理 SQL 生成的方式。所以如果有办法让BETWEEN 工作,当然可以。美好的。但这暂时是多余的。

标签: ruby orm sequel


【解决方案1】:

初始查询的问题在于您依赖于 OR/AND 优先规则,而不是使用显式括号。您的初始查询可以表示为:

SELECT * FROM `some_objects`
WHERE (
  ((`datebegin` >= 1950) AND (`datebegin` <= 1959))
  OR
  ((`dateend` >= 1950) AND (`dateend` <= 1959))
  OR
  ((`datebegin` <= 1950) AND (`dateend` >= 1959) AND (NOT `datebegin` = 0) AND (NOT `dateend` = 0))
)

你可以在 Sequel 中这样表达:

DB[:some_objects].where{((datebegin >= start_year) & (datebegin <= end_year)) | ((dateend >= start_year) & (dateend <= end_year))}.
  or{((datebegin <= start_year) & (dateend >= end_year)) & Sequel.negate(:datebegin => 0)}

产生以下 SQL:

SELECT * FROM `some_objects` WHERE (
  ((`datebegin` >= 1950) AND (`datebegin` <= 1959))
  OR
  ((`dateend` >= 1950) AND (`dateend` <= 1959))
  OR
  ((`datebegin` <= 1950) AND (`dateend` >= 1959) AND (`datebegin` != 0))
)

这应该与您使用的 SQL 执行相同,但它更具可读性(例如 datebegin != 0 而不是 NOT datebegin = 0)。请注意,您不需要 NOT dateend = 0 过滤器,因为它是 dateend &gt;= 1959 过滤器的子集。

【讨论】:

  • 看起来很接近,但没有雪茄。我需要像where(~:date begin =&gt; 0, ~:dateend =&gt; 0) 这样的东西,而你的代码只有` AND (datebegin != 0). Is that solved by simply chaining like this: Sequel.negate(:datebegin => 0).negate(:date end => 0)`。
  • 您显然没有阅读我帖子的结尾。您不需要NOT date_end = 0 条件,因为它是dateend &gt;= 1959 条件的子集。但是如果你真的想添加一个毫无意义的条件:Sequel.negate(:datebegin =&gt; 0, :dateend =&gt; 0)
  • “请注意,您不需要 NOT date end = 0 过滤器,因为它是 date end >= 1959 过滤器的子集。”正确的。谢谢!
【解决方案2】:

请记住,如果您真的遇到了困难,请记住 Sequel,只需用艰难的方式说明您的条件:

records = where(%Q[
  (
    (`datebegin` >= 1950 AND `datebegin` <= 1959)
    OR (`dateend` >= 1950 AND `dateend` <= 1959)
  )
  OR (`datebegin` <= 1950 AND `dateend` >= 1959)
])

从查询的角度来看,那些“零”日期有点烦人,最好使用NULL,这样就不需要在这里排除它们。由于NULL 永远不会大于、小于或等于任何值,因此这些查询看不到它。另一方面,零是有效数字,必须排除。

作为一个例子来说明它是如何与范围一起工作的:

records = where(datebegin: (1950..1959)).or(dateend: (1950..1959))

这些用BETWEEN 运算符表示。从索引的角度来看,通常至少比使用AND 的有界范围快一点。

【讨论】:

  • 您真的阅读了我的问题吗?看看我的 MySQL 理想与您发布的内容。我不需要((AND OR AND OR)) 的链。我需要它为((AND OR AND) OR)
  • 没必要暴躁。您的查询真的很难理解,我正在尽力而为。我刚刚编辑了它。我希望很明显,您可以按原样将 SQL 放在那里并且它会起作用。
  • Sequel 的好处是它会按照您的指示去做,但它有一些方便的方法可以更容易地描述它。当那些失败时,用艰难的方式去做。 documentation on filtering 更详细。
  • 使用字符串插值是asking for trouble。这就是为什么支持数据占位符,例如未命名的 ? 或它们所在的 :name。通常,您可以使用where('datebegin &gt; :start_year', start_year: 1950) 之类的绑定来执行此操作。我不确定您是否可以多次使用同一个占位符,但您可以尝试。该链接文档中的前几个示例更详细。
  • 显然最好的方法是交互式地运行它。请记住,您始终可以在末尾附加 .sql 以查看打算做什么。与 ActiveRecord 不同,这里没有什么神秘之处。
猜你喜欢
  • 1970-01-01
  • 2017-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
  • 1970-01-01
相关资源
最近更新 更多