Как стать автором
Обновить

Комментарии 60

Что у Тинькова с названиями полей? middle_nm? dozv_flg??? Они серьёзно? Или у них 'a' на клавиатуре отсутствует?

Не работал в Тиньке, но работал много где еще. Такие именования видел, что аж поначалу страшно становилось от этих аббревиатур на 20+ символов.
Но, разумеется, что мешает именовать нормально и без дурацких сокращений, чтоб было понятно по названию колонки, а не часа ковыряний доки?

Хотя кстати иногда лучше даже длинное название дать, но читаемое. Во всяком случае, лучше чем:

* длинное и нечитаемое
* короткое нечитаемое

:D

Автор книги "Чистый код" - Дядя Боб плачет. Вторая глава о наименовании тут совсем не работает)

Ну это не самый плохой вариант, серьезно) Видели и похуже)

что мешает именовать нормально и без дурацких сокращений

Тяжёлое оракловая травма в начале карьеры. До 10 версии оракул точно не разрешал имена длиннее 30 символов. А если ещё и по каким-нибудь правилам у таблиц должны быть определённые префиксы, то вообще караул.

...а там где я работаю, нет доки совсем.
И комментариев в таблицах и полях тоже нет.
И связей между таблицами.
И сокращения в названиях — ууу... Ладно 20 символов, тут хоть иногда можно догадаться о чем речь. А вот 2-3-4 символа это да.

Это вы с обфускацией наименований полей, таблиц, процедур и т.п. не сталкивались. Когда столкнулся (в финтехе тоже) был в шоке долгое время. Совсем как в фильме "Операция Ы". Помните причину почему Ы?

чтобы никто не догадался?

Мы недавно столкнулись с названием полей одной крупной интернет-компании (не будем тыкать пальцем), вот там вообще треш)) Просто tbl_clt_fr_crm_ld_ydx_t_cpn - что-то в таком духе)

****ydx*** :D

Не, это типа рекламные кампании были, Яндекс тут не причем)

*_crm_* приходилось видеть в бд битрикса :(

А то, что микс русской и английской транскрипции вас не смущает?

first_nm + middle_nm + last_nm = fio ?

Почему б не full_nm. И раз уж без гласных, то fll_nm.

fio оно свое, родное)

Такой микс говорит о том, что БД ведет начало от разработчиков из учебных или проектных институтов конца 90-х - начала 2000-х, когда софт-скилл английского не превалировал над хард-скиллом инженера БД :)

Не уверен, что это софт скилл. Когда-то нам достался проект, над которым изначально голландцы работали. И все переменные были на голландском. Вот нам "весело" было.

Структура таблиц в базе данных

Что за разноцветная ерунда? Там что, не верят в UML что ли?

Зато красиво))

так а нам зачем минусы ставят, это же не мы базу рисовали :D

Если уж страховаться от левых значений в dozv_flg, то стоит застраховаться от отсутствия там значений вообще, и избежать деления на ноль

Вот да. И по хорошему, сначала посмотреть бы, а какие там данные вообще могут быть. А то вдруг там и 2, и 3...

И окажется, что мы неправильно понимаем значение этого флага и это, скажем, количество, а не флаг, и надо будет просто ">0" в кейс записывать.

Так таблицы-то на листочке даны)

Разумно

Даже интересно стало, сколько они такому джуну предлагают бабла...

Ну в целом ЗП там неплохие, насколько я знаю. Но там тестовое заданий на 50, так что это не все))

А потому что оператор between потеряет все записи за сегодня, если не преобразовать эти поля в дату (это особенность сравнения даты-времени в PostgreSQL). Опять же - мы просто перестраховались.

Спасибо, мы вам перезвоним. Вы потеряли индекс по дате, условие стало non-sargable.

вдруг там еще какие-то значения могут быть

Даже комментировать не буду.

Я правильно понимаю, что к дате надо было приводить сами граничные значения?

Поговорка времен позднего СССР - "лучше один раз снять дворники с чужой машины, чем каждый день снимать со своей" <==> лучше преобразовать границы условий сравнения в предикате один раз, чем преобразовавать сами данные при сравнении - типичнейшая ошибка вплоть до сеньорного уровня, которая, как сказал автор выше, делает индекс по сравниваемой колонке неприменимым

Мы ждали подобных комментариев) В них рождается истина))

А про "комментировать не буду" - если можно, все-таки прокомментируйте)

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

Комментировать различие между БД и помойкой ("вдруг там еще какие-то значения могут быть") тем более не планировал.

Вы очень счастливый человек, если работаете только в тех компаниях, где БД не помойка, а круто спроектированная штука. Жаль, что в 99% случаев это не так.

Хотя, тогда странно, что вы такой не дружелюбный, раз на работе все так чудесно. Мы вроде до оскорблений не скатывались :)

