SQL-лайфхак.
Реальная задача с одного из проектов (немного упрощена для понимания):
Есть таблица с уроками пользователей, которые они проходят.
Необходимо было вычитать уникальные айдишники уроков, по которым нет записей у определенного пользователя, но есть записи у других. Например, при таких данных:
Для пользователя 1233 запрос должен был вернуть 13, 14, 18, потому что эти уроки он не проходил.
Первое, что приходит на ум, это сделать подзапрос
Но во-первых, к подзапросам всегда было внутреннее отторжение, так как они тяжело читаются и меня не покидают сомнения в их производительности. А во-вторых, в реальном примере все это сопровождалось несколькими джойнами как в основном запросе, так и в подзапросе, что еще больше усугубляло перечисленное.
В процессе решения я все никак не мог вспомнить лайфхак для таких случаев. Точно помнил, что он есть и даже в чем его примерный смысл, но не хватало последнего элемента пазла. Что делать в таких случаях? Расчехлять GPT, на этот раз китайский - без регистрации, смс и VPN.
Первым делом он тоже предложил подзапрос, но услышав в ответ
Кидайте свой вариант решения без подзапроса в комменты или смотрите лайфхак ниже:
Лефтджойним эту же таблицу, только с указанием текущего пользователя и находим строки, у которых он не проставился. Профит.
SELECT DISTINCT lesson_id from user_lessons ul1
LEFT JOIN user_lessons ul2 on ul1.lesson_id = ul2.lesson_id AND ul1.user_id = 1233
WHERE ul2.user_id IS NULL
______________________________
@Backend простым языком
Реальная задача с одного из проектов (немного упрощена для понимания):
Есть таблица с уроками пользователей, которые они проходят.
CREATE TABLE user_lessons(
user_id INTEGER NOT NULL,
lesson_id INTEGER NOT NULL
);
Необходимо было вычитать уникальные айдишники уроков, по которым нет записей у определенного пользователя, но есть записи у других. Например, при таких данных:
user_id | lesson_id
1233 | 12
2645 | 12
5823 | 13
5823 | 14
3123 | 18
Для пользователя 1233 запрос должен был вернуть 13, 14, 18, потому что эти уроки он не проходил.
Первое, что приходит на ум, это сделать подзапрос
SELECT DISTINCT lesson_id from user_lessons where lesson_id NOT IN (
SELECT DISTINCT lesson_id from user_lessons where user_id = 1233
)
Но во-первых, к подзапросам всегда было внутреннее отторжение, так как они тяжело читаются и меня не покидают сомнения в их производительности. А во-вторых, в реальном примере все это сопровождалось несколькими джойнами как в основном запросе, так и в подзапросе, что еще больше усугубляло перечисленное.
В процессе решения я все никак не мог вспомнить лайфхак для таких случаев. Точно помнил, что он есть и даже в чем его примерный смысл, но не хватало последнего элемента пазла. Что делать в таких случаях? Расчехлять GPT, на этот раз китайский - без регистрации, смс и VPN.
Первым делом он тоже предложил подзапрос, но услышав в ответ
how about not using subquery?
, выдал необходимое решение 🎯Кидайте свой вариант решения без подзапроса в комменты или смотрите лайфхак ниже:
SELECT DISTINCT lesson_id from user_lessons ul1
LEFT JOIN user_lessons ul2 on ul1.lesson_id = ul2.lesson_id AND ul1.user_id = 1233
WHERE ul2.user_id IS NULL
______________________________
@Backend простым языком
👍5
Нотации.
Краткий ликбез по нотациям - способам именование пременных, папок и тд - в программировании.
Самые популярные, их название и одновременно пример:
-
-
-
-
В Golang (да и везде) в коде обычно используется camelCase. Для наименования файлов и папок мы в команде используем snake_case.
В реляционной бд для названия таблиц и колонок обычно всегда используют snake_case. В любом другом регистре насколько я помню при запросах придется брать название таблицы/колонки в кавычки, а при snake_case в этом необходимости нет.
______________________________
@Backend простым языком
Краткий ликбез по нотациям - способам именование пременных, папок и тд - в программировании.
Самые популярные, их название и одновременно пример:
-
camelCase
-
snake_case
-
PascalCase
-
kebab-case
В Golang (да и везде) в коде обычно используется camelCase. Для наименования файлов и папок мы в команде используем snake_case.
В реляционной бд для названия таблиц и колонок обычно всегда используют snake_case. В любом другом регистре насколько я помню при запросах придется брать название таблицы/колонки в кавычки, а при snake_case в этом необходимости нет.
______________________________
@Backend простым языком
👍6
PostgreSQL timestamp.
TL;DR Всегда используйте для дат timestamp with time zone
Ситуация: В базе есть колонка updated_at, тип которой timestamp, а значение по умолчанию CURRENT_TIMESTAMP.
Проблема: Вычитываем значение этой колонки, сравниваем с time.Now(), и оказывается, что в колонке лежит время +3 часа по сравнению с тем, что дает гошный time. То есть в часовом поясе Москвы, притом без указания таймзоны, что лишает нас возможности перевести time в ту же часовую зону для корректного сравнения времен.
Неожиданно оказалось, что CURRENT_TIMESTAMP при конвертировании в timestamp сохраняет текущее время не в UTC, как можно было бы ожидать, а в часовом поясе клиента, который инициировал запрос (теряя при этом инфу о таймзоне). А если клиентский он не знает, то в часовом поясе сервера, на котором он находится.
Решение:
- Если у вас маленькая база, то накатить миграцию с изменением типа колонки
- Если база большая и не хочется мучаться с аккуратным переездом на новый тип, при сохранении даты явно указывать таймзону timezone('UTC', CURRENT_TIMESTAMP)
- В будущем всегда использовать timestamp with time zone
______________________________
@Backend простым языком
TL;DR Всегда используйте для дат timestamp with time zone
Ситуация: В базе есть колонка updated_at, тип которой timestamp, а значение по умолчанию CURRENT_TIMESTAMP.
Проблема: Вычитываем значение этой колонки, сравниваем с time.Now(), и оказывается, что в колонке лежит время +3 часа по сравнению с тем, что дает гошный time. То есть в часовом поясе Москвы, притом без указания таймзоны, что лишает нас возможности перевести time в ту же часовую зону для корректного сравнения времен.
Неожиданно оказалось, что CURRENT_TIMESTAMP при конвертировании в timestamp сохраняет текущее время не в UTC, как можно было бы ожидать, а в часовом поясе клиента, который инициировал запрос (теряя при этом инфу о таймзоне). А если клиентский он не знает, то в часовом поясе сервера, на котором он находится.
Решение:
- Если у вас маленькая база, то накатить миграцию с изменением типа колонки
- Если база большая и не хочется мучаться с аккуратным переездом на новый тип, при сохранении даты явно указывать таймзону timezone('UTC', CURRENT_TIMESTAMP)
- В будущем всегда использовать timestamp with time zone
______________________________
@Backend простым языком
👍8
Хочешь добавить прогон юнит-тестов в гитлабе при создании мерж-реквеста и подозреваешь, что джоба не будет падать, если тесты не прошли
Раньше:
- Гуглишь, копаешься на стаковерфлоу, страдаешь - минимум минут 10 и больше
Сейчас:
- Спрашиваешь GPT, получаешь ответ - менее минуты (остальные 9 пишешь об этом пост в канал)
______________________________
@Backend простым языком
Раньше:
- Гуглишь, копаешься на стаковерфлоу, страдаешь - минимум минут 10 и больше
Сейчас:
- Спрашиваешь GPT, получаешь ответ - менее минуты (остальные 9 пишешь об этом пост в канал)
______________________________
@Backend простым языком
👍5😁4
Отложенный выстрел в ногу в SQL
Нашел в коде примерно следующий запрос:
То есть получаем айдишники книги, их картинки и имена авторов, которые хранятся в другой таблице. Запрос рабочий, вроде нет проблем, да? До поры до времени.
Сегодня он рабочий, потому что колонка img_url есть только в таблице books, а колонка name только в таблице authors. А вот айдишник есть в обеих таблицах, поэтому мы были вынуждены прописать явно из какой таблицы его брать.
Завтра (или через год) коллеге прилетает задача, что у авторов тоже есть лицо и мы хотим сохранять в таблицу их картинки. Он смело идет и первым делом добавляет в таблицу authors колонку img_url, а далее раскатывает на прод, ведь что можем быть безобиднее, чем просто добавить никого не трогающую новую колонку.
Но не тут то было. В другом конце кода лежит заряженный SQL запрос, который только и ждет, когда спустят курок. С добавлением в таблицу authors колонки img_url, наш запрос выше сломается, так как теперь неясно, какая именно колонка img_url в нем подразумевается. По итогу запрос будет выдавать ошибку
Мораль: если вы в запросе джойните таблицы, всегда в селекте указывайте, откуда брать каждую из колонок
Запрос на предохранителе👇
______________________________
@Backend простым языком
Нашел в коде примерно следующий запрос:
SELECT
books.id,
img_url,
name
FROM
books
JOIN
authors
ON
books.author_id
=
authors.id;
То есть получаем айдишники книги, их картинки и имена авторов, которые хранятся в другой таблице. Запрос рабочий, вроде нет проблем, да? До поры до времени.
Сегодня он рабочий, потому что колонка img_url есть только в таблице books, а колонка name только в таблице authors. А вот айдишник есть в обеих таблицах, поэтому мы были вынуждены прописать явно из какой таблицы его брать.
Завтра (или через год) коллеге прилетает задача, что у авторов тоже есть лицо и мы хотим сохранять в таблицу их картинки. Он смело идет и первым делом добавляет в таблицу authors колонку img_url, а далее раскатывает на прод, ведь что можем быть безобиднее, чем просто добавить никого не трогающую новую колонку.
Но не тут то было. В другом конце кода лежит заряженный SQL запрос, который только и ждет, когда спустят курок. С добавлением в таблицу authors колонки img_url, наш запрос выше сломается, так как теперь неясно, какая именно колонка img_url в нем подразумевается. По итогу запрос будет выдавать ошибку
ERROR:
column
reference
“img_url”
is
ambiguous
Мораль: если вы в запросе джойните таблицы, всегда в селекте указывайте, откуда брать каждую из колонок
Запрос на предохранителе👇
SELECT
b.id,
b.img_url,
a.name
FROM
books
b
JOIN
authors
a ON
b =
a;
______________________________
@Backend простым языком
👍12
This media is not supported in your browser
VIEW IN TELEGRAM
Tab-chaos.
Хочу поделиться подходом к работе с вкладками в редакторе кода, о котором я узнал много лет назад и с тех пор не представляю себе, как можно работать иначе.
Все очень просто: ограничьте в редакторе количество активных вкладок до одной. Как говорил Брайан Трейси: “Я обещаю, это изменит вашу жизнь”. Больше не придется постоянно контролировать этот таб-хаус, клацать “Close other tabs”, искать среди тысячи открытых ту самую. Одна активная вкладка в один момент времени. Все.
А как же тогда находить предыдущие вкладки?
Множество открытых вкладок не сильно помогает в решении этой проблемы. К предыдущим можно вернуться через cmd+tab, но еще лучше настроить хоткей на возврат к предыдущим/следующим положениям курсора - еще один маст хэв в работе.
______________________________
@Backend простым языком
Хочу поделиться подходом к работе с вкладками в редакторе кода, о котором я узнал много лет назад и с тех пор не представляю себе, как можно работать иначе.
Все очень просто: ограничьте в редакторе количество активных вкладок до одной. Как говорил Брайан Трейси: “Я обещаю, это изменит вашу жизнь”. Больше не придется постоянно контролировать этот таб-хаус, клацать “Close other tabs”, искать среди тысячи открытых ту самую. Одна активная вкладка в один момент времени. Все.
А как же тогда находить предыдущие вкладки?
Множество открытых вкладок не сильно помогает в решении этой проблемы. К предыдущим можно вернуться через cmd+tab, но еще лучше настроить хоткей на возврат к предыдущим/следующим положениям курсора - еще один маст хэв в работе.
______________________________
@Backend простым языком
👍7
Делюсь с вами крайне полезным инструментом, который судя по всему далеко не нов, но умудрялся обходить меня стороной. PlantUML - (грубо говоря) язык разметки, который позволяет текстом описывать разные диаграммы (sequence, activity, etc.) и генерить их представление с помощью плагинов. Особенно незаменимая вещь в крупных компаниях, где ценится документирование своей работы и в целом подобные артефакты.
Как начать пользоваться:
- Кидаете кусок кода в chatGPT с запросом generate me plantUML sequence diagram for code below
- Сохраняете результат к себе в проект и редактируете под нужды
- Profit
На сайте есть хорошая документация с множеством примеров, советую ознакомиться.
______________________________
@Backend простым языком
Как начать пользоваться:
- Кидаете кусок кода в chatGPT с запросом generate me plantUML sequence diagram for code below
- Сохраняете результат к себе в проект и редактируете под нужды
- Profit
На сайте есть хорошая документация с множеством примеров, советую ознакомиться.
______________________________
@Backend простым языком
👍11
Какой язык программирования выбрать бэкендеру или почему я советую golang? Часть 1.
Осторожно: Статья супер субъективная. Когда ты пишешь на каком-то языке, создается ощущение, что весь мир крутится вокруг него, а остальные языки умирают (кроме php, там мир тебе напомнит, как обстоят дела), поэтому объективным быть в данном вопросе тяжело.
Теперь по поводу Golang. Предлагаю определиться с основными требованиями при выборе языка программирования. На мой взгляд от языка ожидается, чтобы:
1. Он приносил деньги - количество вакансий, уровень зарплат
2. Его относительно легко можно было освоить - обучающие материалы, курсы, отсутствие исторической нагроможденности языка ненужными возможностями
3. Был современным - тут тоже можно смотреть на вакансии, отсутствие жестких ограничений, использование крупными компаниями
______________________________
@Backend простым языком
Осторожно: Статья супер субъективная. Когда ты пишешь на каком-то языке, создается ощущение, что весь мир крутится вокруг него, а остальные языки умирают (кроме php, там мир тебе напомнит, как обстоят дела), поэтому объективным быть в данном вопросе тяжело.
Теперь по поводу Golang. Предлагаю определиться с основными требованиями при выборе языка программирования. На мой взгляд от языка ожидается, чтобы:
1. Он приносил деньги - количество вакансий, уровень зарплат
2. Его относительно легко можно было освоить - обучающие материалы, курсы, отсутствие исторической нагроможденности языка ненужными возможностями
3. Был современным - тут тоже можно смотреть на вакансии, отсутствие жестких ограничений, использование крупными компаниями
______________________________
@Backend простым языком
👍4🤩1
Какой язык программирования выбрать бэкендеру или почему я советую golang? Часть 2.
Golang как по мне отлично подходит по всем пунктам:
1. С деньгами проблем нет, за него платят как минимум не меньше, чем за конкрутентов. Количество вакансий по ощущениями только растет.
2. Язык изначально заточен под легкость освоения, эта цель стояла у истоков его создания. Он сильно ограничен по количеству ключевых слов, притом не в ущерб возможностям. Есть проблема, что в русскоязычном пространстве ты не найдешь тонны курсов на любой вкус, но это может быть и плюсом.
3. Go создан в 2009ом году, то есть является довольно молодым, притом прогрессивно развивающимся языком, за которым стоит IT гигант Google. В пользу его современности и в целом главным аргументом для меня является то, что крупные компании переходят на него с других языков. Я думаю не нужно быть техническим директором, чтобы понимать: если крупная компания принимает решение о выборе нового основного языка, это должно быть чем-то крайне обоснованным и перспективным, так как сулит большие затраты и риски. Список компаний, ныне использующих Golang можете найти в поисковике, но вот короткий список известных мне компаний, перешедших на него:
- Авито - с PHP на Golang
- Wildberries - с C# на Golang
- Goods (ныне МегаМаркет) - c Nodejs на Golang
Решать в конечном счете вам самим. Главное помнить, что язык это только инструмент, его можно сменить в любой момент, и не стоит думать, что это выбор на всю жизнь.
______________________________
@Backend простым языком
Golang как по мне отлично подходит по всем пунктам:
1. С деньгами проблем нет, за него платят как минимум не меньше, чем за конкрутентов. Количество вакансий по ощущениями только растет.
2. Язык изначально заточен под легкость освоения, эта цель стояла у истоков его создания. Он сильно ограничен по количеству ключевых слов, притом не в ущерб возможностям. Есть проблема, что в русскоязычном пространстве ты не найдешь тонны курсов на любой вкус, но это может быть и плюсом.
3. Go создан в 2009ом году, то есть является довольно молодым, притом прогрессивно развивающимся языком, за которым стоит IT гигант Google. В пользу его современности и в целом главным аргументом для меня является то, что крупные компании переходят на него с других языков. Я думаю не нужно быть техническим директором, чтобы понимать: если крупная компания принимает решение о выборе нового основного языка, это должно быть чем-то крайне обоснованным и перспективным, так как сулит большие затраты и риски. Список компаний, ныне использующих Golang можете найти в поисковике, но вот короткий список известных мне компаний, перешедших на него:
- Авито - с PHP на Golang
- Wildberries - с C# на Golang
- Goods (ныне МегаМаркет) - c Nodejs на Golang
Решать в конечном счете вам самим. Главное помнить, что язык это только инструмент, его можно сменить в любой момент, и не стоит думать, что это выбор на всю жизнь.
______________________________
@Backend простым языком
👍10🤔2🤩2
Forwarded from archakov_blog
Сегодня Израиль (оккупированная территория Палестины) проводит невиданный миру за последние 50 лет геноцид палестницев, и весь "современный" мир закрывает на это глаза! 🇵🇸
Израильские террористы наносят авиаудары по больницам, где находятся больные дети, женщины, старики и через час заявляют, что это сделали сами палестинцы!
Людям навязывается диктат покинуть свои дома и в то же время наносятся авиаудары по мирной колонне!
Израильтяне оставили сегодня палестинцев без воды, света, интернета [1] [2] и медикаментов! При этом люди оказались в полной изоляции, без возможности бежать в соседние страны.
Я понимаю, что это канал про IT и как вы могли заметить, я никогда ранее не затрагивал политические темы, но речь сейчас идёт не о политике, а о преступлении против человечества. И хотите отписывайтесь от меня, хотите нет, но не поделиться этим я не могу!
(на фото так называемые "террористы", слабонервным не смотреть ⚠️)
Израильские террористы наносят авиаудары по больницам, где находятся больные дети, женщины, старики и через час заявляют, что это сделали сами палестинцы!
Людям навязывается диктат покинуть свои дома и в то же время наносятся авиаудары по мирной колонне!
Израильтяне оставили сегодня палестинцев без воды, света, интернета [1] [2] и медикаментов! При этом люди оказались в полной изоляции, без возможности бежать в соседние страны.
Я понимаю, что это канал про IT и как вы могли заметить, я никогда ранее не затрагивал политические темы, но речь сейчас идёт не о политике, а о преступлении против человечества. И хотите отписывайтесь от меня, хотите нет, но не поделиться этим я не могу!
(на фото так называемые "террористы", слабонервным не смотреть ⚠️)
👍20⚡1
Forwarded from JM-APPS | серия приложений
О мусульмане, дуа за нашу Гaззy, дуа за мусульман! О Аллaх, пусть зeмля прoвалится под нoгaми этиx прoклятыx яхудов! Амин
📢 📢 📢
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23
Короткий совет вместо длинного поста:
Создавайте элиасы на айдишники в golang. Как минимум вам самим же легче будет разобраться, где какой айдишник используется и что же это за ключ int64 в мапе.
Еще пример, когда это избавит от потенциальных багов. Имеем функцию
Как можно было сделать лучше? Использовать элиасы:
При такой декларации перепутать аргументы уже куда сложнее 🙂
______________________________
@Backend простым языком
Создавайте элиасы на айдишники в golang. Как минимум вам самим же легче будет разобраться, где какой айдишник используется и что же это за ключ int64 в мапе.
Еще пример, когда это избавит от потенциальных багов. Имеем функцию
func getLesson(userId int64, lessonId int64) (Lesson, error)
При ее вызове легко ошибиться и перепутать местами аргументы. Компилятор на такое не пожалуется, так как оба типа аргументов одинаковые. Как можно было сделать лучше? Использовать элиасы:
func getLesson(userId UserId, lessonId LessonId) (Lesson, error)
При такой декларации перепутать аргументы уже куда сложнее 🙂
______________________________
@Backend простым языком
👍6
Яхья Картоев | Backend простым языком
Короткий совет вместо длинного поста: Создавайте элиасы на айдишники в golang. Как минимум вам самим же легче будет разобраться, где какой айдишник используется и что же это за ключ int64 в мапе. Еще пример, когда это избавит от потенциальных багов. Имеем…
Уточнение по предыдущему посту.
То, о чем я писал, называется Type Definition.
В go 1.19 же оказывается появились Type Alias. При инициализации они отличаются только знаком равно:
Теперь непонятно, как по-русски называть Type Definition, элиасом уже как-то не назовешь. Кастомный тип? Ваши варианты?
______________________________
@Backend простым языком
То, о чем я писал, называется Type Definition.
В go 1.19 же оказывается появились Type Alias. При инициализации они отличаются только знаком равно:
type UserId = int64
. Если рассматривать проблемы, описанные ранее, Type Alias решают только первую - повышение прозрачности кода. Но перепутать аргументы с ними по-прежнему легко, что делает их куда более узко применимыми. Подробнее о различиях тут.Теперь непонятно, как по-русски называть Type Definition, элиасом уже как-то не назовешь. Кастомный тип? Ваши варианты?
______________________________
@Backend простым языком
👍4
Объявляем duration правильно
❌ Плохо:
✅ Хорошо:
В первом случае хоть и стоит комментарий, но:
- на него сходу не падает глаз и ты первые доли секунды не понимаешь, что значит магическое число 720
- если нужно будет сократить количество дней, например до недели, придется считать количество часов в неделе
Во втором случае:
- когда видишь 24 * time.Hour сходу понимаешь, что речь о дне (странно, что нет time.Day), а дальше уже понимаешь, сколько дней. Комментарий дополняет на случай особой непонятливости
- если нужно изменить количество дней - просто меняешь первую цифру на необходимую
❌ Плохо:
const someDuration = 720 * time.Hour // 1 month duration
✅ Хорошо:
const someDuration = 30 * 24 * time.Hour // 1 month duration
В первом случае хоть и стоит комментарий, но:
- на него сходу не падает глаз и ты первые доли секунды не понимаешь, что значит магическое число 720
- если нужно будет сократить количество дней, например до недели, придется считать количество часов в неделе
Во втором случае:
- когда видишь 24 * time.Hour сходу понимаешь, что речь о дне (странно, что нет time.Day), а дальше уже понимаешь, сколько дней. Комментарий дополняет на случай особой непонятливости
- если нужно изменить количество дней - просто меняешь первую цифру на необходимую
👍6🤩4
Безопасное разыменование
Нередко бывает необходимость получить значение по указателю, либо если указателя нет, дефолтное значение типа. Например у тебя в структуре поле типа *string и тебе нужно переложить его в поле другой структуры с типом string
Раньше:
С появлением дженериков это можно спрятать в функцию где-нибудь в /pkg
Нередко бывает необходимость получить значение по указателю, либо если указателя нет, дефолтное значение типа. Например у тебя в структуре поле типа *string и тебе нужно переложить его в поле другой структуры с типом string
Раньше:
var userAddress string
if user.Address != nil {
userAddress = *user.Address
}
return userDto{
address: userAddress
}
С появлением дженериков это можно спрятать в функцию где-нибудь в /pkg
return userDto{
address: Safeder(user.Address)
}
// Safederef safety dereferencing pointer
func Safederef[T any](v *T) T {
var result T
if v != nil {
result = *v
}
return result
}
👍7