<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>@savichm</title><generator>teletype.in</generator><description><![CDATA[@savichm]]></description><link>https://teletype.in/@savichm?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/savichm?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/savichm?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Tue, 26 May 2026 13:15:45 GMT</pubDate><lastBuildDate>Tue, 26 May 2026 13:15:45 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@savichm/NuKvwk2o6SX</guid><link>https://teletype.in/@savichm/NuKvwk2o6SX?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/NuKvwk2o6SX?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>25 cайтов, на которых можно порешать задачи по программированию</title><pubDate>Sun, 09 Apr 2023 12:32:43 GMT</pubDate><media:content medium="image" url="https://img4.teletype.in/files/ba/1e/ba1e4449-e55e-4c6d-8185-0aa025ed707d.png"></media:content><description><![CDATA[<img src="https://media.tproger.ru/uploads/2015/10/codeforces-logo-with-telegram.png"></img>Не секрет, что лучший способ повысить свои навыки в программировании — это практиковаться и только практиковаться. Мы подготовили для вас огромную подборку сайтов с задачами по программированию на самые разные темы.]]></description><content:encoded><![CDATA[
  <p id="y6yg">Не секрет, что лучший способ повысить свои навыки в программировании — это практиковаться и только практиковаться. Мы подготовили для вас огромную подборку сайтов с задачами по программированию на самые разные темы.</p>
  <p id="W2sw">Также можете попытаться решить самые заковыристые <a href="https://tproger.ru/articles/10-logicheskih-zadach-s-sobesedovanij-kotorye-zastavjat-zastrelitsja/" target="_blank">логические задачи с собеседований</a>.</p>
  <figure id="MLcl" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/codeforces-logo-with-telegram.png" width="300" />
  </figure>
  <p id="TdKT"><a href="http://codeforces.com/" target="_blank">Codeforces</a> — несомненно самая популярная и известная платформа во всем мире для проведения соревнований на алгоритмику. Кроме крупных контестов сайт зачастую проводит свои «раунды» — участникам даются 5 задач на два часа. Есть система рейтинга, на основе которой участники делятся на два дивизиона. Таким образом, профи не соревнуются с новичками напрямую. Все задачи можно сдать и проверить даже после соревнований. Кроме «раундов» доступны и «тренировки» — задачи с прошедших соревнований публикуются в режиме дорешивания.</p>
  <hr />
  <figure id="hyWm" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/topcoder_logo_home_sm1-300x43.png" width="300" />
  </figure>
  <p id="w4XC"><a href="https://www.topcoder.com/" target="_blank">TopCoder</a> — ненамного отстающая по популярности от Codeforces американская платформа. Примечательна тем, что кроме алгоритмических контестов, которые описывались ранее, на ней проводятся и соревнования по промышленному программированию и марафоны — соревнования с задачами на исследование, для которых нет единого верного алгоритма, а есть лишь ответ, подходящий больше или меньше. На решение таких задач участникам обычно дается одна или две недели.</p>
  <hr />
  <figure id="mDtd" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/Qcl4tW0.png" width="717" />
  </figure>
  <p id="zGqc"><a href="http://acm.timus.ru/" target="_blank">Timus Online Judge</a> — русскоязычная (хотя английский язык также поддерживается) платформа, на которой более тысячи задач удачно отсортированы по темам и по сложности. Также тут регулярно проводятся контесты уральского региона, которые, впрочем, не представляют для вас ничего интересного, если только вы не студент УрФУ или другого близлежащего вуза 🙂</p>
  <hr />
  <figure id="QO8A" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/2015e.png" width="655" />
  </figure>
  <p id="fCdS"><a href="http://www.spoj.com/" target="_blank">SPOJ</a> — крупный англоязычный сайт с более чем 20000 задачами на абсолютно разные темы: динамическое программирование, графы, структуры данных и т.д. Изредка проводит контесты, которые не представляют интереса, если вы не живете в странах их проведения.</p>
  <hr />
  <figure id="pDkY" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/LqQN37b.png" width="471" />
  </figure>
  <p id="oNNU">informatics.mccme.ru — платформа с множеством теоретических материалов и задач по соответствующим темам. Все очень удобно собрано по категориям и темам. Также содержит большую базу задач с прошедших олимпиад школьников.</p>
  <hr />
  <figure id="Wcwc" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/logo.png" width="540" />
  </figure>
  <p id="8rm0"><a href="https://www.codechef.com/" target="_blank">CodeChef</a> — менее крупный аналог Codeforces и TopCoder, тоже с огромным архивом задач и регулярными контестами.</p>
  <hr />
  <figure id="osPu" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/title.png" width="354" />
  </figure>
  <p id="PWYY"><a href="http://acmp.ru/" target="_blank">acmp.ru</a> — сайт, который будет полезен всем благодаря своему архиву задач, удобно (и по большей части правильно) отсортированному по сложности и темам. Соревнования проводятся, но участвовать в них имеет смысл только школьникам Красноярского края, для которых эта платформа изначально и предназначалась.</p>
  <hr />
  <figure id="TTYy" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/pe_banner.png" width="250" />
  </figure>
  <p id="wUIU"><a href="https://projecteuler.net/" target="_blank">Project Euler</a> — сборник 500 задач, которые невозможно решить без знаний математических и геометрических алгоритмов. Иногда используется на собеседованиях для приема на работу, чтобы лучше выяснить алгоритмическую подготовку претендента.</p>
  <hr />
  <figure id="n3r7" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/site-logo.png" width="240" />
  </figure>
  <p id="Eq0P"><a href="https://www.kaggle.com/" target="_blank">Kaggle</a> — данная платформа отличается от описанных ранее тем, что тут не проводится алгоритмических соревнований — только задачи на исследование (как в марафонах на вышеприведенном TopCoder). Например, одна из задач, на которой сейчас там проверяют свои умения участники, состоит в распознании написанных вручную цифр. Вот несколько символов, для которых это не так просто, как кажется (<em>прим. авт. — </em>некоторые из них я не смог распознать даже не программно):</p>
  <figure id="ohZ4" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/26c5f305bbf24a7a806e21c6633cff24-300x184.jpg" width="300" />
  </figure>
  <hr />
  <figure id="1BUA" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/logo_codingame_hud.5a5a2374.png" width="138" />
  </figure>
  <p id="V9nF"><a href="https://www.codingame.com/start" target="_blank">CodinGame</a> — сайт, на котором программирование и видеоигры сливаются в единое целое. Здесь вы найдете большую коллекцию задач на программирование, оформленных в виде видеоигр. Также тут изредка (раз в два месяца) проводятся контесты, содержащие в себе задачи на оптимизацию и ИИ, победители которых получают ценные призы. А если вы решите много задач, то на вас могут обратить внимание компании, которые набирают на этом сайте работников!</p>
  <hr />
  <figure id="HTOS" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/logo-1.png" width="564" />
  </figure>
  <p id="bwjR"><a href="https://codecombat.com/" target="_blank">CodeCombat</a> будет больше полезен для новичков. Эта платформа наглядно демонстрирует, что обучение программированию — это не так сложно и скучно, как может показаться. Сайт представлен в виде игры, которая разделена на несколько частей, возрастающих по сложности. В каждой части содержится множество задач на те или иные темы, призванные научить программированию с нуля любого человека. Если вы давно мечтали заняться программированием, но никак не находили в себе, обязательно обратите внимание на этот сайт.</p>
  <hr />
  <figure id="kTrw" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/HackerRank_logo_with_slogan.png" width="656" />
  </figure>
  <p id="AQ12"><a href="https://www.hackerrank.com/" target="_blank">HackerRank</a> наоборот будет больше интересен профессионалам, которые уже многое умеют. На этом сайте собрано множество задач на самые разные разделы Computer Science: традиционная алгоритмика, ИИ, машинное обучение и т.д. Если вы решите много задач, то вами могут заинтересоваться работодатели, регуляторно мониторящие эту платформу.</p>
  <hr />
  <figure id="UaAX" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/cpuzzles.gif" width="279" />
  </figure>
  <p id="eFgJ"><a href="http://www.gowrikumar.com/c/index.php" target="_blank">C Puzzles</a> — подборка головоломок, специфичный для языка С, со всеми его причудами. Например, дан код, который, по логике, не должен работать, но, тем не менее, он компилируется и даже правильно выполняет свою задачу. Надо понять, почему так? На этой сайте вы сможете приобрести навык отладки программ и чтения кода других.</p>
  <hr />
  <figure id="jB5k" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/logo-square-red-big-4e51d3c67160dc4d16ffde19adfcd0fc.png" width="180" />
  </figure>
  <p id="iZYR"><a href="http://www.codewars.com/" target="_blank">Codewars</a> — cборник задач на разные темы, от алгоритмов до шаблонов проектирования.</p>
  <hr />
  <p id="Awci"><a href="https://leetcode.com/" target="_blank">LeetCode</a> — сайт с задачами для подготовки к собеседованиям.</p>
  <hr />
  <figure id="27C9" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/praxis.png" width="744" />
  </figure>
  <p id="1yQo"><a href="http://programmingpraxis.com/" target="_blank">Programming Praxis</a> — блог, включающий в себя много интересных задач.</p>
  <hr />
  <p id="ZX2Z"><a href="http://www.pythonchallenge.com/" target="_blank">PythonChallange</a> — сайт с загадками, возрастающими по сложности. Для их решения необходимо написать программу на Python.</p>
  <hr />
  <figure id="kgDA" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/4circles.gif" width="110" />
  </figure>
  <p id="YQkP"><a href="http://azspcs.com/" target="_blank">Al Zimmermann’s Programming Contests</a> — платформа, на которой раз в полгода проводятся контесты с задачами на исследование и оптимизацию. Интересен тем, что писать программу необязательно — даются только тестовые данные. Ответы можно расчитывать вручную, или просто гадать их на кофейной гуще.</p>
  <hr />
  <p id="oVok"><a href="http://rubyquiz.com/" target="_blank">Ruby Quiz</a> — подборка задач для программистов на Ruby, но решения можно писать и на других языках.</p>
  <hr />
  <p id="GLsn"><a href="https://sites.google.com/site/prologsite/" target="_blank">Prolog Problems</a> — аналогично с Ruby Quiz. Подборка задач для программистов, использующих Prolog.</p>
  <hr />
  <figure id="elQj" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/4226d64349f39972e824bfd56cd3171b_400x400-300x300.png" width="300" />
  </figure>
  <p id="ZncA">MindCipher — сборник занимательных математических и логических задач (в том числе и по программированию).</p>
  <hr />
  <p id="nmgH"><a href="http://cppstudio.com/cat/285/" target="_blank">Сборник задач для практики от СppStudio.</a> Рекомендуется решать на С++, но можно и на других языках.</p>
  <hr />
  <figure id="j8wx" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/checkio_logo.jpg" width="390" />
  </figure>
  <p id="vqBO"><a href="http://www.checkio.org/" target="_blank">CheckIO</a> — сайт с задачами для программистов всех уровней, оформленный в виде игры.</p>
  <hr />
  <figure id="JdCb" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/eolimp.gif" width="334" />
  </figure>
  <p id="cvv7"><a href="http://www.e-olymp.com/ru/" target="_blank">E-olimp</a> — украинская тестирующая система с большим архивом задач.</p>
  <hr />
  <figure id="wowS" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2015/10/0330d74f2ef32-300x149.png" width="300" />
  </figure>
  <p id="v0Li">Empire of Code — сайт для программистов, где необходимо писать код, реализующий стратегию и тактику виртуальных бойцов.</p>
  <hr />
  <p id="L9KR"><br />Источник: https://tproger.ru/digest/competitive-programming-practice/</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/BJV2Mxr2CZI</guid><link>https://teletype.in/@savichm/BJV2Mxr2CZI?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/BJV2Mxr2CZI?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>Тестирование десктопа: что важно учитывать перед введением автотестов </title><pubDate>Sun, 09 Apr 2023 12:08:11 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/9c/0f/9c0f4267-fa7c-448d-8fb4-e2f148b6da21.png"></media:content><description><![CDATA[Если крайне важная ядерная система, от которой зависит большинство смежных, создана очень давно, то варианта, как с этим справиться, два. Можно заменить её на другую, более современную. Но переписывать всё не только дорого, но и незачем. Поэтому можно улучшить процессы внутри неё.]]></description><content:encoded><![CDATA[
  <p id="PWiQ">Если крайне важная ядерная система, от которой зависит большинство смежных, создана очень давно, то варианта, как с этим справиться, два. Можно заменить её на другую, более современную. Но переписывать всё не только дорого, но и незачем. Поэтому можно улучшить процессы внутри неё.</p>
  <p id="w5AM">Мы решили пойти по второму пути: добавить автодеплой и автоматизировать часть регрессионных тестов, чтобы команда не тратила время и силы на рутинные процессы, а могла заняться бизнесовыми задачами. Вот как это было.</p>
  <h2 id="N0SY">Как мы пришли к нынешнему устройству системы</h2>
  <p id="ze5w">Большинство сотрудников проходит три стадии принятия при такой ситуации.</p>
  <ul id="Vv9U">
    <li id="hG4b">Отрицание — не может быть, что кто-то в 2023 ещё работает на системе, написанной на Delphi?</li>
    <li id="JAtD">Гнев — почему то, что мне нужно, работает, только если обладаешь тайными знаниями?</li>
    <li id="Rhwt">Принятие — система работает стабильно, большинство доработок можно сделать достаточно просто — и уже понятно, как их проверять.</li>
  </ul>
  <p id="hT0N">Можно пойти дальше и начать улучшать всё вокруг себя: упрощать процессы, автоматизировать «невозможное» и применять лучшие практики.</p>
  <p id="oVSy">Сначала мы привели филиалы к единому виду, создав единую клиентскую и серверную часть. Наша команда разработки проанализировала все существующие процедуры и отчёты и объединила их в эталонный код. Мы выровняли ландшафт, мигрировали данные. Раньше было 14 баз, доработки по которым ставились независимо друг от друга.</p>
  <p id="kW4b">После этого наладили CI/CD процесс. Все внутренние доработки по системе теперь обязательно ставятся через Git. До этого разработчики на все филиалы ставили доработки руками, что отнимало много времени и увеличивало количество ошибок и расхождений в разных базах.</p>
  <p id="UoGE">В дополнение к существующим сложностям у системы есть особенность: доработки идут как от вендора, так и от внутренней команды разработки. Таким образом, внутренние обновления ставятся через Git On Demand по готовности, а доработки вендора также через Git, но в рамках релизов.</p>
  <p id="Mohl">Это связано с тем, что система — монолит, и вендор поставляет ряд доработок как чёрный ящик. Поэтому возникает необходимость проводить полноценное регрессионное тестирование всей системы из-за возможного влияния доработок на смежные модули.</p>
  <h2 id="Zu5Y">Как мы выстроили процесс автоматизации</h2>
  <p id="hLlc">Учитывая необходимость постоянного проведения регресса, мы решили сократить количество регулярных рутинных операций и автоматизировать большинство из них.</p>
  <p id="3pnT">Начали с однообразных кейсов с небольшими изменениями в шагах, отнимающих больше всего времени на проверку. Так мы:</p>
  <ol id="O0RT">
    <li id="HGsE">Ускорили проведение проверок.</li>
    <li id="u5uN">Упростили автоматизацию, так как изменения от кейса к кейсу незначительные.</li>
    <li id="yjTO">Повысили мотивацию команды, так как они стали тратить меньше времени на рутину.</li>
  </ol>
  <p id="M5zA">Далее для облегчения процесса автоматизации подробно описали тест-кейсы с шагами, без ветвлений. Хорошо описанная тестовая модель полезна не только для автоматизации, но и в целом для качественной оценки количества работы, выполняемой командой.</p>
  <p id="k0jC">Например, мы спросили, какие тесты рутинные? Нам скинули один из кейсов. Проанализировав его, мы обнаружили, что он содержит несколько ветвлений. Мы декомпозировали кейс, и вместо одного получили более ста независимых. Именно поэтому он и оказался в списке на автоматизацию.</p>
  <p id="ZhUR">Автоматизация может быть бесконечной, потому что всегда появляются дополнительные продукты, которые также нужно покрывать кейсами, или новые ошибки на проме. За счёт этого регрессионная модель постоянно растёт.</p>
  <p id="mhcw">Сейчас мы автоматизировали уже больше половины регрессионных сценариев. Это позволило существенно сэкономить время, затрачиваемое командой на эти задачи, и использовать его для проверки новой функциональности.</p>
  <p id="1KoJ">Также, начиная автоматизировать систему, важно учитывать, что даже в случае с UI-тестами не всё обязательно делать через UI. Например, предварительные данные или ряд итоговых проверок можно сделать, используя хранимые процедуры.</p>
  <p id="Ncuq">Второе направление, которое важно автоматизировать, — это проверка интеграционных процедур. Отсутствие стабильной работы интеграций при работе с ядерной системой несёт в себе риски возникновения ошибок у конечных пользователей, а это может предполагать как финансовые, так и репутационные риски для компании.</p>
  <p id="ubMp">Мы идём к такому идеалу: когда появляются новые продукты, к ним стоит сразу писать автотесты. Мы хотим перейти к модели, при которой в момент разработки нового сервиса тут же будет ставиться задача на автоматизацию. И когда сервис готов — запускаться автотест, также написанный по документации. Если он отработает — сервис выкатится. Если нет, то его доработают.</p>
  <h2 id="KP69">Какие инструменты мы используем</h2>
  <p id="4rsF">В основном инструменты разработаны под автоматизацию веб-систем, так как веб-разработка становится всё более популярным направлением. Но обычно они не подходят для автоматизации десктопных приложений. Покопавшись в теме, мы нашли подходящие нам инструменты.</p>
  <p id="tosP">Вот что мы выбрали для себя.</p>
  <h3 id="D2QR">Micro Focus Unified Functional Testing</h3>
  <p id="eqHb">Позволяет определить объекты на форме десктопного приложения: окно, radio-button, выпадающий список, поле для ввода. На основе этого к добавленному в библиотеку объекту можно применить то или иное действие: нажать на кнопку или закрыть окно.</p>
  <p id="fXHN">Помимо этих инструментов на рынке есть и другие, например:</p>
  <ul id="jKJr">
    <li id="eVjy">Zeenyx,</li>
    <li id="yDew">Winium,</li>
    <li id="cosz">Katalon Studio,</li>
    <li id="NQNC">Test Complet Desktop.</li>
  </ul>
  <p id="YMql">Можно выбрать тот, который подходит именно вам.</p>
  <h3 id="Oitt">Pywinauto</h3>
  <p id="5FUu">Это open source библиотека для автоматизации десктопных GUI приложений на Microsoft Windows. Он нужен для ряда проверок, например, для операций с длительным ожиданием.</p>
  <h3 id="rMjE">Тестирование на PyTest</h3>
  <p id="Uq3W">Если это доработка вендора и тестирование чёрного ящика, где нужно убедиться, что у реальных пользователей не будет ошибок, мы пишем интерфейсные тесты, используя приведённые выше инструменты. А если нужно проверить интеграционный сервис — API-тест, написанный на Python.</p>
  <p id="BUFm">Проверяя интеграционные кейсы, мы смотрим в том числе минимальную интеграционную обвязку: создаём очереди, вызываем адаптер, который вызывает внутренние процедуры. Также отдельно проверяем функциональность дорабатываемых процедур.</p>
  <p id="g9XS">Python + PyTest позволяют достаточно легко это сделать. А также дают возможность встроить в пайплайн запуск в момент изменения сервиса или связанной процедуры.</p>
  <h3 id="QX7j">Git и TeamCity</h3>
  <p id="ceby">Для поддержания версионности и развёртывания кода. Как единый стандарт в банке. Автотесты мы ведём там же, что упрощает выстраивание общего процесса.</p>
  <h3 id="Qh9A">Использование ранее написанных процедур при подготовке данных</h3>
  <p id="wa28">Руководствуясь лучшими практиками, мы определили, что тест должен сам себе готовить данные, чтобы работать стабильно. При участии разработчиков мы разработали ряд процедур, которые позволяют сгенерировать синтетические данные. Это помогает существенно сократить время на подготовку тестовых данных, стабилизировать и ускорить время выполнения тестов.</p>
  <p id="dSNc">Основная идея в том, чтобы не заводить данные, нужные для начала выполнения теста, через интерфейс, а напрямую вызывать процедуры, которые сделают это. И с одной стороны сохранять ряд проверок, необходимых для консистентности данных, а с другой — ускорять процесс их генерации в десятки раз, исключая ручные действия.</p>
  <h2 id="pPil">Лучшие практики, чтобы выстраивать CI/CD на десктопе</h2>
  <p id="29J9">Тестирование — важная часть CI/CD-процесса, который постоянно совершенствуется. Всегда можно найти более эффективные практики, которые помогут избежать проблем в будущем. Вот ещё несколько принципов, которые помогают улучшить этот процесс.</p>
  <ul id="EJhn">
    <li id="iZnF">Должно быть версионирование кода. Если сейчас у вас этого нет, нужно начинать делать шаги к этому.</li>
    <li id="4mXK">Версионирование кода касается в том числе автотестов.</li>
    <li id="LpVi">Подробно описывайте тест-кейсы, это помогает как с погружением новых сотрудников, так и с написанием автотестов.</li>
    <li id="lhzf">Учитывайте техокна на интеграционных стендах. Если доставлять обновления, не обращая на это внимание, можно помешать другим командам работать. Если в моменте кто-то незапланировано что-то поставит, всё зависнет или вообще сломается.</li>
    <li id="vsQh">Используйте Feature toggle. Он помогает безболезненно поставить доработки на пром, даже если они не работают с включенным toggle.</li>
    <li id="680H">Пишите тесты, а лучше автотесты, до того, как доработку передали в тестирование. Писать тест-кейсы, когда доработки уже готовы, — это плохая практика, можно нахватать проблем, например, не хватит сотрудников, чтобы провести тестирование.</li>
    <li id="tSQW">Чем раньше привлекается тестирование, тем лучше. Ведь уже на этапе прочтения ТЗ можно увидеть слабые места, задать вопросы, исправить их и сделать сразу правильно</li>
  </ul>
  <p id="Ig5C">Источник: https://tproger.ru/articles/testirovanie-desktopa-chto-uchityvat-pered-vvedeniem-avtotestov/</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/bYVljRdpTmV</guid><link>https://teletype.in/@savichm/bYVljRdpTmV?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/bYVljRdpTmV?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>Тестирование десктопа: что учитывать перед введением автотестов</title><pubDate>Sun, 09 Apr 2023 12:04:01 GMT</pubDate><media:content medium="image" url="https://img2.teletype.in/files/9c/0f/9c0f4267-fa7c-448d-8fb4-e2f148b6da21.png"></media:content><description><![CDATA[Если крайне важная ядерная система, от которой зависит большинство смежных, создана очень давно, то варианта, как с этим справиться, два. Можно заменить её на другую, более современную. Но переписывать всё не только дорого, но и незачем. Поэтому можно улучшить процессы внутри неё.]]></description><content:encoded><![CDATA[
  <p id="PWiQ">Если крайне важная ядерная система, от которой зависит большинство смежных, создана очень давно, то варианта, как с этим справиться, два. Можно заменить её на другую, более современную. Но переписывать всё не только дорого, но и незачем. Поэтому можно улучшить процессы внутри неё.</p>
  <p id="w5AM">Мы решили пойти по второму пути: добавить автодеплой и автоматизировать часть регрессионных тестов, чтобы команда не тратила время и силы на рутинные процессы, а могла заняться бизнесовыми задачами. Вот как это было.</p>
  <p id="TAyH"></p>
  <h2 id="N0SY">Как мы пришли к нынешнему устройству системы</h2>
  <p id="ze5w">Большинство сотрудников проходит три стадии принятия при такой ситуации.</p>
  <ul id="Vv9U">
    <li id="hG4b">Отрицание — не может быть, что кто-то в 2023 ещё работает на системе, написанной на Delphi?</li>
    <li id="JAtD">Гнев — почему то, что мне нужно, работает, только если обладаешь тайными знаниями?</li>
    <li id="Rhwt">Принятие — система работает стабильно, большинство доработок можно сделать достаточно просто — и уже понятно, как их проверять.</li>
  </ul>
  <p id="hT0N">Можно пойти дальше и начать улучшать всё вокруг себя: упрощать процессы, автоматизировать «невозможное» и применять лучшие практики.</p>
  <p id="oVSy">Сначала мы привели филиалы к единому виду, создав единую клиентскую и серверную часть. Наша команда разработки проанализировала все существующие процедуры и отчёты и объединила их в эталонный код. Мы выровняли ландшафт, мигрировали данные. Раньше было 14 баз, доработки по которым ставились независимо друг от друга.</p>
  <p id="kW4b">После этого наладили CI/CD процесс. Все внутренние доработки по системе теперь обязательно ставятся через Git. До этого разработчики на все филиалы ставили доработки руками, что отнимало много времени и увеличивало количество ошибок и расхождений в разных базах.</p>
  <p id="UoGE">В дополнение к существующим сложностям у системы есть особенность: доработки идут как от вендора, так и от внутренней команды разработки. Таким образом, внутренние обновления ставятся через Git On Demand по готовности, а доработки вендора также через Git, но в рамках релизов.</p>
  <p id="Mohl">Это связано с тем, что система — монолит, и вендор поставляет ряд доработок как чёрный ящик. Поэтому возникает необходимость проводить полноценное регрессионное тестирование всей системы из-за возможного влияния доработок на смежные модули.</p>
  <h2 id="Zu5Y">Как мы выстроили процесс автоматизации</h2>
  <p id="hLlc">Учитывая необходимость постоянного проведения регресса, мы решили сократить количество регулярных рутинных операций и автоматизировать большинство из них.</p>
  <p id="3pnT">Начали с однообразных кейсов с небольшими изменениями в шагах, отнимающих больше всего времени на проверку. Так мы:</p>
  <ol id="O0RT">
    <li id="HGsE">Ускорили проведение проверок.</li>
    <li id="u5uN">Упростили автоматизацию, так как изменения от кейса к кейсу незначительные.</li>
    <li id="yjTO">Повысили мотивацию команды, так как они стали тратить меньше времени на рутину.</li>
  </ol>
  <p id="M5zA">Далее для облегчения процесса автоматизации подробно описали тест-кейсы с шагами, без ветвлений. Хорошо описанная тестовая модель полезна не только для автоматизации, но и в целом для качественной оценки количества работы, выполняемой командой.</p>
  <p id="k0jC">Например, мы спросили, какие тесты рутинные? Нам скинули один из кейсов. Проанализировав его, мы обнаружили, что он содержит несколько ветвлений. Мы декомпозировали кейс, и вместо одного получили более ста независимых. Именно поэтому он и оказался в списке на автоматизацию.</p>
  <p id="ZhUR">Автоматизация может быть бесконечной, потому что всегда появляются дополнительные продукты, которые также нужно покрывать кейсами, или новые ошибки на проме. За счёт этого регрессионная модель постоянно растёт.</p>
  <p id="mhcw">Сейчас мы автоматизировали уже больше половины регрессионных сценариев. Это позволило существенно сэкономить время, затрачиваемое командой на эти задачи, и использовать его для проверки новой функциональности.</p>
  <p id="1KoJ">Также, начиная автоматизировать систему, важно учитывать, что даже в случае с UI-тестами не всё обязательно делать через UI. Например, предварительные данные или ряд итоговых проверок можно сделать, используя хранимые процедуры.</p>
  <p id="Ncuq">Второе направление, которое важно автоматизировать, — это проверка интеграционных процедур. Отсутствие стабильной работы интеграций при работе с ядерной системой несёт в себе риски возникновения ошибок у конечных пользователей, а это может предполагать как финансовые, так и репутационные риски для компании.</p>
  <p id="ubMp">Мы идём к такому идеалу: когда появляются новые продукты, к ним стоит сразу писать автотесты. Мы хотим перейти к модели, при которой в момент разработки нового сервиса тут же будет ставиться задача на автоматизацию. И когда сервис готов — запускаться автотест, также написанный по документации. Если он отработает — сервис выкатится. Если нет, то его доработают.</p>
  <h2 id="KP69">Какие инструменты мы используем</h2>
  <p id="4rsF">В основном инструменты разработаны под автоматизацию веб-систем, так как веб-разработка становится всё более популярным направлением. Но обычно они не подходят для автоматизации десктопных приложений. Покопавшись в теме, мы нашли подходящие нам инструменты.</p>
  <p id="tosP">Вот что мы выбрали для себя.</p>
  <h3 id="D2QR">Micro Focus Unified Functional Testing</h3>
  <p id="eqHb">Позволяет определить объекты на форме десктопного приложения: окно, radio-button, выпадающий список, поле для ввода. На основе этого к добавленному в библиотеку объекту можно применить то или иное действие: нажать на кнопку или закрыть окно.</p>
  <p id="fXHN">Помимо этих инструментов на рынке есть и другие, например:</p>
  <ul id="jKJr">
    <li id="eVjy">Zeenyx,</li>
    <li id="yDew">Winium,</li>
    <li id="cosz">Katalon Studio,</li>
    <li id="NQNC">Test Complet Desktop.</li>
  </ul>
  <p id="YMql">Можно выбрать тот, который подходит именно вам.</p>
  <h3 id="Oitt">Pywinauto</h3>
  <p id="5FUu">Это open source библиотека для автоматизации десктопных GUI приложений на Microsoft Windows. Он нужен для ряда проверок, например, для операций с длительным ожиданием.</p>
  <h3 id="rMjE">Тестирование на PyTest</h3>
  <p id="Uq3W">Если это доработка вендора и тестирование чёрного ящика, где нужно убедиться, что у реальных пользователей не будет ошибок, мы пишем интерфейсные тесты, используя приведённые выше инструменты. А если нужно проверить интеграционный сервис — API-тест, написанный на Python.</p>
  <p id="BUFm">Проверяя интеграционные кейсы, мы смотрим в том числе минимальную интеграционную обвязку: создаём очереди, вызываем адаптер, который вызывает внутренние процедуры. Также отдельно проверяем функциональность дорабатываемых процедур.</p>
  <p id="g9XS">Python + PyTest позволяют достаточно легко это сделать. А также дают возможность встроить в пайплайн запуск в момент изменения сервиса или связанной процедуры.</p>
  <h3 id="QX7j">Git и TeamCity</h3>
  <p id="ceby">Для поддержания версионности и развёртывания кода. Как единый стандарт в банке. Автотесты мы ведём там же, что упрощает выстраивание общего процесса.</p>
  <h3 id="Qh9A">Использование ранее написанных процедур при подготовке данных</h3>
  <p id="wa28">Руководствуясь лучшими практиками, мы определили, что тест должен сам себе готовить данные, чтобы работать стабильно. При участии разработчиков мы разработали ряд процедур, которые позволяют сгенерировать синтетические данные. Это помогает существенно сократить время на подготовку тестовых данных, стабилизировать и ускорить время выполнения тестов.</p>
  <p id="dSNc">Основная идея в том, чтобы не заводить данные, нужные для начала выполнения теста, через интерфейс, а напрямую вызывать процедуры, которые сделают это. И с одной стороны сохранять ряд проверок, необходимых для консистентности данных, а с другой — ускорять процесс их генерации в десятки раз, исключая ручные действия.</p>
  <h2 id="pPil">Лучшие практики, чтобы выстраивать CI/CD на десктопе</h2>
  <p id="29J9">Тестирование — важная часть CI/CD-процесса, который постоянно совершенствуется. Всегда можно найти более эффективные практики, которые помогут избежать проблем в будущем. Вот ещё несколько принципов, которые помогают улучшить этот процесс.</p>
  <ul id="EJhn">
    <li id="iZnF">Должно быть версионирование кода. Если сейчас у вас этого нет, нужно начинать делать шаги к этому.</li>
    <li id="4mXK">Версионирование кода касается в том числе автотестов.</li>
    <li id="LpVi">Подробно описывайте тест-кейсы, это помогает как с погружением новых сотрудников, так и с написанием автотестов.</li>
    <li id="lhzf">Учитывайте техокна на интеграционных стендах. Если доставлять обновления, не обращая на это внимание, можно помешать другим командам работать. Если в моменте кто-то незапланировано что-то поставит, всё зависнет или вообще сломается.</li>
    <li id="vsQh">Используйте Feature toggle. Он помогает безболезненно поставить доработки на пром, даже если они не работают с включенным toggle.</li>
    <li id="680H">Пишите тесты, а лучше автотесты, до того, как доработку передали в тестирование. Писать тест-кейсы, когда доработки уже готовы, — это плохая практика, можно нахватать проблем, например, не хватит сотрудников, чтобы провести тестирование.</li>
    <li id="tSQW">Чем раньше привлекается тестирование, тем лучше. Ведь уже на этапе прочтения ТЗ можно увидеть слабые места, задать вопросы, исправить их и сделать сразу правильно</li>
  </ul>
  <p id="Ig5C"><br />Источник: https://tproger.ru/articles/testirovanie-desktopa-chto-uchityvat-pered-vvedeniem-avtotestov/</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/88DhCfl6ws1</guid><link>https://teletype.in/@savichm/88DhCfl6ws1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/88DhCfl6ws1?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>Как создать систему распознавание объектов на Python</title><pubDate>Sun, 09 Apr 2023 11:56:21 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/0b/47/0b479d3c-108d-4f9e-8a61-b84399709ec4.png"></media:content><description><![CDATA[<img src="https://itproger.com/img/news/1551167480.jpg"></img>Python является одним из самых перспективных языков, позволяющий воплощать искусственный интеллект в жизнь. В уроке мы создадим систему распознавание объектов при помощи Python и ImageAI.]]></description><content:encoded><![CDATA[
  <p id="DGwJ">Python является одним из самых перспективных языков, позволяющий воплощать искусственный интеллект в жизнь. В уроке мы создадим систему распознавание объектов при помощи Python и ImageAI.</p>
  <p id="WQGj">Одна из самых перспективных наук о компьютерах и программах – <u>компьютерное зрение</u>. Его смысл заключается в способности ПК к распознанию и определению сути картинки. Это важнейшая область в искусственном интеллекте, включающая сразу несколько действий: распознание содержимого фотографии, определение предмета и его классификация или генерация. <strong>Поиск объектов на картинке</strong>, скорее всего, является важнейшей областью компьютерного зрения.</p>
  <p id="nyXg">Определение вещей или живых существ на фотографии активно используется в следующих сферах:</p>
  <ul id="9QvF">
    <li id="kTSs">Поиск автомобилей;</li>
    <li id="9Cy8">Система распознания людей;</li>
    <li id="TBES">Поиск и подсчёт количества пешеходов;</li>
    <li id="T3gD">Усиление системы безопасности;</li>
    <li id="w8X3">Создание беспилотных автомобилей и т. д.</li>
  </ul>
  <p id="vorq">Сегодня удалось разработать много методов для поиска объектов, которые применяются в зависимости от целевой области. В этой сфере, как и в других направлениях использования ИТ-технологий, многое напрямую зависит от программиста. Это отличный инструмент для творчества, с которым <strong>«творение</strong>» может получить собственный ум. Как использовать интеллект программы уже зависит от творческого мышления разработчика.</p>
  <figure id="uPvX" class="m_original">
    <img src="https://itproger.com/img/news/1551167480.jpg" width="1167" />
  </figure>
  <p id="VvgU">Технология действительно перевернула представление об искусственном интеллекте. В дальнейшем она стала основой для следующих методов <strong>R-CNN</strong>, <strong>Fast-RCNN</strong>, <strong>Faster-RCNN</strong>, <strong>RetinaNet</strong>. Среди них и высокоточные, быстрые методы - SSD и YOLO. Для применения перечисленных алгоритмов, в основе которых глубокое обучение, требуется наличие глубоких познаний в математике и доскональное понимание фреймворков.</p>
  <h2 id="z3sS">Начнем</h2>
  <p id="rGRo">Рассмотрение советов следует начинать с функциональной библиотеки <a href="https://github.com/OlafenwaMoses/ImageAI" target="_blank">ImageAI</a>, написанной на Python. Данный фреймворков позволяет с лёгкостью интегрировать инновационные достижения в сфере компьютерного зрения в уже разработанные или новые программы.</p>
  <h2 id="FRnh">Установка Python</h2>
  <p id="SiSz">Без инсталляции Python 3 здесь не обойтись. Нужно всего лишь загрузить файл с <a href="https://python.org/" target="_blank">оф. сайта</a> и запустить процесс установки.</p>
  <h2 id="B7D3">Создание зависимостей</h2>
  <p id="om2V">Сейчас самое время для того, чтобы посредством <code>pip</code> установить зависимости. Принцип создания команды прост: <strong>pip install</strong> и <strong>название библиотеки</strong> (основные фреймворки описаны в списке ниже). Как это выглядит:</p>
  <pre id="ltmb">pip install tensorflow # устанавливает программную среду Tensorflow.</pre>
  <p id="kotC"><u>Какие фреймворки нужно добавить:</u></p>
  <ul id="HlJQ">
    <li id="0seC">Numpy;</li>
    <li id="IxhD">SciPy;</li>
    <li id="FGzA">OpenCV (opencv-python);</li>
    <li id="vEtL">Pillow;</li>
    <li id="DKZJ">Matplotlib;</li>
    <li id="gboy">H5py;</li>
    <li id="9nDx">Keras;</li>
    <li id="2b7c">ImageAI (<a href="https://github.com/OlafenwaMoses/ImageAI/releases/download/2.0.1/imageai-2.0.1-py3-none-any.whl" target="_blank">ссылка</a>).</li>
  </ul>
  <p id="pugw">Просмотреть все фреймворки и команды для их установки вы можете на официальном сайте с <a href="https://imageai.readthedocs.io/en/latest/index.html" target="_blank">документацией по ImageAI</a>.</p>
  <figure id="yOKe" class="m_original">
    <img src="https://itproger.com/img/news/1551167282.png" width="1446" />
  </figure>
  <h2 id="uN7X">Retina Net</h2>
  <p id="zwut">Теперь стоит скачать <a href="https://github.com/OlafenwaMoses/ImageAI/releases/download/1.0/resnet50_coco_best_v2.0.1.h5" target="_blank">файл</a> для модели <strong>Retina Net</strong>. Он участвует в процессе идентификации объектов на изображениях.</p>
  <p id="7QMS">Как только зависимости установлены, уже есть возможность написать первые строки кода для вычисления предметов на картинках. Следует создать файл <strong>FirstDetection</strong> с расширением <code>.py</code>. В созданный файл следует вставить код из следующего раздела. Ещё нужно скопировать файл из модели <strong>Retina</strong> и добавить картинку для обработки в папку с файлом <strong>Python</strong>.</p>
  <h2 id="ny7e">Тестирование</h2>
  <p id="oxEa">Создайте файл и разместите в нем следующий код:</p>
  <pre id="3uHz">from imageai.Detection import ObjectDetection
