Как я могу выбрать лучшие 2 или 3 книги, не используя операцию "сверху"

У меня есть таблица с именами книг (book_name, price). Я использую SQL SERVER 2008. Мне нужно получить две лучшие книги, которые имеют максимальные цены.

Я использовал это:

 Select * From bb n  Where (Select Count(book) From bb  
    Where   book <> n.book
      And
        price >  n.price
        ) >=2 // or 3  it must be flexible 

но дает мне неправильный ответ:( .

не разрешается использовать операции top или order by или функции OLAP, просто запрос.

2 ответа

Решение

Вы могли бы использовать функции ранжирования вместо top

 with cte as(

    select *,row_number() over(order by price desc) as price from table
    )

    select * from cte where price<=2

Отказ от ответственности: я не призываю никого следовать методам, описанным в этом посте, в любой реальной ситуации. Я занимался этим вопросом только потому, что это теоретический вызов.

Я предполагаю, что это теоретический вопрос / учебник / домашнее задание, я не могу придумать причину не использовать order by в реальной жизненной ситуации, но здесь идет:

SELECT * FROM bb
WHERE price = (SELECT MAX(price) FROM bb)
OR price = (SELECT MAX(price) FROM bb WHERE price < (SELECT MAX(price) FROM bb))

Обратите внимание, что это даст более 2 строк, если ваш ценовой столбец не уникален. Вы можете это исправить, но тогда это становится довольно сложно:D

;WITH ids AS
(
    SELECT id FROM bb
    WHERE price = (SELECT MAX(price) FROM bb)
    OR price = (SELECT MAX(price) FROM bb 
        WHERE price < (SELECT MAX(price) FROM bb))
)
SELECT * FROM bb
WHERE id = (SELECT MAX(id) FROM ids)
OR id = (SELECT MAX(id) FROM ids WHERE id < (SELECT MAX(id) FROM ids))

Однако, если вы действительно хотите получить лучшие 2 цены, вы можете сделать это следующим образом:

SELECT MAX(price) as price FROM bb
UNION
SELECT MAX(price) FROM bb WHERE price < (SELECT MAX(price) FROM bb)

edit # 62 Хорошо, я понял это, заняло у меня достаточно много времени. Вы даже заставляли меня дурачиться с рекурсивными cte, пока я не понял, что это действительно так просто, как присвоить звание каждой записи и выбросить мусор. Этот запрос крайне неэффективен, но до настоящего времени он удовлетворял всем вашим требованиям. Также обратите внимание, что результаты не упорядочены. Это именно то, что order by для:D

DECLARE @NumberOfRecords int
SET @NumberOfRecords = 3

;WITH bb_extended AS
(
    SELECT
        (
            SELECT COUNT(*) + 1 FROM bb AS bb2
            WHERE bb2.price > bb.price
            OR (bb2.price = bb.price AND bb2.id < bb.id)
        ) AS rank,
        *
    FROM
        bb
)
SELECT * FROM bb_extended
WHERE rank <= @NumberOfRecords

Вы можете даже сократить это далее (но вы потеряете информацию о ранге):

DECLARE @NumberOfRecords int
SET @NumberOfRecords = 3

SELECT * FROM bb WHERE @NumberOfRecords > (
        SELECT COUNT(*) + 1 FROM bb AS bb2
        WHERE bb2.price > bb.price
        OR (bb2.price = bb.price AND bb2.id < bb.id)
    )

Шутки в сторону. Не делайте этого.

Другие вопросы по тегам