Y
There might be a way to rearrange parts of the correlated subquery so that it can reference the outer level's products_id directly, rather than in a subquery/derived table of its own, but in the end I had too little patience to find it.
Instead, I can offer you a different approach to calculating the same results, which uses variables:
SELECT
p.products_id
, (
SELECT
SUM(
DATEDIFF(to_date, GREATEST(from_date, CURDATE() - INTERVAL 400 DAY)) * factor
)
FROM
(
SELECT
IF(h.products_id = @p, @d, CURDATE() + INTERVAL 1 DAY) AS to_date
, @d := h.inventory_date AS from_date
, (quantity > 0) AS factor
, @p := h.products_id AS products_id
FROM
inventory_history AS h
ORDER BY
h.products_id ASC
, h.inventory_date DESC
) AS v1
WHERE
v1.products_id = p.products_id
AND v1.to_date >= CURDATE() - INTERVAL 400 DAY
) AS count
FROM
(SELECT @p := null, @d := null) AS init
, products AS p
WHERE
p.master_categories_id=264
;
With the help of two variables – @p to keep track of the current products_id, and @d to carry over inventory_date to the preceding record of the same product – the v1 derived table turns inventory_history into a set of date ranges, with a flag (factor) to indicate whether the range qualifies for the count result or not.
The derived table's definition is two levels deep, so it cannot reference the main query's products_id. It simply returns the entire set. However, the SELECT that is using v1 is where p.products_id is valid, and that is where filtering on products_id happens.
The count itself is then simply a matter of adding up the date ranges' lengths, using the DATEDIFF(end_date, start_date) function. The starting day expression (GREATEST(...)) simply makes sure that the earliest date range's starting point, for the purposes of the query, is no earlier than 400 days from now.
Here is a link to a live demo of this solution at dbfiddle.uk using your test setup:
https://dbfiddle.uk/?rdbms=mariadb_10.3&fiddle=dc5baefec8902a99bf807115aaa235ba