2010-01-21 5 views
2

Давайте представим, что у нас есть «автомобили» таблица с такой простой структурой ...SQL: найти строки и отсортировать их по количеству совпадающих столбцов?

car_id INT 
color ENUM('black','white','blue') 
weight ENUM('light','medium','heavy') 
type ENUM('van','sedan','limo') 

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

Возможно ли выполнить эту сортировку с использованием SQL?

Извините за мой английский, но я очень надеюсь, что мой вопрос ясен для вас.

Спасибо.

+0

Это действительно трудно понять. Как связаны автомобили? – Codesleuth

+0

Я считаю, что он выбирает одну запись, затем сравнивает все остальные против нее и подсчитывает количество совпадающих полей для каждой записи. Затем он хочет сортировать записи по количеству полей, которые соответствуют (одинаковы). Таким образом, в основном сортировка по подобию одной записи выбрана. – sleske

+0

@sleske: Я все еще смущен, я позволю вам ответить на этот вопрос: P – Codesleuth

ответ

2

Есть, вероятно, несколько способов оптимизации суб-запросы, но без использования case заявлений или суб-оптимального объединения статей:

select 
     * 
    from 
     (
      select 
        selection.CarId, 
        selection.Colour, 
        selection.Weight, 
        selection.Type, 
        3 as Relevance 
       from 
        tblCars as selection 
       where 
        selection.Colour = 'black' and selection.Weight = 'light' and selection.Type = 'van' 
      union all 
      select 
        cars.CarId, 
        cars.Colour, 
        cars.Weight, 
        cars.Type, 
        count(*) as Relevance 
       from 
        tblCars as cars 
       inner join 
        (
         select 
           byColour.CarId 
          from 
           tblCars as cars 
          inner join 
           tblCars as byColour 
          on 
           cars.Colour = byColour.Colour 
          where 
           cars.Colour = 'black' and cars.Weight = 'light' and cars.Type = 'van' 
           and 
           byColour.CarId <> cars.CarId 
         union all 
         select 
           byWeight.CarId 
          from 
           tblCars as cars 
          inner join 
           tblCars as byWeight 
          on 
           cars.Weight = byWeight.Weight 
          where 
           cars.Colour = 'black' and cars.Weight = 'light' and cars.Type = 'van' 
           and 
           byWeight.CarId <> cars.CarId 
         union all 
         select 
           byType.CarId 
          from 
           tblCars as cars 
          inner join 
           tblCars as byType 
          on 
           cars.Type = byType.Type 
          where 
           cars.Colour = 'black' and cars.Weight = 'light' and cars.Type = 'van' 
           and 
           byType.CarId <> cars.CarId 
        ) as matches 
       on 
        cars.CarId = matches.CarId 
       group by 
        cars.CarId, 
        cars.Colour, 
        cars.Weight, 
        cars.Type 
     ) as results 
    order by 
     Relevance desc 

Выход:

CarId Colour Weight Type Relevance 
1  black light van  3 
3  white light van  2 
4  blue light van  2 
5  black medium van  2 
6  white medium van  1 
7  blue medium van  1 
8  black heavy limo 1 
+0

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

+0

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

+0

Спасибо за полное объяснение! – Kirzilla

4

не слишком эффективны, но ...

SELECT 
    exact.car_id AS e_car_id, exact.color AS e_color, 
    exact.weight AS e_weight, exact.type AS e_type, 
    related.car_id AS r_car_id, related.color AS r_color, 
    related.weight AS r_weight, related.type AS r_type, 
    CASE WHEN related.color = exact.color  THEN 1 ELSE 0 END 
    + CASE WHEN related.weight = exact.weight THEN 1 ELSE 0 END 
    + CASE WHEN related.type = exact.type  THEN 1 ELSE 0 END 
    AS rank 
