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

Kopusha
#1 2025-02-17 09:25

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

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

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

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 так же есть

 


Dit bericht is bewerkt door Kopusha (2025-02-17 09:32, 4 maanden 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:

пробую так

		$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;");