MySQL - левое соединение с отметкой времени MAX

Я знаю, что это распространенный вопрос, потому что я задавал его раньше и просмотрел еще несколько, но все же, несмотря на то, что я основываю свой запрос на этом, я не могу заставить его работать. см.: соединение Mysql на основе max(timestamp) и mysql LEFT join для максимального значения правой таблицы

В основном у меня есть столы с разными предметами («стулья», «столы»), и каждый тип имеет запись со своим идентификатором для определенных стульев или столов. Затем у меня есть таблица местоположений, в которой хранятся все места, которые когда-либо были, включая текущее. Текущее местоположение отмечено меткой времени MAX.

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

РЕДАКТИРОВАТЬ: я не сказал, как это терпит неудачу: он получает записи и выполняет соединение, и он возвращает максимальную метку времени, но не информацию о записи, которая соответствует записи MAX (метка времени). Таким образом, он дает правильную отметку времени, но пол и т. Д. НЕ из записи с отметкой времени MAX.

Спасибо!

Запрос:

$q = "SELECT
        'chairs' AS work_type,
        chairs.id AS work_id,
        chairs.title,
        chairs.dimensions,
        CurrentLocations.floor,
        CurrentLocations.bin,
        CurrentLocations.bay,
        CurrentLocations.other
       FROM chairs
             LEFT JOIN ( 
                        SELECT 
                              locations.id AS loc_id,
                              locations.work_type AS clwt,
                              locations.work_id AS clwi,
                              locations.floor,
                              locations.bin,
                              locations.bay,
                              locations.other,
                              MAX(locations.timestamp) AS latestLocation
                            FROM
                               locations
                            GROUP BY
                               locations.work_type, locations.work_id
                            ) CurrentLocations
                ON CurrentLocations.clwi = chairs.id AND CurrentLocations.clwt = 'chairs'
        WHERE cur_loc LIKE '%19th Street%'
        ORDER BY 
            FIELD(floor, 'Basement', '3rd Floor', '4th Floor', '5th Floor'), 
            bin, 
            bay,
            other";

person dgig    schedule 16.01.2013    source источник
comment
как не сработало, какая у тебя ошибка? у тебя неверный результат?   -  person echo_Me    schedule 17.01.2013
comment
Да, извините, я не знал: он дает результаты, но запись о местоположении, которую он выбирает, не основана на последней отметке времени.   -  person dgig    schedule 17.01.2013
comment
Для начала вы обычно должны GROUP BY каждое неагрегированное поле в данном предложении SELECT. Например. ВЫБЕРИТЕ field1, field2, MAX (field3) FROM my_table GROUP BY field1, field2; Кроме того, ваша таблица местоположений выглядит так, как будто она нуждается в нормализации.   -  person Strawberry    schedule 17.01.2013


Ответы (1)


Попробуй это:

    SELECT
        'chairs' AS work_type,        -- do you really need this in the result?
        c.id AS work_id,
        c.title,
        c.dimensions,
        l.floor,
        l.bin,
        l.bay,
        l.other
    FROM 
        chairs AS c
      LEFT JOIN
        ( SELECT work_id,
                 MAX(timestamp) AS latestLocation
          FROM   locations
          WHERE  work_type = 'chairs'
          GROUP BY work_id
        ) AS cl                                  -- CurrentLocations
          ON  cl.work_id = c.id
      LEFT JOIN
        locations AS l 
          ON  l.work_type = 'chairs'
          AND l.work_id = cl.work_id
          AND l.timestamp = cl.latestLocation 
    WHERE 
        c.cur_loc LIKE '%19th Street%'
    ORDER BY 
        FIELD(l.floor, 'Basement', '3rd Floor', '4th Floor', '5th Floor'), 
        l.bin, 
        l.bay,
        l.other ;

Описание:

Ты хочешь:

В основном у меня есть таблицы разных предметов (chairs, tables), и у каждого типа есть запись со своим идентификатором для определенных стульев или столов. Затем у меня есть таблица locations, в которой хранятся все местоположения, которые когда-либо были, включая текущее. Текущее местоположение отмечено максимальным timestamp.

Мой запрос предназначен для получения всех предметов (стульев) и присоединения к текущему местоположению.

Итак, для каждого элемента (то есть chair) вам нужен только последний location. Для каждого work_id в таблице location вам нужно MAX(timestamp). Это можно легко найти с помощью GROUP BY, как вы уже выяснили:

        ( SELECT work_id,
                 MAX(timestamp) AS latestLocation
          FROM   locations
          GROUP BY work_type
                 , work_id
        ) AS cl

Но вы также хотите использовать другие столбцы из этой таблицы, и если вы просто добавите их в список выбора, он покажет неправильные результаты. (Обратите внимание, что в большинстве СУБД, чтобы использовать столбцы, которых нет в списке GROUP BY, вам придется применить функцию агрегации, как вы применили MAX() к timestamp. MySQL допускает такое поведение, которое при правильном использовании может иметь некоторые преимущества. , Но он не реализовал его на 100% отказоустойчивым, и поэтому его применение в некоторых случаях, таких как этот, может дать неправильные результаты. Postgres имеет гораздо лучшую реализацию этой функции, которая не допускает ошибочных результатов.)

Итак, что вам нужно сделать, так это использовать приведенный выше запрос GROUP BY в качестве производной таблицы (назовите ее cl или CurrentLocation, как хотите) и соединить ее с таблицей locations, используя все столбцы из списка GROUP BY и агрегированного списка (timestamp ):

      JOIN
        locations AS l 
          ON  l.work_type = cl.work_type
          AND l.work_id = cl.work_id
          AND l.timestamp = cl.latestLocation 

После этого вы можете присоединить их к таблице chairs как обычно. Поскольку в исходном запросе у вас было chairs LEFT JOIN locations, я тоже оставил их в ответе.

Еще одно небольшое изменение заключалось в том, что, поскольку у вас было CurrentLocations.work_id = 'chairs', вам нужны были только те строки из таблицы locations. Таким образом, довольно избыточно сначала выполнять группировку, а затем отклонять все строки, которые не соответствуют этому условию. Вероятно, более эффективно сначала применить условие, а затем сгруппировать по. Окончательный запрос группировки и условия соединения изменяются соответствующим образом.

Также обратите внимание, что индекс для (work_type, work_id, timestamp) повысит эффективность запроса (подзапрос cl будет обрабатываться с использованием только этого индекса, а JOIN locations также будет использовать индекс для поиска необходимых строк из таблицы местоположения.)

person ypercubeᵀᴹ    schedule 16.01.2013
comment
Спасибо. Можете ли вы — если хотите, вы уже сделали достаточно — как бы описать, что здесь происходит и в чем разница? Но это круто, большое спасибо. - person dgig; 17.01.2013
comment
Спасибо за эту помощь и объяснение. Это очень полезно. Если бы я мог снова проголосовать за вас, я бы это сделал. - person dgig; 17.01.2013