FROM 
    cars AS exact 
    INNER JOIN cars AS related ON (
    related.car_id <> exact.car_id 
    AND CASE WHEN related.color = exact.color THEN 1 ELSE 0 END 
     + CASE WHEN related.weight = exact.weight THEN 1 ELSE 0 END 
     + CASE WHEN related.type = exact.type  THEN 1 ELSE 0 END 
    >= 1 
) 
WHERE 
    exact.car_id = 1 /* black, heavy, limo */ 
ORDER BY 
    rank DESC 

Это не будет работать очень быстро на больших наборах данных, так как ни присоединитесь, ни ORDER BY можно использовать индекс. Вероятно, существует более оптимальная версия.

Вывод на моей тестовой конфигурации выглядит следующим образом:

 
e_car_id e_color e_weight e_type r_car_id r_color r_weight r_type rank 
1   black heavy  limo 7   black heavy  limo 3 
1   black heavy  limo 2   black light  limo 2 
1   black heavy  limo 3   black heavy  van  2 
1   black heavy  limo 4   black medium van  1 
1   black heavy  limo 5   blue  light  limo 1 
+0

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

0
mysql> select * from cars; 
+--------+-------+--------+-------+ 
| car_id | color | weight | type | 
+--------+-------+--------+-------+ 
|  1 | black | light | van | 
|  2 | black | light | sedan | 
|  3 | black | light | limo | 
|  4 | black | medium | van | 
|  5 | black | medium | sedan | 
|  6 | black | medium | limo | 
|  7 | black | heavy | van | 
|  8 | black | heavy | sedan | 
|  9 | black | heavy | limo | 
|  10 | white | light | van | 
|  11 | white | light | sedan | 
|  12 | white | light | limo | 
|  13 | white | medium | van | 
|  14 | white | medium | sedan | 
|  15 | white | medium | limo | 
|  16 | white | heavy | van | 
|  17 | white | heavy | sedan | 
|  18 | white | heavy | limo | 
|  19 | blue | light | van | 
|  20 | blue | light | sedan | 
|  21 | blue | light | limo | 
|  22 | blue | medium | van | 
|  23 | blue | medium | sedan | 
|  24 | blue | medium | limo | 
|  25 | blue | heavy | van | 
|  26 | blue | heavy | sedan | 
|  27 | blue | heavy | limo | 
+--------+-------+--------+-------+ 
27 rows in set (0.00 sec) 

select *, 
(case 
    when color = 'black' and weight = 'heavy' and type = 'limo' 
    then 3 
    when (color = 'black' and type = 'limo') or 
     (color = 'black' and weight = 'heavy') or 
     (weight = 'heavy' and type = 'limo') 
    then 2 
    else 1 
end) sort_order 
from cars 
where color = 'black' or weight = 'heavy' or type = 'limo' 
order by sort_order desc; 


+--------+-------+--------+-------+------------+ 
| car_id | color | weight | type | sort_order | 
+--------+-------+--------+-------+------------+ 
|  9 | black | heavy | limo |   3 | 
|  27 | blue | heavy | limo |   2 | 
|  18 | white | heavy | limo |   2 | 
|  8 | black | heavy | sedan |   2 | 
|  7 | black | heavy | van |   2 | 
|  6 | black | medium | limo |   2 | 
|  3 | black | light | limo |   2 | 
|  24 | blue | medium | limo |   1 | 
|  25 | blue | heavy | van |   1 | 
|  21 | blue | light | limo |   1 | 
|  26 | blue | heavy | sedan |   1 | 
|  17 | white | heavy | sedan |   1 | 
|  16 | white | heavy | van |   1 | 
|  15 | white | medium | limo |   1 | 
|  12 | white | light | limo |   1 | 
|  5 | black | medium | sedan |   1 | 
|  4 | black | medium | van |   1 | 
|  2 | black | light | sedan |   1 | 
|  1 | black | light | van |   1 | 
+--------+-------+--------+-------+------------+ 
19 rows in set (0.00 sec) 
Смежные вопросы