SEDE Query to Find... Weird Questio​n/Protect Combinations
One of the first things you should do if you have an arbitrary data set (as you do in your list of UserIds) would be to extract it out of the WHERE clause. Just make a @TempTable variable. If the data set gets very large, consider a &quot;physical&quot; #TempTable.
DECLARE @UsersToExclude TABLE (UserId INT PRIMARY KEY);
INSERT INTO @UsersToExclude (UserId) 
VALUES
  /*Some comments to explain these numbers would be good:*/
  (419),
  (1228),
  (1288),
  (19679),
  (23354),
  (50049),
  (59303),
  (102937),
  (106224),
  (246246),
  (-1);

Then you can just do an existence check in your query:
   AND ph.UserID NOT IN (
     SELECT usrExcl.UserID from @UsersToExclude as usrExcl
   )


Please get into the habit of using the (optional) keyword AS to reference to table aliases. If not used it can be ambiguous whether it's supposed to be an alias or a query hint for something else, like NOLOCK.
FROM PostHistory AS ph
  JOIN Posts AS p ON ph.PostID = p.id


This bracketing style is a bit unusual:

LEFT JOIN
  ( SELECT foo
   FROM bar
   WHERE ...) AS Unprotected ON Unprotected.PostID=ph.PostID


It looks like LISP-style brackets. Most often in SQL either C# or Java style brackets are used (depending on the programming shop).
C#-style
LEFT JOIN
(
    SELECT foo
    FROM bar
    WHERE ...
) 
AS Unprotected ON Unprotected.PostID=ph.PostID

Java-style
LEFT JOIN (
    SELECT foo
    FROM bar
    WHERE ...
) AS Unprotected ON Unprotected.PostID=ph.PostID


What does this number mean?

WHERE ph.PostHistoryTypeID=19


No easy way to tell. Consider something like:
DECLARE @QuestionProtected INT = (SELECT Id FROM PostHistoryTypes WHERE Name = 'Question Protected');

Then it's easier to follow:
WHERE ph.PostHistoryTypeID = @QuestionProtected


SEDE magic
You can get this valueat the top, or assign a default value for newbies like me:
-- postViews: Number of post views
DECLARE @postViews INT = ##postViews:int?10000##;

The other one is a script variable so it can't be manipulated as easily, but can still be assigned a default value (and documented):
WHERE 
  ph.UserID IN ( 
    -- NumbUsers: Max number of users
    SELECT TOP ##NumbUsers:int?5000## 


One more thing,
  AND isnull(Protected.ProtectCount,0) != isnull(Unprotected.UnprotectedCount,0)

The != operator is not SQL standard. Instead, use &lt;&gt;. Also, consider using COALESCE() instead of ISNULL() for some of the reasons listed here. Performance is right about the same, but it handles types better, usually (and can take more than 2 arguments, if needed).

Everything combined (demo on SEDE)
-- postViews: Number of post views
DECLARE @postViews INT = ##postViews:int?10000##;

DECLARE @UsersToExclude TABLE (UserId INT PRIMARY KEY);
DECLARE @QuestionProtected INT = (SELECT Id FROM PostHistoryTypes WHERE Name = 'Question Protected');
INSERT INTO 
  @UsersToExclude (UserId)
VALUES
  /*Some comments to explain these numbers would be good:*/
  (419),
  (1228),
  (1288),
  (19679),
  (23354),
  (50049),
  (59303),
  (102937),
  (106224),
  (246246),
  (-1);
SELECT 
  [Post Link] = ph.PostId,
  [User Link] = ph.UserId,
  Protected.ProtectCount,
  Unprotected.UnprotectedCount,
  p.ViewCount
FROM 
  PostHistory AS ph
  INNER JOIN 
    Posts AS p ON ph.PostID = p.id
  LEFT JOIN ( 
    SELECT 
      count(UserID) AS ProtectCount,
      PostID
    FROM PostHistory ph
    WHERE ph.PostHistoryTypeID = @QuestionProtected
      GROUP BY PostID
  ) AS Protected ON Protected.PostID = ph.PostID
  LEFT JOIN ( 
    SELECT 
      count(ph.UserID) AS UnprotectedCount,
      PostID
     FROM PostHistory ph
     WHERE ph.PostHistoryTypeID=20
     GROUP BY PostID
  ) AS Unprotected ON Unprotected.PostID = ph.PostID
WHERE 
  ph.UserID IN ( 
    -- NumbUsers: Max number of users
    SELECT TOP ##NumbUsers:int?5000## 
      ph.UserId
    FROM PostHistory ph
      JOIN Posts p ON ph.PostID=p.id
    WHERE ph.PostHistoryTypeId = @QuestionProtected
      AND p.ViewCount &lt; @postViews /* exclude former moderators */
      AND ph.UserID NOT IN (
         SELECT usrExcl.UserID from @UsersToExclude as usrExcl
      )
    GROUP BY ph.UserId
    ORDER BY COUNT(ph.UserId) DESC
  )
  AND ph.PostHistoryTypeId = @QuestionProtected
  AND p.ClosedDate IS NULL
  AND p.ViewCount &lt; @postViews
  AND COALESCE(Protected.ProtectCount,0) &lt;&gt; COALESCE(Unprotected.UnprotectedCount,0)
ORDER BY ph.UserId ASC;