Проблема началась, когда мне нужно было применить условное форматирование к таблице с плавным изменением цвета в службах отчетов MS SQL Server (SSRS). Это невозможно со стандартной функциональностью SSRS. Но вы можете использовать табличные данные для плавного изменения цвета с параметром Lightness в цветовой модели HSL.
Вопрос в том, как преобразовать HSL в используемые в SSRS HEX или RGB цветовые коды с помощью SQL.
Ни на Stackoverflow, ни где-либо еще ответов не нашел, только для других языков программирования
Ведь я пришел к тому, чтобы переписать функцию VBA с здесь на SQL
Он имеет следующие параметры:
Результат - цветовой код в выбранном формате
create function dbo.f_convertHSL (
@HueDegree numeric(3,0),
@Saturation numeric(6,3),
@Lightness numeric(6,3),
@Format varchar(3) )
returns varchar(100)
as
begin
declare @HuePercent numeric(6,3),
@Red numeric(6,3),
@Green numeric(6,3),
@Blue numeric(6,3),
@Temp1 numeric(6,3),
@Temp2 numeric(6,3),
@TempR numeric(6,3),
@TempG numeric(6,3),
@TempB numeric(6,3),
@Result varchar(100);
if @Saturation = 0
begin
select @Red = @Lightness * 255,
@Green = @Lightness * 255,
@Blue = @Lightness * 255;
if @Format = 'RGB'
select @Result = cast(cast(@Red as int) as varchar) + ', '
+ cast(cast(@Green as int) as varchar) + ', '
+ cast(cast(@Blue as int) as varchar);
else if @Format = 'HEX'
select @Result = '#' + convert(varchar(2), convert(varbinary(1), cast(@Red as int)), 2)
+ convert(varchar(2), convert(varbinary(1), cast(@Green as int)), 2)
+ convert(varchar(2), convert(varbinary(1), cast(@Blue as int)), 2);
else select @Result = 'Format should be RGB or HEX';
return @Result;
end;
if @Lightness < 0.5
select @Temp1 = @Lightness * (1 + @Saturation);
else
select @Temp1 = @Lightness + @Saturation - @Lightness * @Saturation;
select @Temp2 = 2 * @Lightness - @Temp1
, @HuePercent = @HueDegree / 360.0;
select @TempR = @HuePercent + 0.333
, @TempG = @HuePercent
, @TempB = @HuePercent - 0.333;
if @TempR < 0 select @TempR = @TempR + 1;
if @TempR > 1 select @TempR = @TempR - 1;
if @TempG < 0 select @TempG = @TempG + 1;
if @TempG > 1 select @TempG = @TempG - 1;
if @TempB < 0 select @TempB = @TempB + 1;
if @TempB > 1 select @TempB = @TempB - 1;
if @TempR * 6 < 1 select @Red = @Temp2 + (@Temp1 - @Temp2) * 6 * @TempR
else if @TempR * 2 < 1 select @Red = @Temp1
else if @TempR * 3 < 2 select @Red = @Temp2 + (@Temp1 - @Temp2) * (0.666 - @TempR) * 6
else select @Red = @Temp2;
if @TempG * 6 < 1 select @Green = @Temp2 + (@Temp1 - @Temp2) * 6 * @TempG
else if @TempG * 2 < 1 select @Green = @Temp1
else if @TempG * 3 < 2 select @Green = @Temp2 + (@Temp1 - @Temp2) * (0.666 - @TempG) * 6
else select @Green = @Temp2;
if @TempB * 6 < 1 select @Blue = @Temp2 + (@Temp1 - @Temp2) * 6 * @TempB
else if @TempB * 2 < 1 select @Blue = @Temp1
else if @TempB * 3 < 2 select @Blue = @Temp2 + (@Temp1 - @Temp2) * (0.666 - @TempB) * 6
else select @Blue = @Temp2;
select @Red = round(@Red * 255, 0),
@Green = round(@Green * 255, 0),
@Blue = round(@Blue * 255, 0);
if @Format = 'RGB'
select @Result = cast(cast(@Red as int) as varchar) + ', '
+ cast(cast(@Green as int) as varchar) + ', '
+ cast(cast(@Blue as int) as varchar);
else if @Format = 'HEX'
select @Result = '#' + convert(varchar(2), convert(varbinary(1), cast(@Red as int)), 2)
+ convert(varchar(2), convert(varbinary(1), cast(@Green as int)), 2)
+ convert(varchar(2), convert(varbinary(1), cast(@Blue as int)), 2);
else select @Result = 'Format should be RGB or HEX';
return @Result;
end;
Примеры использования:
select dbo.f_convertHSL(24, 0.83, 0.74, 'RGB')
result: 244, 178, 134
select dbo.f_convertHSL(24, 0.83, 0.74, 'HEX')
result: #F4B286
Результат можно подтвердить например здесь
Должен признать, для T-SQL этот вид очень многословен; похоже, что это написано программно. Я также ожидаю, что если вы предоставляете 3 параметра для H, S и L, то вы должны получить 3 значения для R, G и B, а не одно значение с разделителями (и денормализованное). Для шестнадцатеричного значения значение binary(3)
имело бы больше смысла. Вероятно, здесь вам нужны две функции: одна для возврата набора данных со значениями R, G и B, а другая — для возврата значения binary(3)
для Hex. Кроме того, обе, вероятно, должны быть встроенными функциями табличного значения, а не многострочной скалярной функцией.
Функция представляет собой пошаговый sql-перевод упомянутой функции VBA. И основной целью в моем случае было использовать его для SSRS, который принимает значение varchar с цветовым кодом HEX. Разумеется, функцию можно не использовать как есть, а оптимизировать и модифицировать для любых целей.
Да, и это действительно проблема. VB(A) и SQL совершенно разные; VB(A) — язык программирования, SQL — язык запросов.
@Larnu, буду очень признателен, если вы предложите более оптимальный вариант этой функции в отдельном ответе
Я взял за основу следующее решение этой статьи. Как уже упоминалось, здесь я использую 2 функции, и я также возвращаю набор данных в обеих (3 столбца для RGB, 1 для шестнадцатеричного):
CREATE OR ALTER FUNCTION dbo.HSLtoRGB (@H numeric(3,0),@S numeric(4,3), @L numeric(4,3))
RETURNS table
AS RETURN
SELECT CONVERT(tinyint,ROUND((RGB1.R1+m.m)*255,0)) AS R,
CONVERT(tinyint,ROUND((RGB1.G1+m.m)*255,0)) AS G,
CONVERT(tinyint,ROUND((RGB1.B1+m.m)*255,0)) AS B
FROM (VALUES(@H, @S, @L))HSL(Hue,Saturation,Lightness)
CROSS APPLY(VALUES((1-ABS((2*HSL.Lightness - 1))) * HSL.Saturation)) C(Chroma)
CROSS APPLY(VALUES(HSL.Hue/60,C.Chroma * (1 - ABS((HSL.Hue/60) % 2 - 1))))H([H`],X)
CROSS APPLY(SELECT TOP (1) * --It's unlikely there would be 2 rows, but just incase limit to 1
FROM (VALUES(C.Chroma,H.X,0,0,1),
(H.X,C.Chroma,0,1,2),
(0,C.Chroma,H.X,2,3),
(0,H.X,C.Chroma,3,4),
(H.X,0,C.Chroma,4,5),
(C.Chroma,0,H.X,5,6))V(R1,G1,B1,S,E)
WHERE V.S <= H.[H`] AND H.[H`] <= V.E
ORDER BY V.E DESC) RGB1
CROSS APPLY (VALUES(HSL.Lightness - (C.Chroma / 2)))m(m);
GO
CREATE OR ALTER FUNCTION dbo.HSLtoRGB_HEX (@H numeric(3,0),@S numeric(4,3), @L numeric(4,3))
RETURNS table
AS RETURN
SELECT CONVERT(binary(3),CONCAT(CONVERT(varchar(2),CONVERT(binary(1),CONVERT(tinyint,ROUND((RGB1.R1+m.m)*255,0))),2),
CONVERT(varchar(2),CONVERT(binary(1),CONVERT(tinyint,ROUND((RGB1.G1+m.m)*255,0))),2),
CONVERT(varchar(2),CONVERT(binary(1),CONVERT(tinyint,ROUND((RGB1.B1+m.m)*255,0))),2)),2) AS RGB
FROM (VALUES(@H, @S, @L))HSL(Hue,Saturation,Lightness)
CROSS APPLY(VALUES((1-ABS((2*HSL.Lightness - 1))) * HSL.Saturation)) C(Chroma)
CROSS APPLY(VALUES(HSL.Hue/60,C.Chroma * (1 - ABS((HSL.Hue/60) % 2 - 1))))H([H`],X)
CROSS APPLY(SELECT TOP(1) * --It's unlikely there would be 2 rows, but just incase limit to 1
FROM (VALUES(C.Chroma,H.X,0,0,1),
(H.X,C.Chroma,0,1,2),
(0,C.Chroma,H.X,2,3),
(0,H.X,C.Chroma,3,4),
(H.X,0,C.Chroma,4,5),
(C.Chroma,0,H.X,5,6))V(R1,G1,B1,S,E)
WHERE V.S <= H.[H`] AND H.[H`] <= V.E
ORDER BY V.E DESC) RGB1
CROSS APPLY (VALUES(HSL.Lightness - (C.Chroma / 2)))m(m);
GO
SELECT *
FROM (VALUES(210,.79,.3),
(24,.83,.74),
(360,1,1),
(0,0,0))V(H,S,L)
CROSS APPLY dbo.HSLtoRGB(V.H, V.S, V.L) RGB
CROSS APPLY dbo.HSLtoRGB_Hex(V.H, V.S, V.L) RGBhex;
Существует прямое математическое соответствие между цветовыми пространствами HSL и RGB. Не то чтобы вы действительно должны делать это в T-SQL, но что мешает вам сделать это? Что вы пробовали?