ruby-on-rails – 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_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) > '--- 900n...n')" 所有帮助赞赏. 解决方法
如果您的effective_end_date和effective_start_date列确实是日期,则您的查询毫无意义,因为日期的最小分辨率为一天,900s比86400s(AKA 25 * 60 * 60或1天)小得多.所以我假设你的“日期”列实际上是日期时间(AKA时间戳)列;如果这是真的那么你可能想要重命名列以避免在维护期间混淆,effective_starts_at和effective_ends_at可能是通常的Rails约定的良好匹配.如果此假设无效,则应更改列类型或停止使用900s.
回到真正的问题. ActiveRecord使用 def quote(value,column = nil) # records are quoted as their primary key return value.quoted_id if value.respond_to?(:quoted_id) case value #... else "'#{quote_string(YAML.dump(value))}'" end end 因此,如果您尝试使用某些东西作为占位符的值,并且没有为该类型内置任何特定处理,那么您将获得YAML(默认IMO的奇怪选择).此外,900.seconds是一个ActiveSupport :: Duration对象(尽管900.seconds.class说的是),并且case值没有ActiveSupport :: Duration的分支,因此900.seconds将获得YAMLified. PostgreSQL适配器在 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 使用该补丁后,您可以获得PostgreSQL在使用ActiveSupport :: Duration时所理解的间隔: > 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')" 如果你在MySQL适配器的引用中添加一个类似的补丁(这是作为读者的练习),那么像: UserRole.where("(effective_end_date - effective_start_date) > ?",900.seconds) 将在PostgreSQL和MySQL中做正确的事情,你的代码将不必担心它. 也就是说,在不同的数据库上开发和部署是一个非常糟糕的主意,这将使圣诞老人哭泣,并寻找一些煤(可能含有砷,可能是放射性的)供你放养.所以不要这样做. 另一方面,如果您正在尝试构建与数据库无关的软件,那么您可以享受一些快乐的乐趣!数据库可移植性在很大程度上是一个神话,与数据库无关的软件总是意味着在您的平台提供的ORM和数据库接口之上编写自己的可移植层.您将不得不详尽地测试您计划支持的每个数据库上的所有内容,每个人都为SQL Standard提供口头服务,但似乎没有人完全支持它,每个人都有自己的扩展和怪癖需要担心.您最终将编写自己的可移植层,该层由实用方法和猴子补丁组成. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |