Как проверить таблицу соединений для строк в диапазоне времени? - PullRequest
1 голос
/ 07 мая 2020

Мне нужно найти Подписки, а затем проверить каждую Подписку, есть ли какие-либо Уведомления, связанные с этой Подпиской, за последние n часов, а если нет, возьмите эту Подписку

Я придумал два решения, но я не совсем доволен ими обоими. Может ли кто-нибудь помочь сделать это наиболее эффективно?

Решение 1:

SELECT
    "Subscription"."id",
FROM
    "Subscriptions" AS "Subscription"   
WHERE
    "Subscription"."UserId" = 2
    AND "Subscription"."isActive" = TRUE
    AND "Subscription"."FeedId" = 35
    AND NOT EXISTS (
        SELECT* FROM "Notifications" AS "Notification"
        WHERE  
           "Notification"."SubscriptionId" = "Subscription"."id"
           AND (("Notification"."createdAt" > now() - interval '1 hours' AND "Notification"."CreativeId" = 70)
           OR ("Notification"."createdAt" > now() - interval '6 hours' AND "Notification"."FeedId" = 35))
    )
GROUP BY
    "Subscription"."id"

LIMIT 15000 OFFSET 0;

Решение 2:

SELECT
          "Subscription"."id",
        FROM
          "Subscriptions" AS "Subscription"
          LEFT OUTER JOIN "Notifications" AS "Notification" ON "Subscription"."id" = "Notification"."SubscriptionId" AND "Notification"."createdAt" > now()::date - interval '6 hours'
              AND ("Notification"."FeedId" = 35 OR "Notification"."CreativeId" = 70)

        WHERE
          "Subscription"."UserId" = 2
          AND "Subscription"."isActive" = TRUE
          AND "Subscription"."FeedId" =35

        GROUP BY
          "Subscription"."id"
        HAVING
          COUNT("Notification"."SubscriptionId") FILTER (WHERE "Notification"."FeedId" = 35
            AND "Notification"."createdAt" > now() - interval '6 hours') < 1
          AND
          COUNT("Notification"."SubscriptionId") FILTER (WHERE "Notification"."CreativeId" = 70
            AND "Notification"."createdAt" > now() - interval '1 hours') < 1

 LIMIT 15000 OFFSET 0;

Когда я делаю и то, и другое в TablePlus, первое кажется чтобы сделать немного лучше, но когда я тестирую их на своем бэкэнде, второй выигрывает разительно. Пытаюсь понять почему, но раз уж через пару дней начал работать с чистым SQL а go, решил тут спросить

1 Ответ

2 голосов
/ 07 мая 2020

Вам не нужно group by. Первое решение может показаться способом go, но я бы написал его как:

SELECT s.id
FROM Subscriptions s  
WHERE s.user_id = 2 AND
      s.isActive AND
      s.FeedId = 35 AND
      NOT EXISTS (SELECT 1
                  FROM Notification n
                  WHERE n.SubscriptionId = s.id AND
                        n.createdAt > now() - interval '6 hours' AND
------------------------^ I added this redundant condition to help the optimizer
                        ( (n.createdAt > now() - interval '1 hours' AND n.CreativeId = 70) or
                          (n.createdAt > now() - interval '6 hours' AND n.FeedId = 35)   
                        )
                 )
LIMIT 15000 OFFSET 0;

Для повышения производительности вам нужны индексы на subscriptions(user_id, is_active, feed_id) и notifications(subscription_id, created_at, creative_id, feed_id).

Примечания:

  • Я удалил все двойные кавычки! Не используйте их при определении базы данных. Они просто усложняют написание и чтение запросов.
  • Столбец id не должен дублироваться в Subscriptions, поэтому агрегирование не требуется.
  • С другой стороны, ORDER BY обычно используется с LIMIT.
  • Используйте псевдонимы таблиц для упрощения имен таблиц. Просто повторять их не особо полезно.
...