In SqlServer, option 1 can be implemented using the materialized view , in which case it will cost up to the third option.
Create a view:
create view dbo.LikeCount with schemabinding as select post_id, Cnt = count_big(*) from dbo.Like group by post_id GO
Add an index that materializes the view:
create unique clustered index IX_LikeCount on dbo.LikeCount (post_id) GO
You can create a similar view for the Comment table. Then you can request the data as follows:
select p.id, p.title, isnull(lc.Cnt, 0) as LikeCnt, isnull(cc.Cnt, 0) as CommentCnt from Post p left join LikeCount lc with (noexpand) on lc.post_id = p.id left join CommentCount cc with (noexpand) on cc.post_id = p.id
The convenience of this option is that when data changes in the Comment and Like tables, the numbers will be automatically recalculated (in options 2 and 3, this logic will have to be implemented specifically - in triggers, or procedures / queries that work with adding / deleting likes and comments).
The disadvantage is that there are separate submissions for likes and comments, as a result of which there are two additional connections in the request (in option 3 it will be one, and in option 2 it will not be at all). Also, if there is a possibility that in the future it may be necessary to delete users, leaving likes, then this option will not work, and it is better to look towards options 2 and 3.
Option 2 is more profitable for select (the request does not require an additional connection to pull out the number of likes and comments). Option 3 is better from the point of view of the independence of posts from the quantities accompanying them (if there is a change in the entry corresponding to some post, and at the same time someone decided to like this entry, then these two actions will not block each other).