T-SQL: нужно, чтобы Top N всегда возвращал N строк, даже если он пустой или пустой - PullRequest
2 голосов
/ 08 мая 2019

T-SQL: нужно, чтобы Top N всегда возвращал N строк, даже если он пуст или пуст

Обычно команда

Select Top 5 * FROM ourTable

возвращает до 5 строк, но меньше,в зависимости от того, существуют ли строки.

Я хочу убедиться, что он всегда возвращает 5 строк (или вообще N строк).Каков синтаксис для достижения этого?

Идея состоит в том, чтобы обобщить концепцию LINQ «FirstOrDefault» в «First_N_OrDefault», но с использованием TSQL, а не LINQ.

Очевидно, что «лишние» строки будут иметь нулевые или пустые столбцы.

Это для Microsoft SQL Server 2014 с использованием SSMS 14.0.17

Я хочу использовать синтаксис «TOP», если это вообще возможно, поэтому он отличается от возможного дубликата.Кроме того, как отмечено ниже, это, возможно, что-то, что может быть решено на другом уровне в системе, но было бы неплохо иметь это и для TSQL.

Ответы [ 3 ]

5 голосов
/ 08 мая 2019
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
1 голос
/ 08 мая 2019

Вы можете использовать ограниченную таблицу подсчета вместе с генерируемым без пробелов номером строки, как здесь:

SELECT всегда одинаково.Меняется только количество строк в таблице макетов :

DECLARE @TopCount INT=5;

- вариант 1: более 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: Менее 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;

- Случай 3: ровно одна строка в таблице

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: Таблица пуста

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;

Это вернет все строки из источника, но по крайней мере указанное количество.

Если вы хотите ограничить набор ровно 5 строками (например,в «случае 1») вы можете использовать SELECT TOP(@TopCount) * и поместить соответствующее ORDER BY.В любом случае это вернет указанное количество строк.

1 голос
/ 08 мая 2019

Вы можете использовать другую фиктивную таблицу со строками, чтобы сгенерировать пустые строки вашей таблицы с несовпадающим 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 используется для генерации дополнительных строк. Вы можете выбрать любую другую таблицу со строками. Вы можете использовать значение TOP N до количества строк в правой таблице (здесь: INFORMATION_SCHEMA.COLUMNS).


Вы также можете создать таблицу с множеством строк (как в таблице календаря):

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
...