` дают NULL и, как и там, не считаются. Работает по * индексу (source_id, object_id) без подсчёта на каждую строку. * * Строка обязана принадлежать ровно одной записи: общие строки (названия * рубрик в карточках, подвал) есть на каждой записи, и если бы они * входили в счёт, то «переведённой» оказалась бы любая запись сайта. * * @param string $alias Псевдоним таблицы вхождений во внешнем запросе (`a`); внутренний — `{alias}2`. */ public static function uniqueNotLinkSql( string $alias ): string { $occurrences = Schema::table( 'occurrences' ); return "NOT EXISTS (SELECT 1 FROM {$occurrences} {$alias}2 WHERE {$alias}2.source_id = {$alias}.source_id AND {$alias}2.object_type = '" . OccurrenceRepository::OBJECT_TYPE_POST . "' AND {$alias}2.object_id <> {$alias}.object_id)" . ' AND ' . self::notLinkSql( "{$alias}.source_id" ); } /** * Условие «вхождение — строка именно этой записи» (коррелированное). * * @param string $alias Псевдоним таблицы вхождений (`a`). * @param string $postIdExpr SQL-выражение с id записи (`wp_posts.ID`). */ public static function ownCondition( string $alias, string $postIdExpr ): string { return "{$alias}.object_type = '" . OccurrenceRepository::OBJECT_TYPE_POST . "' AND {$alias}.object_id = {$postIdExpr} AND " . self::uniqueNotLinkSql( $alias ); } /** * Подзапрос «строки записей»: пары (object_id, source_id) без повторов. * * Псевдоним таблицы вхождений — `o`. Фильтр по записям вызывающий задаёт * сам (`o.object_id IN (%d, %d)` или «все записи»). * * @param string $objectFilterSql SQL-условие на `o.object_id`. * @param bool $withSeo Добавить колонку `is_seo` (1/0). */ public static function ownRowsSql( string $objectFilterSql, bool $withSeo = false ): string { $occurrences = Schema::table( 'occurrences' ); $seo = $withSeo ? ', ' . self::isSeoSql( 'o.source_id' ) . ' AS is_seo' : ''; return "SELECT DISTINCT o.object_id, o.source_id{$seo} FROM {$occurrences} o WHERE o.object_type = '" . OccurrenceRepository::OBJECT_TYPE_POST . "' AND {$objectFilterSql} AND " . self::uniqueNotLinkSql( 'o' ); } }