Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails Rails ActiveRecord日期比较数据库不可知?_Ruby On Rails_Postgresql_Datetime_Activerecord_Rails Activerecord - Fatal编程技术网

Ruby on rails Rails ActiveRecord日期比较数据库不可知?

Ruby on rails Rails ActiveRecord日期比较数据库不可知?,ruby-on-rails,postgresql,datetime,activerecord,rails-activerecord,Ruby On Rails,Postgresql,Datetime,Activerecord,Rails Activerecord,我试图找到一种与数据库无关的方法来比较日期和活动记录查询。我有以下疑问: UserRole.where("(effective_end_date - effective_start_date) > ?", 900.seconds) 这在MySQL上运行良好,但在PG上产生错误,因为它生成的sql不包含“interval”语法。从控制台: ←[1m←[36mUserRole Load (2.0ms)←[0m ←[1mSELECT "user_roles".* FROM "user_r

我试图找到一种与数据库无关的方法来比较日期和活动记录查询。我有以下疑问:

UserRole.where("(effective_end_date - effective_start_date) > ?", 900.seconds)
这在MySQL上运行良好,但在PG上产生错误,因为它生成的sql不包含“interval”语法。从控制台:

  ←[1m←[36mUserRole Load (2.0ms)←[0m  ←[1mSELECT "user_roles".* FROM "user_roles" WHERE "user_roles"."effective_end_date" IS NULL AND ((effective_end_d
ate - effective_start_date) > '--- 900
...
')←[0m
ActiveRecord::StatementInvalid: PG::Error: ERROR:  invalid input syntax for type interval: "--- 900
当我使用to_sql I选项运行此命令时,我得到:

irb(main):001:0> UserRole.where("effective_end_date - effective_start_date) > ?", 900.seconds).to_sql
=> "SELECT \"user_roles\".* FROM \"user_roles\"  WHERE \"user_roles\".\"effective_end_date\" IS NULL AND (effective_end_date - effective_start_date) >
'--- 900\n...\n')"
感谢所有帮助。

如果您的“有效结束日期”和“有效开始日期”列确实是日期,那么您的查询就没有意义了,因为日期的最小分辨率为一天,900秒比86400秒(即25*60*60或1天)小得多。所以我假设您的日期列实际上是datetime,又称时间戳列;如果这是真的,那么您可能希望重命名这些列以避免在维护过程中出现混乱,有效地\u开始\u和有效地\u结束\u可能与通常的Rails约定非常匹配。如果此假设无效,则应更改列类型或停止使用900s

回到真正的问题上来。ActiveRecord使用以下方法将Ruby值转换为SQL值:

因此,如果您尝试使用某个内容作为占位符的值,并且没有为该类型内置任何特定的处理,那么YAML将得到一个奇怪的默认值选择。此外,900.seconds是一个ActiveSupport::Duration对象,不管900.seconds.class怎么说,并且case值没有ActiveSupport::Duration的分支,因此900.seconds将得到YAMLified

PostgreSQL适配器在中提供了自己的报价,但也不知道ActiveSupport::Duration。MySQL适配器也不知道ActiveSupport::Duration。你可以在这些引用方法中添加一些含义。在初始值设定项中类似这样的内容:

class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
    # Grab an alias for the standard quote method
    alias :std_quote :quote
    # Bludgeon some sense into things
    def quote(value, column = nil)
        return "interval '#{value.to_i} seconds'" if(value.is_a?(ActiveSupport::Duration))
        std_quote(value, column)
    end
end
有了这个补丁,当您使用ActiveSupport::Duration时,您可以获得PostgreSQL能够理解的时间间隔:

如果您在MySQL适配器的引用中添加了类似的补丁,作为读者的练习,那么:

UserRole.where("(effective_end_date - effective_start_date) > ?", 900.seconds)
在PostgreSQL和MySQL中都能做正确的事情,您的代码不必为此担心

这就是说,在不同的数据库上开发和部署是一个非常糟糕的主意,这会让圣诞老人哭着去寻找一些可能含有砷的煤,可能是你的袜子里的放射性煤。所以不要那样做


另一方面,如果您正试图构建与数据库无关的软件,那么您将迎来一些快乐的时光!数据库可移植性在很大程度上是一个神话,数据库不可知软件总是意味着在平台提供的ORM和数据库接口之上编写自己的可移植层。您将不得不在计划支持的每个数据库上彻底测试所有内容,每个人都口头上支持SQL标准,但似乎没有人完全支持它,每个人都有自己的扩展和怪癖需要担心。您将编写自己的可移植性层,该层将由实用程序方法和monkey修补程序组成。

该用户角色。其中。。。与从日志中挖掘的SQL不匹配;有些东西在你的日志中的查询中做了一个900.to_yaml,这是没有意义的。这就是正在生成的。我还没有在下面粘贴完整的rails错误堆栈,但是如果有用的话,我可以。我已经添加了一个sql版本,这样您就可以知道它生成了什么。谢谢你的帮助。你知道有效结束日期为空是从哪里来的吗?900.5秒对我来说毫无意义。而且你在你的中丢失了一个。哪里,复制粘贴错误?有效开始日期和有效结束日期是日期还是日期时间/时间戳?嗯,我的查询使用的是。其中,900秒也是900秒,Rails在做什么样的过分聪明的废话?当900.seconds.class说Fixnum时,实际上返回一个ActiveSupport::Duration。叹气。这是一个好问题-没有发现有效结束日期为空。它来自模型上的默认范围。我将不受限制地重新运行它,看看它是否改变了什么。非常感谢您提供了超出我当前知识水平的详细答案,但我理解您的意思。知道数据库无关软件不是rails的一个给定工具是非常有用的。我很高兴坚持使用一个DB,但希望尽可能多的选择。谢谢您的帮助。@Paul:数据库不可知软件的存在方式与操作系统不可知软件的存在方式相同:
> Model.where('a - b > ?', 900.seconds).to_sql
 => "SELECT \"models\".* FROM \"models\"  WHERE (a - b > interval '900 seconds')" 
> Model.where('a - b > ?', 11.days).to_sql
 => "SELECT \"models\".* FROM \"models\"  WHERE (a - b > interval '950400 seconds')"
UserRole.where("(effective_end_date - effective_start_date) > ?", 900.seconds)