MySQL выбирает строки с максимальным идентификатором и соответствует другим условиям - PullRequest
4 голосов
/ 08 декабря 2010

Используя приведенные ниже таблицы в качестве примера и перечисленный запрос в качестве базового запроса, я хочу добавить способ выбора только строк с максимальным идентификатором!Без необходимости делать второй запрос!

TABLE VEHICLES

id      vehicleName
-----   --------
1       cool car
2       cool car
3       cool bus
4       cool bus
5       cool bus
6       car
7       truck
8       motorcycle
9       scooter
10      scooter
11      bus

TABLE VEHICLE NAMES

nameId  vehicleName
------  -------
1       cool car
2       cool bus
3       car
4       truck
5       motorcycle
6       scooter
7       bus

TABLE VEHICLE ATTRIBUTES

nameId  attribute
------  ---------
1       FAST
1       SMALL
1       SHINY
2       BIG
2       SLOW
3       EXPENSIVE
4       SHINY
5       FAST
5       SMALL
6       SHINY
6       SMALL
7       SMALL

И базовый запрос:

select a.*
  from vehicle         a
  join vehicle_names   b using(vehicleName)
  join vehicle_attribs c using(nameId)
 where c.attribute in('SMALL', 'SHINY')
 and a.vehicleName like '%coo%'
 group 
    by a.id
having count(distinct c.attribute) = 2;

Итак, я хочу добиться, чтобы выбрать строки с определенными атрибутами, которые соответствуют имени, нотолько одна запись для каждого имени, которое совпадает с тем, где идентификатор является самым высоким!

Таким образом, рабочее решение в этом примере вернет следующие строки:

id      vehicleName
-----   --------
2       cool car
10      scooter

, если оно использует какой-то видmax для идентификатора

на данный момент я получаю все записи для крутых автомобилей и скутеров.

Моя база данных реального мира имеет аналогичную структуру и содержит 10 тысяч записей, поэтому запроскак выше, может легко вернуть 3000+ результатов.Я ограничиваю результаты до 100 строк, чтобы сократить время выполнения, так как результаты используются при поиске на моем сайте.Причина, по которой у меня есть повторения «транспортных средств» с тем же именем, но только с другим идентификатором, заключается в том, что новые модели постоянно добавляются, но я оставляю старые для тех, кто хочет их откопать!Но при поиске по названию автомобиля я не хочу возвращать старые карты, только самые новые карты с самым высоким ID!

Правильный ответ адаптирует запрос, который я предоставил выше, что яв настоящее время используется и возвращает только те строки, где имя совпадает, но имеет самый высокий идентификатор!

Если это невозможно, предложения о том, как мне добиться того, чего я хочу, без значительного увеличения времени выполнения поиска, будутбыть оцененным!

Ответы [ 4 ]

5 голосов
/ 12 декабря 2010

Если вы хотите сохранить свою логику, вот что я бы сделал:

select a.*
from vehicle a
    left join vehicle a2 on (a.vehicleName = a2.vehicleName and a.id < a2.id)
    join vehicle_names   b on (a.vehicleName = b.vehicleName)
    join vehicle_attribs c using(nameId)
where c.attribute in('SMALL', 'SHINY')
    and a.vehicleName like '%coo%'
    and a2.id is null
group by a.id
having count(distinct c.attribute) = 2;

Какой выход:

+----+-------------+
| id | vehicleName |
+----+-------------+
|  2 | cool car    |
| 10 | scooter     |
+----+-------------+
2 rows in set (0.00 sec)

Как уже говорилось, нормализация может быть выполнена на нескольких уровнях:

Сохраняя вашу текущую таблицу vehicle_names в качестве основной таблицы поиска, я бы изменил:

update vehicle a
    inner join vehicle_names b using (vehicleName)
set a.vehicleName = b.nameId;
alter table vehicle change column vehicleName nameId int;

create table attribs (
    attribId int auto_increment primary key,
    attribute varchar(20),
    unique key attribute (attribute)
);
insert into attribs (attribute)
    select distinct attribute from vehicle_attribs;
update vehicle_attribs a
    inner join attribs b using (attribute)
set a.attribute=b.attribId;
alter table vehicle_attribs change column attribute attribId int;

Что привело к следующему запросу:

select a.id, b.vehicleName
from vehicle a
    left join vehicle a2 on (a.nameId = a2.nameId and a.id < a2.id)
    join vehicle_names b on (a.nameId = b.nameId)
    join vehicle_attribs c on (a.nameId=c.nameId)
    inner join attribs d using (attribId)
where d.attribute in ('SMALL', 'SHINY')
    and b.vehicleName like '%coo%'
    and a2.id is null
group by a.id
having count(distinct d.attribute) = 2;
3 голосов
/ 08 декабря 2010

Таблица не выглядит нормализованной, однако это поможет вам сделать это:

select max(id), vehicleName
from VEHICLES
group by vehicleName
having count(*)>=2;
1 голос
/ 10 декабря 2010

Я не уверен, что полностью понимаю вашу модель, но следующий запрос удовлетворяет вашим требованиям. Первый подзапрос находит последнюю версию автомобиля. Второй запрос удовлетворяет вашим условиям "и". Тогда я просто присоединяюсь к запросам на VehicleName (что является ключом?).

select a.id
      ,a.vehiclename
  from (select a.vehicleName, max(id) as id
          from vehicle a
         where vehicleName like '%coo%'
        group by vehicleName
       ) as a
  join (select b.vehiclename
          from vehicle_names   b
          join vehicle_attribs c using(nameId)
         where c.attribute in('SMALL', 'SHINY') 
        group by b.vehiclename
        having count(distinct c.attribute) = 2
       ) as b on (a.vehicleName = b.vehicleName);

Если вам необходимо много работать с этой логикой «последнего автомобиля», небольшим предложением будет создание представления (см. Ниже), которое возвращает последнюю версию каждого автомобиля. Тогда вы можете использовать представление вместо find-max-query. Обратите внимание, что это просто для простоты использования, оно не дает никаких преимуществ в производительности.

select *
  from vehicle a
 where id = (select max(b.id)
               from vehicle b
              where a.vehiclename = b.vehiclename);
0 голосов
/ 10 декабря 2010

Не вдаваясь в надлежащую редизайн вашей модели, вы можете

1) Добавить столбец IsLatest, которым может управлять ваше приложение.

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

UPDATE a
SET IsLatest = 0
WHERE IsLatest = 1

INSERT new a

UPDATE a
SET IsLatest = 1
WHERE nameId = @last_inserted_id

в транзакции или триггер

2) В качестве альтернативы вы можете узнатьmax_id перед отправкой запроса

SELECT MAX(nameId)
FROM a
WHERE vehicleName = @name

3) Вы можете сделать это в одном SQL, и, предоставляя индексы (vehicleName, nameId), он должен иметь приличную скорость с

select a.*
  from vehicle         a
  join vehicle_names   b ON a.vehicleName = b.vehicleName
  join vehicle_attribs c ON b.nameId = c.nameId AND c.attribute = 'SMALL'
  join vehicle_attribs d ON b.nameId = c.nameId AND d.attribute = 'SHINY'
  join vehicle         notmax ON a.vehicleName = b.vehicleName AND a.nameid < notmax.nameid 
 where a.vehicleName like '%coo%'
       AND notmax.id IS NULL

Я удалил ваши GROUP BY и HAVING и заменил их другим соединением (при условии, что возможен только один атрибут для nameId).

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

Существуют и другие способы, поищите «max на группу sql».Также см. здесь , хотя и не завершено.

...