Я предпочитаю подсчет за такие вещи. Как только вы начинаете работать с большими наборами строк, производительность rCTe очень быстро падает:
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --Up to 100 rows. Add more cross joins to N for more rows
SELECT YT.PromotionID
FROM dbo.YourTable YT
JOIN Tally T ON YT.NumberOfCodes >= T.I;