如何使用 Ruby 中的 Sequel GEM 将 AND 与 OR 语句组合在一起?

Jak*_*uld -5 ruby orm sequel

我相当擅长 PHP (OOP & flat)。在过去一年左右的时间里,我的任务是维护 Ruby 代码库;我还在学习的一项技能。我不太清楚如何使用续集因此采用正确的过滤链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)
)
;
Run Code Online (Sandbox Code Playgroud)

这是我正在使用的 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)
Run Code Online (Sandbox Code Playgroud)

这就是我实际得到的:

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)
)
;
Run Code Online (Sandbox Code Playgroud)

我还尝试了相同 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)
Run Code Online (Sandbox Code Playgroud)

和这个:

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)
Run Code Online (Sandbox Code Playgroud)

但我最终还是得到了第一个 SQL 结构,其中整个块基本上是((AND OR AND OR))

(
  ((`datebegin` >= 1950) AND (`datebegin` <= 1959)) OR ((`dateend` >= 1950) AND (`dateend` <= 1959))
  OR
  ((`datebegin` <= 1950) AND (`dateend` >= 1959))
)
Run Code Online (Sandbox Code Playgroud)

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

(
  ((`datebegin` >= 1950) AND (`datebegin` <= 1959)) OR ((`dateend` >= 1950) AND (`dateend` <= 1959))
)
OR
((`datebegin` <= 1950) AND (`dateend` >= 1959))
Run Code Online (Sandbox Code Playgroud)

Jer*_*ans 5

您初始查询的问题在于您依赖于 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))
)
Run Code Online (Sandbox Code Playgroud)

您可以在 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)}
Run Code Online (Sandbox Code Playgroud)

这会产生以下 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))
)
Run Code Online (Sandbox Code Playgroud)

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