简体   繁体   中英

How do I order mysql query results by values from a joined table's rows (not columns)?

I have a table items and a table parameters . A join table of item_parameters stores the value for each possible parameter for an item, instead of having a separate column for each parameter.

For example (simplified):

parameters:             items:              
id: 1, name: color      id: 1, name: bike
id: 2, name: size       id: 2, name: phone
id: 3, name: weight     id: 3, name: shoe

item_parameters: 
id: 1, item_id: 1, parameter_id: 1, value: "blue"
id: 2, item_id: 1, parameter_id: 2, value: "large"
id: 3, item_id: 1, parameter_id: 3, value: "heavy"
id: 4, item_id: 2, parameter_id: 1, value: "black"
id: 5, item_id: 2, parameter_id: 2, value: "medium"
id: 6, item_id: 2, parameter_id: 3, value: "medium"
id: 7, item_id: 3, parameter_id: 1, value: "white"
id: 8, item_id: 3, parameter_id: 2, value: "small"
id: 9, item_id: 3, parameter_id: 3, value: "light"

How can I search for items (applying a filter for specific parameters) and order the results based on the value of a particular parameter for the items ?

I know how to apply the filter part, using a where clause with item_parameters.parameter_id = ? AND item_parameters.value = ? item_parameters.parameter_id = ? AND item_parameters.value = ? But for the ordering part, simply adding ORDER BY item_parameters.value desc doesn't seem to be enough to order by only those item_parameters.value where item_parameters.parameter_id = ? .

In my results of items, I want all item_parameters for that item including the parameters that weren't used in the order by clause.

This query is still a work in progress because I don't yet have it returning all item_parameters for each item :

 SELECT DISTINCT
         `items`.name,
         `parameters`.name,
         `item_parameters`.value
 FROM `items` 
 LEFT OUTER JOIN `item_parameters` ON `item_parameters`.`item_id` = `items`.`id`
 LEFT OUTER JOIN `parameters` ON `item_parameters`.`parameter_id` = `parameters`.`id`
 WHERE (item_parameters.parameter_id = 3 AND item_parameters.value = 'light') 
 ORDER BY item_parameters.value LIMIT 10000;

In addition currently not returning all item_parameters, the relevant missing piece above for this post is that the order logic (pseudocode) needs to be ORDER BY items_parameters.value where item_parameters.parameter_id = 1

Here is an example of desired results, assumes from a larger example data set than the provided example above:

item name     color       size      weight
cup           brown       small     light
shirt         green       medium    light
candy         pink        large     light
chair         tan         medium    light
paper         white       large     light

Notice that the results are all filtered on parameter 3 (weight) = light and ordered on parameter 1 (color).

Thanks for any guidance on this.

I'm not sure it's best for performance but here's how I would logically construct the query.

First SELECT all the items and their parameters:

   SELECT i.name, p.name, ip.value
     FROM items i
     JOIN item_parameters ip
       ON ip.item_id = i.id
     JOIN parameters p
       ON p.id = ip.parameter_id

Then make sure you only have items that have the correct value for a particular param:

     JOIN item_parameters ip3
       ON ip3.parameter_id = 3
      AND ip3.item_id = i.id
      AND ip3.value = 'light'

Then include the param that you are ordering by:

     JOIN item_parameters ip1
       ON ip1.parameter_id = 1
      AND ip1.item_id = i.id

Then add the ORDER BY:

 ORDER BY ip1.value DESC, i.id, p.id

If you are ordering by a parameter that may not be present, you'll have to LEFT JOIN to that one and choose where to put the resultant NULL values.

This also will NOT pivot the table as shown in your example results, but will keep the items grouped together nicely.

If you wish to have the pivoted table, you'll probably have to join all the parameters separately, similar to 1 and 3 above. You can either do this manually or by dynamically building the query from a list of expected/required parameters.

UPDATE

For the pivoted results shown, with a bit of hard coding:

  SELECT i.name, color.value color, size.value size, weight.value weight
    FROM items i
    JOIN item_parameters color
      ON color.parameter_id = 1
     AND color.item_id = i.id
    JOIN item_parameters size
      ON size.parameter_id = 2
     AND size.item_id = i.id
    JOIN item_parameters weight
      ON weight.parameter_id = 3
     AND weight.item_id = i.id
   WHERE weight.value = 'light'
ORDER BY color.value DESC

You could join on the parameters table to get the names too just in case they change, but this gives you an outline.

The downside of this approach is that the query will have to be updated every time you add a new parameter.

Personally, I prefer the group style method, the pivot table can be generated using application code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM