Как рисовать с помощью SQL?

от автора

Видимо я сделала какое-то очень плохое зло, поэтому живу во время перемен. Справиться с эмоциями и повысить свою конкурентоспособность на рынке Data Enigneer’ов мне помогает сайт Hackerrank. На пути к решению вообще всех задач по SQL с этого сайта мне попалась задачка на нетривиальные запросы.

В задачке требовалось звёздочками нарисовать прямоугольный треугольник.

Понятно, что можно было сделать как-то так:

SELECT '*'  UNION ALL SELECT '* *'  UNION ALL ...

Но это дико скучно и некрасиво.

Давайте разберемся, как рисовать с помощью SQL, и при этом ощущать себя настоящим творцом!

Оригинальный текст задачи:

P(R) represents a pattern drawn by Julia in R rows. The following pattern represents P(5):

* * * * * * * * * * * * * * *

Write a query to print the pattern P(20).

Моё решение здесь и далее на MySQL 8.0:

SET @n = 20; WITH RECURSIVE seq AS (     SELECT 1 AS val      UNION ALL      SELECT val + 1      FROM seq      WHERE val+1 <= @n )  SELECT REPEAT('* ', val)  FROM seq  ORDER BY val DESC;

В следующей задачке треугольник надо было перевернуть, что решается удалением ORDER BY.

После этих задач я задумалась: а как не менее изящно нарисовать равнобедренный треугольник?

SET @n = 4; SET @fill = '8'; SET @sp = '.'; WITH RECURSIVE seq AS (     SELECT 1 AS val      UNION ALL      SELECT val + 1      FROM seq      WHERE val+1 <= @n )  SELECT CONCAT(REPEAT(@sp, @n - val),                REPEAT(CONCAT(@fill, @sp), val),                REPEAT(@sp, @n - val - 1)) FROM seq;

Здесь я решила использовать как переменные кирпичики нашей картины, чтобы получать более творческие картинки.

Вот результат:

...8... ..8.8.. .8.8.8. 8.8.8.8.

А что насчёт кружочка?

SET @n = 20; SET @fill = '8'; SET @sp = '.'; SET @r_x = 8; SET @r_y = 5;  /* Procedure which draws lines by points */ DELIMITER $$  CREATE FUNCTION draw_circle(x_points VARCHAR(255))  RETURNS varchar(255)  NO SQL BEGIN    DECLARE c INT;   DECLARE str VARCHAR(255);   SET c = 0;   SET str = '';    line_loop: LOOP     IF c > @n THEN LEAVE line_loop; END IF;     IF FIND_IN_SET(CAST(c AS CHAR), x_points)         THEN SET str = CONCAT(str, @fill);         ELSE SET str = CONCAT(str, @sp);     END IF;     SET c = c + 1;     ITERATE line_loop;   END LOOP;   RETURN str; END $$ DELIMITER ;  /* Here we get points for circle */ WITH RECURSIVE seq AS (     SELECT 0 AS x, 0 AS y, 0 AS val     UNION ALL      SELECT        round(@r_x + @r_x * COS(2 * PI() * val / @n)) as x,        round(@r_y + @r_y * SIN(2 * PI() * val / @n)) as y,       val + 1     FROM seq      WHERE val < @n ) , points AS (     SELECT GROUP_CONCAT(x) AS xs, y     FROM seq     WHERE val > 0     GROUP BY y     ORDER BY y ASC ) SELECT     draw_circle(xs) FROM     points ;

Здесь пришлось попотеть: разобраться с функциями и циклами в MySQL, дискретизировать формулу окружности.

Зато каков результат:

.......8..8..8.......  ....8...........8....  ..8...............8..  8...................8  8...................8  8...................8  ..8...............8..  ....8...........8....  .......8..8..8....... 

Теперь нарисуем шахматную доску.