import os

exec_path = os.getcwd()

detector = ObjectDetection()
detector.setModelTypeAsRetinaNet()
detector.setModelPath(os.path.join(
	exec_path, &quot;resnet50_coco_best_v2.0.1.h5&quot;)
)
detector.loadModel()

list = detector.detectObjectsFromImage(
	input_image=os.path.join(exec_path, &quot;objects.jpg&quot;),
	output_image_path=os.path.join(exec_path, &quot;new_objects.jpg&quot;),
	minimum_percentage_probability=90,
	display_percentage_probability=True,
	display_object_name=False
)</pre>
  <p id="w7PI">Осталось запустить код и ожидать появление результатов работы в консоли. Дальше следует пройти в каталог, где установлен файл <code>FirstDetection.py</code>. Здесь же должна появиться новая фотография или несколько. Чтобы лучше понимать, что произошло, следует открыть изначальную и новую картинку.</p>
  <p id="pOKC"><strong>Время рассмотреть принцип работы кода:</strong></p>
  <pre id="osBs">from imageai.Detection import ObjectDetection
import os

exec_path = os.getcwd()</pre>
  <p id="WwSA"><strong>Описание строк:</strong></p>
  <ul id="9ADa">
    <li id="v8MP"><strong>1 строка:</strong> перенос ImageAI и класса для поиска предмета;</li>
    <li id="r2A7"><strong>2 строка:</strong> импорт Python os;</li>
    <li id="h1A6"><strong>4 строка:</strong> создание переменной, в которой указывается путь к директории с файлом Python, RetinaNet, моделью и образом.</li>
  </ul>
  <pre id="D6CV">detector = ObjectDetection()
