Расширенный SQL в MySQL и PostgreSQL. Сравнение возможностей. Даниил Каменский sakila@sqlinfo.ru План доклада Расширения SQL пользовательские переменные оконные функции рекурсивные запросы расширения для работы с таблицами 2 План доклада Особенности и отличия базовых функций в MySQL и PostgreSQL типы таблиц, особенности физического размещения работа с индексами оптимизация подзапросов дополнительно 3 Польз. переменные MySQL Поддерживаются с версии 3.23.6 Возможность присваивать значения и использовать далее в рамках сессии SELECT @min_price:=MIN(`price`) FROM `shop`; SELECT * FROM `shop` WHERE `price`=@min_price; 4 Пример №1: Пики загрузки Поиск максимального различия между смежными значениями 5 Структура таблицы 6 Без явной сортировки ALTER table `t` ORDER BY `t` ASC; SET @data=NULL,@max=0; SELECT @max:=if(v-@data>@max,v-data,@max), @data:=v FROM `t`; SELECT @max; 7 Сортировка индексом CREATE INDEX t_v ON t(t, v); SET @data=NULL,@max=0; SELECT @max:=if(v-@data>@max,v-data,@max), @data:=v FROM `t` FORCE INDEX (t_v); SELECT @max; 8 Сортировка в запросе SET @data=NULL,@max=0; SELECT @max:=if(v-@data>@max,v-data,@max), @data:=v FROM (SELECT * FROM `t` ORDER BY t); SELECT @max; 9 Аналог в модели РБД SELECT MAX( t1.v - ( SELECT v FROM t t2 WHERE t2.t < t1.t ORDER BY t2.t DESC LIMIT 1) ) FROM t t1; 10 Пример №2 Оценка загрузки каналов связи за определенные временные промежутки 11 Реализация через ПП select @c:=0; select t, v from ( select t, if (t % 5, @c := @c+v, @c := v) as v, if( (t + 1) % 5,1,0) as mark from t) tt where mark = 0; select sum(v) from t group by (t-(second(t)%5)); 12 Детализация запроса 13 Длительности выполнения 14 Пример №3 Поиск n элементов группы по критерию. CREATE TABLE salary ( dep_id int, --идентификатор отдела emp_id int, --идентификатор работника sal int -- зарплата ); 15 Запрос через ПП set @n=2, @i=0, @p=0; SELECT * FROM (SELECT * FROM `salary` ORDER BY dep_id ASC, sal DESC) t WHERE if (@p=dep_id, @i:=@i+1,(@i:=0) or (@p:=dep_id)) AND @i<@n; 16 Аналог в РБД SELECT t1.* FROM salary t1 HAVING ( (SELECT count(*) FROM `salary` t2 WHERE t2.dep_id=t1.dep_id AND t2.sal>t1.sal) <2 ) ORDER BY dep_id, sal DESC; 17 SWAP колонок, rownum UPDATE t SET field1=field2, field2=field1 Неправильный результат! set @tmp=''; UPDATE t SET field1=(@tmp:=field1), field1=field2, field2=@tmp; SET @n:=0; SELECT @n:=@n+1 AS rownum, t.* FROM t; 18 Оконные функции PostgreSQL Выполнение вычисления над списком строк в таблице, которые так или иначе относятся к текущей строке Вычисление агрегатного значения без использования GROUP BY 19 Примеры функций lag() – предыдущее значение в разбиении row_number() – номер строки в разбиении first_value() – первое значение в разбиении 20 Оконные функции SELECT dep_id, emp_id, sal, 100 * sal::float / (sum(sal) OVER (PARTITION BY dep_id)) FROM salary WHERE dep_id = 1; 21 Макс. перепад через ПП В postgresql.conf custom_variable_classes = 'lv' select set_config ('lv.data', ‘0', true); select set_config ('lv.max_value', '0', true); select current_setting('lv.data')::integer 22 Макс. перепад через ОФ SELECT max(v) FROM (SELECT p.v - lag(t.v) OVER (ORDER BY t) as v FROM t ) tt; 23 Макс. перепад через функцию create function public.temp_func() returns int as $$ DECLARE prev integer; curr integer; max_ integer; BEGIN max_ := 0; prev := 0; FOR curr IN SELECT v FROM pp LOOP if (curr - prev > max_) then max_ := curr - prev; end if; prev := curr; END LOOP; return max_; END $$ language plpgsql; 24 Скорости выполнения запросов в PostgreSQL Таблица на 10,000,000 записей Оконные функции: 2,5 cек Пользовательские переменные: 13 сек Функция на plpgsql: 2 сек Запрос с подзапросами: 20 сек 25 Элементы групп через ОФ SELECT dep_id, emp_id, sal FROM ( SELECT row_number() OVER (partition BY dep_id ORDER BY sal desc ) num, dep_id, emp_id, sal FROM salary ) t WHERE num < 3; 26 Рекурсивные запросы with [recursive] <имя_алиаса_запроса> [ (<список столбцов>) ] as (<запрос>) <основной запрос> 27 Дерево, структура 28 Обход дерева в ширину WITH RECURSIVE recursetree(val, id, level, pathstr) AS ( SELECT val, id, 0, cast('' as text) FROM tree WHERE parent_id = 0 UNION ALL SELECT t.val, t.id, rt.level + 1, rt.pathstr || '=>' || t.val::text FROM tree t JOIN recursetree rt ON rt.id = t.parent_id ) SELECT space(level) || val, id, level, pathstr FROM recursetree ORDER BY level, id; 29 Вывод рекурсивного запроса 30 Intersect SELECT t1.val FROM set_operations_1 t1 INTERSECT SELECT t2.val FROM set_operations_2 t2; SELECT val FROM set_operations_1 WHERE val IN (SELECT val FROM set_operations_2); SELECT t1.val FROM set_operations_1 t1 JOIN set_operations_2 t2 using(val); 31 Except SELECT t1.val FROM set_operations_1 t1 EXCEPT SELECT t2.val FROM set_operations_2 t2; SELECT val FROM set_operations_1 WHERE val NOT IN (SELECT val FROM set_operations_2); SELECT t1.val FROM set_operations_1 t1 LEFT JOIN set_operations_2 t2 USING (val) WHERE t2.val IS NULL 32 33 Работа с таблицами ON DUPLICATE KEY insert into param(param, val) values(‘project_version’,100) on duplicate key update val=‘100'; REPLACE INTO replace into param(param, val) values(‘project_version’, ‘100’); 34 Работа с таблицами INSERT IGNORE insert ignore into param(param, val) values(‘project_version’,100) insert into param(param, val) select 'project_version', ‘100' where 1 not in (select 1 from param where param = 'project_version'); 35 Реализация upsert в PG CREATE OR REPLACE FUNCTION upsert(id_ integer, val_ integer) RETURNS void AS $$ BEGIN LOOP UPDATE t SET val = val_ WHERE id = id_ IF found THEN RETURN; END IF; BEGIN INSERT INTO t(id, val) VALUES (id_, val_); RETURN; EXCEPTION WHEN unique_violation THEN END; END LOOP; END; $$ LANGUAGE 'plpgsql'; 36 Создание правила CREATE RULE "t_on_duplicate_ignore" AS ON INSERT TO "t" WHERE EXISTS(SELECT 1 FROM y WHERE pk_col = NEW.pk_col_1) DO INSTEAD NOTHING; 37 Типы таблиц в MySQL InnoDB MyISAM Memory Merge Archive Blackhole 38 Типы таблиц в MySQL tmp_table_size max_heap_table_size innodb_table_per_file 39 Индексы Основные типы Btree Hash MySQL – только memory-таблицы PostgreSQL – hash не рекомендуется 40 Индексные алгоритмы MySQL: Невозможность использовать составные индексы при нестрогом равенстве в начале SELECT * FROM t WHERE a<3 AND b<5; Директива USE/IGNORE/FORCE INDEX PostgreSQL: Невозможность указать индекс явно. Возможность регуляции через set enable_***scan=1 и set enable_***scan=0 41 Алгоритмы JOIN MySQL: nested loop PostgreSQL: nested loop join merge join hash join 42 Функц. и сост. индексы CREATE INDEX i ON billing(is_payed) WHERE is_payed = false; CREATE INDEX i ON users ( substr(email, strrevpos(email , '.') + 1 ) ); CREATE INDEX i ON y(f1 ASC, f2 DESC); 43 MySQL - подзапросы SELECT * FROM users WHERE id IN (SELECT poster_id FROM posts WHERE poster_ip LIKE ‘212.194.55.%’); SELECT * FROM users u JOIN posts p ON u.id=p.poster_id WHERE p.poster_ip LIKE ‘212.194.55.% ’; 44 Анонимные блоки кода MySQL: CREATE/DROP IF EXISTS/IF NOT EXISTS PostgreSQL: DO $$ begin if exists (select 1 from pg_tables where tablename = ‘t') then drop table t;end if; end $$; 45 Дополнение MySQL: Фальшивые Foreign Keys у таблиц MyISAM Фальшивые hash-индексы у таблиц не memory При @@sql_mode <> ‘ANSI’ возможность делать ошибки – например SELECT avg(a), b FROM t; PostgreSQL: Отсутствие возможности менять колонки местами Генерация исключений в хранимых процедурах 46 Спасибо за внимание! 47 Планы исполнения запросов А. Запрос с ПП Синтаксический анализ запроса Создание плана исполнения Определение хранилища базы (engine) Цикл foreach { Обращение к engine } Б. Цикл внутри ХП { Синтаксический анализ запроса (курсор) Определение хранилища базы (engine) Обращение к engine Переход к следующему шагу цикла (интерпретатор языка SQL) } 48