You can use a limited tally table together with a gaplessly generated row-number like here:
The SELECT
is always the same. The only thing changing is the amount of rows in the mockup-table:
DECLARE @TopCount INT=5;
--Case 1: More then 5 rows in the table
DECLARE @tbl TABLE(ID INT IDENTITY,SomeValue VARCHAR(100));
INSERT INTO @tbl VALUES
('Value1'),('Value2'),('Value3'),('Value4'),('Value5'),('Value6'),('Value7');
WITH Tally(Nmbr) AS(SELECT TOP(@TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM @tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 2: Less than 5 rows in the table
DELETE FROM @tbl WHERE ID BETWEEN 2 AND 5;
WITH Tally(Nmbr) AS(SELECT TOP(@TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM @tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 3: Exactly one row in the table
DELETE FROM @tbl WHERE ID <> 6;
WITH Tally(Nmbr) AS(SELECT TOP(@TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM @tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
--Case 4: Table is empty
DELETE FROM @tbl;
WITH Tally(Nmbr) AS(SELECT TOP(@TopCount) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
,NumberedRows AS(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS GeneratedRowNumber, * FROM @tbl)
SELECT *
FROM NumberedRows nr
FULL OUTER JOIN Tally t ON nr.GeneratedRowNumber=t.Nmbr;
This will return all rows from the source, but at least the specified count.
If you want to limit the set to exactly 5 rows (e.g. in "Case 1"), you can use SELECT TOP(@TopCount) *
and place an appropriate ORDER BY
. This would return the specified row count in any case.