detector.setModelTypeAsRetinaNet()
detector.setModelPath(os.path.join(
	exec_path, &quot;resnet50_coco_best_v2.0.1.h5&quot;)
)
detector.loadModel()

list = detector.detectObjectsFromImage(
	input_image=os.path.join(exec_path, &quot;objects.jpg&quot;),
	output_image_path=os.path.join(exec_path, &quot;new_objects.jpg&quot;),
	minimum_percentage_probability=90,
	display_percentage_probability=True,
	display_object_name=False
)</pre>
  <p id="fArR"><strong>Описание строк:</strong></p>
  <ul id="IrDp">
    <li id="PWiS"><strong>1 строка: </strong>объявление нового класса для поиска объектов;</li>
    <li id="8BL1"><strong>2 строка:</strong> установка типа модели RetinaNet;</li>
    <li id="4OkS"><strong>3 строка:</strong> указание пути к модели RetinaNet;</li>
    <li id="NtaA"><strong>6 строка:</strong> загрузка модели внутрь класса для поиска;</li>
    <li id="ENUW"><strong>8 строка:</strong> вызов функции обнаружения (<em>распознавания объектов</em>) и запуск парсинга пути начального и конечного изображений.</li>
  </ul>
  <p id="BTKF"><strong>ImageAI</strong> имеет поддержку массы различных настроек для поиска объектов. Например, можно настроить извлечение всех найденных объектов во время обработки картинки. Класс поиска способен создать отдельную папку с названием image, а затем извлечь, сохранить и вернуть массив с путём ко всем объектам.</p>
  <pre id="D3Gb">list, extracted_images = detector.detectObjectsFromImage
	(input_image=os.path.join(execution_path , &quot;objects.jpg&quot;), 
	output_image_path=os.path.join(execution_path , &quot;new_objects.jpg&quot;), 
	extract_detected_objects=True)</pre>
  <h2 id="b6D3">Видео обзор</h2>
  <p id="S7af">Для более детального рассмотрения библиотеки советуем просмотреть видео обзор этой библиотеки. В ходе видео будет показано не только распознавание объектов на фото, но также вы узнаете про рассмотрение объектов на видео.</p>
  <figure id="pmlt" class="m_custom">
    <iframe src="https://www.youtube.com/embed/Uj4O2_dwRiA?autoplay=0&loop=0&mute=0"></iframe>
  </figure>
  <p id="SCxj"><strong>Ссылки из видео:</strong></p>
  <ol id="CNTH">
    <li id="EBty">Установка <a href="https://www.python.org/" target="_blank">Python</a>;</li>
    <li id="HLcx">Установка <a href="https://pip.pypa.io/en/stable/installing/" target="_blank">Pip</a>;</li>
    <li id="Jp4t">Редактор <a href="https://www.jetbrains.com/pycharm/" target="_blank">PyCharm</a>;</li>
    <li id="4JMb">Большой курс по <a href="https://itproger.com/course/python-full" target="_blank">языку Python</a>;</li>
    <li id="IVve"><a href="https://github.com/OlafenwaMoses/ImageAI/tree/master/imageai/Detection" target="_blank">ImageAI GitHub</a>;</li>
    <li id="uOaU">ImageAI <a href="https://imageai.readthedocs.io/en/latest/" target="_blank">документация</a>.</li>
  </ol>
  <p id="6gNS">В ходе урока было создано распознавание объектов на видео. Код приведен ниже:</p>
  <pre id="9Wqx">from imageai.Detection import VideoObjectDetection
import os

execution_path = os.getcwd()

detector = VideoObjectDetection()
detector.setModelTypeAsYOLOv3()
detector.setModelPath( os.path.join(execution_path , &quot;yolo.h5&quot;))
detector.loadModel()

video_path = detector.detectObjectsFromVideo(
	input_file_path=os.path.join(execution_path, &quot;traffic.mp4&quot;),
	output_file_path=os.path.join(execution_path, &quot;traffic_detected&quot;),
	frames_per_second=20,
	log_progress=True
)