SET @n = 8; SET @fill = '#'; SET @sp = '.';  /* Procedure which draws lines by points */ DROP FUNCTION IF EXISTS draw_chessboard; DELIMITER $$  CREATE FUNCTION draw_chessboard(i INTEGER)  RETURNS varchar(255)  NO SQL BEGIN    DECLARE c INT;   DECLARE str VARCHAR(255);   SET c = 0;   SET str = '';    line_loop: LOOP     IF c > @n THEN LEAVE line_loop; END IF;     IF i mod 2 = 0 THEN         IF c mod 2 <> 0          THEN SET str = CONCAT(str, @fill);          ELSE SET str = CONCAT(str, @sp);         END IF;     ELSEIF i mod 2 <> 0 THEN         IF c mod 2 = 0          THEN SET str = CONCAT(str, @fill);          ELSE SET str = CONCAT(str, @sp);         END IF;     END IF;     SET c = c + 1;     ITERATE line_loop;   END LOOP;   RETURN str; END $$ DELIMITER ;  WITH RECURSIVE seq AS (     SELECT 1 AS val     UNION ALL      SELECT        val + 1 AS val     FROM seq      WHERE val < @n ) SELECT     draw_chessboard(val) FROM     seq ;

Результат:

#_#_#_#_#         _#_#_#_#_         #_#_#_#_#         _#_#_#_#_         #_#_#_#_#         _#_#_#_#_         #_#_#_#_#         _#_#_#_#_   

Под конец перейдем к чему-то посерьезнее! К сердечку!

SET @n = 40; SET @fill = '8'; SET @sp = '.'; SET @r_x = 16; SET @r_y = 16;  /* Procedure which draws lines by points */ DROP FUNCTION IF EXISTS draw_heart; DELIMITER $$  CREATE FUNCTION draw_heart(x_points VARCHAR(255))  RETURNS varchar(255)  NO SQL BEGIN    DECLARE c INT;   DECLARE str VARCHAR(255);   SET c = 0;   SET str = '';    line_loop: LOOP     IF c > @r_x * 2 THEN LEAVE line_loop; END IF;     IF FIND_IN_SET(CAST(c AS CHAR), x_points)         THEN SET str = CONCAT(str, @fill);         ELSE SET str = CONCAT(str, @sp);     END IF;     SET c = c + 1;     ITERATE line_loop;   END LOOP;   RETURN str; END $$ DELIMITER ;  /* Here we get points for heart */ WITH RECURSIVE seq AS (     SELECT 0 AS x, 0 AS y, 0 AS val     UNION ALL      SELECT        round(@r_x + 16 * POWER(SIN(2 * PI() * val / @n), 3)) as x,        round(2 * @r_y - ( 13 * COS(2 * PI() * val / @n) - 5 * COS(4 * PI() * val / @n) - 2 * COS(6 * PI() * val / @n) - COS(8 * PI() * val / @n) ) ) as y,       val + 1     FROM seq      WHERE val < @n ) , points AS (     SELECT GROUP_CONCAT(x) AS xs, y     FROM seq     WHERE val > 0     GROUP BY y     ORDER BY y ASC ) SELECT     draw_heart(xs) FROM     points ;

Формулу для сердца я добыла здесь: http://www.wolframalpha-ru.com/2012/03/blog-post.html, и это было самой сложной частью рисунка.

Результат:

........8.8...........8.8........  .....8.....................8.....  .............8.....8.............  ..8............8.8............8..  .8..............8..............8.  ................8................  8...............................8  .8.............................8.  ..8...........................8..  .....8.....................8.....  ........8...............8........  ..........8...........8..........  .............8.....8.............  ...............8.8...............  ................8................  ................8................ 

Вот таким образом можно почувствовать себя творцом, будучи простым дата-аналитиком. Тем не менее, уверена, что мои решения далеки от оптимальности, поэтому жду ваших комментариев и замечаний.


ссылка на оригинал статьи https://habr.com/ru/post/664834/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *