diff --git a/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php b/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php
index 33b3595fe4f57cb2ad47424b04a81efea482ceb1..d3abf84394c2f4ef5640823cf9f21be2ed992d95 100644
--- a/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php
+++ b/typo3/sysext/core/Classes/Database/Query/QueryBuilder.php
@@ -26,6 +26,7 @@ use Doctrine\DBAL\Query\Expression\CompositeExpression;
 use TYPO3\CMS\Core\Database\Connection;
 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
 use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer;
+use TYPO3\CMS\Core\Database\Query\Restriction\LimitToTablesRestrictionContainer;
 use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -111,6 +112,16 @@ class QueryBuilder
         $this->restrictionContainer = $restrictionContainer;
     }
 
+    /**
+     * Limits ALL currently active restrictions of the restriction container to the table aliases given
+     *
+     * @param array $tableAliases
+     */
+    public function limitRestrictionsToTables(array $tableAliases): void
+    {
+        $this->restrictionContainer = GeneralUtility::makeInstance(LimitToTablesRestrictionContainer::class)->addForTables($this->restrictionContainer, $tableAliases);
+    }
+
     /**
      * Re-apply default restrictions
      */
diff --git a/typo3/sysext/core/Classes/Database/Query/Restriction/LimitToTablesRestrictionContainer.php b/typo3/sysext/core/Classes/Database/Query/Restriction/LimitToTablesRestrictionContainer.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd191ba813f2fbcdd43cfea50e19fce8b2312b8b
--- /dev/null
+++ b/typo3/sysext/core/Classes/Database/Query/Restriction/LimitToTablesRestrictionContainer.php
@@ -0,0 +1,115 @@
+<?php
+
+declare(strict_types=1);
+namespace TYPO3\CMS\Core\Database\Query\Restriction;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression;
+use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
+
+/**
+ * Restriction container that applies added restrictions only to the given table aliases.
+ * Enforced restrictions are treated equally to all other restrictions.
+ */
+class LimitToTablesRestrictionContainer implements QueryRestrictionContainerInterface
+{
+    /**
+     * @var QueryRestrictionInterface[]
+     */
+    private $restrictions = [];
+
+    /**
+     * @var QueryRestrictionContainerInterface[]
+     */
+    private $restrictionContainer = [];
+
+    /**
+     * @var array
+     */
+    private $applicableTableAliases;
+
+    public function removeAll(): QueryRestrictionContainerInterface
+    {
+        $this->applicableTableAliases = $this->restrictions = $this->restrictionContainer = [];
+        return $this;
+    }
+
+    public function removeByType(string $restrictionType): QueryRestrictionContainerInterface
+    {
+        unset($this->applicableTableAliases[$restrictionType], $this->restrictions[$restrictionType]);
+        foreach ($this->restrictionContainer as $restrictionContainer) {
+            $restrictionContainer->removeByType($restrictionType);
+        }
+        return $this;
+    }
+
+    public function add(QueryRestrictionInterface $restriction): QueryRestrictionContainerInterface
+    {
+        $this->restrictions[get_class($restriction)] = $restriction;
+        if ($restriction instanceof QueryRestrictionContainerInterface) {
+            $this->restrictionContainer[get_class($restriction)] = $restriction;
+        }
+        return $this;
+    }
+
+    /**
+     * Adds the restriction, but also remembers which table aliases it should be applied to
+     *
+     * @param QueryRestrictionInterface $restriction
+     * @param array $tableAliases flat array of table aliases, not table names
+     * @return QueryRestrictionContainerInterface
+     */
+    public function addForTables(QueryRestrictionInterface $restriction, array $tableAliases): QueryRestrictionContainerInterface
+    {
+        $this->applicableTableAliases[get_class($restriction)] = $tableAliases;
+        return $this->add($restriction);
+    }
+
+    /**
+     * Main method to build expressions for given tables, but respecting configured filters.
+     *
+     * @param array $queriedTables Array of tables, where array key is table alias and value is a table name
+     * @param ExpressionBuilder $expressionBuilder Expression builder instance to add restrictions with
+     * @return CompositeExpression The result of query builder expression(s)
+     */
+    public function buildExpression(array $queriedTables, ExpressionBuilder $expressionBuilder): CompositeExpression
+    {
+        $constraints = [];
+        foreach ($this->restrictions as $name => $restriction) {
+            $constraints[] = $restriction->buildExpression(
+                $this->filterApplicableTableAliases($queriedTables, $name),
+                $expressionBuilder
+            );
+        }
+        return $expressionBuilder->andX(...$constraints);
+    }
+
+    private function filterApplicableTableAliases(array $queriedTables, string $name): array
+    {
+        if (!isset($this->applicableTableAliases[$name])) {
+            return $queriedTables;
+        }
+
+        $filteredTables = [];
+        foreach ($this->applicableTableAliases[$name] as $tableAlias) {
+            if (!isset($queriedTables[$tableAlias])) {
+                throw new \LogicException(sprintf('Applicable table alias "%s" is not in queried tables', $tableAlias), 1558354033);
+            }
+            $filteredTables[$tableAlias] = $queriedTables[$tableAlias];
+        }
+
+        return $filteredTables;
+    }
+}
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-87776-LimitRestrictionToTablesInQueryBuilder.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-87776-LimitRestrictionToTablesInQueryBuilder.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d97a68c2fbf91ae6b8550e42897d98cc11d95230
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-87776-LimitRestrictionToTablesInQueryBuilder.rst
@@ -0,0 +1,71 @@
+.. include:: ../../Includes.txt
+
+==============================================================
+Feature: #87776 - Limit Restriction to table/s in QueryBuilder
+==============================================================
+
+See :issue:`87776`
+
+Description
+===========
+
+In some cases it is needed to apply restrictions only to a certain table.
+With the new `\TYPO3\CMS\Core\Database\Query\Restriction\LimitToTablesRestrictionContainer`
+it is possible to apply restrictions to a query only for a given set of tables, or to be precise, table aliases.
+Since it is a restriction container, it can be added to the restrictions of the query builder and
+it can hold restrictions itself. The restrictions it holds can be limited to tables like this:
+
+Example implementation:
+-----------------------
+
+.. code-block:: php
+
+   $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
+   $queryBuilder->getRestrictions()
+       ->removeByType(HiddenRestriction::class)
+       ->add(
+           GeneralUtility::makeInstance(LimitToTablesRestrictionContainer::class)
+               ->addForTables(GeneralUtility::makeInstance(HiddenRestriction::class), ['tt'])
+       );
+   $queryBuilder->select('tt.uid', 'tt.header', 'sc.title')
+       ->from('tt_content', 'tt')
+       ->from('sys_category', 'sc')
+       ->from('sys_category_record_mm', 'scmm')
+       ->where(
+           $queryBuilder->expr()->eq('scmm.uid_foreign', $queryBuilder->quoteIdentifier('tt.uid')),
+           $queryBuilder->expr()->eq('scmm.uid_local', $queryBuilder->quoteIdentifier('sc.uid')),
+           $queryBuilder->expr()->eq('tt.uid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT))
+       );
+
+In this example the HiddenRestriction is only applied to `tt` table alias of `tt_content`.
+
+Furthermore it is possible to restrict the complete set of restrictions of a query builder to a
+given set of table aliases
+
+.. code-block:: php
+
+   $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
+   $queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
+   $queryBuilder->getRestrictions()->limitRestrictionsToTables(['c2']);
+   $queryBuilder
+      ->select('c1.*')
+      ->from('tt_content', 'c1')
+      ->leftJoin('c1', 'tt_content', 'c2', 'c1.parent_field = c2.uid')
+      ->orWhere($queryBuilder->expr()->isNull('c2.uid'), $queryBuilder->expr()->eq('c2.pid', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)));
+
+Which will result in:
+
+.. code-block:: sql
+
+   SELECT "c1".*
+   FROM "tt_content" "c1"
+   LEFT JOIN "tt_content" "c2" ON c1.parent_field = c2.uid
+   WHERE (("c2"."uid" IS NULL) OR ("c2"."pid" = 1)) AND ("c2"."hidden" = 0))
+
+Impact
+======
+
+It is now easily possible to add restrictions that are only applied to certain tables/ table aliases,
+by using `\TYPO3\CMS\Core\Database\Query\Restriction\LimitToTablesRestrictionContainer`.
+
+.. index:: Database, ext:core, API
diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php
index 7b680142d11d79e81222671967f6aa5fe53a8b0f..40e44ab3d0d651416065c09cc54c4940a67105f8 100644
--- a/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php
+++ b/typo3/sysext/core/Tests/Unit/Database/Query/QueryBuilderTest.php
@@ -1446,4 +1446,105 @@ class QueryBuilderTest extends UnitTestCase
         $this->connection->quoteIdentifier('aField')->shouldHaveBeenCalled();
         self::assertSame($expectation, $result);
     }