print(video_path)</pre>
  <h2 id="v161">Заключение</h2>
  <p id="h7ld">В конце советов по глубокому изучению следует добавить небольшую выборку из самых полезных функций ImageAI, ведь её возможности выходят далеко за пределы обычного обнаружения объектов:</p>
  <ul id="0njT">
    <li id="TDnU"><strong>Установка порога минимальной вероятности:</strong> стандартные настройки исключают из выборки все объекты с вероятностью до <code>50%</code>. Они даже не записываются в лог. При желании можно изменить в большую или меньшую сторону вероятности для определённых случаев;</li>
    <li id="9aMY"><strong>Особые настройки обнаружения:</strong>с помощью класса <strong>CustomObject</strong>, есть возможность попросить приложение передавать информацию об определении некоторых уникальных объектов;</li>
    <li id="BMH2"><strong>Скорость поиска: </strong>существует возможность вручную снизить время, которое затрачивает приложение для сканирования фотографии. Есть 3 режима работы: fast, faster, fastest;</li>
    <li id="RFcB"><strong>Входящие типы:</strong> поддерживает указание в качесиве пути картинке – Numpy-массива, а также файлового потока;</li>
    <li id="liKN"><strong>Выходные типы:</strong> можно установить, чтобы функция <code>detectObjectsFromImage</code>возвращала картинки файлом или массивом Numpy.</li>
  </ul>
  <p id="YNTU">Конечно, охватить всё компьютерное зрение нереально даже за целую книгу, но основные понятия, надеемся, мы смогли донести</p>
  <p id="1LmI"></p>
  <p id="SAnA">Источник: https://itproger.com/news/raspoznavanie-obaektov-na-python-glubokoe-mashinnoe-obuchenie</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/M-XlVV9kl0Q</guid><link>https://teletype.in/@savichm/M-XlVV9kl0Q?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/M-XlVV9kl0Q?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>Почему Python такой популярный</title><pubDate>Sun, 09 Apr 2023 11:25:36 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/82/64/82649c4e-ad56-451c-bd90-76d04a581720.png"></media:content><description><![CDATA[<img src="https://media.tproger.ru/uploads/2023/04/1923509a-6920-424d-b5ba-f31ab040bbd4.png"></img>В 2022 году Python возглавил список самых популярных языков программирования по данным индекса TIOBE, опередив таких гигантов, как Java и C++.]]></description><content:encoded><![CDATA[
  <p id="d0gd">В 2022 году Python возглавил список самых популярных языков программирования по данным индекса TIOBE, опередив таких гигантов, как Java и C++.</p>
  <p id="Ygp5">Именно с изучения «питона» начинают свой путь в программировании начинающие пользователи, а профессионалы используют его для решения широкого спектра задач — от научных исследований до веб-разработки и искусственного интеллекта.</p>
  <p id="IF38">Что сделало Python таким популярным, и какие факторы повлияли на его успех? В этой статье мы исследуем историю развития этого языка программирования, а также оцениваем перспективы в различных областях IT.</p>
  <p id="wv8Y"></p>
  <h2 id="5AgS">История языка Python</h2>
  <p id="YcKv">Язык программирования Python создал Гвидо ван Россум, нидерландский программист. Он начал работу над языком в конце 1989 года. Первая официальная версия Python 0.9.0 была опубликована в феврале 1991 года.</p>
  <p id="d0Fk">Вот краткая история развития языка за прошедшие десятилетия:</p>
  <p id="6fU2"><strong>1990-е годы:</strong> Разработка и выпуск Python 0.9.0 (февраль 1991) и Python 1.0 (январь 1994).</p>
  <ul id="nHAb">
    <li id="4Dke">Простой и понятный синтаксис привлекает программистов, ищущих альтернативу языкам, таким как Perl и C.</li>
    <li id="YE9w">Формируется и растет сообщества разработчиков, которые активно участвуют в разработке и совершенствовании языка.</li>
    <li id="E3Bz">Появляются первые сторонние библиотеки и инструменты, такие как Numeric, для научных вычислений.</li>
  </ul>
  <p id="T4Xd"><strong>2000-е годы:</strong> Выпуск Python 2.0 (октябрь 2000) с новыми функциями, такими как поддержка Unicode и сборка мусора.</p>
  <ul id="4rXq">
    <li id="YFBO">Улучшены производительность и стабильность языка.</li>
    <li id="r3V0">Разработаны и выпущены важные сторонние библиотеки и фреймворки, таких как Django (июль 2005), для разработки веб-приложений.</li>
    <li id="we72">Python начинают активно использовать в научных исследованиях и обработке данных, благодаря библиотекам NumPy (2006), SciPy и Matplotlib.</li>
    <li id="ylCg">Выпускается версия Python 3.0 (декабрь 2008) с обратно несовместимыми изменениями и улучшениями в синтаксисе и поддержке Unicode.</li>
  </ul>
  <p id="NbNm"><strong>2010-е годы:</strong> Сообщество программистов переходит на Python 3. Активно разрабатываются и обновляются библиотеки и инструменты для совместимости с новой версией языка.</p>
  <ul id="HRPh">
    <li id="xoa1">Развивается область машинного обучения и искусственного интеллекта с использованием Python, благодаря таким библиотекам и фреймворкам, как TensorFlow (2015), Keras и PyTorch.</li>
    <li id="bTob">Возрастает популярность в образовательных учреждениях и среди начинающих программистов благодаря простому синтаксису и большому количеству доступных обучающих материалов.</li>
    <li id="zn83">Расширяется применение Python в различных областях, таких как веб-разработка, научные исследования, анализ данных, машинное обучение, автоматизация.</li>
    <li id="8CVu">Разрабатываются новые версии языка, таких как Python 3.5 (сентябрь 2015) с операторами async/await для асинхронного кода, Python 3.6 (декабрь 2016) с f-строками и улучшениями производительности, и других версий с дополнительными улучшениями и новыми возможностями.</li>
    <li id="qtjA">Появляется Jupyter Notebook (2014) — популярный инструмент для интерактивного программирования, особенно в области научных исследований и анализа данных.</li>
    <li id="7Zo4">Растет популярность языка среди разработчиков и компаний, что делает Python одним из самых востребованных языков программирования на рынке труда.</li>
  </ul>
  <p id="CvtK"><strong>Краткий обзор версий:</strong></p>
  <figure id="qwAc" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/04/1923509a-6920-424d-b5ba-f31ab040bbd4.png" width="630" />
  </figure>
  <p id="TF63"><strong>Примечание:</strong> таблица отражает только основные версии Python и их ключевые изменения. В реальности существует больше версий с минорными изменениями и улучшениями.</p>
  <p id="2VX3">В результате активного развития и применения Python в самых разных областях, а также благодаря его простому синтаксису, огромному количеству библиотек и открытому сообществу разработчиков, Python стал одним из самых популярных и востребованных языков программирования в мире.</p>
  <h2 id="dgdg">Отличительные черты Python</h2>
  <p id="BQ1j">Python — это высокоуровневый язык программирования, который обладает рядом особенностей и преимуществ по сравнению с другими языками программирования. Перечислим несколько преимуществ, которые отличают Python от других языков:</p>
  <ol id="Ownd">
    <li id="pfSn">Читабельность и простота. Python известен своей чистой и лаконичной синтаксической структурой, что делает его легко читаемым и понятным даже для новичков. Этим он выгодно отличается от C++ или Java с более сложным и запутанным синтаксисом.</li>
    <li id="pfOI">Большая стандартная библиотека. Python имеет обширную стандартную библиотеку, которая включает множество модулей и пакетов для различных задач, таких как веб-разработка, обработка данных, машинное обучение, сетевое программирование и многое другое. Это сокращает время, необходимое для разработки программ, и уменьшает зависимость от сторонних библиотек.</li>
    <li id="zB0h">Кросс-платформенность. Python является кросс-платформенным языком. Программы на «питоне» можно запускать в различных операционных системах (Windows, macOS и Linux) без изменения исходного кода.</li>
    <li id="f3UQ">Динамическая типизация. В отличие от статически типизированных языков (Java или C++), Python использует динамическую типизацию. Прием позволяет программистам не объявлять тип переменной заранее. В итоге мы получаем более гибкий код и сокращенное количество строк.</li>
    <li id="Zq1J">Интерпретируемость. Python — интерпретируемый язык. Его код выполняется построчно, без необходимости компиляции. Это упрощает процесс разработки и облегчает отладку программ.</li>
    <li id="rtit">Большое сообщество. Python обладает одним из самых больших и активных сообществ разработчиков, что способствует созданию множества ресурсов, таких как учебники, курсы, библиотеки и фреймворки, которые помогают ускорить процесс разработки и обучения.</li>
    <li id="V7e6">Применимость в различных областях. Python широко используется в различных сферах, таких как веб-разработка, научные исследования, анализ данных, машинное обучение, сетевое программирование, автоматизация и многих других. Это делает Python универсальным и гибким языком программирования, подходящим для широкого круга задач и проектов.</li>
    <li id="E6YR">Поддержка множественных парадигм программирования. Python поддерживает несколько парадигм программирования: объектно-ориентированное, процедурное и функциональное. Это позволяет разработчикам выбирать наиболее подходящий метод для решения конкретной задачи и создания гибких, масштабируемых приложений.</li>
    <li id="8CPY">Широкое использование в индустрии. Python используется множеством крупных компаний (Google, Facebook, Instagram, Spotify), что говорит о высоком уровне доверия к языку и его применимости для решения сложных задач.</li>
    <li id="f8a5">Открытый исходный код Python доступен для просмотра и модификации. Это позволяет разработчикам вносить свой вклад в развитие языка и использовать его в коммерческих проектах без оплаты лицензионных сборов.</li>
  </ol>
  <p id="Iop3"><strong>Вывод:</strong> Python отличается от других языков программирования своей простотой, читабельностью, гибкостью, широкой поддержкой со стороны сообщества и применимостью в различных областях.</p>
  <h2 id="FETr">Плюсы и минусы Python по сравнению с Java</h2>
  <p id="EXi6">Python и Java — два наиболее популярных языка программирования, которые широко используются в различных областях. Несмотря на то, что эти языки имеют много общего, у каждого из них есть свои преимущества и недостатки. Рассмотрим их подробнее.</p>
  <p id="KOJD"><strong>Плюсы Python по сравнению с Java:</strong></p>
  <ol id="d5Et">
    <li id="U7iK">Простота и читаемость кода. Python обладает более кратким и понятным синтаксисом, что упрощает чтение и написание кода. Java требует больше строк кода для реализации аналогичных задач, и его синтаксис может быть более громоздким.</li>
    <li id="9qKx">Большое количество библиотек для машинного обучения и анализа данных. Python имеет множество библиотек, таких как TensorFlow, Keras, PyTorch, Scikit-learn, Pandas. Этот язык более предпочтителен для работы с данными и машинным обучением. Java также имеет некоторые библиотеки для машинного обучения, такие как Deeplearning4j и Weka, но они менее разнообразны и популярны, чем аналогичные библиотеки для Python.</li>
    <li id="WDNE">Быстрое прототипирование. Python позволяет быстро создавать прототипы и экспериментировать благодаря своей динамической типизации и интерпретируемости. Java, будучи статически типизированным и компилируемым языком, требует больше времени для компиляции и исправления ошибок.</li>
  </ol>
  <p id="UVzj"><strong>Минусы Python по сравнению с Java:</strong></p>
  <ol id="0fX4">
    <li id="4sD8">Производительность. Python является интерпретируемым языком, что снижает его производительность по сравнению с компилируемыми языками, такими как Java. В некоторых случаях, когда требуется высокая производительность, использование Java может быть предпочтительнее.</li>
    <li id="kmyf">Поддержка многопоточности. Python имеет GIL (Global Interpreter Lock), ограничивающий выполнение нативных потоков, что затрудняет эффективное использование многопоточности. Java, с другой стороны, имеет встроенную поддержку многопоточности и синхронизации, что позволяет создавать более эффективные многопоточные приложения.</li>
    <li id="HNhz">Мобильная разработка. Java является основным языком для разработки Android-приложений и имеет более широкую поддержку мобильных платформ. Python имеет ограниченную поддержку мобильных платформ и не является предпочтительным выбором для мобильной разработки.</li>
  </ol>
  <p id="Cge1"><strong>Резюме:</strong> Python хорошо подходит для быстрого прототипирования, работы с данными, машинного обучения и веб-разработки, благодаря своему простому синтаксису и обширному набору библиотек.</p>
  <p id="AiVP">Java же имеет преимущества в производительности, поддержке многопоточности и мобильной разработке. Выбор между Python и Java в конкретных ситуациях зависит от требований проекта, предпочтений разработчика и доступности ресурсов.</p>
  <p id="54RI">Оба языка являются популярными и широко используемыми, что обеспечивает поддержку сообщества и обширные возможности для роста и развития.</p>
  <h3 id="ZbfF">Плюсы и минусы Python по сравнению с С-языками</h3>
  <p id="3bI7">Python и C-языки — это две разные категории языков программирования. C-языки, включая C, C++, и Objective-C, относятся к низкоуровневым языкам.</p>
  <p id="J9Uv">Они используются для разработки операционных систем, драйверов устройств и других системных приложений. Python является высокоуровневым языком, который широко используется в области научных исследований, машинного обучения, веб-разработки и других областях.</p>
  <p id="zirb">Рассмотрим плюсы и минусы Python по сравнению с С-языками.</p>
  <p id="91rx"><strong>Плюсы Python по сравнению с C-языками:</strong></p>
  <ol id="g1g2">
    <li id="OdIy">Простота. Python — это простой и легкий в освоении язык программирования, который не требует изучения сложных концепций и синтаксиса, в отличие от C-языков.</li>
    <li id="FAv5">Высокая скорость разработки. Python обладает большим набором библиотек и инструментов, которые упрощают и ускоряют разработку приложений.</li>
    <li id="UgOG">Читаемость кода. Код Python легко читается, что делает его более подходящим для командной разработки и поддержки приложений.</li>
    <li id="Tyrj">Динамическая типизация. Python позволяет использовать динамическую типизацию. Тип переменной определяется автоматически во время выполнения кода.</li>
  </ol>
  <p id="yTtR"><strong>Минусы Python по сравнению с C-языками:</strong></p>
  <ol id="6fbI">
    <li id="XL9z">Медленная скорость выполнения. Код Python код выполняется медленнее, чем код на C-языках, из-за динамической типизации и управления памятью.</li>
    <li id="nXpY">Низкая производительность. Python может быть неэффективным для разработки системных приложений с повышенной производительностью из-за высокого уровня абстракции.</li>
    <li id="3HFw">Ограниченный доступ к низкоуровневым ресурсам. Python имеет ограниченный доступ к низкоуровневым ресурсам, таким как память, процессор и другие аппаратные ресурсы.</li>
    <li id="skeT">Неудобство в разработке приложений с большим объемом данных. Python может столкнуться с проблемами производительности при работе с большим объемом данных, так как не имеет таких мощных возможностей для работы с памятью и процессором, как C-языки.</li>
  </ol>
  <p id="5cZ1">Выбор языка зависит от конкретной задачи, которую вы пытаетесь решить. Если вы разрабатываете системные приложения или приложения с высокой производительностью, то C-языки могут быть более предпочтительными. Если же вам нужно быстро и просто разработать приложение, стоит выбрать Python.</p>
  <h2 id="nb5g">Перспективы Python в дальнейшем</h2>
  <p id="XiVR">Python активно используется во многих областях технологий и программирования, в том числе в AI/ML, веб-разработке, разработке приложений и в областях Big Data/Data Science:</p>
  <ul id="UHDm">
    <li id="C5x4">AI/ML. Python остается одним из наиболее популярных языков программирования для разработки решений и моделей искусственного интеллекта и машинного обучения. Причина: большое количество библиотек и инструментов, таких как TensorFlow, Keras, PyTorch и Scikit-learn. Python продолжит играть ключевую роль в разработке решений AI/ML в ближайшем будущем.</li>
    <li id="1e8f">В веб-разработке Python также имеет свои преимущества. Язык используется для создания веб-серверов и приложений, в том числе с использованием фреймворков, таких как Django и Flask. Python позволяет быстро и легко разрабатывать функциональные веб-приложения и сайты, особенно те, которые не требуют больших объемов вычислительных ресурсов.</li>
    <li id="TSne">В разработке приложений. Python также остается популярным языком для разработки приложений для настольных компьютеров, мобильных устройств, встраиваемых систем. Благодаря большому количеству библиотек и инструментов, таких как PyInstaller и Py2exe, Python может быть легко превращен в приложение, доступное для запуска на большинстве платформ.</li>
    <li id="O7tZ">В областях Big Data/Data Science. Python также широко используется в области обработки больших данных и Data Science. Благодаря библиотекам, таким как Pandas, Numpy и Matplotlib, Python может быть использован для обработки, анализа и визуализации больших объемов данных. Python также может использоваться для разработки алгоритмов машинного обучения и статистического анализа данных.</li>
  </ul>
  <p id="HFu0">Благодаря своей простоте, гибкости и большому количеству библиотек и инструментов, Python останется одним из наиболее популярных языков программирования в ближайшем будущем.</p>
  <h2 id="PUfG">Заключение</h2>
  <p id="kt0e">Python стал самым популярным языком программирования благодаря своей простоте, мощным библиотекам и широкому применению в различных областях.</p>
  <p id="bTpY">Однако у него есть свои недостатки, такие как меньшая производительность и неподходящий для некоторых видов разработки</p>
  <p id="eioG"><br /></p>
  <p id="8HMC">Источник: https://tproger.ru/articles/pochemu-python-takoj-populyarnyj-napisano-chelovekom/</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/r-eijFSKKii</guid><link>https://teletype.in/@savichm/r-eijFSKKii?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/r-eijFSKKii?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>Выбираем кресло для программиста: топ-5 кресел</title><pubDate>Sat, 01 Apr 2023 11:33:10 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/6f/c0/6fc0ebdf-fca2-4918-80d9-02769debd266.png"></media:content><description><![CDATA[<img src="https://media.tproger.ru/uploads/2021/04/ofisnoekreslo-autoconverted-244x330.jpeg"></img>В этой статье мы рассмотрим характеристики, которыми должно обладать хорошее кресло для программиста, и расскажем, на какие модели стоит обратить внимание.]]></description><content:encoded><![CDATA[
  <p id="gWoH">В этой статье мы рассмотрим характеристики, которыми должно обладать хорошее кресло для программиста, и расскажем, на какие модели стоит обратить внимание.</p>
  <h2 id="RtXv">Виды кресел</h2>
  <h3 id="HI7w">Офисное кресло</h3>
  <figure id="1UgE" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/ofisnoekreslo-autoconverted-244x330.jpeg" width="244" />
  </figure>
  <p id="74gw">Обычное офисное кресло, которое можно встретить повсеместно. Позволяет настроить высоту кресла и жёсткость спинки. Опора для поясницы отсутствует, а откидывающаяся спинка не позволяет облокотиться на неё во время работы (некоторые модели позволяют зафиксировать спинку).</p>
  <h3 id="SnmU">Геймерское кресло</h3>
  <figure id="gMOQ" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/gejmerskoekreslo-autoconverted-217x330.jpeg" width="217" />
  </figure>
  <p id="sCan">Геймерские кресла имеют множество настроек и поясничный упор с подголовником. У таких кресел обычно есть механизм качания (его можно зафиксировать), который позволяет откинуться в кресле целиком, а не на одну спинку.</p>
  <h3 id="sxZN">Ортопедическое кресло</h3>
  <figure id="O8b3" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/ortopedicheskoekreslo-autoconverted-234x330.jpeg" width="234" />
  </figure>
  <p id="cd71">Ортопедические кресла внешне могут сильно отличаться друг от друга, их объединяет то, что спинка или сиденье у них состоят из нескольких частей, положение которых можно настроить.</p>
  <h3 id="g9xP">Ортопедический стул</h3>
  <figure id="pbbD" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/kolennyjstul-autoconverted-330x306.jpeg" width="330" />
  </figure>
  <p id="w8uw">Это коленный стул. Сидящий на нём упирается в нижнюю подушку коленями. Есть отзывы, что он улучшает осанку, однако это не подтверждено научными исследованиями. Кроме того, в данном случае увеличивается нагрузка на колени. Переход на такой стул требует привыкания.</p>
  <h3 id="IhAn">Стул седло</h3>
  <figure id="QiCw" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/kreslosedlo-autoconverted-301x330.jpeg" width="301" />
  </figure>
  <p id="mqbv">Такими стульями пользуются стоматологи. Сидящий на нём находится в промежуточном положении — полустоя. Есть отзывы, что от него не устаёт спина, однако к стулу нужно привыкнуть.</p>
  <h2 id="8Zg6">Как правильно сидеть</h2>
  <figure id="b3WI" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/03/content_pravilno.gif" width="252" />
  </figure>
  <p id="vEti">Согласно <a href="https://medlineplus.gov/guidetogoodposture.html" target="_blank">исследованиям</a> у правильно сидящего человека:</p>
  <ul id="FHjp">
    <li id="MgmJ">руки и ноги согнуты на 90-120 градусов;</li>
    <li id="ZsCj">локти находятся в районе туловища;</li>
    <li id="zRp0">поясница немного изогнута;</li>
    <li id="qtmV">монитор расположен на уровне глаз или ниже;</li>
    <li id="ZLwI">ступни полностью касаются пола;</li>
  </ul>
  <p id="U3ca">Какой бы правильной ни была поза сидящего, рекомендуется делать перерывы, вставать, потягиваться и совершать прогулки.</p>
  <h2 id="kcPO">Что важно при выборе кресла для программиста</h2>
  <h3 id="Ahr1">Механизмы</h3>
  <p id="HUpY">Механизм, который позволяет регулировать высоту кресла называется газлифт.</p>
  <p id="cZij">Есть несколько классов газлифтов, отличающихся максимальным весом, который они могут выдержать:</p>
  <ul id="m26P">
    <li id="Vrv6">1 класс — вес до 80кг;</li>
    <li id="ZFJT">2 класс — вес до 100кг;</li>
    <li id="iMAN">3 класс — вес до 150кг;</li>
    <li id="A36V">4 класс — вес до 200кг.</li>
  </ul>
  <p id="cgaY">Лучше выбирать класс с запасом, потому что есть риск сломать стул, если слишком резко сесть.</p>
  <h3 id="hCRy">Материалы</h3>
  <p id="nCuu">Обивка чаще всего бывает чётырех видов:</p>
  <ul id="TAFx">
    <li id="TSZL">текстиль — долговечен и дешев;</li>
    <li id="1b1d">сетка — различается по качеству, стоит дороже текстиля, принимает форму тела;</li>
    <li id="Gqej">экокожа — стоит дороже, быстро изнашивается (зависит от качества);</li>
    <li id="zvEc">натуральная кожа — самое дорогое покрытие, держится дольше чем экокожа.</li>
  </ul>
  <h2 id="BHbA">Топ-5 кресел для программистов</h2>
  <p id="O3Da">Подобрали для вас несколько моделей, в том числе по рекомендациям сотрудников Tproger :).</p>
  <h3 id="HbE5">Metta BK-8 Ch</h3>
  <p id="hNms"><em>~10 000 ₽</em></p>
  <figure id="KOz8" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/metta-autoconverted-260x330.jpeg" width="260" />
  </figure>
  <p id="nnZd">Офисное кресло с небольшим подголовником и упором для поясницы. Расположение упора не регулируется. Есть механизм качания, который можно зафиксировать. Спинка сделана из сетки, а сиденье из ткани. Подлокотники покрыты кожзамом. Максимальная нагрузка — 120 кг.</p>
  <h3 id="IUev">Бюрократ VIKING LOFT</h3>
  <p id="cBeM"><em>~18 000 ₽</em></p>
  <figure id="X7nH" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/bjurokrat-autoconverted-241x330.jpeg" width="241" />
  </figure>
  <p id="DWtP">Геймерское кресло с тканевой обивкой. Есть подголовник и регулируемый упор для поясницы. Настраивается наклон спинки. Механизм качания также настраивается. Высота подлокотников регулируется. Максимальная нагрузка — 150 кг.</p>
  <h3 id="ca7s">AeroCool Earl</h3>
  <p id="9Fcd"><em>~20 000 ₽</em></p>
  <figure id="VJhB" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/aerocool-autoconverted-227x330.jpeg" width="227" />
  </figure>
  <p id="aH3v">Геймерское кресло с обивкой из ткани и искусственной кожи. Также есть регулируемый упор для поясницы и подголовник. Подлокотники не регулируются. Механизм качания не регулируется. Максимальный угол отклонения спинки — 180 градусов. Максимальная нагрузка — 150 кг.</p>
  <h3 id="jwLQ">ThunderX3 EC3</h3>
  <p id="NtPj"><em>~18 500 ₽</em></p>
  <figure id="qkZi" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/thunder-autoconverted-223x330.jpeg" width="223" />
  </figure>
  <p id="nm4X">Ещё одно геймерское кресло, эта модель полностью обита искусственной кожей. По размеру немного меньше предыдущего кресла. Имеет регулируемый синхромеханизм качания. Максимальная нагрузка — 120 кг.</p>
  <h3 id="nuR0">HARAсhair NIETZSCHE</h3>
  <p id="pnwV"><em>~41 000 ₽</em></p>
  <figure id="UQOJ" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2021/04/nietzhe-autoconverted-237x330.jpeg" width="237" />
  </figure>
  <p id="9wic">Ортопедическое кресло, спинка и сиденье которого состоят из нескольких частей. Их положение можно настраивать. Сиденье изменяет свою форму в случае неравномерной нагрузки. Кресло обито сеткой. Есть синхромеханизм качания, который изменяет положение сиденья относительно спинки. Подголовник регулируется по высоте. Съёмный поясничный упор. Подлокотники не регулируются. Максимальная нагрузка — 90 кг.</p>
  <p id="sDre">Никакое идеальное кресло для программиста не избавит вас от необходимости делать перерывы. Это полезно и для спины, и <a href="https://tproger.ru/articles/naprjazhenie-glaz-otkuda-ono-berjotsja-i-kak-s-nim-spravitsja/" target="_blank">для глаз</a>.</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/UH2r6Ctlv2y</guid><link>https://teletype.in/@savichm/UH2r6Ctlv2y?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/UH2r6Ctlv2y?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>Почему стоит использовать Babel в разработке. Инструкция по написанию Babel плагина</title><pubDate>Sat, 01 Apr 2023 11:23:08 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/a8/f0/a8f0b06e-8e6d-4bc4-955c-7686615285cc.png"></media:content><description><![CDATA[<img src="https://img3.teletype.in/files/26/a8/26a8897b-f7a9-479f-ba4c-98446b71facd.png"></img>В языке JavaScript изменения происходят ежегодно, а некоторые ECMAScript конвенции вносят даже слишком много дополнительного синтаксического сахара, настолько, что браузерные движки исполняющие JS код не успевают обновляться под изменения. В связи с этим у разработчиков стоит выбор:]]></description><content:encoded><![CDATA[
  <figure id="OdAz" class="m_original">
    <img src="https://img3.teletype.in/files/26/a8/26a8897b-f7a9-479f-ba4c-98446b71facd.png" width="300" />
  </figure>
  <h2 id="BaXX">Какую проблему решает Babel</h2>
  <p id="WfBZ">В языке JavaScript изменения происходят ежегодно, а некоторые ECMAScript конвенции вносят даже слишком много дополнительного синтаксического сахара, настолько, что браузерные движки исполняющие JS код не успевают обновляться под изменения. В связи с этим у разработчиков стоит выбор:</p>
  <p id="aJuf">1. Писать в legacy стиле, не пользуясь новыми фишками;</p>
  <p id="9BId">2. Писать в новом стиле, и код будет работать только на новых версиях движков;</p>
  <p id="bJhm">3. Самый оптимальный вариант — писать код в современном стиле и транспилировать его с помощью Babel в более старые версии ECMAScript, чтобы его могли исполнять даже старые браузерные движки. В общем, Babel — это единый инструмент для того, чтобы ваш код поддерживался даже старыми браузерами.</p>
  <figure id="9MBe" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/232f2e33-9f53-4af3-93c4-328476d4592b.jpg" width="1279" />
  </figure>
  <p id="caption-attachment-238907">Пример, как выглядит код, преобразованный с помощью Babel</p>
  <h2 id="MoGl">Как работает Babel под капотом</h2>
  <p id="qtp9">Чтобы разобраться в том, что написал разработчик, и перевести это в более старую версию JS, Babel требуется максимально декомпозировать код. Делает он это с помощью составления AST дерева и его последующего анализа. Babel разбивает наш код на самые мелкие частицы, даже «‎;»‎ или «‎пробел»‎ — отдельная часть AST дерева. После того как Babel создал такое дерево, он может пройтись по каждому узлу и преобразовать его нужным образом.</p>
  <figure id="rwUH" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/670a707c-4bcc-41e1-b16d-774ec332bec5.jpg" width="1280" />
  </figure>
  <p id="caption-attachment-238908">Пример AST</p>
  <h2 id="K8BG">Особая сила Babel</h2>
  <p id="tlT2">Особая сила Babel заключается в огромном количестве разнообразных плагинов, а самое главное — плагин можно написать самому. Плагины имеют доступ к абстрактному синтаксическому дереву AST и могут изменять его на этапе транспиляции кода перед выходом в итоговый бандл. Babel плагины работают по паттерну Visitor. Суть паттерна в том, что если у нас есть некая абстракция (в нашем случае это AST), мы не взаимодействуем с ней напрямую, а работаем с ней через посетителя, то есть Visitor. Наша абстракция будет иметь метод, задача которого простая – просто передать себя посетителю, а он уже будет работать с ним.</p>
  <h2 id="ikgD">Пишем свой Babel плагин</h2>
  <p id="LtrG">Я напишу простой плагин в учебных целях, который будет менять все операторы «+»‎ на «-»‎. Он бесполезный, но на его примере можно понять, как пишутся плагины в целом.</p>
  <p id="1tKm">Суть простая.</p>
  <p id="r6OX">1. Вы смотрите в AST Explorer, как выглядят определённые ноды в абстрактном синтаксическом дереве;</p>
  <p id="WZi5">2. Обрабатываете эти ноды в коде так, как вы хотите.</p>
  <figure id="R4Im" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/17835600-2a01-4a58-b55b-03f9a53fb0a3.jpg" width="1280" />
  </figure>
  <p id="eR8Z">Я написал код в AST Explorer, после установки курсора на значок «+» увидел, что мне нужен BinaryExpression, и написал вот такой вот плагин, опираясь на паттерн Visitor:</p>
  <figure id="Q8RU" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/94a7ebaa-0173-447d-996e-f725e0a1a1c3.jpg" width="1280" />
  </figure>
  <p id="zour">Вот так подключил его:</p>
  <figure id="9VNn" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/e939abc8-3c02-4378-b73b-a7c6a1517e75.jpg" width="1168" />
  </figure>
  <p id="gjJn">В итоге мой код скомпилировался и на выходе в сближенном файле я получил вот такой консоль лог, где видно, что наш плагин работает и меняет «+»‎ на «-».</p>
  <figure id="nqnR" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/7932a68f-0a33-4e12-9392-19673d8ef3d7.jpg" width="1168" />
  </figure>
  <p id="lTn7">Исходный код можно найти: <a href="https://vk.com/away.php?to=https%3A%2F%2Fgithub.com%2Fdacorm%2Ftest-babel-plugin&cc_key=" target="_blank">https://github.com/dacorm/test-babel-plugin</a></p>
  <p id="Iy67">На примере этого простого плагина вы ознакомились с ещё одним нетривиальным способом решения некоторых проблем в коде. Иногда на работе случаются такие баги, что решать их приходится с помощью Babel плагина. Это буквально суперсила, которой владеют немногие</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/mUqj9S5rgJ7</guid><link>https://teletype.in/@savichm/mUqj9S5rgJ7?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/mUqj9S5rgJ7?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>SQL для начинающих: 10 правил построения «точных» запросов</title><pubDate>Sat, 01 Apr 2023 11:17:15 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ed/2a/ed2a071d-87e7-4c34-9f58-3c39493679f0.png"></media:content><description><![CDATA[<img src="https://img3.teletype.in/files/65/db/65db7224-cfe9-4daf-bebd-a582c4452af8.png"></img>«Точный» SQL-запрос возвращает «чистые» данные в необходимом и достаточном количестве, при этом потребляет как можно меньше памяти и справляется за минимальное время. Скорость работы с базой влияет на производительность. Потребление памяти может негативно сказаться даже на безопасности. Всё это прямо и косвенно влияет на прибыль компании. В статье разберёмся, как не допускать ошибок.]]></description><content:encoded><![CDATA[
  <p id="raHW">«Точный» <a href="https://ru.wikipedia.org/wiki/SQL#%D0%92%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5" target="_blank">SQL-запрос</a> возвращает «чистые» данные в необходимом и достаточном количестве, при этом потребляет как можно меньше памяти и справляется за минимальное время. Скорость работы с базой влияет на производительность. Потребление памяти может негативно сказаться даже на безопасности. Всё это прямо и косвенно влияет на прибыль компании. В статье разберёмся, как не допускать ошибок.</p>
  <p id="bELn">Для наших целей понадобятся тестовые данные. Будем работать с базой данных Oracle Database. Примеры в статье будут приводиться на языке SQL, PL/SQL. Нам важен подход, который можно адаптировать под другую реляционную систему управления базами данных — <a href="https://ru.wikipedia.org/wiki/%D0%A1%D0%A3%D0%A0%D0%91%D0%94" target="_blank">РСУБД</a>. <br /></p>
  <figure id="dDKc" class="m_original">
    <img src="https://img3.teletype.in/files/65/db/65db7224-cfe9-4daf-bebd-a582c4452af8.png" width="700" />
  </figure>
  <h2 id="FRVu">Тестовые данные</h2>
  <p id="Qqlb">⚒ Создадим тестовую таблицу 1:</p>
  <pre id="3Ice">CREATE SEQUENCE TEST_DATA_1_SEQ
  NOMAXVALUE
  NOMINVALUE
  NOCYCLE
