SQL : find rows and sort according to number of matching columns?

后端 未结 4 1329
星月不相逢
星月不相逢 2020-12-19 23:11

Let\'s imagine that we are having \"cars\" table with such a simple structure...

car_id INT
color ENUM(\'black\',\'white\',\'blue\')
weight ENUM(\'light\',\'         


        
相关标签:
4条回答
  • 2020-12-19 23:47

    There are probably a few ways to optimise the sub-queries, but without using case statements or sub-optimal join clauses:

    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
    

    Output:

    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 讨论(0)
  • 2020-12-20 00:01

    Not overly efficient, but...

    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
    

    This would not run very fast on large data sets, since neither the JOIN nor the ORDER BY can make use of an index. Very probably a more optimal version exists.

    Output on my test setup looks like this:

    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 讨论(0)
  • 2020-12-20 00:06
    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)
    
    0 讨论(0)
  • 2020-12-20 00:11

    I know this is an old question, but you should be able to wrap an expression in parenthesis to evaluate it

    SELECT   *           
    FROM     `cars`
    WHERE    `color` = "black"
       OR    `weight` = "heavy"
       OR    `type` = "limo"
    ORDER BY (   (`color` = "black")
               + (`weight` = "heavy")
               + (`type` = "limo") 
             ) DESC
    

    Each expression inside parenthesis will equal 1 if true, 0 if false; thus the sum of which will be the number of matches.

    0 讨论(0)
提交回复
热议问题