Как рисовать с помощью 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/


Интересные статьи

Интересные статьи

...Что, если бы мы могли сделать наш код полностью пригодным для тестирования таким образом, чтобы от нас не требовалось вводить какие-либо новые протоколы, всевозможные моки или сложные абстракции? Д...
При написании нового метода или сервиса мы стараемся его максимально абстрагировать от внешних зависимостей, чтобы новый функционал реализовывал только заложенную ему лог...
Еще в апреле 2020 года Citizenlab сообщил о довольно слабом шифровании Zoom и заявил, что Zoom использует аудиокодек SILK. К сожалению, статья не содержала исходных данных, чтобы эт...
Привет, на связи контент-студия Хабра! 30 апреля мы проведем вебинар о крутом инструменте по работе с контентом, доступном для всех корпоративных блогов, но почему-то редко используемом, — о ...
Реализация ORM в ядре D7 — очередная интересная, перспективная, но как обычно плохо документированная разработка от 1с-Битрикс :) Призвана она абстрагировать разработчика от механики работы с табл...