【问题标题】:Converting a complex postgresql query/subquery to Rails activerecord syntax OR convert array to active record relation?将复杂的 postgresql 查询/子查询转换为 Rails activerecord 语法或将数组转换为活动记录关系?
【发布时间】:2018-12-12 00:37:57
【问题描述】:

所以我花了相当多的时间来编写这个查询,然后发现这是返回一个数组而不是一个 activerecord 关系的困难方式。卫生部。这不是问题,但我需要对这些结果使用 Ransack,这需要关系。

所以,基本上,我需要使用语法 .joins() 和 .select() 将其转换为 Rails,但我尝试过的所有操作都出错了。我猜我可能需要潜入 AREL?

或者,如果这可以轻松转换为具有最小性能问题的 activerecord 关系并保留我的别名列,那么它也可以!

感谢任何帮助或建议!

find_by_sql("
  SELECT
    subq.*, 
    renewal_date,
    days_until_due,
    renewal_stage_sort,
    (
      CASE
        WHEN renewal_stage_sort IS NOT NULL THEN
          CASE
            WHEN days_until_due > 42 AND renewal_stage_sort >= 1 THEN TRUE
            WHEN days_until_due > 28 AND days_until_due < 43 AND renewal_stage_sort >= 2 THEN TRUE
            WHEN days_until_due > 13 AND days_until_due < 29 AND renewal_stage_sort >= 3 THEN TRUE
            WHEN days_until_due > -1 AND days_until_due < 14 AND renewal_stage_sort >= 4 THEN TRUE
            WHEN days_until_due < 0 AND renewal_stage_sort >= 5 THEN TRUE
            ELSE FALSE
          END
        ELSE FALSE
      END
    )
    AS on_target
  FROM (   
     SELECT DISTINCT ON (renewals.id) renewals.*,
     CASE
       WHEN renewal_types.name = 'IEP-504' THEN patients.iep_renewal_date
       WHEN renewal_types.name = 'RR' THEN patients.rr_date
       ELSE NULL
     END 
     AS renewal_date,
     (  CASE
          WHEN renewal_types.name = 'IEP-504' THEN patients.iep_renewal_date::date
          WHEN renewal_types.name = 'RR' THEN patients.rr_date::date
          ELSE NULL
        END
        - current_date)
     AS days_until_due, 
     renewal_stages.sort_order AS renewal_stage_sort
     FROM renewals
     INNER JOIN renewal_types ON renewal_types.id = renewals.renewal_type_id
     LEFT JOIN renewal_stages ON renewal_stages.id = renewals.renewal_stage_id
     INNER JOIN patients ON patients.id = renewals.patient_id AND patients.deleted_at IS NULL
     WHERE renewals.deleted_at IS NULL
   ) subq
")    

【问题讨论】:

    标签: postgresql ruby-on-rails-4 activerecord ransack arel


    【解决方案1】:

    并没有真正完成整个工作,但这是一个开始......

    http://www.scuttle.io/

    给予:

    RenewalStages.sortOrder.select(
      [
        Subq.arel_table[Arel.star], :renewal_date, :days_until_due, :renewal_stage_sort, Arel::Nodes::Group.new(
          Arel.sql(
            'CASE        WHEN renewal_stage_sort IS NOT NULL THEN          CASE            WHEN days_until_due > 42 AND renewal_stage_sort >= 1 THEN TRUE            WHEN days_until_due > 28 AND days_until_due < 43 AND renewal_stage_sort >= 2 THEN TRUE            WHEN days_until_due > 13 AND days_until_due < 29 AND renewal_stage_sort >= 3 THEN TRUE            WHEN days_until_due > -1 AND days_until_due < 14 AND renewal_stage_sort >= 4 THEN TRUE            WHEN days_until_due < 0 AND renewal_stage_sort >= 5 THEN TRUE            ELSE FALSE          END        ELSE FALSE      END'
          )
        ).as('on_target')
      ]
    ).where(Renewal.arel_table[:deleted_at].eq(nil))
    

    是的,显然需要 Arel 表

    【讨论】:

      【解决方案2】:

      一般的解决方案是使用 .from(subquery_sql) 让您做最少的工作。然后,您可以从子查询中提取连接等,并逐个提取到 .joins 中。

      User.
        from("(select distinct * from users where id > 0) users ").
        select("users.*, false as mystatus").
        first.
        mystatus 
      

      既然你想要一个 ActiveRecord::Relation,你需要使用正确的方法 https://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html 如果你直接使用 Arel 来构建一个查询,它不会解决你的问题,因为它不知道模型,它只知道表、select、group by、查询参数等,都是低级的。

      你没有发布你的架构,所以我没有验证下面的代码是否有效,但是像这样

      fromsql = <<-SQL
      (   
      SELECT DISTINCT ON (renewals.id) renewals.*,
      CASE
        WHEN renewal_types.name = 'IEP-504' THEN patients.iep_renewal_date
        WHEN renewal_types.name = 'RR' THEN patients.rr_date
        ELSE NULL
      END 
      AS renewal_date,
      (  CASE
          WHEN renewal_types.name = 'IEP-504' THEN patients.iep_renewal_date::date
          WHEN renewal_types.name = 'RR' THEN patients.rr_date::date
          ELSE NULL
        END
        - current_date)
      AS days_until_due, 
      renewal_stages.sort_order AS renewal_stage_sort
      FROM renewals
      INNER JOIN renewal_types ON renewal_types.id = renewals.renewal_type_id
      LEFT JOIN renewal_stages ON renewal_stages.id = renewals.renewal_stage_id
      INNER JOIN patients ON patients.id = renewals.patient_id AND patients.deleted_at IS NULL
      WHERE renewals.deleted_at IS NULL
      ) renewals
      SQL
      
      select_sql = <<-SQL
        renewals.*, 
        renewal_date,
        days_until_due,
        renewal_stage_sort,
        (
          CASE
            WHEN renewal_stage_sort IS NOT NULL THEN
              CASE
                WHEN days_until_due > 42 AND renewal_stage_sort >= 1 THEN TRUE
                WHEN days_until_due > 28 AND days_until_due < 43 AND renewal_stage_sort >= 2 THEN TRUE
                WHEN days_until_due > 13 AND days_until_due < 29 AND renewal_stage_sort >= 3 THEN TRUE
                WHEN days_until_due > -1 AND days_until_due < 14 AND renewal_stage_sort >= 4 THEN TRUE
                WHEN days_until_due < 0 AND renewal_stage_sort >= 5 THEN TRUE
                ELSE FALSE
              END
            ELSE FALSE
          END
        )
        AS on_target
      SQL
      
      Referal.from(fromsql).select(select_sql).to_sql
      

      【讨论】:

        猜你喜欢
        • 2021-11-27
        • 2018-12-08
        • 1970-01-01
        • 2011-10-24
        • 2013-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多