Две неща:1) не използвате базата данни в пълната й степен и 2) проблемът ви е чудесен пример за персонализирано разширение на PostgreSQL. Ето защо.
Използвате само база данни като хранилище, съхранявайки цветове като плаващи елементи. Във вашата текуща конфигурация, независимо от типа на заявката, базата данни винаги ще трябва да проверява всички стойности (направете последователно сканиране). Това означава много IO и много изчисления за малко върнати съвпадения. Опитвате се да намерите най-близките N цвята, така че има няколко възможности как да избегнете извършването на изчисления върху всички данни.
Просто подобрение
Най-простият начин е да ограничите изчисленията си до по-малък набор от данни. Можете да предположите, че разликата ще бъде по-голяма, ако компонентите се различават повече. Ако можете да намерите безопасна разлика между компонентите, където резултатите винаги са неподходящи, можете да изключите тези цветове като цяло, като използвате диапазонирани WHERE с btree индекси. Въпреки това, поради естеството на цветовото пространство L*a*b, това вероятно ще влоши резултатите ви.
Първо създайте индексите:
CREATE INDEX color_lab_l_btree ON color USING btree (lab_l);
CREATE INDEX color_lab_a_btree ON color USING btree (lab_a);
CREATE INDEX color_lab_b_btree ON color USING btree (lab_b);
След това адаптирах вашата заявка, за да включва клауза WHERE за филтриране само на цветове, където всеки от компонентите се различава с най-много 20.
Актуализация: След още един преглед, добавянето на ограничение от 20 много вероятно ще влоши резултатите, тъй като намерих поне една точка в пространството, за която това е вярно.:
SELECT
c.rgb_r, c.rgb_g, c.rgb_b,
DELTA_E_CIE2000(
25.805780252087963, 53.33446637366859, -45.03961353720049,
c.lab_l, c.lab_a, c.lab_b,
1.0, 1.0, 1.0) AS de2000
FROM color c
WHERE
c.lab_l BETWEEN 25.805780252087963 - 20 AND 25.805780252087963 + 20
AND c.lab_a BETWEEN 53.33446637366859 - 20 AND 53.33446637366859 + 20
AND c.lab_b BETWEEN -45.03961353720049 - 20 AND -45.03961353720049 + 20
ORDER BY de2000 ;
Напълних таблицата с 100 000 произволни цвята с вашия скрипт и тествах:
Време без индекси:44006,851 ms
Време с индекси и заявка за диапазон:1293 092 ms
Можете да добавите тази клауза WHERE към delta_e_cie1976_query
също, при моите произволни данни намалява времето за заявка от ~110 ms на ~22 ms.
BTW:Получих числото 20 емпирично:опитах с 10, но получих само 380 записа, което изглежда малко малко и може да изключи някои по-добри опции, тъй като ограничението е 100. С 20 пълният набор беше 2900 реда и един може да бъде доста сигурен, че най-близките съвпадения ще бъдат там. Не проучих подробно цветовото пространство DELTA_E_CIE2000 или L*a*b*, така че прагът може да се нуждае от корекция по различни компоненти, за да е наистина вярно, но принципът за изключване на неинтересни данни е в сила.
Пренапишете Delta E CIE 2000 на C
Както вече казахте, Delta E CIE 2000 е сложен и доста неподходящ за внедряване в SQL. В момента използва около 0,4 ms на повикване на моя лаптоп. Прилагането му в C трябва значително да ускори това. PostgreSQL присвоява стойността по подразбиране на SQL функциите като 100 и C функциите като 1. Предполагам, че това се основава на реален опит.
Актуализация: Тъй като това също ме дразни, внедрих отново функциите Delta E от модула colormath в C като разширение на PostgreSQL, достъпно на PGXN . С това мога да видя ускоряване от около 150x за CIE2000, когато правя заявки за всички записи от таблицата със 100k записа.
С тази C функция получавам времена за заявка между 147 ms и 160 ms за 100k цвята. С допълнително WHERE времето за заявка е около 20 ms, което изглежда доста приемливо за мен.
Най-доброто, но усъвършенствано решение
Въпреки това, тъй като вашият проблем е N търсене на най-близки съседи в 3-измерно пространство, можете да използвате K-Nearest-Neighbor Indexing, което е в PostgreSQL от версия 9.1 .
За да работи това, трябва да поставите L*a*b* компоненти в куб . Това разширение все още не поддържа оператор за разстояние ( работи се ), но дори и да го направи, няма да поддържа Delta E разстояния и ще трябва да го внедрите отново като C разширение.
Това означава внедряване на операторен клас GiST индекс (btree_gist PostgreSQL разширение
в contrib прави това), за да поддържа индексиране според Delta E разстояния. Добрата част е, че можете да използвате различни оператори за различни версии на Delta E, напр. <->код> за Delta E CIE 2000 и
<#>
за Delta E CIE 1976 и заявките биха били наистина много бързи
за малък LIMIT дори с Delta E CIE 2000.
В крайна сметка може да зависи от вашите (бизнес) изисквания и ограничения.