MySQL - Как выбрать только первые X строк, которые имеют одинаковое значение поля? - PullRequest
1 голос
/ 18 марта 2012

Вот простая таблица, которая описывает событие в календаре:

Event
--------------------
Id      int
DayId   int         # Foreign key to Day table
Title   varchar(32)
Start   datetime
Finish  datetime

И произвольный оператор SELECT для получения некоторого набора результатов:

select
    Id,
    DayId,
    Title,
    Start,
    Finish
from Event
where Start > now()
order by Start

Приведенный выше запрос выбора вернет всех событий в будущем, что нежелательно. Но использование limit означает, что вы должны знать, сколько вы хотите ограничить.

Я бы хотел иметь возможность выбрать первые X строк , которые имеют одинаковые DayId значения.


Некоторые примеры, которые помогут лучше объяснить эту ситуацию:

Пример результатов:

Id: 26,   DayId: 08,   Title: "Foo",    Start: "2012-03-19 23:00:00"
Id: 27,   DayId: 08,   Title: "Bar",    Start: "2012-03-20 00:00:00"
Id: 28,   DayId: 09,   Title: "Baz",    Start: "2012-03-21 09:00:00"
Id: 29,   DayId: 10,   Title: "Barbaz", Start: "2012-03-22 11:00:00"
Id: 30,   DayId: 09,   Title: "Fooboo", Start: "2012-03-25 15:00:00"

Предполагая, что приведенный выше запрос вернул результирующий набор, подобный этому, запрос, который я ищу, будет возвращать только первые две строки, поскольку они являются следующими происходящими событиями с тем же DayId .

Однако после 19 марта условие Start > now() вернет другой набор результатов:

Id: 28,   DayId: 09,   Title: "Baz",    Start: "2012-03-21 09:00:00"
Id: 29,   DayId: 10,   Title: "Barbaz", Start: "2012-03-22 11:00:00"
Id: 30,   DayId: 09,   Title: "Fooboo", Start: "2012-03-25 15:00:00"

И в этом случае результат должен возвращать только первую строку . Обратите внимание, что (для пояснения) последний результат имеет с тем же DayId, но поскольку он отделен другим DayId, его следует игнорировать.

Ответы [ 5 ]

1 голос
/ 19 марта 2012

Насколько я понимаю, вам нужны все строки с первым идентификатором дня.Так как насчет этого

SELECT
  e1.Id,
  e1.DayId,
  e1.Title,
  e1.Start,
  e1.Finish
FROM Event e1.
    LEFT JOIN Event e2 ON e2.DayId>e1.DayId AND e2.Start<e1.Start
WHERE e1.DayId = (
    SELECT MIN(DayId) FROM Event WHERE Start > now()
  )
  AND e2.id IS NULL

Итак, вы получаете первый DayId во всех будущих событиях в подзапросе, а затем получаете все события, имеющие этот DayId.

1 голос
/ 18 марта 2012

Попробуйте:

select id, dayid, title start from (
  select id,
    @equal := @equal and dayId = @dayId ShouldReturn,
    @dayId := dayId as dayId,
    title,
    start
  from t, (
    select @dayId := dayid, @equal := true from t
    where start = (
      select min(start) from t
      where start > now()
    )) init
  where start > now()
  order by start
) as final
where ShouldReturn
1 голос
/ 18 марта 2012

Возможно, это не самый оптимальный способ достижения желаемого результата, но он будет работать:

select
    Id,
    DayId,
    Title,
    Start,
    Finish
from Event
where Start > now(), AND DayId IN (SELECT DayId FROM Event WHERE Start > now() ORDER BY Start)
order by Start

Просто понял, что вы не можете использовать LIMIT для подзапроса, сейчас этот ответ не будет работать

0 голосов
/ 19 марта 2012
SELECT 
    e.*
FROM 
        Event AS e
    JOIN
        ( SELECT DayId
          FROM Event
          WHERE Start > NOW()
          ORDER BY Start
          LIMIT 1
        ) AS good
      ON  e.Start > NOW()
      AND e.Start < COALESCE(
            ( SELECT Start
              FROM Event
              WHERE Start > NOW()
                AND DayId <> good.DayId
              ORDER BY Start
              LIMIT 1
            )
            , '9999-12-31') 
ORDER BY 
    e.Start

или

SELECT 
    e.*
FROM 
        Event AS e
    CROSS JOIN
        ( SELECT Start
          FROM 
                Event AS ee
            CROSS JOIN
              ( SELECT DayId
                FROM Event 
                WHERE Start > NOW()
                ORDER BY Start
                LIMIT 1
              ) AS good
          WHERE Start > NOW()
            AND ee.DayId <> good.DayId
          ORDER BY Start
          LIMIT 1
        ) AS bad
WHERE 
    e.Start > NOW()
  AND 
    e.Start < COALESCE(bad.Start, '9999-12-31') 
ORDER BY 
    e.Start
0 голосов
/ 19 марта 2012

ответ Чеви почти идеален. Он должен работать, но у MySQL есть ограничение на невозможность использования limit внутри подзапроса. Но возможен простой обходной путь:

select
    Id,
    DayId,
    Title,
    Start,
    Finish
from Event
where Start > now(), 
    and DayId in (

    -- Prepare yourself for a mega-hack!

        select * from (
            select `DayId`
            from Event
            where Start > now
            order by Start
            limit 1

            -- You are allowed a limit here for some reason

        ) alias
    )
order by Start

Этот работает , но подзапрос, действующий как псевдоним таблицы, не кажется оптимальным решением, поэтому этот ответ не будет помечен как ответный, если кто-то может помочь ответить на вопрос лучше.

...