Список email-адресов пользователей, следящих за конкретной категорией

У меня есть запрос в Data Explorer, который выполняет следующее:

SELECT * FROM category_users WHERE category_id = '10'

Это возвращает результат, который выглядит так:

Как мне также отобразить электронную почту пользователей в этом выводе?

(Чтобы предотвратить обсуждение вопросов конфиденциальности, отмечу следующее: мы используем приватный Discourse для платных участников, которые индивидуально дали согласие на использование их персональных данных в целях предоставления им услуг. У нас есть системы, которые не взаимодействуют автоматически, и мы используем электронную почту для ручного связывания пользователей в двух разных системах.)

Вам нужно присоединить таблицу user_emails по полю user_id из таблицы category_users. Попробуйте что-то вроде этого:

SELECT
cu.*,
ue.email
FROM category_users cu
JOIN user_emails ue
ON ue.user_id = cu.user_id
WHERE category_id = '10'
AND ue.primary = true

Спасибо, Саймон, и извините за долгий ответ!

Я только что проверил ваш запрос, и он делает именно то, что мне нужно! :folded_hands:

Есть ли способ получить те же данные по всему сайту, а не только для конкретной категории?

Я спрашиваю об этом, потому что мы планируем перестроить наш форум, используя более детальные категории, что делает мой план по созданию отдельного запроса в Data Explorer для каждой категории менее осуществимым.

Я разобрался, как запрашивать несколько категорий, используя что-то вроде этого:

WHERE (category_id = '48') OR (category_id = '66') OR (category_id = '57')

Но мне придется помнить об обновлении запроса после изменения категорий, а я, скорее всего, об этом забуду :smiley:

Вы можете просто полностью убрать фильтр category_id = <number>, тогда запрос будет выглядеть примерно так:

SELECT
    cu.*,
    ue.email
FROM category_users cu
JOIN user_emails ue
  ON ue.user_id = cu.user_id
WHERE ue.primary = true

Data Explorer отобразит названия категорий для вас, но они не будут показаны при экспорте результатов. Если это для вас проблема, вы можете явно добавить название категории как столбец, примерно так:

SELECT
    c.name,
    cu.*,
    ue.email
FROM category_users cu
JOIN user_emails ue
  ON ue.user_id = cu.user_id
JOIN categories c
  ON cu.category_id = c.id
WHERE ue.primary = true
ORDER BY c.name

Спасибо за ваш вопрос, @simonk!

Не понимаю, почему вы использовали WHERE ue.primary = true вместо AND ue.primary = true. Всегда ли в запросе требуется ключевое слово WHERE?

Не совсем так. Возможно, станет понятнее, если мы немного переформатируем запрос от @simon:

SELECT
    cu.*,
    ue.email
FROM category_users cu
JOIN user_emails ue ON ue.user_id = cu.user_id
WHERE (category_id = '10' AND ue.primary = true)

Условия category_id и ue.primary оба являются частью предложения WHERE, соединенного оператором AND. Если вы удалите одно из условий, вы уберете AND, но оставите предложение WHERE.

Большинство простых SQL-запросов следуют этой форме:

SELECT <нужные_вещи>
FROM <таблицы>
WHERE <условия_фильтрации>

Вы можете полностью опустить предложение WHERE, но тогда вы получите каждую строку из указанных таблиц.

Вот ваш исходный запрос (переформатированный):

SELECT *
FROM category_users
WHERE category_id = '10'
  • SELECT *” означает, что вы хотите, чтобы запрос возвращал все столбцы из всех задействованных таблиц.

  • FROM category_users” указывает таблицу, которую вы хотите запросить. Таблица category_users содержит строки, выглядящие примерно так:

    id category_id user_id notification_level
    1 1 1 3
    2 1 2 3
    3 3 1 3

    category_id и user_id называются внешними ключами, потому что они ссылаются на строку в другой таблице (в данном случае на таблицы categories и users). Таким образом, три строки выше означают, что пользователь с id 1 следит за категориями 1 и 3, а пользователь с id 2 следит за категорией 1. Поле notification_level указывает, находятся ли они в статусе Слежение, Слежение за первым сообщением или Отслеживание.

  • WHERE category_id = '10'” означает, что вас интересуют только строки, где значение в столбце category_id равно 10. Без этой строки вы получили бы каждую строку из таблицы category_users.

@simon предоставил вам новую версию, добавившую адрес электронной почты пользователя:

Этот запрос внес несколько изменений по сравнению с вашим исходным по двум причинам: адреса электронной почты хранятся в отдельной таблице (таблица user_emails), и у пользователей может быть более одного адреса электронной почты.

  • В предложении SELECT:

    • cu.*” означает “все столбцы из таблицы cu
    • ue.email” означает “столбец email из таблицы ue
  • В предложении FROM:

    • Таблица category_users теперь имеет псевдоним “cu”, что экономит набор текста, если нужно ссылаться на неё более одного раза.

    • Мы выполнили JOIN с таблицей user_emails и присвоили ей псевдоним ue.

      Таблица user_emails содержит строки, подобные этим:

      id user_id email primary
      1 1 alex@example.com true
      2 1 alex@other.example.com false
      3 2 simon@example.com true

      Это означает, что пользователь с id 1 имеет два адреса электронной почты: alex@example.com (основной адрес) и alex@other.example.com (вторичный адрес). У пользователя с id 2 только один адрес.

      Когда вы выполняете JOIN двух таблиц в SQL, вам обычно нужно указать базе данных условие соединения. Если вы этого не сделаете, база данных не будет знать, какие значения в каждой из таблиц должны совпадать, и вы получите все возможные комбинации строк из двух таблиц. Если бы вы написали этот запрос:

      SELECT *
      FROM category_users
      JOIN user_emails
      

      …с приведенными выше примерами данных, вы получили бы 9 строк: вы бы получили первую строку из category_users три раза, по одному разу с каждой строкой из user_emails, затем аналогично получили бы вторую строку category_users три раза, и наконец, получили бы третью строку category_users три раза.

      Условие соединения обычно сообщает базе данных, какой столбец в двух таблицах представляет одно и то же значение. В данном случае столбец category_users.user_id и столбец user_emails.user_id оба представляют одно и то же значение. Записав ON ue.user_id = cu.user_id после JOIN user_emails ue, мы говорим базе данных сопоставить строки user_emails с соответствующими строками category_users.

    • Даже с условием JOIN мы всё равно получим 4 строки для пользователя с ID 1, потому что он следит за 2 категориями и имеет 2 адреса электронной почты — мы получим строку для каждой комбинации. Поэтому @simon добавил дополнительное условие в предложение WHERE, чтобы запрос возвращал только строки с основным адресом электронной почты пользователя. Это условие добавляется к уже существующему условию (ограничению ID категории) — для возврата строки должны иметь category_id = '10' И ue.primary = true.

Затем, поскольку вы не хотели ограничивать поиск одной категорией, вам просто нужно было убрать фильтр category_id. Вы не хотите удалять всё предложение WHERE, потому что всё ещё хотите возвращать только основные адреса электронной почты. Иными словами, ваше условие фильтрации изменилось с:

category_id = '10' AND ue.primary = true

на

ue.primary = true

Уф! Надеюсь, всё стало понятно :nerd_face:

Спасибо за невероятно подробный пост, @simonk! Признаюсь, SQL для меня полная загадка, и ваше объяснение действительно помогло мне начать его понимать. Я очень ценю, что вы нашли время мне помочь! :folded_hands: