Forums / National / Russian / Тех. поддержка / Вопрос про большую выборку в sql

Kopusha
#1 2025-02-17 09:25

В $db_stories некоторые записи создаються "только для друзей", при этом item_only_friends у них == 1.

Есть таблица "с парами друзей", friendid/userid будет пара друзей, если они в "активной" дружбе - со статусом 1.

Мне нужно построить выборку в list.stories с зависимостью от того показываем ли мы историю этому конкретному пользователю или нет.

PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
if ($usr['id'])
        {
            //Get all friends of a user
            if (cache_exists('friendlist_'.$usr['id']) && $cfg['apcu_cache'])
            {
                //получаем id пользователей которые у текущего в друзьях из кеша в виде 1 2 3 4 5
                $friends_ids = cache_get('friendlist_'.$usr['id']);
            }
            if (!isset($friends_ids) || !is_array($friends_ids))
            {
                global $db_cot_social_friendslist, $db_x;
                $db_cot_social_friendslist = (isset($db_cot_social_friendslist)) ? $db_cot_social_friendslist : $db_x . 'social_friendslist';
                //если в кеше их нет то получаем их из БД
                $friends_ids = $db->query("
                    SELECT
                        CASE
                            WHEN userid = :userid THEN friendid
                            ELSE userid
                        END AS friend_id
                    FROM $db_cot_social_friendslist
                    WHERE status = 1
                    AND (userid = :userid OR friendid = :userid)
                ", [':userid' => $usr['id']])->fetchAll(PDO::FETCH_COLUMN) ?: [];
 
                $friends_ids = array_unique($friends_ids);         
                 
                if(!empty($friends_ids))
                {
                    if($cfg['apcu_cache'])
                    {
                        cache_store('friendlist_'.$usr['id'], $friends_ids, 3200);
                    }
                }
            }  
            //если пользователь не администратор
            if (!$usr['isadmin'])
            {
                if (empty($friends_ids)) {
                    //и у него нет друзей - не показываем истории которые только для друзей
                    $where_friends = "AND (item_only_friends=0 OR item_userid=".$usr['id'].")";
                } else {
                    //и у него есть друзья то показываем истории что для всех, которые его и которые для друзей и в списке его друзей есть автор истории
                    $where_friends = "AND (item_only_friends=0 OR item_userid=".$usr['id']." OR item_userid IN (".implode(',', array_map('intval', $friends_ids))."))";
                }
            }
        }
        else
        {
            $where_friends = "AND (item_only_friends=0)";
        }
 
        $totalitems = $db->query("SELECT COUNT(*) FROM $db_stories AS f
            $join_condition
            LEFT JOIN $db_users AS u ON u.user_id=f.item_userid
            " . $where . "
            $where_condition
            $where_friends
            ")->fetchColumn();
             
        //и выборка самих историй по такому же принципу
        $sqllist = $db->query("SELECT f.*, u.* $join_columns
            FROM $db_stories AS f $join_condition
            LEFT JOIN $db_users AS u ON u.user_id=f.item_userid
            " . $where . "
            $where_condition
            $where_friends
            " . $order . "
            LIMIT $d, " . $maxrowsperpage); //$maxrowsperpage = 30
             
        $sqllist_rowset = $sqllist->fetchAll();
        $sqllistcount = $sqllist->rowCount();
         

По сути мой вопрос в чем - если friends_ids будет сотни и тысячи значений, не станут ли запросы в БД слишко тяжелыми и как лучше поступить в такой ситуации?

Added 56 seconds later:

собственно беспокойство вызывает OR item_userid IN (".implode(',', array_map('intval', $friends_ids))."))";

Added 5 minutes later:

PS
индексы в БД для нужных полей есть

item_userid для db_stories
user_id для db_users изначально есть в котонти

Композитный индекс для userid, friendid, status в таблице $db_cot_social_friendslist так же есть

 

This post was edited by Kopusha (2025-02-17 09:32, 5 months ago)
Kort
#2 2025-02-17 09:40

Самое простое -- закэшировать результат на разумный срок

SED.by - создание сайтов, разработка плагинов и тем для Котонти
Kopusha
#3 2025-02-17 10:03

для каждого конкретного пользователя? для юзера 1 одни списки для юзера 2 (у него другой список друзей) другие и тд

и это выборка первых 30 записей, после почти сразу +30 инфинитскроллом и так далее

Added 1 minute later:

пробую так

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$db->exec("
    CREATE TEMPORARY TABLE temp_friends (
        friend_id INT PRIMARY KEY
    ) ENGINE=MEMORY;
");
if (!empty($friends_ids)) {
    $placeholders = implode(',', array_fill(0, count($friends_ids), '?'));
    $stmt = $db->prepare("INSERT INTO temp_friends (friend_id) VALUES ($placeholders)");
    $stmt->execute($friends_ids);
}      
         
// Запрос с временной таблицей
$totalitems = $db->query("SELECT COUNT(*)
    FROM $db_stories AS f
    LEFT JOIN $db_users AS u ON u.user_id=f.item_userid
    LEFT JOIN temp_friends tf ON tf.friend_id = f.item_userid
    $join_condition
    " . $where . "
    $where_condition
    $where_friends
")->fetchColumn();
 
$db->exec("DROP TEMPORARY TABLE IF EXISTS temp_friends;");