Get specific result without using loop
First add a row_number() value to the mix, because I'm guessing you can't rely on ID to have no gaps.

WITH NumberedRows AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY Number, Name ORDER BY SubNumber) AS RowNum
  FROM [Table]
)


--now compare RowNum to SubNumber. When the difference changes, there's been a gap in the sequence of SubNumber values.

, Diffs AS (
  SELECT *, SubNumber - RowNum AS TheDiff
  FROM NumberedRows
)
, Ranges AS (
  SELECT Number, Name,
    MIN(SubNumber) AS StartRange, 
    MAX(SubNumber) AS EndRange
  FROM Diffs
  GROUP BY Number, Name, TheDiff
)
, RangeStrings AS (
  SELECT Number, Name, 
    CASE WHEN StartRange = EndRange 
         THEN CAST(StartRange AS VARCHAR(10))
         ELSE CAST(StartRange AS VARCHAR(10)) + '-' + CAST(EndRange AS VARCHAR(10))
    END AS RangeString
  FROM Ranges
)


--And then just do your concatenation as you have already solved. :)

SELECT ...
FROM RangeStrings;