ORDER BY Color with Hex Code as a criterio in MySQL

眉间皱痕 提交于 2019-12-05 12:19:42
tlehman

You want to sort hex codes by wavelength, this roughly maps onto the hue-value. Given a hexcode as a six character string: RRGGBB.

You just need to make a function that takes in a hexcode string and outputs the hue value, here's the formula from this Math.SO answer:

R' = R/255

G' = G/255

B' = B/255

Cmax = max(R', G', B')

Cmin = min(R', G', B')

Δ = Cmax - Cmin

I wanted to see if this would work, so I whipped up a sample program in Ruby, it samples 200 random colors uniformly from RGB-space, and sorts them, the output looks like a rainbow!

Here's the Ruby source:

require 'paint'

def hex_to_rgb(hex)
  /(?<r>..)(?<g>..)(?<b>..)/ =~ hex
  [r,g,b].map {|cs| cs.to_i(16) }
end

def rgb_to_hue(r,g,b)
  # normalize r, g and b
  r_ = r / 255.0
  g_ = g / 255.0
  b_ = b / 255.0

  c_min = [r_,g_,b_].min
  c_max = [r_,g_,b_].max

  delta = (c_max - c_min).to_f

  # compute hue
  hue = 60 * ((g_ - b_)/delta % 6) if c_max == r_
  hue = 60 * ((b_ - r_)/delta + 2) if c_max == g_
  hue = 60 * ((r_ - g_)/delta + 4) if c_max == b_

  return hue
end

# sample uniformly at random from RGB space
colors = 200.times.map {  (0..255).to_a.sample(3).map { |i| i.to_s(16).rjust(2, '0')}.join   }

# sort by hue
colors.sort_by { |color| rgb_to_hue(*hex_to_rgb(color)) }.each do |color|
  puts Paint[color, color]
end

Note, make sure to gem install paint to get the colored text output.

Here's the output:

It should be relatively straight-forward to write this as a SQL user-defined function and ORDER BY RGB_to_HUE(hex_color_code), however, my SQL knowledge is pretty basic.

EDIT: I posted this question on dba.SE about converting the Ruby to a SQL user defined function.

aexl

This is based on the answer by @dliff. I initially edited it, but it turns out my edit was rejected saying "it should have been written as a comment or an answer". Seeing this would be too large to post as a comment, here goes.

The reason for editing (and now posting) is this: there seems to be a problem with colors like 808080 because their R, G and B channels are equal. If one needs this to sort or group colors and keep the passed grayscale/non-colors separate, that answer won't work, so I edited it.

DELIMITER $$

DROP FUNCTION IF EXISTS `hex_to_hue`$$

CREATE FUNCTION `hex_to_hue`(HEX VARCHAR(6)) RETURNS FLOAT
BEGIN
    DECLARE r FLOAT;
    DECLARE b FLOAT;
    DECLARE g FLOAT;
    DECLARE MIN FLOAT;
    DECLARE MAX FLOAT;
    DECLARE delta FLOAT;
    DECLARE hue FLOAT;
    IF(HEX = '') THEN
        RETURN NULL;
    END IF;
    SET r = CONV(SUBSTR(HEX, 1, 2), 16, 10)/255.0;
    SET g = CONV(SUBSTR(HEX, 3, 2), 16, 10)/255.0;
    SET b = CONV(SUBSTR(HEX, 5, 2), 16, 10)/255.0;
    SET MAX = GREATEST(r,g,b);
    SET MIN = LEAST(r,g,b);
    SET delta = MAX - MIN;
    SET hue=
    (CASE 
        WHEN MAX=r THEN (60 * ((g - b)/delta % 6))
        WHEN MAX=g THEN (60 * ((b - r)/delta + 2))
        WHEN MAX=b THEN (60 * ((r - g)/delta + 4))
        ELSE NULL
    END);
    IF(ISNULL(hue)) THEN
        SET hue=999;
    END IF;
    RETURN hue;
END$$

DELIMITER ;

Again, I initially wanted to edit the original answer, not post as a separate one.

If your products can have lots of color probably a good UI will require a color picker, normally those are rectangular, so not really something possible with the order by.

If the products have a manageable number of colors you have different choice, the easiest to implement is an order table, where for every possible color is defined an order position, this table can then be joined to your query, something like

SELECT ...
FROM   (SELECT ... 
               ...
               ...
             , ci.color_order
        FROM   customization_options co 
               LEFT JOIN customization_option_groups cog 
                         ON cog.id = co.customization_option_group_id
               LEFT JOIN color_ ci
                         ON designer_hex_color = ci.color
        WHERE  ...) a
ORDER BY color_order

Another way to go is to transform the RGB color to hue and use this as the order.
There are different formula for this conversion, depending on wich order you want the primary color to have, all of them can be found on the wikipedia page for hue, I can update the answer to help you convert one of those to T-SQL, if needed.

MySQL function Hex to Hue. Based on Tobi's answer. :)

CREATE FUNCTION `hex_to_hue`(hex varchar(6)) RETURNS float
BEGIN
declare r float;
declare b float;
declare g float;
declare min float;
declare max float;
declare delta float;
declare hue float;

set r = conv(substr(hex, 1, 2), 16, 10)/255.0;
set g = conv(substr(hex, 3, 2), 16, 10)/255.0;
set b = conv(substr(hex, 5, 2), 16, 10)/255.0;

set max = greatest(r,g,b);
set min = least(r,g,b);

set delta = max - min;

set hue=
(case 
    when max=r then (60 * ((g - b)/delta % 6))
    when max=g then (60 * ((b - r)/delta + 2))
    when max=b then (60 * ((r - g)/delta + 4))
    else null
end);

RETURN hue;
END
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!