If the character field "looks" like numbers and they are only integers and not randomly padded with zeros or spaces, you can effectively do the sort as:
order by length(cm.row_id), cm.row_id
Edit:
Your values are not numbers. If you know the maximum depth, you can use regexp_substr()
:
order by cast(regexp_substr(col, '[^.]+', 1, 1) as number),
cast(regexp_substr(col, '[^.]+', 1, 2) as number),
cast(regexp_substr(col, '[^.]+', 1, 3) as number),
cast(regexp_substr(col, '[^.]+', 1, 4) as number)
Here is a rextester. And a working query with your sample data:
select *
from (select '4.6' as col from dual union all
select '5.2' as col from dual union all
select '3.1' as col from dual union all
select '5.3' as col from dual union all
select '3.5' as col from dual union all
select '5.4' as col from dual union all
select '2.11' as col from dual union all
select '2.12' as col from dual union all
select '2.13' as col from dual union all
select '2.14' as col from dual union all
select '2.15' as col from dual union all
select '5.5' as col from dual union all
select '5.6' as col from dual union all
select '5.7' as col from dual union all
select '2.17' as col from dual union all
select '5.8' as col from dual union all
select '5.9' as col from dual
) x
order by cast(regexp_substr(col, '[^.]+', 1, 1) as number),
cast(regexp_substr(col, '[^.]+', 1, 2) as number),
cast(regexp_substr(col, '[^.]+', 1, 3) as number),
cast(regexp_substr(col, '[^.]+', 1, 4) as number)