привет

#ЖИТЬ https://www.youtube.com/watch?v=X__GJAvZ4_M

Далее

Первое сентября Сегодня старший мой идет в первый класс.

Далее

Немного альтернативной истории — Да, победили, и что толку? — вздыхал Саня. — Столько крови пролили, в половине Европы социализм установили. Теперь нам никогда не войти в европейскую семью народов, мы не уважали их ценности. А знаешь, сколько немок изнасиловали наши солдаты, когда заняли Берлин? Миллион! Это же ужасно! Саня, либеральный сетевой журналист, отлично...

Далее

#котозависимость Та же проблема...

Далее

MS SQL и агрегатная функция умножения

Рубрика : Программизмы

Как известно, в MS SQL нет агрегатной функции, которая бы вычислила произведение всех элементов столбца (наподобие того, как SUM вычисляет сумму всех элементов). Но очень часто такая функция нужна.
Традиционно используется brute force решение: пробегается по курсору и выполняется перемножение элементов построчно. Медленно и глупо. Поиск вариантов решения привел к четырем идеям:
1) Курсор;
2) Математический;
3) Итеративный;
4) CLR.

Для тестов сделана таблицу из 1000 элементов в диапазоне от 0,5 до 1,5, по которой будет в цикле прогоняться 10000 операций вычисления произведения всех элементов.
Теперь решения:
1. Курсор

	declare @Value float, @Result float
	set @Result = 1
	declare c cursor for select value from #temp
	open c
	while 1=1
		begin			
			fetch next from c into @Value
			if @@fetch_status <> 0 break
			set @Result = @Result*@Value
		end
	close c
	deallocate c

Результат: 4,84804064970134E-22
Время выполнения: 220 секунд.

2. Математический

	declare @Result float
	select @Result=exp(sum(log(Value))) from #temp

Результат: 4,84804064970128E-22
Время выполнения: 2.3 секунды.

3. Итерационный

	declare @Result float
	set @Result=1
	select @Result=Value*@Result from #temp

Результат: 4,84804064970134E-22
Время выполнения: 1.7 секунды.

4. CLR
а) Создаем решение — библиотеку классов в студии. Код класса, который будет выполнять работу агрегатора:

    
namespace TestCLR 
{
    [Serializable]
    [SqlUserDefinedAggregate(
        Format.UserDefined, 
        MaxByteSize = -1)
    ]
    public class MultFunction : IBinarySerialize
    {
        public double AccValue;

        public void Init()
        {
            AccValue = 1;
        }

        public void Accumulate(double value)
        {
            AccValue *= value;
        }

        public void Merge(MultFunction value)
        {
            AccValue *= value.AccValue;
        }

        public double Terminate()
        {
            return AccValue;
        }

        public void Read(BinaryReader r)
        {
            AccValue = r.ReadSingle();
        }

        public void Write(BinaryWriter w)
        {
            w.Write(AccValue);
        }
    }
}

б) Строим решение и регистрируем его в MS SQL:

	CREATE ASSEMBLY Mult FROM 'C:\...\CLR_MULT.dll';
	GO
	CREATE AGGREGATE Mult (@input float) RETURNS float
	EXTERNAL NAME Mult.[TestCLR.MultFunction];

в) сам код:

	declare @Result float
	select @Result=dbo.Mult(Value) from #temp

Результат: 4,84804064970134E-22
Время выполнения: 5.1 секунды.

Время дано среднее, по результатам 10 испытаний.

Выводы:
1) Курсор — швах.
2) Математический метод, который рекомендуют на форумах — швах (не самый быстрый и неточный).
3) CLR — хорошая штука, такая агрегатная функция конечно более затратна, чем 2 и 3 способы, но у агрегатных функций несколько отличная от запросов-подзапросов область применения. Так что полезность такой функции, да и возможности CLR, нельзя недооценивать из за легкой тормознутости (которая, вероятно, сойдет на нет в более сложной задаче).
4) Для вычислений в критических участках лучше всего подходит нативный запрос, ч и т.д.

Прокомментировать