Объедините результаты запроса из двух таблиц, которые не связаны друг с другом, но имеют одинаковые внешние ключи в один запрос - PullRequest
0 голосов
/ 03 октября 2019

Мне нужно сделать немного статистики по использованию приложения. У меня есть 2 модели, которые копят данные:

PullLog

id
user_id
foo_id 
bar_id 
created_at

RestateLog

id
user_id
foo_id 
change_to 
created_at

Foo

  • has_many pull_logs
  • has_many restate_logs

Пользователь

  • has_many pull_logs
  • has_many restate_logs

Пример данных, которые мне нужно получитьследующие:

  • сколько pull_logs сделал пользователь?
  • сколько restate_logs сделал пользователь?

Я использую rails 6 и postgresи желательно, чтобы я хотел выразить эти запросы в активной записи

Я предложил 2 запроса:

SELECT
    pull_logs.user_id,
    count(pull_logs.user_id) as user_pull_logs_count,
    count(bar.id) FILTER ( WHERE bar.uuid = 'uuid') as example_bar_count
    count(*) FILTER (WHERE pull_logs.bar_id IS NULL) as direct_problems_count
FROM
    problem_pull_logs
LEFT JOIN 
    bar ON pull_logs.bar_id = bar.id
GROUP BY
    pull_logs.user_id

Второй оператор SQL:

SELECT
    user_id,
    COUNT(*) AS restate_count,
    COUNT(*) FILTER ( WHERE restate_logs.change_to = 'to_call' ) as to_call_count,
    COUNT(*) FILTER ( WHERE restate_logs.change_to = 'success' ) as to_success_count,
    COUNT(*) FILTER ( WHERE restate_logs.change_to = 'canceled' ) as to_canceled_count
FROM
    restate_logs
GROUP BY
    restate_logs.user_id

Есть лиспособ объединить эти 2 запроса?

Я также хотел бы получить такие данные, как:

  • сколько pull_logs пользователь сделал за день?

или

  • сколько pull_logs сделал пользователь bar_id?

Мне также нужно иметь возможность добавлять фильтры, такие как получение данных. т.е только для данного пользователя или только для данного дня или bar_id

Редактировать: пример данных:

pull_log

id: 1 ; foo_id: 1 ; user_id: 2 ; bar_id: 1 ; created_at: 2019-09-17  
id: 2 ; foo_id: 1 ; user_id: 2 ; bar_id: 1 ; created_at: 2019-09-18  
id: 3 ; foo_id: 2 ; user_id: 1 ; bar_id: 2 ; created_at: 2019-09-18  

restate_log

id: 1 ; foo_id: 1 ; user_id: 2 ; change_to: "to_call" ; created_at: 2019-09-17  
id: 2 ; foo_id: 1 ; user_id: 2 ; change_to: "success" ; created_at: 2019-09-18  
id: 3 ; foo_id: 2 ; user_id: 1 ; change_to: "to_call" ; created_at: 2019-09-18  

Ожидаемые результаты:

user_id 1  
pull_count 1  
pull_from_bar_1_count 0  
pull_from_bar_2_count 1  
restate_count  1  
restate_to_call_count 1  
restate_to_success_count 0  

user_id 2  
pull_count 2  
pull_from_bar_1_count 2  
pull_from_bar_2_count 0  
restate_count 2  
restate_to_call_count 1  
restate_to_success_count 1  

РЕДАКТИРОВАТЬ:
После ответа от GMB

Я перенес эти два запроса в ActiveRecord следующим образом:

def pull_logs
    PullLog.select(<<-SQL.squish
      pull_logs.user_id as user_id,
      count(pull_logs.user_id) as user_pull_logs_count,
      count(foo.id) FILTER (
        WHERE foo.uuid = '66ca4921-b66b-47d8-bd75-5c1ac8e92118'
      ) as foo_1_count,
      count(*) FILTER (WHERE pull_logs.foo_id IS NULL)
        as direct_count
      SQL
      ).joins(<<-SQL.squish
      LEFT JOIN foo
      ON pull_logs.foo_id = foo.id
      SQL
      ).group("pull_logs.user_id")
      .order("pull_logs.user_id")
  end

def restate_logs
    RestateLog.select(<<-SQL.squish
      restate_logs.user_id,
      COUNT(*) AS restate_count,
      COUNT(*) FILTER ( WHERE restate_logs.change_to = 'to_call' ) as to_call_count,
      COUNT(*) FILTER ( WHERE restate_logs.change_to = 'success' ) as to_success_count,
      COUNT(*) FILTER ( WHERE restate_logs.change_to = 'canceled' ) as to_canceled_count
      SQL
      ).group("restate_logs.user_id")
      .order("restate_logs.user_id")
  end

Я объединяю это2 запроса в

Model.select("*")
      .from(<<-SQL.squish
        (#{pull_logs}) as pull_logs
        LEFT JOIN (#{restate_logs}) as restate_logs
        ON pull_logs.user_id = restate_logs.user_id)
        SQL
      )

Интересно, может ли быть более простой способ объединить результаты, чтобы представить их в виде одной таблицы?

1 Ответ

0 голосов
/ 03 октября 2019

Объединение запросов будет непростым, поскольку они адресованы различным таблицам и выполняют агрегирование. Простой способ продолжить, не вдаваясь в функциональные аспекты запросов, - это JOIN результаты обоих запросов по идентификатору пользователя. Это предполагает, что все пользователи имеют результаты в обоих запросах:

SELECT 
    pl.*,
    rl.restate_count,
    rl.to_call_count,
    rl.to_success_count
    rl.to_canceled_count
FROM (
    SELECT
        pull_logs.user_id,
        count(pull_logs.user_id) as user_pull_logs_count,
        count(bar.id) FILTER ( WHERE bar.uuid = 'uuid') as example_bar_count
        count(*) FILTER (WHERE pull_logs.bar_id IS NULL) as direct_problems_count
    FROM
        problem_pull_logs
    LEFT JOIN bar ON pull_logs.bar_id = bar.id
    GROUP BY
        pull_logs.user_id
    ) pl
INNER JOIN (
    SELECT
        user_id,
        COUNT(*) AS restate_count,
        COUNT(*) FILTER ( WHERE restate_logs.change_to = 'to_call' ) as to_call_count,
        COUNT(*) FILTER ( WHERE restate_logs.change_to = 'success' ) as to_success_count,
        COUNT(*) FILTER ( WHERE restate_logs.change_to = 'canceled' ) as to_canceled_count
    FROM
        restate_logs
    GROUP BY
        restate_logs.user_id
) rl ON rl.user_id = pl.user_id
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...