/

CREATE TABLE TEST_DATA_1
(
  TEST_DATA_1_ID NUMBER DEFAULT TEST_DATA_1_SEQ.NEXTVAL NOT NULL
  ,TYPE          VARCHAR2(64) NOT NULL
  ,VALUE         VARCHAR2(128) NOT NULL
  ,PC_USR        VARCHAR2(30) DEFAULT USER NOT NULL
  ,PC_DT         TIMESTAMP(6) DEFAULT SYSTIMESTAMP NOT NULL
)
/

ALTER TABLE TEST_DATA_1
  ADD CONSTRAINT TEST_DATA_1_PK PRIMARY KEY (TEST_DATA_1_ID)
  USING INDEX
/

ALTER TABLE TEST_DATA_1
  ADD CONSTRAINT TEST_DATA_1_TYPE_CHK CHECK (TYPE in (&#x27;CITY&#x27;, &#x27;DATE&#x27;, &#x27;EMPLOYEE&#x27;, &#x27;STOCK MARKET&#x27;))
/

CREATE UNIQUE INDEX TEST_DATA_1_UIDX1 ON TEST_DATA_1 (VALUE)
/

COMMENT ON TABLE TEST_DATA_1 IS &#x27;Тестовые данные 1&#x27;
/</pre>
  <p id="6MVT">⚒ Заполним тестовую таблицу 1 данными:</p>
  <pre id="vpfI">/* Добавление данных */
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;CITY&#x27;, &#x27;МОСКВА&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;CITY&#x27;, &#x27;САНКТ-ПЕТЕРБУРГ&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;EMPLOYEE&#x27;, &#x27;СОТРУДНИК 1. ПОЛ М. ВОЗРАСТ 18&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;EMPLOYEE&#x27;, &#x27;СОТРУДНИК 2. ПОЛ Ж. ВОЗРАСТ 19&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;EMPLOYEE&#x27;, &#x27;СОТРУДНИК 3. ПОЛ Ж. ВОЗРАСТ 20&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;DATE&#x27;, &#x27;01 января 2000&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;DATE&#x27;, &#x27;02 января 2000&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;DATE&#x27;, &#x27;01.01.2001&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;DATE&#x27;, &#x27;02.01.2001&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;DATE&#x27;, &#x27;03.01.2001&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;DATE&#x27;, &#x27;04.01.2001&#x27;);

/* Извлечение всех  данных */
SELECT t1.*
  FROM TEST_DATA_1 t1;</pre>
  <figure id="NNbe" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image23.png" width="463" />
  </figure>
  <pre id="RtWP">/* Удаление всех данных без проверок */
TRUNCATE TABLE TEST_DATA_1;</pre>
  <p id="i5LM">⚒ Создадим тестовую таблицу 2:</p>
  <pre id="7NVK">CREATE SEQUENCE TEST_DATA_2_SEQ
  NOMAXVALUE
  NOMINVALUE
  NOCYCLE
/

CREATE TABLE TEST_DATA_2
(
  TEST_DATA_2_ID NUMBER DEFAULT TEST_DATA_2_SEQ.NEXTVAL NOT NULL
  ,TEST_DATA_1_ID NUMBER NOT NULL
  ,TYPE           VARCHAR2(64) NOT NULL
  ,VALUE          VARCHAR2(128) NOT NULL
  ,PC_USR         VARCHAR2(30) DEFAULT USER NOT NULL
  ,PC_DT          TIMESTAMP(6) DEFAULT SYSTIMESTAMP NOT NULL
)
/

ALTER TABLE TEST_DATA_2
  ADD CONSTRAINT TEST_DATA_2_PK PRIMARY KEY (TEST_DATA_2_ID)
  USING INDEX
/

ALTER TABLE TEST_DATA_2
  ADD CONSTRAINT TEST_DATA_2_TYPE_CHK CHECK (TYPE in (&#x27;STREET&#x27;, &#x27;DATE&#x27;, &#x27;EMPLOYEE&#x27;, &#x27;STOCK MARKET&#x27;))
/

CREATE UNIQUE INDEX TEST_DATA_2_UIDX1 ON TEST_DATA_2 (TEST_DATA_1_ID, VALUE)
/

COMMENT ON TABLE TEST_DATA_2 IS &#x27;Тестовые данные 2&#x27;
/</pre>
  <p id="gGpX">⚒ Заполним тестовую таблицу 2 данными:</p>
  <pre id="vOJk">/* Добавление данных */
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (1 /*ID МОСКВА*/, &#x27;STREET&#x27;, &#x27;УЛИЦА КАРЛА МАРКСА&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (1 /*ID МОСКВА*/, &#x27;STREET&#x27;, &#x27;УЛИЦА КРУПСКОЙ&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (1 /*ID МОСКВА*/, &#x27;STREET&#x27;, &#x27;МАЛЫЙ ПОЛУЯРОСЛАВСКИЙ ПЕРЕУЛОК&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (2 /*ID САНКТ-ПЕТЕРБУРГ*/, &#x27;STREET&#x27;, &#x27;УЛИЦА КАРЛА МАРКСА&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (2 /*ID САНКТ-ПЕТЕРБУРГ*/, &#x27;STREET&#x27;, &#x27;УЛИЦА КРУПСКОЙ&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (3 /*ID СОТРУДНИК 1*/, &#x27;EMPLOYEE&#x27;, &#x27;ПРОЖИВАЕТ В ГОРОДЕ МОСКВА&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (4 /*ID СОТРУДНИК 2*/,&#x27;EMPLOYEE&#x27;, &#x27;ПРОЖИВАЕТ В ГОРОДЕ МОСКВА&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (5 /*ID СОТРУДНИК 3*/,&#x27;EMPLOYEE&#x27;, &#x27;ПРОЖИВАЕТ В  ГОРОДЕ САНКТ-ПЕТЕРБУРГ&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (6 /*ID 01 января 2000*/, &#x27;DATE&#x27;, &#x27;Формат день числом, месяц словом, год числом&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (7 /*ID 02 января 2000*/, &#x27;DATE&#x27;, &#x27;Формат день числом, месяц словом, год числом&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (8 /*ID 01.01.2001*/, &#x27;DATE&#x27;, &#x27;Формат день числом, месяц числом, год числом&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (9 /*ID 02.01.2001*/, &#x27;DATE&#x27;, &#x27;Формат день числом, месяц числом, год числом&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (10 /*ID 03.01.2001*/, &#x27;DATE&#x27;, &#x27;Формат день числом, месяц числом, год числом&#x27;);
INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (11 /*ID 04.01.2001*/, &#x27;DATE&#x27;, &#x27;Формат день числом, месяц числом, год числом&#x27;);

/* Извлечение всех данных */
SELECT t2.*
  FROM TEST_DATA_2 t2;</pre>
  <figure id="VPsG" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image12.png" width="683" />
  </figure>
  <pre id="Ghoz">/* Удаление всех данных без проверок */
TRUNCATE TABLE TEST_DATA_2;</pre>
  <h2 id="9FCt">1. Объявляя имена таблиц, обращайся к записям через псевдонимы таблиц</h2>
  <p id="X19p">Допустим, есть таблица с некоторым количество колонок. К ней можно обратиться двумя разными способами:</p>
  <p id="dMtr">⚠️ Опасный подход:</p>
  <pre id="rtsz">SELECT TYPE
      ,VALUE
  FROM TEST_DATA_1;</pre>
  <p id="UkV4">✅ Безопасный подход заключается в обращении через псевдоним:</p>
  <pre id="MyWJ">SELECT t1.TYPE AS TYPE
      ,t1.VALUE AS VALUE
  FROM TEST_DATA_1 t1;</pre>
  <p id="MBKZ"><a href="https://ru.wikipedia.org/wiki/Alias_(SQL)" target="_blank">Псевдоним (анг. Alias)</a> — это имя, назначенное источнику данных в SQL-запросе при использовании выражения в качестве источника данных или для упрощения ввода и прочтения инструкции <a href="https://ru.wikipedia.org/wiki/SQL" target="_blank">SQL</a>. Это полезно, если имя источника слишком длинное или его трудно вводить.</p>
  <p id="t2WS">Псевдонимы можно использовать для переименования таблиц и колонок. В отличие от настоящих имён, они могут не соответствовать ограничениям базы данных и содержать до 255 знаков (включая пробелы, цифры и специальные символы).</p>
  <p id="bWsO">В случае извлечения данных из одной таблицы без псевдонимов можно обойтись. Рисков нет. Синтаксический анализатор базы данных однозначно знает, данные из какой колонки таблицы запрашиваются. Но рекомендуется всё же использовать их — чтобы выработать привычку.</p>
  <p id="1Wpm">В случае извлечения данных из нескольких таблиц отказ от использования псевдонимов увеличивает риск получения некорректного результата. Допустим, что у таблиц есть колонки с одинаковым именем. Когда данные извлекаются и SQL-запрос звучит как: «Получаю записи из таблиц колонку А», то о какой колонке «А» идёт речь: из первой или второй таблицы? Если для таблицы назначен псевдоним, то SQL-запрос может звучать уже так: «Получаю записи из таблицы Т1 колонку А».</p>
  <p id="u2MU">К SQL-запросу, возможно, придётся вернуться через какое-то время, чтобы внести в него изменения. В таких случаях подсказки в виде псевдонима (alias) помогут определить нужную колонку. Практически со стопроцентной уверенностью будет понятно, из какой таблицы что извлекали.</p>
  <p id="Q3s5">⚠️ Опасный подход:</p>
  <pre id="77R9">SELECT TEST_DATA_1.TYPE
      ,TEST_DATA_1.VALUE
      ,TEST_DATA_2.TYPE
      ,TEST_DATA_2.VALUE
  FROM TEST_DATA_1
      ,TEST_DATA_2
WHERE TEST_DATA_1.TEST_DATA_1_ID = TEST_DATA_2.TEST_DATA_1_ID;</pre>
  <figure id="RPKt" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image24.png" width="753" />
  </figure>
  <p id="Dnov">✅ Безопасный подход заключается в обращении через псевдоним:</p>
  <pre id="YSbi">SELECT t1.TYPE AS TYPE_1 /* Колонка TYPE из таблицы TEST_DATA_1 */
      ,t1.VALUE AS VALUE_1 /* Колонка VALUE из таблицы TEST_DATA_1 */
      ,t2.TYPE AS TYPE_2 /* Колонка TYPE из таблицы TEST_DATA_2 */
      ,t2.VALUE AS VALUE_2 /* Колонка VALUE из таблицы TEST_DATA_2 */
 FROM TEST_DATA_1 t1
     ,TEST_DATA_2 t2
WHERE t1.TEST_DATA_1_ID = t2.TEST_DATA_1_ID;</pre>
  <figure id="bywS" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image11.png" width="752" />
  </figure>
  <h2 id="OMcz">2. Извлекай только те данные, которые планируешь использовать</h2>
  <p id="4gAy">База данных зачастую является неотъемлемой частью приложения. По мере усложнения функционала в отдельной взятой таблице может увеличиваться количество колонок.</p>
  <p id="5mXV"><strong>Рассмотрим пример «Карточка сотрудника».</strong> У нас есть таблица «Сотрудник» с колонками ФИО, пол, возраст. Данные из них извлекаются и выводятся на форму «Карточка сотрудника». SQL-запрос можно написать следующим образом: «Извлекаю все колонки из таблицы по указанному сотруднику». В таком случае извлекаются все колонки.</p>
  <p id="ida3">⚠️ Опасный подход заключается в извлечении всех данных:</p>
  <pre id="t7tC">SELECT t1.*
  FROM TEST_DATA_1 t1
 WHERE t1.TEST_DATA_1_ID = 3 /* ID EMPLOYEE = СОТРУДНИК 1 */;</pre>
  <figure id="1sgF" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image25.png" width="447" />
  </figure>
  <p id="m4p7">В будущем могут появиться дополнительные колонки в базе данных — например, описание должностных обязанностей или адрес проживания — в рамках нового информационного потока использования базы данных. То есть вне «Карточки сотрудника».</p>
  <pre id="2xgs">/* Добавление новой колонки в таблицу */
ALTER TABLE TEST_DATA_1
  ADD DESCRIPTION VARCHAR2(4000)
/

/* Обновление данных в таблице */
UPDATE TEST_DATA_1 t1
   SET T1.DESCRIPTION = &#x27;ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ ПО СОТРУДНИКУ 1&#x27;
 WHERE t1.TEST_DATA_1_ID = 3 /* ID EMPLOYEE = СОТРУДНИК 1 */;

/* Извлечение данных из таблицы */
SELECT t1.*
  FROM TEST_DATA_1 t1
 WHERE t1.TEST_DATA_1_ID = 3 /* ID EMPLOYEE = СОТРУДНИК 1 */;</pre>
  <figure id="XIG8" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image18.png" width="750" />
  </figure>
  <p id="ZAKq">В результате данные по новым полям заполняются уже не только формой «Карточки сотрудника». И SQL-запрос получения информации для формы начинает работать медленнее. Причина в том, что приходится извлекать данные из большего количества колонок.</p>
  <p id="m2rL">Деградация скорости получения данных может происходить постепенно или резко — но в самый неподходящий момент. Зачастую это связано с тем, что поля свободного ввода данных могут быть большими. То есть база данных должна больше информации подгрузить в память и потом отдать клиенту, приложение которого не готово к такому потоку данных.</p>
  <p id="tEVb"><strong>Рассмотрим пример «Телефон».</strong> На телефоне пользователя установлено приложение. Сам телефон старый. Пользователь не выполнял обновления программного обеспечения (ПО), но замечает, что с какого-то момента времени приложение начало работать медленнее. У другого пользователя на новом телефоне то же приложение работает быстро. Ошибка «плавающая», но для разработчика неприятная.</p>
  <p id="eFpH">Как правило, дело в том, как написано приложение. Данных извлекается больше, чем надо, и более современный телефон, у которого памяти больше, этого не заметит. Но старый не может себе этого позволить.</p>
  <p id="vAUr">Чтобы таких неожиданностей не возникало, нужно извлекать строго те данные, которые требуется использовать и показывать на форме. В данном случае нужно было написать: «Извлекаю колонки ФИО, возраст, пол из таблички сотрудника, с фильтрацией по сотруднику».</p>
  <p id="KyOD">✅ Безопасный подход заключается в получении нужных данных:</p>
  <pre id="Z6Q8">SELECT t1.TYPE AS TYPE_1 /* Колонка TYPE из таблицы TEST_DATA_1 */
      ,t1.VALUE AS VALUE_1 /* Колонка VALUE из таблицы TEST_DATA_1 */
  FROM TEST_DATA_1 t1
WHERE t1.TEST_DATA_1_ID = 3 /* ID EMPLOYEE = СОТРУДНИК 1 */;</pre>
  <figure id="MlEu" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image27.png" width="340" />
  </figure>
  <h2 id="X7ZB">3. По максимуму используй данные, которые извлёк из таблицы</h2>
  <p id="judH">Каждый SQL-запрос к базе данных чего-то стоит. В тот момент, когда данные извлечены и находятся в памяти, надо по максимуму использовать то, что получено, чтобы оптимизировать время и ресурсы.</p>
  <p id="Kb1H">После обращения к таблице Table1, нужно постараться написать SQL-запрос так, чтобы не пришлось извлекать данные из неё несколько раз. Это не всегда возможно, но попытаться стоит.</p>
  <p id="8n3W">⚠️ Опасный подход:</p>
  <pre id="YhK3">SELECT t1.TYPE AS TYPE_1
      ,t1.VALUE AS VALUE_1
      ,t2.VALUE AS VALUE_2
  FROM TEST_DATA_1 t1
      ,TEST_DATA_2 T2
WHERE T1.TEST_DATA_1_ID = T2.TEST_DATA_1_ID
  AND t2.VALUE = &#x27;ПРОЖИВАЕТ В ГОРОДЕ МОСКВА&#x27;
UNION ALL
SELECT t1.TYPE AS TYPE_1
      ,t1.VALUE AS VALUE_1
      ,t2.VALUE AS VALUE_2
  FROM TEST_DATA_1 t1
      ,TEST_DATA_2 T2
WHERE T1.TEST_DATA_1_ID = T2.TEST_DATA_1_ID
  AND t2.VALUE = &#x27;ПРОЖИВАЕТ В ГОРОДЕ САНКТ-ПЕТЕРБУРГ&#x27;
ORDER BY VALUE_1;</pre>
  <figure id="qMV0" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image15.png" width="605" />
  </figure>
  <pre id="nYFf">/* План запроса */</pre>
  <figure id="IorP" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image9.png" width="671" />
  </figure>
  <p id="jEke">✅ Безопасный подход заключается в использовании полученных данных максимально продуктивно:</p>
  <pre id="vmok">SELECT t1.TYPE AS TYPE_1
      ,t1.VALUE AS VALUE_1
      ,t2.VALUE AS VALUE_2
  FROM TEST_DATA_1 t1
      ,TEST_DATA_2 T2
WHERE T1.TEST_DATA_1_ID = T2.TEST_DATA_1_ID
  AND t2.VALUE IN (&#x27;ПРОЖИВАЕТ В ГОРОДЕ МОСКВА&#x27;, &#x27;ПРОЖИВАЕТ В ГОРОДЕ САНКТ-ПЕТЕРБУРГ&#x27;)
ORDER BY VALUE_1;</pre>
  <figure id="r95W" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image13.png" width="601" />
  </figure>
  <pre id="8UEr">/* План запроса */</pre>
  <figure id="tEet" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image5.png" width="675" />
  </figure>
  <p id="zBPp">Неоптимальный SQL-запрос может выполняться дольше, уронить инфраструктуру и даже повлиять на безопасность системы.</p>
  <p id="Oj2g">⚒ Рассмотрим тестовый пример:</p>
  <pre id="SBzu">/*
 * Тестовый пример
 * Каждый случай запроса выполняется 1 000 000 раз в “холостую”
 */

declare
  start_time pls_integer;
  end_time   pls_integer;
begin
  /* 1 Случай */
  start_time := dbms_utility.get_time;

  for indx in 1 .. 1000000 loop
     for cur in (select t1.TYPE as TYPE_1
                    ,t1.VALUE as VALUE_1
                    ,t2.VALUE as VALUE_2
                from TEST_DATA_1 t1
                    ,TEST_DATA_2 T2
               where T1.TEST_DATA_1_ID = T2.TEST_DATA_1_ID
                 and t2.VALUE = &#x27;ПРОЖИВАЕТ В ГОРОДЕ МОСКВА&#x27;
               union all
               select t1.TYPE as TYPE_1
                    ,t1.VALUE as VALUE_1
                    ,t2.VALUE as VALUE_2
                from TEST_DATA_1 t1
                    ,TEST_DATA_2 T2
               where T1.TEST_DATA_1_ID = T2.TEST_DATA_1_ID
                 and t2.VALUE = &#x27;ПРОЖИВАЕТ В ГОРОДЕ САНКТ-ПЕТЕРБУРГ&#x27;
                order by VALUE_1) loop
      null;
    end loop;
  end loop;

  end_time := dbms_utility.get_time;
  dbms_output.put_line(&#x27;execution time 1 --&gt; &#x27; || (end_time - start_time) / 100 || &#x27; sec&#x27;);

  /* 2 Случай */
  start_time := dbms_utility.get_time;

  for indx in 1 .. 1000000 loop
    for cur in (select t1.TYPE as TYPE_1
                   ,t1.VALUE as VALUE_1
                   ,t2.VALUE as VALUE_2
               from TEST_DATA_1 t1
                   ,TEST_DATA_2 T2
              where T1.TEST_DATA_1_ID = T2.TEST_DATA_1_ID
                and t2.VALUE in (&#x27;ПРОЖИВАЕТ В ГОРОДЕ МОСКВА&#x27;, &#x27;ПРОЖИВАЕТ В ГОРОДЕ САНКТ-ПЕТЕРБУРГ&#x27;)
               order by VALUE_1) loop
      null;
    end loop;
  end loop;

  end_time := dbms_utility.get_time;
  dbms_output.put_line(&#x27;execution time 2 --&gt; &#x27; || (end_time - start_time) / 100 || &#x27; sec&#x27;);
end;
/

/*
 * Результат выполнения
 * Важно не время, которое зависит от ресурсов на ПК, а разница выполнения
 */
/*
Done in 64,516 seconds
 */
execution time 1 --&gt; 46.83 sec
execution time 2 --&gt; 17.67 sec</pre>
  <p id="OBo4"><strong>Рассмотрим пример «Работа ЦОД».</strong>Есть Центр Обработки Данных (ЦОД). В нём, на одном из ресурсов внутри приложения, выполняется некий SQL-запрос, который постепенно использует всю доступную память без ограничений. И приложениям, которые стоят на том же ресурсе, со временем перестаёт хватать памяти на стабильную работу. Это может привести к их падению.</p>
  <p id="XeDy">Конечно, многое будет зависеть от качества приложений. Например, если во время падения они закрываются нетипично, подвисают или не отвечают на запросы пользователя — и выходят за рамки границ безопасности — хакеры могут этим пользоваться для входа в систему. И тем самым её скомпрометировать.</p>
  <h2 id="O0P5">4. Проверяй запросы SQL на индексы</h2>
  <p id="e2yP">SQL-запросы бывают простые и сложные. Иногда извлекается мало данных, иногда — много. Если таблица большая, и в ней очень разнообразные данные, то в зависимости от того, как обращаться к этим данным, использовать индекс или нет, можно потерять время.</p>
  <p id="VX0G"><strong>Рассмотрим пример «Брокерская биржа».</strong> В рамках отдельного процесса извлекаются данные для покупки-продажи акций. Используя оптимизированный SQL-запрос, можно быстро получать информацию, по какой цене торгуется каждая акция. И делать прогноз — покупать или продавать.</p>
  <p id="NILu">Если SQL-запрос не оптимизирован, извлечение данных занимает больше времени. И пользователь вынужден ждать, хотя мог за это время сделать что-то, что принесло бы ему деньги.</p>
  <p id="bvvt"><a href="https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%B4%D0%B5%D0%BA%D1%81_(%D0%B1%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85)" target="_blank">Индексы</a> — это инструмент оптимизации извлечения данных. Конечно, это не панацея, и если таблица маленькая, по ней проще пройти прямым перебором и получить данные.</p>
  <p id="qDpF">Добавим в тестовую таблицу 1 новые данные:</p>
  <pre id="SPVy">/* Добавление новых данных */
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;STOCK MARKET&#x27;, &#x27;АКЦИЯ 1. СТОИМОСТЬ 101 РУБ&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;STOCK MARKET&#x27;, &#x27;АКЦИЯ 2. СТОИМОСТЬ 102 РУБ&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;STOCK MARKET&#x27;, &#x27;АКЦИЯ 3. СТОИМОСТЬ 103 РУБ&#x27;);
INSERT INTO TEST_DATA_1 (TYPE, VALUE) VALUES (&#x27;STOCK MARKET&#x27;, &#x27;АКЦИЯ 4. СТОИМОСТЬ 104 РУБ&#x27;);</pre>
  <p id="WMAN">⚠️ Опасный подход заключается в игнорировании использования индексов:</p>
  <pre id="nPQe">/* Извлечение всех данных TYPE = STOCK MARKET */
SELECT t1.TYPE AS TYPE_1
      ,t1.VALUE AS VALUE_1
  FROM TEST_DATA_1 t1
WHERE t1.TYPE = &#x27;STOCK MARKET&#x27;;

/* План запроса */</pre>
  <figure id="eH2V" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image14.png" width="672" />
  </figure>
  <p id="YIVv">Добавим в тестовую таблицу 1 новый индекс</p>
  <pre id="n9YT">/* Добавление нового индекса */
CREATE INDEX TEST_DATA_1_IDX1 ON TEST_DATA_1 (TYPE)
/</pre>
  <p id="l8zG">✅ Безопасный подход заключается в использовании индексов:</p>
  <pre id="J4oR">/* Извлечение всех данных TYPE = STOCK MARKET */
SELECT t1.TYPE AS TYPE_1
      ,t1.VALUE AS VALUE_1
  FROM TEST_DATA_1 t1
WHERE t1.TYPE = &#x27;STOCK MARKET&#x27;;</pre>
  <figure id="YD7t" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image6.png" width="337" />
  </figure>
  <pre id="xBFf">/* План запроса */</pre>
  <figure id="GiRg" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image7.png" width="675" />
  </figure>
  <p id="yKXk"><strong>Рассмотрим пример «Доставка почты».</strong> Показательный пример работы индексов — доставка почты из точки А в одном городе, в точку Б в другом. Зная, куда конкретно нужно доставить посылку, мы можем идти по индексам и определить, где и когда повернуть, чтобы довезти посылку за максимально короткое время. Если везти посылку на машине, то это сокращает расход топлива — а значит, и материальные издержки на доставку.</p>
  <p id="f6wM">В противном случае можно сворачивать не там, спрашивать дорогу у прохожих, которые знают её плохо. И, вместо того чтобы доставить посылку за Время Т1, опоздать на Время Т2. В итоге покупатель ждёт, а продавец теряет деньги.</p>
  <p id="EcVj">Индексы помогают ускорить извлечение данных.</p>
  <h2 id="eutT">5. Начинай запрос SQL с таблицы с меньшим набором записей</h2>
  <p id="Ls9H">Допустим, нам нужно соединить две таблицы: с маленьким количеством записей и с большим. Стоит сделать следующее:</p>
  <ul id="OMbL">
    <li id="spgu">начинать извлечение данных из таблицы с меньшим набором данных;</li>
    <li id="ZBUP">продолжать извлечение данных из таблицы с большим набором данных.</li>
  </ul>
  <p id="OPnU">⚒ Добавим в тестовую таблицу 2 новые данные:</p>
  <pre id="He8Y">/* Добавление новых данных */
declare
  l_type       test_data_2.type%type := &#x27;STOCK MARKET&#x27;;
  l_value_2 test_data_2.value%type := &#x27;&#x27;;
  l_sql          varchar2(128) := &#x27;&#x27;;
begin
  /* Извлечение данных из тестовой таблицы 1 */
  for cur_t1 in (select t1.test_data_1_id as test_data_1_id
                       ,t1.type as type_1
                       ,t1.value as value_1
                   from TEST_DATA_1 t1
                  where type = l_type) loop
    /* Цикл до 1 000 000  на каждую полученную запись из тестовой таблицы 1 */
    for indx in 1 .. 1000000 loop
      l_value_2 := cur_t1.value_1 || &#x27;. &#x27; || &#x27;ЗАПИСЬ &#x27; || indx;
      l_sql     := &#x27;INSERT INTO TEST_DATA_2 (TEST_DATA_1_ID, TYPE, VALUE) VALUES (&#x27; || --
                cur_t1.TEST_DATA_1_ID || &#x27;, &#x27; || --
                &#x27;&#x27;&#x27;&#x27; || l_type || &#x27;&#x27;&#x27;, &#x27; || --
                &#x27;&#x27;&#x27;&#x27; || l_value_2 || &#x27;&#x27;&#x27;)&#x27;;
      /* Выполнение динамического запроса */
      execute immediate l_sql;
    end loop;
  end loop;
end;
/

/* Общее число записей в таблице TEST_DATA_1 */
SELECT COUNT(1) AS CNT
  FROM  TEST_DATA_1
/</pre>
  <figure id="Zp6o" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image1.png" width="74" />
  </figure>
  <pre id="YhKc">/* Общее число записей в таблице TEST_DATA_2 */
SELECT COUNT(1) AS CNT
  FROM TEST_DATA_2
/</pre>
  <figure id="1S2o" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image22.png" width="107" />
  </figure>
  <pre id="rY3d">/* Добавление нового индекса для таблицы TEST_DATA_1 */
CREATE INDEX TEST_DATA_1_IDX2 ON TEST_DATA_1 (TEST_DATA_1_ID, TYPE)
/

/* Добавление нового индекса для таблицы TEST_DATA_2 */
CREATE INDEX TEST_DATA_2_IDX1 ON TEST_DATA_2 (TEST_DATA_1_ID, TYPE)
/
CREATE INDEX TEST_DATA_2_IDX2 ON TEST_DATA_2 (TEST_DATA_1_ID)
/
CREATE INDEX TEST_DATA_2_IDX3 ON TEST_DATA_2 (TYPE)
/

/* Сбор статистики после добавления данных */
declare
  l_user varchar2(30 char) := user;
begin
  /* Для таблицы TEST_DATA_1 */
  DBMS_STATS.GATHER_TABLE_STATS(ownname =&gt; l_user --
                               ,tabname =&gt; &#x27;TEST_DATA_1&#x27;
                               ,cascade =&gt; true);

  /* Для таблицы TEST_DATA_2 */
  DBMS_STATS.GATHER_TABLE_STATS(ownname =&gt; l_user --
                               ,tabname =&gt; &#x27;TEST_DATA_2&#x27;
                               ,cascade =&gt; true);
end;
/</pre>
  <p id="lGVj">Если поступить наоборот, то мы потеряем время, потому что перебирать данные из большей таблицы дольше.</p>
  <p id="PpvD">⚒ Рассмотрим тестовый пример:</p>
  <pre id="FZkL">/*
 * Тестовый пример
 * Каждый случай запроса выполняется 100 раз в “холостую”
 * Запросы усложнены и их можно упростить, добиваясь большей производительности и схожего результата
 * Попробуйте поэкспериментировать
 */

declare
  start_time pls_integer;
  end_time   pls_integer;
begin
  /* 1 Случай. От большего к меньшему */
  start_time := dbms_utility.get_time;

  for indx in 1 .. 100 loop
    for cur in (select t1.type as type_1
                      ,t1.value as value_1
                      ,t2_.type_2 as type_2
                      ,t2_.value_2_min as value_2_min
                      ,t2_.value_2_max as value_2_max
                      ,t2_.value_2_cnt as value_2_cnt
               from (select t2.TEST_DATA_1_ID as TEST_DATA_1_ID
                           ,t2.TYPE as TYPE_2
                           ,min(t2.VALUE) as VALUE_2_MIN
                           ,max(t2.VALUE) as VALUE_2_MAX
                           ,count(t2.VALUE) as VALUE_2_CNT
                      from TEST_DATA_2 t2
                     where t2.type = &#x27;STOCK MARKET&#x27;
                     group by t2.TEST_DATA_1_ID
                             ,t2.TYPE
                     order by t2.TEST_DATA_1_ID) t2_
                join TEST_DATA_1 t1
                  on t1.TEST_DATA_1_ID = t2_.TEST_DATA_1_ID
                 and t1.type = &#x27;STOCK MARKET&#x27;
               order by t1.value) loop
      null;
    end loop;
  end loop;end_time := dbms_utility.get_time;
  dbms_output.put_line(&#x27;execution time 1 --&gt; &#x27; || (end_time - start_time) / 100 || &#x27; sec&#x27;);

  /* 2 Случай. От меньшего к большему */
  start_time := dbms_utility.get_time;

  for indx in 1 .. 100 loop
    for cur in (select t1_.type_1 as type_1
                     ,t1_.value_1 as value_1
                     ,t2.type as type_2
                     ,min(t2.value) as value_2_min
                     ,max(t2.value) as value_2_max
                     ,count(t2.value) as value_2_cnt
                from (select t1.TEST_DATA_1_ID as TEST_DATA_1_ID
                            ,t1.TYPE as TYPE_1
                            ,t1.VALUE as VALUE_1
                        from TEST_DATA_1 t1
                       where t1.TYPE = &#x27;STOCK MARKET&#x27;
                       order by t1.value) t1_
                 join TEST_DATA_2 t2
                   on t2.TEST_DATA_1_ID = t1_.TEST_DATA_1_ID
                  and t2.TYPE = &#x27;STOCK MARKET&#x27;
                group by t1_.type_1
                        ,t1_.value_1
                        ,t2.type) loop
      null;
    end loop;
  end loop;

  end_time := dbms_utility.get_time;
  dbms_output.put_line(&#x27;execution time 2 --&gt; &#x27; || (end_time - start_time) / 100 || &#x27; sec&#x27;);
end;
/

/*
 * Результат выполнения
 * Важно не время, которое зависит от ресурсов на ПК, а разница выполнения
 * Каждый вариант возвращает одинаковые данные
 */</pre>
  <figure id="wblm" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image17.png" width="1128" />
  </figure>
  <pre id="M1ff">/* Executed in 269,203 seconds */

execution time 1 --&gt; 149.49 sec
execution time 2 --&gt; 119.68 sec</pre>
  <p id="L2oY"><strong>Рассмотрим пример «Очередь клиентов». </strong>Есть поток клиентов, каждого из которых нужно обслужить. Операторы, заполняя форму «Анкета» задают серию вопросов. Один из них, влияет на дальнейший ход общения: «Вам исполнилось 18 лет?». Если клиент отвечает нет, то оператор прекращает общение, иначе продолжает задавать вопросы.</p>
  <p id="ADEq">Если оператор задаст вопрос про возраст в конце общения, то любой потенциальный клиент должен будет заполнить всю анкету, даже если в этом нет смысла. Рациональный подход в общении с клиентами помогает операторам за одно и то же время обслужить большее число клиентов. С базами данных всё так же.</p>
  <h2 id="t0fP">6. Не допускай декартового произведения между таблицами</h2>
  <p id="mKcO">Результатом <a href="https://ru.wikipedia.org/wiki/%D0%9C%D0%BD%D0%BE%D0%B6%D0%B5%D1%81%D1%82%D0%B2%D0%BE#%D0%94%D0%B5%D0%BA%D0%B0%D1%80%D1%82%D0%BE%D0%B2%D0%BE_%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5" target="_blank">декартового </a>— или перекрёстного — произведения множеств будет такое множество, элементами которого являются все возможные упорядоченные пары элементов исходных множеств. <strong>Рассмотрим пример «Адрес»</strong>. Возьмём две таблицы «Город», «Улица». В первой таблице «Город» есть две записи: Москва и Санкт-Петербург. Во второй таблице «Улица» сохранены следующие записи:</p>
  <ul id="a1ZG">
    <li id="nEnX">улица Карла Маркса, которая одновременно есть и в Москве, и в Санкт-Петербурге;</li>
    <li id="TpZN">улица Крупской аналогично и в Москве, и в Санкт-Петербурге;</li>
    <li id="bmMs">Малый Полуярославский переулок только в Москве.</li>
  </ul>
  <p id="AepI">Пишем запрос: «Получаю из таблицы «Улица», которые принадлежат городу Москва».</p>
  <p id="5C8B">⚠️ Опасный подход:</p>
  <pre id="VBW6">SELECT t1.TYPE AS TYPE_1 /* Колонка TYPE из таблицы TEST_DATA_1 */
       ,t1.VALUE AS VALUE_1 /* Колонка VALUE из таблицы TEST_DATA_1 */
       ,t2.TYPE AS TYPE_2 /* Колонка TYPE из таблицы TEST_DATA_2 */
       ,t2.VALUE AS VALUE_2 /* Колонка VALUE из таблицы TEST_DATA_2 */
  FROM TEST_DATA_1 t1
       ,TEST_DATA_2 t2
WHERE t1.TYPE = &#x27;CITY&#x27;
  AND t2.TYPE = &#x27;STREET&#x27;;</pre>
  <figure id="oeOF" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image10.png" width="520" />
  </figure>
  <p id="LCHY">SQL-запрос написан без условия, то есть: «Извлекаю улицы, относящиеся к городам, без соединения таблиц». База данных, не понимая, по какому городу делается SQL-запрос, соединит со всеми улицами и Москву, и Санкт-Петербург. Всего вернётся 2* 5 = 10 записей.</p>
  <p id="8QKu">✅ Безопасный подход заключается в наличии связей:</p>
  <pre id="Lvrh">SELECT t1.TYPE AS TYPE_1 /* Колонка TYPE из таблицы TEST_DATA_1 */
       ,t1.VALUE AS VALUE_1 /* Колонка VALUE из таблицы TEST_DATA_1 */
       ,t2.TYPE AS TYPE_2 /* Колонка TYPE из таблицы TEST_DATA_2 */
       ,t2.VALUE AS VALUE_2 /* Колонка VALUE из таблицы TEST_DATA_2 */
  FROM TEST_DATA_1 t1
       ,TEST_DATA_2 t2
WHERE t1.TEST_DATA_1_ID = t2.TEST_DATA_1_ID
  AND t1.TYPE = &#x27;CITY&#x27;
  AND t2.TYPE = &#x27;STREET&#x27;;</pre>
  <figure id="ZwMl" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image20.png" width="520" />
  </figure>
  <p id="YSCd">Этот SQL-запрос написан с условием, то есть: «Извлекаю улицы, относящиеся к городу Москве, соединяя две таблицы условием». В нём указывается, по какому городу нужно выполнить фильтрацию. Поэтому возвращено 3 записи.<br />Когда данные извлекаются больше чем из одной таблицы, важно, как они соединяются между собой. Неправильное соединение будет возвращать неверные данные и не в ожидаемом количестве.</p>
  <h2 id="fW0B">7. Проверяй, что имена параметров процедур не совпадают с именами колонок</h2>
  <p id="vKIu">Процедуры, функции могут использоваться для разных целей. Одно из возможных предназначений — обновление записей в таблице.</p>
  <p id="wR8a">Допустим, есть строковый параметр А, который передаётся на вход процедуры с целью фильтрации. Можно сказать, что написано так: «Обновляю таблицу, задав новое значение для колонки, где выполняется фильтрация по колонке А равной параметру А». В этом случае наблюдается полное совпадение А = А. База данных обновит все записи в этой таблице.</p>
  <p id="LFqs">Чтобы этого не было, параметру добавляют префикс или постфикс. Например, параметр будет называться не А, а РА. В изменённом виде можно сказать, что написано так: «Обновляю таблицу, задав новое значение для колонки, где выполняется фильтрация по колонке А равной параметру PА».</p>
  <p id="O7Zu">⚠️ Опасный подход:</p>
  <pre id="DwsT">/* 1 вариант процедуры с ошибкой */
create or replace procedure e_test_data_1_upd_description(test_data_1_id in TEST_DATA_1.TEST_DATA_1_ID%type
                                                                                                            ,description        in TEST_DATA_1.DESCRIPTION%type) as
begin
  update TEST_DATA_1 t1 –
     set t1.DESCRIPTION = description /* Обновление записи */
   where t1.TEST_DATA_1_ID = test_data_1_id;
exception
  when others then
    /* Блок перехвата ошибок */
    null;
end e_test_data_1_upd_description;
/

/* Пример вызова 1 варианта процедуры с ошибкой */
declare
begin
  e_test_data_1_upd_description(test_data_1_id =&gt; 4 /* ID EMPLOYEE = СОТРУДНИК 2 */
                              ,description        =&gt; &#x27;ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ ПО СОТРУДНИКУ 2&#x27;);
end;
/

/*
 * Результат изменений
 * Визуально изменений нет
 */
SELECT T1.TEST_DATA_1_ID AS TEST_DATA_1_ID
       ,T1.TYPE AS TYPE_1
       ,T1.VALUE AS VALUE_1
       ,T1.DESCRIPTION AS DESCRIPTION
  FROM TEST_DATA_1 T1;</pre>
  <figure id="yvKo" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image3.png" width="775" />
  </figure>
  <p id="7db3">⚠️ Опасный подход:</p>
  <pre id="KQAG">/* 2 вариант процедуры с ошибкой */
create or replace procedure e_test_data_1_upd_description(test_data_1_id    in TEST_DATA_1.TEST_DATA_1_ID%type
                                                        ,description_new in TEST_DATA_1.DESCRIPTION%type) as
begin
  update TEST_DATA_1 t1 –
     set t1.DESCRIPTION = description_new /* Обновление записи */
   where t1.TEST_DATA_1_ID = test_data_1_id;
exception
  when others then
    /* Блок перехвата ошибок */
    null;
end e_test_data_1_upd_description;
/

/* Пример вызова 2 варианта процедуры с ошибкой */
declare
begin
  e_test_data_1_upd_description(test_data_1_id    =&gt; 4 /* ID EMPLOYEE = СОТРУДНИК 2 */
                               ,description_new  =&gt; &#x27;ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ ПО СОТРУДНИКУ 2&#x27;);
end;
/

/*
 * Результат изменений
 * Изменены все записи
 */
SELECT T1.TEST_DATA_1_ID AS TEST_DATA_1_ID
      ,T1.TYPE AS TYPE_1
      ,T1.VALUE AS VALUE_1
      ,T1.DESCRIPTION AS DESCRIPTION
  FROM TEST_DATA_1 T1;</pre>
  <figure id="ZLcA" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image21.png" width="773" />
  </figure>
  <p id="rGGj">✅ Безопасный подход заключается в передаче параметра, имя которого не совпадает с именем колонки в таблице:</p>
  <pre id="oTfM">/* Вариант процедуры без ошибки */
create or replace procedure e_test_data_1_upd_description(p_test_data_1_id in TEST_DATA_1.TEST_DATA_1_ID%type
                                                                                                            ,p_description        in TEST_DATA_1.DESCRIPTION%type) as
begin
  update TEST_DATA_1 t1 –
     set t1.DESCRIPTION = p_description /* Обновление записи */
   where t1.TEST_DATA_1_ID = p_test_data_1_id;
exception
  when others then
    /* Блок перехвата ошибок */
    null;
end e_test_data_1_upd_description;
/

/* Пример вызова варианта процедуры без ошибки */
declare
begin
  e_test_data_1_upd_description(p_test_data_1_id =&gt; 4 /* ID EMPLOYEE = СОТРУДНИК 2 */
                               ,p_description        =&gt; &#x27;ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ ПО СОТРУДНИКУ 2&#x27;);
end;
/

/*
 * Результат изменений
 * Изменена 1 требуемая запись
 */
SELECT T1.TEST_DATA_1_ID AS TEST_DATA_1_ID
      ,T1.TYPE AS TYPE_1
      ,T1.VALUE AS VALUE_1
      ,T1.DESCRIPTION AS DESCRIPTION
  FROM TEST_DATA_1 T1;</pre>
  <figure id="gY4p" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image8.png" width="774" />
  </figure>
  <h2 id="vPXf">8. Следи за временем выполнения SQL-запроса</h2>
  <p id="tXjJ">Время, пожалуй, один из самых бесценных ресурсов. Пренебрежение за контролем времени выполнения SQL-запроса приведёт к трате усилий и денег.</p>
  <p id="FgzQ"><strong>Рассмотрим пример «Мониторинг времени выполнения».</strong> Допустим, на уровне базы данных продуктовой среды настроен специальный <a href="https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B8%D0%B3%D0%B3%D0%B5%D1%80_(%D0%B1%D0%B0%D0%B7%D1%8B_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85)" target="_blank">триггер</a>. Его предназначение сводится к следующему:</p>
  <ul id="2L79">
    <li id="v0k9">прерывать сессию, которая выполняется дольше N-минут;</li>
    <li id="bjqQ">сохранить информацию об SQL-запросе в журнал для последующего анализа или постановки на мониторинг.</li>
  </ul>
  <p id="AlBs">Вариант триггера на таблицу с искусственно генерируемой ошибкой в момент обновления данных:</p>
  <pre id="59Du">/* Вариант триггера */
create or replace trigger TEST_DATA_1_AIUDR_PTCL
  after insert or update or delete on TEST_DATA_1
  for each row
begin
  if UPDATING then
    if (:old.test_data_1_id = 5 and :new.description is not null) then
     DBMS_OUTPUT.PUT_LINE(&#x27;Log entry.&#x27;);
     raise_application_error(-20001, &#x27;No Update with id 5 and new description.&#x27;);
     rollback;
    end if;
  end if;
end;
/</pre>
  <p id="cbcl">Специалисту рассказывали про этот триггер. Он проигнорировал это или забыл — и реализовал, поставленную задачу на непродуктовой среде таким образом, что одно из действий выполняется больше N-минут. Передал всё на установку в продуктовую среду. Получилось, что реализованный функционал не работает полностью или частично.</p>
  <p id="T2ky">Вариант процедуры с искусственно завышенным временем выполнения</p>
  <pre id="pYyQ">/* Вариант процедуры */
create or replace procedure e_test_data_1_upd_description(p_test_data_1_id in TEST_DATA_1.TEST_DATA_1_ID%type
                                                      ,p_description    in TEST_DATA_1.DESCRIPTION%type) as
begin
   /* Цикл добавлен для увеличения времени выполнения блока программной логики */
  for indx in 1 .. 1000000 loop
    for cur in (select t1.TYPE  as TYPE_1
                      ,t1.VALUE as VALUE_1
                      ,t2.VALUE as VALUE_2
                 from TEST_DATA_1 t1
                     ,TEST_DATA_2 T2
                where t1.TEST_DATA_1_ID = t2.TEST_DATA_1_ID
                  and t1.TEST_DATA_1_ID = p_test_data_1_id) loop
      null;
    end loop;
  end loop;
/* Блок программной логики */
  update TEST_DATA_1 t1 –
     set t1.DESCRIPTION = p_description /* Обновление записи */
   where t1.TEST_DATA_1_ID = p_test_data_1_id;
end e_test_data_1_upd_description;
/

/* Пример вызова процедуры */
declare
begin
  e_test_data_1_upd_description(p_test_data_1_id =&gt; 5 /* ID EMPLOYEE = СОТРУДНИК 3 */
                               ,p_description        =&gt; &#x27;ДОПОЛНИТЕЛЬНЫЕ ДАННЫЕ ПО СОТРУДНИКУ 3&#x27;);
end;
/</pre>
  <figure id="4KU2" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image4.png" width="486" />
  </figure>
  <p id="SoGa">Задача специалиста смотреть на поставленную задачу шире, учитывая разные аспекты, применяя разные подходы. Можно попробовать оптимизировать SQL-запрос, например, добавляя индексы. Можно менять алгоритмы выполнения действий, добиваясь требуемого результата.</p>
  <h2 id="cqXE">9. Используй копию данных для построения отчётности</h2>
  <p id="Z9pJ">Отчётность — это извлечение массива данных из базы для последующей обработки, аналитики, построения прогноза, прочее. Для неё может извлекаться значительный объём данных.</p>
  <p id="IXYB"><strong>Рассмотрим пример «Отчёт о расходах за период». </strong>У нас есть промышленная среда, на которой развёрнуто приложение с подключением к базе данных. С приложением работают сотрудники. Задачей одних является внесение информации о приходе и расходе денежных средств. Задачей других — подготовка отчёта о расходе денежных средств за период. Информация вносится периодически и в небольшом объёме. Извлекается реже, но вся, что была внесена за конкретный период.</p>
  <p id="EZeo">При ограниченных ресурсах базы данных извлечение может приводить к замедлению работы приложения. Потому что на стороне БД подключаются сотрудники из обеих групп, ресурсы делятся между ними, и отклик происходит медленнее. Избежать подобного эффекта можно при помощи копии базы данных с применением механизма <a href="https://ru.wikipedia.org/wiki/%D0%A0%D0%B5%D0%BF%D0%BB%D0%B8%D0%BA%D0%B0%D1%86%D0%B8%D1%8F_(%D0%B2%D1%8B%D1%87%D0%B8%D1%81%D0%BB%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%B0%D1%8F_%D1%82%D0%B5%D1%85%D0%BD%D0%B8%D0%BA%D0%B0)" target="_blank">репликации</a>. Так, клон клон с определённой периодичностью синхронизируется с основной базой данных (их может быть несколько).</p>
  <p id="K00B">Создание копии базы данных — задача администраторов базы данных (Database administrator, <a href="https://ru.wikipedia.org/wiki/%D0%90%D0%B4%D0%BC%D0%B8%D0%BD%D0%B8%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%BE%D1%80_%D0%B1%D0%B0%D0%B7_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85" target="_blank">DBA</a>). Для большего погружения в механизм репликации можно обратиться к официальной справочной информации соответствующей базы данных. Например:</p>
  <ul id="8siM">
    <li id="vsdM">Oracle —<a href="https://docs.oracle.com/cd/E29542_01/admin.1111/e10029/int_rep.htm#OIDAG011" target="_blank"> Setting Up Replication (oracle.com);</a></li>
    <li id="o4I1">MSSQL —<a href="https://learn.microsoft.com/ru-ru/sql/relational-databases/replication/tutorial-preparing-the-server-for-replication?view=sql-server-ver15&viewFallbackFrom=aps-pdw-2016-au7" target="_blank"> Учебник. Подготовка к репликации — SQL Server | Microsoft Learn;</a></li>
    <li id="29SW">PostgreSQL —<a href="https://postgrespro.ru/docs/postgresql/15/high-availability" target="_blank"> PostgreSQL : Документация: 15: Глава 27. Отказоустойчивость, балансировка нагрузки и репликация : Компания Postgres Professional.</a></li>
    <li id="a2bM">MySQL —<a href="https://dev.mysql.com/doc/refman/8.0/en/replication-setup-replicas.html?ff=nopfpls" target="_blank"> MySQL :: MySQL 8.0 Reference Manual :: 17.1.2.6 Setting Up Replicas</a>.</li>
  </ul>
  <p id="uDu6">Взаимодействие с базой данных можно трансформировать следующим образом. Сотрудники, которые вводят информацию, так и продолжают работать с основной базой данных. Сотрудники, которые заняты отчётностью, работают с её копией. Информационные потоки разведены. Влияние устранено.</p>
  <p id="sDkM">Подход использовать не основную базу данных для отчётности, а её клон (копию) — это рекомендация. И не для любой отчётности требуется создавать копию. Например, если с базой данных работает один сотрудник, который практически не загружает её. Или если у неё много свободных ресурсов.</p>
  <h2 id="nRph">10. Проверяй формат данных</h2>
  <p id="759T">Бывает, что отчёт, который обычно работает хорошо, возвращает ошибку, если ввести другие входные данные. Это связано с тем, что у новых входных данных другой формат.</p>
  <p id="GjTQ"><strong>Рассмотрим пример «Отчёт».</strong> У нас есть отчёт, строящийся на данных, которые заполняются внешним приложением. Одна из его колонок — дата. Поле ввода на форме, в которой происходит её заполнение — строковое. В подавляющем большинстве случаев формат: день числом, месяц числом, год числом, например, 01.01.2001. Изредка — день числом, месяц словом, год числом, например, «1 января 2001».</p>
  <p id="rIHv">Приложение позволяет вводить в любом виде. Конечные пользователи ошибку не видят, но для отчёта это — потенциальная проблема. Она может заключаться в неверном предположении, что дата всегда заносится в базу данных в одном виде.</p>
  <p id="6crK">⚠️ Опасный подход заключается в игнорировании формата используемых данных:</p>
  <pre id="PoRi">SELECT t1.TYPE AS TYPE_1 /* Колонка TYPE из таблицы TEST_DATA_1 */
      ,to_date(t1.VALUE, &#x27;DD.MM.RRRR&#x27;) AS VALUE_1 /* Колонка VALUE из таблицы TEST_DATA_1 */
      ,t2.TYPE AS TYPE_2 /* Колонка TYPE из таблицы TEST_DATA_2 */
      ,t2.VALUE AS VALUE_2 /* Колонка VALUE из таблицы TEST_DATA_2 */
  FROM TEST_DATA_1 t1
      ,TEST_DATA_2 t2
WHERE t1.TEST_DATA_1_ID = t2.TEST_DATA_1_ID
  AND t1.TYPE = &#x27;DATE&#x27;
  AND t2.TYPE = &#x27;DATE&#x27;;</pre>
  <figure id="ttSp" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image2.png" width="491" />
  </figure>
  <p id="GJqw">✅ Безопасный подход заключается в понимании формата используемых данных:</p>
  <pre id="KinU">SELECT t1.TYPE AS TYPE_1 /* Колонка TYPE из таблицы TEST_DATA_1 */
      ,to_date(t1.VALUE, &#x27;DD.MM.RRRR&#x27;) AS VALUE_1 /* Колонка VALUE из таблицы TEST_DATA_1 */
      ,t2.TYPE AS TYPE_2 /* Колонка TYPE из таблицы TEST_DATA_2 */
      ,t2.VALUE AS VALUE_2 /* Колонка VALUE из таблицы TEST_DATA_2 */
  FROM TEST_DATA_1 t1
      ,TEST_DATA_2 t2
WHERE t1.TEST_DATA_1_ID = t2.TEST_DATA_1_ID
  AND t1.TYPE = &#x27;DATE&#x27;
  AND t2.TYPE = &#x27;DATE&#x27;
  AND t2.VALUE = &#x27;Формат день числом, месяц числом, год числом&#x27;;</pre>
  <figure id="GMiv" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/image16.png" width="577" />
  </figure>
  <p id="I41j">Наличие разных данных можно узнать заранее. Для этого, когда делается отчёт, можно выполнить проверку на всех данных, а не только на части. Это — залог стабильной работать и уверенность, что созданный отчёт будет работать.</p>
  <h2 id="OigD">Вспомним, что написано выше, и закрепим правила:</h2>
  <ol id="J3ZG">
    <li id="PDx6">Объявляя имена таблиц, обращайся к записям через имена таблиц.</li>
    <li id="r2fN">Извлекай только те данные, которые планируешь использовать.</li>
    <li id="6q8P">По максимуму используй данные, которые извлёк из таблицы.</li>
    <li id="yrfk">Проверяй запросы SQL на индексы.</li>
    <li id="qyUi">Начинай запрос SQL с таблицы с меньшим набором записей.</li>
    <li id="7wbD">Не допускай декартового произведения между таблицами.</li>
    <li id="rAEB">Проверяй, что имена параметров процедур не совпадают с именами колонок.</li>
    <li id="0G8c">Следи за временем выполнения SQL-запроса.</li>
    <li id="n3zo">Используй копию данных для построения отчётности.</li>
    <li id="kJoU">Проверяй формат данных.</li>
  </ol>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@savichm/1ADVkNAhUzB</guid><link>https://teletype.in/@savichm/1ADVkNAhUzB?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm</link><comments>https://teletype.in/@savichm/1ADVkNAhUzB?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=savichm#comments</comments><dc:creator>savichm</dc:creator><title>Весенний бум: 6 востребованных IT-специальностей</title><pubDate>Sat, 01 Apr 2023 11:12:50 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/e4/84/e484383a-e9d2-4d8c-bbd7-dc967351f4f9.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/de/5e/de5e0a5a-d61a-4a97-a406-3a27537b8249.png"></img>На волне развития искусственного интеллекта топ востребованных IT специальностей претерпел изменения. Собрали для вас список из шести IT-профессий, где остро требуются толковые специалисты.]]></description><content:encoded><![CDATA[
  <p id="M3cr">На волне развития искусственного интеллекта топ востребованных IT специальностей претерпел изменения. Собрали для вас список из шести IT-профессий, где остро требуются толковые специалисты.</p>
  <figure id="HKrO" class="m_original">
    <img src="https://img2.teletype.in/files/de/5e/de5e0a5a-d61a-4a97-a406-3a27537b8249.png" width="1200" />
  </figure>
  <h2 id="B4oc">Computer Vision Engineer</h2>
  <p id="TqDT">Computer Vision Engineer обучает ИИ распознавать объекты и реагировать на них без необходимости в «глазах». Эта отрасль инженерии привлекает не только технарей, но и гуманитариев, ведь чтобы ИИ научился распознавать, большое количество видео и фото обрабатывается вручную, описывая изображенные на них объекты.</p>
  <figure id="eJMf" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/d45ee3be-9108-4b05-92d9-45d56b16922a.jpg" width="960" />
  </figure>
  <p id="caption-attachment-238840"><a href="https://www.maxpixel.net/Biometrics-Scan-Face-Detection-Scanning-Monitoring-4791810" target="_blank">Источник</a></p>
  <p id="O1AF">Разумеется, опыт работы с кодом, способность быстро адаптироваться к новым технологиям и задействовать алгоритмы помогают в достижении успеха в этой отрасли.</p>
  <h2 id="dBoX">Инженер робототехники</h2>
  <p id="zBr6">Инженер-робототехник — это специалист, который разрабатывает архитектуру и вводит в эксплуатацию роботов, приборы и сложные робототехнические системы.</p>
  <p id="i3CW">Они используются для:</p>
  <ul id="Z5ih">
    <li id="tlXv">производственных целей;</li>
    <li id="8gsr">научно-исследовательских целей;</li>
    <li id="eXCw">боевых действий;</li>
    <li id="f4k2">развлечений;</li>
    <li id="0MOd">бытового применения.</li>
  </ul>
  <p id="bR72">Роботы позволяют выполнять рутинную работу и сложные задачи практически без участия человека.</p>
  <p id="OSz3">Инженеры-робототехники могут работать в автоконцернах, авиационных и космических предприятиях, инженерных компаниях и стартапах, занимающихся созданием роботов.</p>
  <h2 id="uZH7">Работа с данными</h2>
  <p id="4MjJ">Компаниям, желающим обойти конкурентов и оптимизировать бизнес-процессы, чтобы получать большую прибыль, потребуются исследования данных. В связи с этим активно растёт спрос на профессионалов таких как аналитик BI, <a href="https://tproger.ru/video/data-science-dlja-nachinajushhih-12-proektov-na-python-za-3-chasa/" target="_blank">Data Scientist</a>, аналитик данных, специалист Machine Learning, etc.</p>
  <h3 id="VCSU">Data Analyst</h3>
  <p id="4Cg3">Data аналитик собирает данные, на основе которых принимается решение. Он оценивает широкий спектр данных, после чего выбирает и обрабатывает те, которые важны для принятия решений. Далее аналитик визуализирует полученные данные, разрабатывает рекомендации и предлагает концепции ведения бизнеса.</p>
  <figure id="QDs0" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/77032fff-a414-4110-97b4-723e99683215.jpg" width="1200" />
  </figure>
  <p id="RtNS">Чтобы стать аналитиком данных, необходимо:</p>
  <ol id="McAz">
    <li id="ga30">Обладать знаниями и навыками в области анализа данных.</li>
    <li id="9gAf">Иметь опыт работы с основными инструментами анализа данных.</li>
    <li id="xIDX">Уметь работать с большим объемом разнородной информации.</li>
  </ol>
  <h3 id="MRr7">Data Engineer</h3>
  <p id="LpgZ">Data engineer — это специалист, отвечающий за сбор, хранение и преобразование данных. Данные используются для создания и обучения моделей искусственного интеллекта (ИИ). При построении пирамиды потребностей ИИ в первую очередь на повестку дня выходят три основных этапа, делающиеся дата-инженером: извлечение, перенос/хранение и анализ/предварительная обработка данных.</p>
  <figure id="6KeN" class="m_custom">
    <img src="https://media.tproger.ru/uploads/2023/03/d8d4dc43-c698-4cc3-a782-16ff510c3f89.png" width="1024" />
  </figure>
  <p id="caption-attachment-238839"><a href="https://mc.today/kto-takoj-data-engineer-skolko-on-zarabatyvaet-i-kak-im-stat/" target="_blank">Источник</a></p>
  <p id="ouqe">Machine Learning Engineer</p>
  <p id="tBbS">ML-инженер — это специалист, который работает в сфере Data Science. Задачи специалиста сводятся к практическому обучению компьютера поиску взаимосвязей в данных и принятию на их основе тех или иных решений.</p>
  <p id="jUlM">Вот лишь некоторые примеры результатов ML-инженерии:</p>
  <ul id="z2vt">
    <li id="E3T2">«умные ленты» в социальных сетях;</li>
    <li id="lYIW">алгоритмы рекомендаций на музыкальных стримингах;</li>
    <li id="ea13">сервисы перевода типа Google Translate;</li>
    <li id="VPz4">боты-помощники типа Олега банка «Тинькофф» и Алисы «Яндекса».</li>
  </ul>
  <h2 id="DkDW">AI Ethicists (Этик ИИ)</h2>
  <p id="mXDf">Для того, чтобы ИИ мог действительно принимать правильные решения, необходимо наличие эксперта по этике ИИ. Он должен понимать и учитывать человеческие чувства, политические взгляды и привязанности, чтобы программировать ИИ так, чтобы тот был нейтральным и при этом понимал, что является правильным или неправильным.</p>
  <p id="WYaC">Этика ИИ даёт нейросети понимание таких сложных этических понятий, как любовь, война, те или иные слова вежливости, etc.</p>
  <p id="v9A1">Да, ИИ всё ещё лишён чувств, поэтому для принятия правильных решений и нужен эксперт по этике ИИ, который понимает и учитывает нюансы, недоступные искусственному интеллекту.</p>

]]></content:encoded></item></channel></rss>