Вы очень счастливый человек, если работаете только в тех компаниях, где БД не помойка, а круто спроектированная штука. Жаль, что в 99% случаев это не так.
Где неизвестнодаже содержимое полей — 99%? Вы и тут чепуху пишете и упираетесь.

Мы вроде до оскорблений не скатывались
Тут нет вообще никаких оскорблений. А есть толстый намек, что писать в сообщество разработчиков, не имея даже начальных представлений о предмете — очень, очень нелепо и просто неразумно.
  1. Это не наша база. Дана картинка с табличками - решите тестовое. Причем тут вообще мы :)

  2. Если ваши представления настолько глубоки, что вы сходу можете сказать незнакомым людям, что "у них нет даже начальных представлений о предмете" - так может быть вы поделитесь свои опытом и напишете какую-нибудь статейку на тему? :) А то у вас статей 0, а такие "дилетанты" как мы вынуждены заполнять инфополе своим контентом)

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

А то у вас статей 0, а такие «дилетанты» как мы вынуждены заполнять инфополе своим контентом)
Да, это действительно очень плохо. Но да, я статей лучше, чем, например, Брент Озар, Пол Рэндал или, если местами сразу по-русски, то Дмитрия Пилюгина — не напишу. И нет, это совсем не означает, что инфополе нужно гхм… «заполнять» абсолютно безграмотными статейками, лишь бы накрутить свою упоминаемость.

А потому что оператор between потеряет все записи за сегодня, если не преобразовать эти поля в дату (это особенность сравнения даты-времени в PostgreSQL). Опять же - мы просто перестраховались.

Если уж перестраховываться, то лучше привести Now к концу суток (или вместо Now тупо задать 9999 год, как удобнее. Но тут возникает вопрос, не может ли оказаться во входящих данных будущее время).

А если развить мысль, то, при условии нормальных данных, зачем там вообще верхняя граница?

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

Одна дата у кастомера тоже вызывает вопросы - если ставится дата первого обращения, то мау не посчитать, если дата последнего - имеет смысл только мау за последний некалендарный месяц (если кастомер может обращаться в компанию несколько раз)

Почему "одна"? Я так понял, что это таблица всех звонков (call_id) с таймстэмпом начала и окончания.

Таблицы не было в схеме, ок

Все равно большие вопросы - использование неполного текущего месяца потянет среднее вниз, и баг с тем, что используется только номер месяца, и если данные за несколько лет то все одинаковые месяцы склеятся в один

Кстати насчет «MAU всегда должен быть одним числом» хотелось бы поспорить.

В результате вашего запроса получается та самая «средняя температура по больнице».

В случае столбца Месяц — MAU видна динамика продукта, например,

Январь — 5000
Февраль — 7000
Март — 2000
Апрель — 13000
Май — 15000

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

Если же брать MAU как среднее за месяцы, то будет 8400. При этом абсолютно не видна динамика, может это уже остатки от прежних 5000 или наоборот, была 1000, а стало 8400 и это хорошо! Как абсолютное число смысл имеет, но небольшой.

Так мы поэтому и сказали, что это вообще далеко не единственный способ. Медиана, MAU за отдельные месяцы - все это имеет смысл сравнивать. Тогда что-то плюс-минус разумное можно получить.

Во второй задачке я бы вместо case использовал filter, который как-то логически кажется более уместным. Но могу ошибаться.

Вариант, но не для всех СУБД же. Но постгря - так что норм)

select 
	concat_ws('—', first_nm, middle_nm, last_nm) as fio, 
	birth_dt
from employees

Интересно, а как в постгресс ведет себя эта функция, если один из операндов - Null или пустая строка? (Я просто не знаю). Для MSSQLSERVER - все будет гораздо вычурнее:

select 
	Stuff(concat('—' + Nullif(first_nm, ''), '-' + Nullif(middle_nm, ''), '-' + Nullif(last_nm,'')), 1, 1, '') as fio, 
	birth_dt
from employees

У человека может не быть отчества, фамилии или даже имени. В любом сочетании.

select
	start_dttm::date as "date", 
	count(case when dozv_flg=1 then 1 end) /
  	count(case when dozv_flg in (1, 0) then 1 end) as sla
from calls
where start_dttm::date between '2020-10-01' and now()::date
group by start_dttm::date

Не знаю, как в постгрессе (опять же, не специалист), в MSSQLSERVER так делать категорически нельзя. Я про where start_dttm::date

Это не саргабельно. В случае, если по start_dttm есть индекс, ОН НЕ БУДЕТ ЗАДЕЙСТВОВАН, ну, или будет задействован в режиме полного сканирования. Так что но, найн, нихт, отрывать руки по самый афедрон! Еще один момент: Функция count возвращает результат в int. Поделив int на int (а деление будет ЦЕЛОЧИСЛЕННЫМ, т.к. оба операнда - целые) - вы наверняка получите не то, что нужно.
Выглядеть должно так (в MSSQLSERVER):

