Something like this will work without being inside the function:
DECLARE @LowerDate DATE SET @LowerDate = GETDATE() DECLARE @UpperLimit DATE SET @UpperLimit = '20111231' ;WITH Firsts AS ( SELECT DATEADD(DAY, -1 * DAY(@LowerDate) + 1, @LowerDate) AS 'FirstOfMonth' UNION ALL SELECT DATEADD(MONTH, 1, f.FirstOfMonth) AS 'FirstOfMonth' FROM Firsts f WHERE DATEADD(MONTH, 1, f.FirstOfMonth) <= @UpperLimit ) SELECT * FROM Firsts
It uses a thing called CTE (Common Table Expression) - available in SQL Server 2005 and later and other database systems.
In this case, I start the recursive CTE by determining the first month for the specified @LowerDate date, and then repeat adding one month to the previous first month until the upper limit is reached.
Or if you want to pack it into a stored function, you can also do this:
CREATE FUNCTION dbo.GetFirstOfMonth(@LowerLimit DATE, @UpperLimit DATE) RETURNS TABLE AS RETURN WITH Firsts AS ( SELECT DATEADD(DAY, -1 * DAY(@LowerLimit) + 1, @LowerLimit) AS 'FirstOfMonth' UNION ALL SELECT DATEADD(MONTH, 1, f.FirstOfMonth) AS 'FirstOfMonth' FROM Firsts f WHERE DATEADD(MONTH, 1, f.FirstOfMonth) <= @UpperLimit ) SELECT * FROM Firsts
and then call it like this:
SELECT * FROM dbo.GetFirstOfMonth('20100522', '20100831')
to get this conclusion:
FirstOfMonth 2010-05-01 2010-06-01 2010-07-01 2010-08-01
PS: using the DATE data type, which is present in SQL Server 2008 and newer, I fixed two "errors" that Richard commented on. If you are on SQL Server 2005, you will have to use DATETIME instead - and deal with the fact that you also get some of the time.