【问题标题】:Obtain Union all result in ActiveRecord在 ActiveRecord 中获取 Union 所有结果
【发布时间】:2019-08-14 08:45:12
【问题描述】:

如何在Rails中获取UNION运算结果。

鉴于我有以下 SQL 语句

SELECT  "sip_trunks".* FROM "sip_trunks" WHERE "sip_trunks"."default" = t LIMIT 1 UNION ALL SELECT  "sip_trunks".* FROM "sip_trunks" WHERE "sip_trunks"."default" = f LIMIT 1 

到目前为止,我已经设法使用 AREL 和 union all 语句构建 SQL。

SipTrunk.where(default: true).limit(1).union(:all,SipTrunk.where(default: false).limit(1))

但尝试查询此结果和 AREL,即 Arel::Nodes::UnionAll,我无法获得数据库结果。

同时在语句上运行 to_sql 会产生这样的 SQL..

( SELECT  "sip_trunks".* FROM "sip_trunks" WHERE "sip_trunks"."default" = $1 LIMIT 1 UNION ALL SELECT  "sip_trunks".* FROM "sip_trunks" WHERE "sip_trunks"."default" = $2 LIMIT 1 )

这似乎是一个准备好的语句,但我在 DB 中没有看到任何准备好的语句

尝试通过find_by_sql使用上述SQL

SipTrunk.find_by_sql(SipTrunk.where(default: true).limit(1).union(:all,SipTrunk.where(default: false).limit(1)).to_sql,[['default',true],['default',false]])

出现以下错误

ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:  syntax error at or near "UNION"
LINE 1: ...trunks" WHERE "sip_trunks"."default" = $1 LIMIT 1 UNION ALL ...

如何从这里获取最终的 SQL 行?

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 activerecord union arel


    【解决方案1】:

    以下是我将如何执行此操作。

    sql1 = SipTrunk.where(default: true).limit(1).arel
    sql2 = SipTrunk.where(default: false).limit(1).arel
    subquery = Arel::Nodes::As.new(
     Arel::Nodes::UnionAll.new(sql1,sql2),
     SipTrunk.arel_table
    ) 
    SipTrunk.from(subquery)
    

    这将导致以下 SQL

    SELECT 
      sip_trunks.*
    FROM 
      ( SELECT 
          sip_trunks.* 
        FROM 
          sip_trunks
        WHERE 
          sip_trunks.default = t
        LIMIT 1
        UNION ALL 
        SELECT 
          sip_trunks.* 
        FROM 
          sip_trunks
        WHERE 
          sip_trunks.default = f
        LIMIT 1) AS sip_trunks
    

    这将返回ActiveRecord::RelationSipTrunk 对象。

    【讨论】:

    • 这是最好的方法,因为#from 返回一个 ActiveRecord::Relation,然后您可以将其与其他表等连接。
    【解决方案2】:

    你可以像这样做一个union,连接两个sql查询。

    sql1 = SipTrunk.where(default: true).limit(1).to_sql
    sql2 = SipTrunk.where(default: false).limit(1).to_sql
    
    @sip_trunks = SipTrunk.find_by_sql("#{sql1} UNION #{sql2}")
    

    如果你想花哨或有多个 sql 查询加入你可以加入这个

    final_sql = [sql1, sql2].join(' UNION ')
    @sip_trunks = SipTrunk.find_by_sql(final_sql)
    

    【讨论】:

    • hmm .. 听起来不错,但我放弃了这个想法,因为我希望 AR 为我准备语句以避免 SQL 计划时间。不幸的是,上面提到的find_by_sql 并没有这样做。 SipTrunk.connection.execute('select * from pg_prepared_statements;').values => ntuples=0 nfields=5 cmd_tuples=0 运行上述find_by_sql
    • 几毫秒的额外成本对您的项目是否至关重要?
    • 而我不想自己准备语句的原因是因为我使用的是多线程系统并且数据库连接丢失(由于网络中断是常见的事情)导致 AR每次发生这种情况时都创建一个新连接。
    • 是的。我们必须在 250 毫秒内做出响应,但我们永远不会在 250 毫秒内完成所有请求。我们希望它尽可能少,一般在 50-100 毫秒以下
    • 好的,很公平,您应该将该信息添加到问题中,因为它会改变解决方案的所有方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多