select
	Convert(date, start_dttm) as "date", 
	(count(case when dozv_flg=1 then 1 end) * 1.0) /
  	count(case when dozv_flg in (1, 0) then 1 end) as sla
from calls
where start_dttm >= '20201001' and start_dttm <= Convert(date, Dateadd(day, 1, Current_timestamp))
group by Convert(date, start_dttm)

Как то так, остальные разборы разберу потом :-)

Про саргабельность - кажется, это тема заслуживает отдельной статьи. Уверены, что на эту позицию вполне допустимо такого не знать - по уровню заданий это понятно.

Но отличная мысль для следующего материала)

Вот так, конечно же start_dttm < Convert(date, Dateadd(day, 1, Current_timestamp)). Не вычитал, прошу прощения.

concat_ws завезли в MS SQL 2017 (https://docs.microsoft.com/en-us/sql/t-sql/functions/concat-ws-transact-sql).

Всё хорошо с null.

concat_ws('-', 'A', 'B', null, 'D') вернёт 'A-B-D'

Еще не было)

А можно психотип человека кто идет после всего в теперь гос компанию? )

Ну это не свежее тестовое, на самом деле.

В следующем кейсе - явная ошибка, причем не технического плана, а именно ошибка реализации:

with a as (
	select 
		to_char(calendar_dt, 'MM') as mon, 
		count(distinct id) as cnt
	from clients 
	group by mon
)
select avg(cnt) as mau
from a

Дело в том, что в таблице clients могут быть данные ЗА РАЗНЫЕ ГОДЫ.

И тогда ваш запрос вернет хрень (простите мой французский).

Запрос должен выглядеть как:

with a as (
	select 
		month(calendar_dt) as mon, 
		count(distinct id) as cnt
	from clients 
	group by year(calendar_dt), month(calendar_dt)
)
select avg(cnt) as mau
from a

Ну, и не забываем, что среднее от int - будет int. А то вдруг вы там десятые ждете? Тогда avg(cnt * 1.0), ну, или явное преобразование к какому либо плавающему или фиксированному типу.

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

Потому что результатом выполнения этого запроса будет одно число, месяцы вы там не увидите

Условие

Дана таблица clinets:

- id клиента

- calendar_at - дата входа в мобильное приложение...

Либо у меня приступ временной слепоты, либо я чего-то не понимаю. В схеме данных НЕТ таблицы clients. По полям похожая - только system...

---

Ну и резануло то, что в тексте задания расхождения по сравнению с кодом (clinets вместо clients, calendar_at вместо calendar_dt).

Или это дополнительный уровень проверки?

Задача 1 ...

Есть ещё проблема, которую Вы просмотрели. Видите ли, бывают люди, не имеющие отчества. В таблице в этом поле может оказаться NULL. И если использовать CONCAT() или оператор конкатенации, то некоторые СУБД вернут итогом NULL (что очевидно неверно), а некоторые отнесутся к нему как к пустой строке, и в конце итогового значения получится ни к чему не пристёгнутый дефис. В отличие от CONCAT_WS(). А если в поле может в описанном случае оказаться пустая строка - то потребуется ещё и NULLIF(). Ну или CASE - для универсальности.

Кстати, PostgreSQL в этом случае вообще оригинал - функция конкатенации CONCAT() и оператор конкатенации || ведут себя по-разному. Для многих это оказывается сюрпризом - иногда неприятным.

Задача 2

Мы просто перестраховались — вдруг там еще какие-то значения могут быть. Например, 2.

По-моему, условие чётко устанавливает, что для любого поступившего звонка значение поля может быть или 0, или 1 (или звонок принят, или нет). Никаких 2, никаких NULL и иных значений там быть не может, если считать условие задания верным. А поскольку непоступившие звонки в таблице присутствовать не могут, то насчёт перестраховки - это Вы совершенно безосновательно придумали.

И, если принять, что условие не содержит ошибок, CASE вообще становится не нужен, а процент считается простейшим SUM(dozv_flg)/COUNT(dozv_flg). Либо SUM(dozv_flg)/COUNT(*). Что выбрать - опять зависит от конкретной СУБД, в некоторых эти выражения могут дать разную итоговую производительность запроса.

where start_dttm::date between '2020-10-01' and now()::date

А это "прощай производительность". Правильно - не приводить поле к дате, чтобы можно было использовать красивый BETWEEN, а where start_dttm >= '2020-10-01' and start_dttm < CURRENT_DATE + INTERVAL '1 day'.

" — " в условии первого задания окружено пробелами (Имя - Фамилия - Отчество), в результате же будет без них: Имя-Фамилия-Отчество.
Ну и да, от нулов не перестраховались.

По поводу названий

иногда возникает ситуация что в витрине не совсем понятно что именно содержится ....
иногда .... ну конечно же ..... иногда ....

а в некоторых случаях плюнул и назвал колонки по Русски - что бы аналитики не приставали с вопросами - А где мне взять то-то ? А что здесь ?

:) вот такой лайф хак :)



Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации