T-SQL:需要 Top N 总是 return N 行,即使为空或空白

T-SQL: Need Top N to always return N rows, even if null or blank

T-SQL:需要前 N 行总是 return N 行,即使为空或空白

通常,命令

Select Top 5 * FROM ourTable

将 return 最多 5 行,但会更少,具体取决于这些行是否存在。

我想确保它总是 returns 5 行,(或者通常是 N 行)。 实现此目的的语法是什么?

想法是将 "FirstOrDefault" 的 LINQ 概念概括为 "First_N_OrDefault",但使用 TSQL 而不是 LINQ。

显然,'extra' 行将包含空列或空列。

这适用于使用 SSMS 14.0.17 的 Microsoft SQL Server 2014

我想使用 "TOP" 语法,如果可能的话,因此它与可能的副本不同。此外,如下所述,这可能是可以在系统中的不同层解决的问题,但如果 TSQL 也有就好了。

select top (5) c1, c2, c3 from (
  select top (5) c1, c2, c3, 0 as priority from ourTable
  union all
  select c1, c2, c3, 1 from (values (null, null, null), (null, null, null), (null, null, null), (null, null, null), (null, null, null)) v (c1, c2, c3)
) t
order by priority

您可以使用另一个带有行的虚拟 table 来生成 table 的空行,但不匹配 JOIN。所以你不必重复 UNION ALL 部分中的列和行:

SELECT TOP 5 * FROM (
   SELECT 0 AS isDummy, * FROM table_name
   -- WHERE column_name = value
   UNION ALL
   SELECT 1 AS isDummy, t1.* FROM table_name t1 
      RIGHT JOIN INFORMATION_SCHEMA.COLUMNS ON t1.id = -1000 -- not valid condition so t1 columns are empty.
) t2
ORDER BY isDummy ASC

在这种情况下,INFORMATION_SCHEMA.COLUMNS table 用于生成额外的行。您可以选择任何其他 table 行。您可以使用 TOP N 值,直到右侧的行数 table(此处:INFORMATION_SCHEMA.COLUMNS)。


您还可以生成包含多行的 table(就像在日历上 table):

SELECT TOP 5 * FROM (
    SELECT 0 isDummy, * FROM table_name
    -- WHERE column_name = value
    UNION ALL
    SELECT 1 isDummy, t1.* FROM table_name t1 RIGHT JOIN (
        SELECT * FROM 
            (SELECT 0 t0 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t0,
            (SELECT 0 t1 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t1,
            (SELECT 0 t2 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t2,
            (SELECT 0 t3 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t3,
            (SELECT 0 t4 UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) t4
    ) t2 ON t1.id = -1000 -- not valid condition so t1 columns are empty.
)x
ORDER BY isDummy ASC

您可以将有限的 tally table 与无缝生成的 row-number 一起使用,如下所示:

SELECT总是一样的。唯一改变的是 mockup-table:

中的行数
DECLARE @TopCount INT=5;

--案例1:table

中多于5行
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;

--案例2:table

中少于5行
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;

--案例三: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;

--情况4:Table为空

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;

这将从源中 return 所有 行,但至少指定的计数。

如果您想将集合限制为恰好 5 行(例如在 "Case 1" 中),您可以使用 SELECT TOP(@TopCount) * 并放置适当的 ORDER BY。在任何情况下,这都会 return 指定的行数。