+
+    /**
+     * @test
+     */
+    public function limitRestrictionsToTablesLimitsRestrictionsInTheContainerToTheGivenTables(): void
+    {
+        $GLOBALS['TCA']['tt_content']['ctrl'] = $GLOBALS['TCA']['pages']['ctrl'] = [
+            'delete' => 'deleted',
+            'enablecolumns' => [
+                'disabled' => 'hidden',
+            ],
+        ];
+
+        $this->connection->quoteIdentifier(Argument::cetera())
+            ->willReturnArgument(0);
+        $this->connection->quoteIdentifiers(Argument::cetera())
+            ->willReturnArgument(0);
+
+        $connectionBuilder = GeneralUtility::makeInstance(
+            \Doctrine\DBAL\Query\QueryBuilder::class,
+            $this->connection->reveal()
+        );
+
+        $expressionBuilder = GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal());
+        $this->connection->getExpressionBuilder()->willReturn($expressionBuilder);
+
+        $subject = new QueryBuilder(
+            $this->connection->reveal(),
+            null,
+            $connectionBuilder
+        );
+        $subject->limitRestrictionsToTables(['pages']);
+
+        $subject->select('*')
+            ->from('pages')
+            ->leftJoin(
+                'pages',
+                'tt_content',
+                'content',
+                'pages.uid = content.pid'
+            )
+            ->where($expressionBuilder->eq('uid', 1));
+
+        $this->connection->executeQuery(
+            'SELECT * FROM pages LEFT JOIN tt_content content ON pages.uid = content.pid WHERE (uid = 1) AND ((pages.deleted = 0) AND (pages.hidden = 0))',
+            Argument::cetera()
+        )->shouldBeCalled();
+
+        $subject->execute();
+    }
+
+    /**
+     * @test
+     */
+    public function restrictionsCanStillBeRemovedAfterTheyHaveBeenLimitedToTables(): void
+    {
+        $GLOBALS['TCA']['tt_content']['ctrl'] = $GLOBALS['TCA']['pages']['ctrl'] = [
+            'delete' => 'deleted',
+            'enablecolumns' => [
+                'disabled' => 'hidden',
+            ],
+        ];
+
+        $this->connection->quoteIdentifier(Argument::cetera())
+            ->willReturnArgument(0);
+        $this->connection->quoteIdentifiers(Argument::cetera())
+            ->willReturnArgument(0);
+
+        $connectionBuilder = GeneralUtility::makeInstance(
+            \Doctrine\DBAL\Query\QueryBuilder::class,
+            $this->connection->reveal()
+        );
+
+        $expressionBuilder = GeneralUtility::makeInstance(ExpressionBuilder::class, $this->connection->reveal());
+        $this->connection->getExpressionBuilder()->willReturn($expressionBuilder);
+
+        $subject = new QueryBuilder(
+            $this->connection->reveal(),
+            null,
+            $connectionBuilder
+        );
+        $subject->limitRestrictionsToTables(['pages']);
+        $subject->getRestrictions()->removeByType(DeletedRestriction::class);
+
+        $subject->select('*')
+            ->from('pages')
+            ->leftJoin(
+                'pages',
+                'tt_content',
+                'content',
+                'pages.uid = content.pid'
+            )
+            ->where($expressionBuilder->eq('uid', 1));
+
+        $this->connection->executeQuery(
+            'SELECT * FROM pages LEFT JOIN tt_content content ON pages.uid = content.pid WHERE (uid = 1) AND (pages.hidden = 0)',
+            Argument::cetera()
+        )->shouldBeCalled();
+
+        $subject->execute();
+    }
 }
