Запрос с подзапросом, который требует нескольких значений - PullRequest
0 голосов
/ 20 марта 2020

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

Проблема: Я хочу вернуть массив сообщений с каждым сообщением, содержащим количество лайков. Счет лайков предназначен для специфика c поста , но для всех пользователей, которым он понравился

Например:

const posts = [
  {
    post_id: 1,
    like_count: 100
  },
  {
    post_id: 2,
    like_count: 50
  }
]

сейчас с моим текущим решением я не думаю, что это возможно, но вот что у меня есть.

Мой запрос в настоящее время выглядит следующим образом (произведенный TypeORM):

SELECT
   "p"."uid" AS "p_uid",
   "p"."created_at" AS "post_created_at",
   "l"."uid" AS "like_uid",
   "l"."post_liked" AS "post_liked",
   "ph"."path" AS "path",
   "ph"."title" AS "photo_title",
   "u"."name" AS "post_author",
   (
      SELECT
         COUNT(like_id) AS "like_count" 
      FROM
         "likes" "l" 
         INNER JOIN
            "posts" "p" 
            ON "p"."post_id" = "l"."post_id" 
      WHERE
         "l"."post_liked" = true 
         AND l.post_id = $1
   )
   AS "like_count" 
FROM
   "posts" "p" 
   LEFT JOIN
      "likes" "l" 
      ON "l"."post_id" = "p"."post_id" 
   INNER JOIN
      "photos" "ph" 
      ON "ph"."photo_id" = "p"."photo_id" 
   INNER JOIN
      "users" "u" 
      ON "u"."user_id" = "p"."user_id"

At $1 - это то место, где post.post_id должен go (но ради тестирования я вставил туда первый пост), предполагая, что у меня есть готовый для размещения массив post_ids.

Мой TypeORM запрос выглядит следующим образом:

  async findAll(): Promise<Post[]> {
    return await getRepository(Post)
    .createQueryBuilder('p')
    .select(['p.uid'])
    .addSelect(subQuery => 
      subQuery
        .select('COUNT(like_id)', 'like_count')
        .from(Like, 'l')
        .innerJoin('l.post', 'p')
        .where('l.post_liked = true AND l.post_id = :post_id', {post_id: 'a16f0c3e-5aa0-4cf8-82da-dfe27d3f991a'}), 'like_count'
    )
    .addSelect('p.created_at', 'post_created_at')
    .addSelect('u.name', 'post_author')
    .addSelect('l.uid', 'like_uid')
    .addSelect('l.post_liked', 'post_liked')
    .addSelect('ph.title', 'photo_title')
    .addSelect('ph.path', 'path')
    .leftJoin('p.likes', 'l')
    .innerJoin('p.photo', 'ph')
    .innerJoin('p.user', 'u')
    .getRawMany()
  }

Почему я это делаю? Чего я пытаюсь избежать, так это набираю count для каждого поста на моей странице, чтобы вернуть количество лайков для каждый пост. Я думал, что смогу как-то сделать это в подзапросе, но теперь я не уверен, возможно ли это.

Может кто-нибудь предложить более эффективный способ сделать что-то подобное? Или этот подход совершенно неправильный?

1 Ответ

1 голос
/ 20 марта 2020

Я считаю, что работать с ORM ужасно и не могу вам с этим помочь. Но сам запрос имеет fl aws:

  1. Вы хотите одну строку на пост, но вы присоединяетесь к likes, таким образом, получая по одной строке на пост и т. Д.
  2. Ваш подзапрос не связан с вашим основным запросом. Вместо этого он должен относиться к сообщению основного запроса.

Исправленный запрос:

SELECT
   p.uid,
   p.created_at,
   ph.path AS photo_path,
   ph.title AS photo_title,
   u.name AS post_author,
   (
      SELECT COUNT(*)
      FROM likes l 
      WHERE l.post_id = p.post_id
      AND l.post_liked = true 
   ) AS like_count 
FROM posts p 
JOIN photos ph ON ph.photo_id = p.photo_id 
JOIN users u ON u.user_id = p.user_id
ORDER BY p.uid;

Я полагаю, вам довольно легко преобразовать это в TypeORM. Между прочим, нет ничего плохого в подсчете каждого поста. Даже необходимо получить результат, к которому вы стремитесь.

Подзапрос также можно переместить в предложение FROM, используя GROUP BY l.post_id внутри. Вы получаете все сообщения, независимо от того, есть у них лайки или нет. Переместив подзапрос в предложение FROM, вы можете вместо этого выбрать между INNER JOIN и LEFT OUTER JOIN.

Для запроса будет полезен следующий индекс:

CREATE INDEX idx ON likes (post_id, post_liked);

Укажите это индекс, если запрос кажется слишком медленным.

...