You can do the following (all the code below is available on the fiddle here):
CREATE TABLE booking
(
bk_no INTEGER NOT NULL PRIMARY KEY,
bk_nights INTEGER NOT NULL CHECK (bk_nights > 0 AND bk_nights < 100), -- 100 arbitrary
bk_sdt DATE NOT NULL
);
Populate the table:
INSERT INTO booking
VALUES
(1000, 5, '2021-05-06'),
(1001, 3, '2021-05-06');
And create a new table to hold our booking nights:
CREATE TABLE booking_nights
(
nt_no INTEGER NOT NULL,
bk_no INTEGER NOT NULL,
bk_night DATE NOT NULL,
PRIMARY KEY (bk_no, bk_night)
);
Then run the following query - it's a RECURSIVE CTE:
WITH cte (n, bkno, bknt, bkdt) AS
(
SELECT
1,
bk_no,
bk_nights,
bk_sdt
FROM booking
UNION ALL
SELECT
n + 1,
bkno,
bknt,
DATEADD(DAY, 1, bkdt)
FROM cte
WHERE n < bknt
)
INSERT INTO booking_nights
SELECT c.n, c.bkno, c.bkdt
FROM cte c
ORDER BY c.bkno, c.bkdt;
and then:
SELECT * FROM booking_nights
ORDER BY bk_no, bk_night;
Result:
nt_no bk_no bk_night
1 1000 2021-05-06
2 1000 2021-05-07
3 1000 2021-05-08
4 1000 2021-05-09
5 1000 2021-05-10
1 1001 2021-05-06
2 1001 2021-05-07
3 1001 2021-05-08
8 rows
A note on the CHECK constraint part of the table definition, i.e.:
bk_nights INTEGER NOT NULL CHECK (bk_nights > 0 AND bk_nights < 100), -- 100 arbitrary
with the use of this constraint in conjuction with the n variable in the main RCTE, the maximum number of iterations will be 100. Not having a limit on this risks going into an infinite loop in the event of some data error!
setting OPTION (MAXRECURSION 0); is dangerous, because that brake is deactivated (dbfiddle will eventually give a Run failed error).
An alternative might be to set MAXRECURSION to something like 100 and not have the constraint in the table definition, but to "drive" without brakes is never advisable. The maximum this can be set to is 32,767 (the highest possible value of a signed 32bit INTEGER). This is discussed in another answer of mine here.
A performance analysis is available here.