diff --git a/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/LimitToTablesRestrictionContainerTest.php b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/LimitToTablesRestrictionContainerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3355398573e50b81053fda5caaaee055b78ad4d
--- /dev/null
+++ b/typo3/sysext/core/Tests/Unit/Database/Query/Restriction/LimitToTablesRestrictionContainerTest.php
@@ -0,0 +1,95 @@
+<?php
+
+declare(strict_types=1);
+namespace TYPO3\CMS\Core\Tests\Unit\Database\Query\Restriction;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Database\Query\Restriction\DefaultRestrictionContainer;
+use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
+use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
+use TYPO3\CMS\Core\Database\Query\Restriction\LimitToTablesRestrictionContainer;
+use TYPO3\CMS\Core\Database\Query\Restriction\QueryRestrictionContainerInterface;
+
+class LimitToTablesRestrictionContainerTest extends AbstractRestrictionTestCase
+{
+    /**
+     * @test
+     */
+    public function buildExpressionAddsRestrictionsOnlyToGivenAlias(): void
+    {
+        $GLOBALS['TCA']['bTable']['ctrl']['enablecolumns']['disabled'] = 'hidden';
+        $subject = new LimitToTablesRestrictionContainer();
+        $subject->addForTables(new HiddenRestriction(), ['bt']);
+        $expression = $subject->buildExpression(['aTable' => 'aTable', 'bTable' => 'bTable', 'bt' => 'bTable'], $this->expressionBuilder);
+
+        self::assertSame('"bt"."hidden" = 0', (string)$expression);
+    }
+
+    /**
+     * @test
+     */
+    public function buildExpressionAddsRestrictionsOfDefaultRestrictionContainerOnlyToGivenAlias(): void
+    {
+        $GLOBALS['TCA']['bTable']['ctrl']['enablecolumns']['disabled'] = 'hidden';
+        $GLOBALS['TCA']['bTable']['ctrl']['delete'] = 'deleted';
+        $subject = new LimitToTablesRestrictionContainer();
+        $subject->addForTables(new DefaultRestrictionContainer(), ['bt']);
+        $expression = $subject->buildExpression(['aTable' => 'aTable', 'bTable' => 'bTable', 'bt' => 'bTable'], $this->expressionBuilder);
+
+        self::assertSame('("bt"."deleted" = 0) AND ("bt"."hidden" = 0)', (string)$expression);
+    }
+
+    /**
+     * @test
+     */
+    public function removeByTypeRemovesRestrictionsByTypeAlsoFromDefaultRestrictionContainer(): void
+    {
+        $GLOBALS['TCA']['bTable']['ctrl']['enablecolumns']['disabled'] = 'hidden';
+        $GLOBALS['TCA']['bTable']['ctrl']['delete'] = 'deleted';
+        $subject = new LimitToTablesRestrictionContainer();
+        $subject->addForTables(new DefaultRestrictionContainer(), ['bt']);
+        $subject->removeByType(DeletedRestriction::class);
+        $expression = $subject->buildExpression(['aTable' => 'aTable', 'bTable' => 'bTable', 'bt' => 'bTable'], $this->expressionBuilder);
+
+        self::assertSame('"bt"."hidden" = 0', (string)$expression);
+    }
+
+    /**
+     * @test
+     */
+    public function removeByTypeRemovesRestrictionsByTypeAlsoFromAnyRestrictionContainer(): void
+    {
+        $GLOBALS['TCA']['bTable']['ctrl']['enablecolumns']['disabled'] = 'hidden';
+        $GLOBALS['TCA']['bTable']['ctrl']['delete'] = 'deleted';
+        $subject = new LimitToTablesRestrictionContainer();
+        $containerProphecy = $this->prophesize(QueryRestrictionContainerInterface::class);
+        $containerProphecy->removeByType(DeletedRestriction::class)->shouldBeCalled();
+        $containerProphecy->buildExpression(['bt' => 'bTable'], $this->expressionBuilder)->willReturn($this->expressionBuilder->andX([]))->shouldBeCalled();
+        $subject->addForTables($containerProphecy->reveal(), ['bt']);
+        $subject->removeByType(DeletedRestriction::class);
+        $subject->buildExpression(['aTable' => 'aTable', 'bTable' => 'bTable', 'bt' => 'bTable'], $this->expressionBuilder);
+    }
+
+    /**
+     * @test
+     */
+    public function buildRestrictionsThrowsExceptionWhenGivenAliasIsNotInQueriedTables(): void
+    {
+        $this->expectException(\LogicException::class);
+        $subject = new LimitToTablesRestrictionContainer();
+        $subject->addForTables(new HiddenRestriction(), ['bt']);
+        $subject->buildExpression(['aTable' => 'aTable'], $this->expressionBuilder);
+    }
+}