Нда, вы меня озадачили. И дело вовсе не в том, что похоже вы лидируете в этом негласном соревновании. Я думаю тут имеет место косяк в планировщике PostgreSQL, по идее (моей), ваш distinct и то что я предложил с max должны были бы давать одинаковый результат по времени. Для меня неочевидно, где max проигрывает. Но вот что я заметил. Похоже, когда я писал статью, я отключил set enable_seqscan to false; Потому что изначально экспериментировал с маленькой таблицей, а потом забыл включить его обратно. А это, оказалось, важно. Тестирование ниже проводилось с выбранным вами индексом ON employees USING btree (department_id, salary DESC) Если set enable_seqscan to false; то method | execution_time ----------+--------------------- distinct | 0.21507000043988228 max | 0.24675499747693538 rank | 0.3294130029976368 window | 0.8126180001497268
А вот если set enable_seqscan to true; -- это по умолчанию method | execution_time ----------+--------------------- rank | 0.3307799963951111 distinct | 0.46438600090146065 max | 0.5562520008087158 window | 0.8275660008192063
И тут rank, внезапно, выходит на первое место (хотя быстрее работать не стал). Очевидно (по времени выполнения), что это ошибка планировщика. Здесь по дефолту стояло random_page_cost=4. Я попытался исправить это выставив random_page_cost=1, что разумно, всё целиком и полностью помещается в кэш ОЗУ. set enable_seqscan to true; set random_page_cost=1; method | execution_time ----------+--------------------- distinct | 0.21147700223326682 max | 0.30990499967336654 rank | 0.32765100106596945 window | 0.8102550018429756
Уже похоже на правду. но не очень. После random_page_cost=1 почти все демонстрируют такие же хорошие результаты как и с set enable_seqscan to false, кроме max. Который чуть-чуть все же работает медленнее и если сравнить планы: Nested Loop -> HashAggregate Group Key: employees.department_id -> Seq Scan on employees -> Index Scan using ds2 on employees e Index Cond: ((department_id = employees.department_id) AND (salary = (max(employees.salary)))) А с отключенным сиквенс сканом Nested Loop -> GroupAggregate Group Key: employees.department_id -> Index Only Scan using ds2 on employees -> Index Scan using ds2 on employees e Index Cond: ((department_id = employees.department_id) AND (salary = (max(employees.salary)))) Видно, что проблема в оптимизаторе планировщика. И теперь я ломаю голову, чего бы еще такого подкрутить в конфиге, чтобы с включенным сиквенс сканом, postgresql все же бы выдавал оптимальный план (с двумя индекс сканами) для метода max. Изменение effective_cache_size не помогает.
По поводу первого довода - контрдоводы. Не все, но некоторые компании практикуют, чтобы главным в проекте (продукте), был product owner, причем ключевое качество для такого по сути начальника это... не иметь вообще никакой профессии. Быть "эффективным менеджером" или бюрократом. Поскольку профессии у него нет, то он особенно сильно боится быть уволенным и панический держится за своё место. Очень сомнительная кадровая политика. Так вот, от таких начальником максимум чего добьёшься это фразы: "Хочу, чтобы всё было круто!" Но если сверху прилетит недовольство, он тут же переобуется и всю вину свалит на тех, кто ниже, т.е. на вас. Но не в этом суть. Представьте себе строго математическую задачу по нахождению элементов из какого-то множества обладающих определённым свойством. Если таких элементов несколько, а вы в ответе напишите только одно, то ответ будет неправильным. Запрос, тем более такой, в пяток строк, это не софтверная библиотека. Вот софтверную библиотеку там да, надо проектировать с взглядом на будущее. А проектировать запрос в пять строк с прицелом, как вы его будите переписывать под другие задачи, чтобы код был универсальным и reusable. Ну нет, выглядит гротескно. И дело даже не в количестве строк. Другое дело если бы вы мутили функцию, тогда да, можно было бы подумать об более широком её использовании в будущем.
Почему? Если посмотрите направо, то там такая же. Тянется от к первичного ключа к foreign key и у foreign key значок бесконечность. Так и тут. У каждого босса может быть много сотрудников. Подумайте и вы поймете.
Ну нет. Даже на "один раз запустить" можно по быстрому накидать такое решение, которое положит всю базу данных, точнее не её, а микросервис, он перестанет отвечать на запросы. Как сказал один мой знакомый: "я пишу говнокод, зато делаю это очень быстро". Не надо так делать в любом случае. Что же касается задачи, то если ответ условиям задачи соответствует, то он должен быть принят. Нужен другой ответ, изменяйте условия задачи. Например вместо сотрудника с максимальной зарплатой попросить вывести топ 3 было бы корректно, хотя и там можно было бы решить не через rank(). Но кандидат бы показал, что он умеет. А вот "угадай, какой вариант я знаю, там есть оконные функции", это уже неправильная постановка задачи. Такие задачи надо задавать не на собеседовании на тех.должность, а на битве экстрасенсов.
За "Гости из будущего" (если я не ошибаюсь) респект, а за "помучил", повторю еще раз. На собеседовании вы будите показывать то, как вы реально собираетесь работать, свои рабочие навыки. За демонстрацию того, что в армии называется "тупить", вы можете услышать "спасибо, было очень интересно" очень быстро. :)
Из всех собеседований два стоят особняком, то в Национальный Российский Депозитарий и в Промсвязьбанк, через который проходит гос. и оборонный заказ. Обе конторы официально крышует ФСБ. И только там на собеседовании собеседовали мне нагло и беспардонно врали, самым уверенным голосом. Например в НРД вешали лапшу на уши, что если в столбце одно значение преобладает 100, то он считается "нерепрезентативным" и его postgresql никогда в индексированном поиске использоваться не будет. Я возражал, что не бывает нерепрезентативных столбцов, всё дело в конкретном значении и Postgresql не использует поиск по 100 потому что он находится в списке наиболее часто встречаемых значений. И для доказательства своей правоты предложил вместо 100 выбрать 1. На что собеседователь сверился с какой-то табличкой и выбрал 5, как я понимаю еще одно число из списка наиболее часто встречаемых значений и тем самым "доказал" мою неправоту. А еще там меня убеждали, что если в процедуре встречается begin end, то это "анонимный блок" и входя в него postgresql всегда открывает вложенную транзакцию. В Промсвязьбанке, меня убеждали, что если написать выражение, например: with cte as (select * from table) delete from cte; то оно нормально отработает и якобы он сам лично это протестировал и так работает начиная с версии 9.4. Разумеется ложь. Парадокс в том, что жулики занимают ведущие, руководящие посты в тех.отделах именно там, где ФСБ следит, чтобы этого не было. Другими словами значимых для государства конторах. А в обычных коммерческих фирмах я ни разу не встречал ничего подобного, разная степень знаний, но всегда адекватное поведение, никаких наглых развешиваний лапши. Так что с вашими талантами нести пургу, идите туда. Там вы отлично впишитесь в коллектив.
Как бы да, ваш вариант хорош. Но например при приёме на работу в государственные или окологосударственные структуры, которые крышует ФСБ и в которые есть реальный интерес у вражеских разведок внедрить своих людей, там накладывается ограничение, что им важно видеть, что именно этот человек реально решает задачу, а не группа поддержки из вражеских разведок. :) Да даже если это коммерческий банк, у него такой же интерес, но быть может уже против агентов из ОПГ. Так что дать задачку на дом это встречается очень редко. Чаще собеседование должно проводится онлайн, а иногда и требуют обязательно видеть твоё лицо при этом. :)
Ну смотрите, статья на самом деле не про то, какой самый эффективный алгоритм для решения поиска строки с максимальным значением. Как в комментах мудро заметили, вообще нет смысла заморачиваться с оптимизацией, если нет выборки реальных данных. Ваш "вырожденный" случай с одним отделом означает что в предприятии вообще отделов нет, а это уже противоречит условию задачи. Статья была про другое, о чём я и написал в выводах статьи. Но если вам интересно очень тщательно изучить алгоритмы поиска строки с максимальным значением, вы можете это сделать сами.
Ну смотрите. В отделе два сотрудника с максимальной зарплатой. Вы выводите только одного. Причем случайного. Как вы объясните, почему у вас возникло такое "усмотрение"? Потому что так вам было ленивее и проще? :) Даже не смотря на то, что вы придумали какое-то "негласное правило на собеседовании", которого нет, на собеседовании вы демонстрируете именно то, как вы потом и будите работать. Если так, то умный человек вас на работу не возьмёт.
Так я ничего не говорил плохого про код или функционал. А вот документация иногда несовершенна, все же программистам больше нравится писать код, а не обычный текст. Нравилось бы писать текст, стали бы писателями. Но её исправляют, если предложить как.
Я же не просто рассказал, как было на собеседовании, тут вы могли бы придумать, что это я всё нафантазировал, всё было по другому, уж вы то точно знаете, вас там не было. Это хрестоматийная задача, которая есть и на Stack Overflow (с лидирующим решением как раз через rank()) и я привел ссылку на оригинал в блоге JitBit. Последнее я даже перевёл и выложил на Хабр.
Разница по времени небольшая, но всё равно говорит о том, что max() можно оптимизировать до такой же скорости.
Нда, вы меня озадачили. И дело вовсе не в том, что похоже вы лидируете в этом негласном соревновании. Я думаю тут имеет место косяк в планировщике PostgreSQL, по идее (моей), ваш distinct и то что я предложил с max должны были бы давать одинаковый результат по времени. Для меня неочевидно, где max проигрывает. Но вот что я заметил. Похоже, когда я писал статью, я отключил
set enable_seqscan to false;
Потому что изначально экспериментировал с маленькой таблицей, а потом забыл включить его обратно. А это, оказалось, важно. Тестирование ниже проводилось с выбранным вами индексом
ON employees USING btree (department_id, salary DESC)
Если
set enable_seqscan to false;
то
method | execution_time
----------+---------------------
distinct | 0.21507000043988228
max | 0.24675499747693538
rank | 0.3294130029976368
window | 0.8126180001497268
А вот если
set enable_seqscan to true; -- это по умолчанию
method | execution_time
----------+---------------------
rank | 0.3307799963951111
distinct | 0.46438600090146065
max | 0.5562520008087158
window | 0.8275660008192063
И тут rank, внезапно, выходит на первое место (хотя быстрее работать не стал). Очевидно (по времени выполнения), что это ошибка планировщика. Здесь по дефолту стояло random_page_cost=4. Я попытался исправить это выставив random_page_cost=1, что разумно, всё целиком и полностью помещается в кэш ОЗУ.
set enable_seqscan to true; set random_page_cost=1;
method | execution_time
----------+---------------------
distinct | 0.21147700223326682
max | 0.30990499967336654
rank | 0.32765100106596945
window | 0.8102550018429756
Уже похоже на правду. но не очень. После random_page_cost=1 почти все демонстрируют такие же хорошие результаты как и с set enable_seqscan to false, кроме max. Который чуть-чуть все же работает медленнее и если сравнить планы:
Nested Loop
-> HashAggregate
Group Key: employees.department_id
-> Seq Scan on employees
-> Index Scan using ds2 on employees e
Index Cond: ((department_id = employees.department_id) AND (salary = (max(employees.salary))))
А с отключенным сиквенс сканом
Nested Loop
-> GroupAggregate
Group Key: employees.department_id
-> Index Only Scan using ds2 on employees
-> Index Scan using ds2 on employees e
Index Cond: ((department_id = employees.department_id) AND (salary = (max(employees.salary))))
Видно, что проблема в оптимизаторе планировщика.
И теперь я ломаю голову, чего бы еще такого подкрутить в конфиге, чтобы с включенным сиквенс сканом, postgresql все же бы выдавал оптимальный план (с двумя индекс сканами) для метода max. Изменение effective_cache_size не помогает.
Может он по запаху перегара от студента ориентировался.
Это значит, что ошибка в дашборде, надо там исправлять. Исправлять надо там, где "не подумали".
По поводу первого довода - контрдоводы. Не все, но некоторые компании практикуют, чтобы главным в проекте (продукте), был product owner, причем ключевое качество для такого по сути начальника это... не иметь вообще никакой профессии. Быть "эффективным менеджером" или бюрократом. Поскольку профессии у него нет, то он особенно сильно боится быть уволенным и панический держится за своё место. Очень сомнительная кадровая политика. Так вот, от таких начальником максимум чего добьёшься это фразы: "Хочу, чтобы всё было круто!" Но если сверху прилетит недовольство, он тут же переобуется и всю вину свалит на тех, кто ниже, т.е. на вас.
Но не в этом суть. Представьте себе строго математическую задачу по нахождению элементов из какого-то множества обладающих определённым свойством. Если таких элементов несколько, а вы в ответе напишите только одно, то ответ будет неправильным.
Запрос, тем более такой, в пяток строк, это не софтверная библиотека. Вот софтверную библиотеку там да, надо проектировать с взглядом на будущее. А проектировать запрос в пять строк с прицелом, как вы его будите переписывать под другие задачи, чтобы код был универсальным и reusable. Ну нет, выглядит гротескно. И дело даже не в количестве строк. Другое дело если бы вы мутили функцию, тогда да, можно было бы подумать об более широком её использовании в будущем.
Видимо у вас очень убедительный голос.
Почему? Если посмотрите направо, то там такая же. Тянется от к первичного ключа к foreign key и у foreign key значок бесконечность. Так и тут. У каждого босса может быть много сотрудников. Подумайте и вы поймете.
Как говорили древние хакеры: "RTFM".
https://www.postgresql.org/docs/current/index.html
Ну нет. Даже на "один раз запустить" можно по быстрому накидать такое решение, которое положит всю базу данных, точнее не её, а микросервис, он перестанет отвечать на запросы. Как сказал один мой знакомый: "я пишу говнокод, зато делаю это очень быстро". Не надо так делать в любом случае.
Что же касается задачи, то если ответ условиям задачи соответствует, то он должен быть принят. Нужен другой ответ, изменяйте условия задачи. Например вместо сотрудника с максимальной зарплатой попросить вывести топ 3 было бы корректно, хотя и там можно было бы решить не через rank(). Но кандидат бы показал, что он умеет. А вот "угадай, какой вариант я знаю, там есть оконные функции", это уже неправильная постановка задачи. Такие задачи надо задавать не на собеседовании на тех.должность, а на битве экстрасенсов.
По поводу первого тезиса аргумент не принимается. Запросы надо писать с нуля под задачу, а не костылить старые под "похожую" задачу.
А по поводу вашего варианта, уже было.
https://habr.com/ru/articles/828728/comments/#comment_27040894
За "Гости из будущего" (если я не ошибаюсь) респект, а за "помучил", повторю еще раз. На собеседовании вы будите показывать то, как вы реально собираетесь работать, свои рабочие навыки. За демонстрацию того, что в армии называется "тупить", вы можете услышать "спасибо, было очень интересно" очень быстро. :)
Из всех собеседований два стоят особняком, то в Национальный Российский Депозитарий и в Промсвязьбанк, через который проходит гос. и оборонный заказ. Обе конторы официально крышует ФСБ. И только там на собеседовании собеседовали мне нагло и беспардонно врали, самым уверенным голосом.
Например в НРД вешали лапшу на уши, что если в столбце одно значение преобладает 100, то он считается "нерепрезентативным" и его postgresql никогда в индексированном поиске использоваться не будет. Я возражал, что не бывает нерепрезентативных столбцов, всё дело в конкретном значении и Postgresql не использует поиск по 100 потому что он находится в списке наиболее часто встречаемых значений. И для доказательства своей правоты предложил вместо 100 выбрать 1. На что собеседователь сверился с какой-то табличкой и выбрал 5, как я понимаю еще одно число из списка наиболее часто встречаемых значений и тем самым "доказал" мою неправоту. А еще там меня убеждали, что если в процедуре встречается begin end, то это "анонимный блок" и входя в него postgresql всегда открывает вложенную транзакцию.
В Промсвязьбанке, меня убеждали, что если написать выражение, например:
with cte as (select * from table) delete from cte; то оно нормально отработает и якобы он сам лично это протестировал и так работает начиная с версии 9.4. Разумеется ложь.
Парадокс в том, что жулики занимают ведущие, руководящие посты в тех.отделах именно там, где ФСБ следит, чтобы этого не было. Другими словами значимых для государства конторах. А в обычных коммерческих фирмах я ни разу не встречал ничего подобного, разная степень знаний, но всегда адекватное поведение, никаких наглых развешиваний лапши.
Так что с вашими талантами нести пургу, идите туда. Там вы отлично впишитесь в коллектив.
Видать забылось. Или учились на другого. Ссылку на методичку я привел выше.
Как бы да, ваш вариант хорош. Но например при приёме на работу в государственные или окологосударственные структуры, которые крышует ФСБ и в которые есть реальный интерес у вражеских разведок внедрить своих людей, там накладывается ограничение, что им важно видеть, что именно этот человек реально решает задачу, а не группа поддержки из вражеских разведок. :) Да даже если это коммерческий банк, у него такой же интерес, но быть может уже против агентов из ОПГ. Так что дать задачку на дом это встречается очень редко. Чаще собеседование должно проводится онлайн, а иногда и требуют обязательно видеть твоё лицо при этом. :)
Ну смотрите, статья на самом деле не про то, какой самый эффективный алгоритм для решения поиска строки с максимальным значением. Как в комментах мудро заметили, вообще нет смысла заморачиваться с оптимизацией, если нет выборки реальных данных. Ваш "вырожденный" случай с одним отделом означает что в предприятии вообще отделов нет, а это уже противоречит условию задачи.
Статья была про другое, о чём я и написал в выводах статьи. Но если вам интересно очень тщательно изучить алгоритмы поиска строки с максимальным значением, вы можете это сделать сами.
Ну смотрите. В отделе два сотрудника с максимальной зарплатой. Вы выводите только одного. Причем случайного. Как вы объясните, почему у вас возникло такое "усмотрение"? Потому что так вам было ленивее и проще? :) Даже не смотря на то, что вы придумали какое-то "негласное правило на собеседовании", которого нет, на собеседовании вы демонстрируете именно то, как вы потом и будите работать. Если так, то умный человек вас на работу не возьмёт.
Вас научили врать твёрдым и уверенным голосом? :)
Так я ничего не говорил плохого про код или функционал. А вот документация иногда несовершенна, все же программистам больше нравится писать код, а не обычный текст. Нравилось бы писать текст, стали бы писателями. Но её исправляют, если предложить как.
Я же не просто рассказал, как было на собеседовании, тут вы могли бы придумать, что это я всё нафантазировал, всё было по другому, уж вы то точно знаете, вас там не было. Это хрестоматийная задача, которая есть и на Stack Overflow (с лидирующим решением как раз через rank()) и я привел ссылку на оригинал в блоге JitBit. Последнее я даже перевёл и выложил на Хабр.
Даже боюсь предположить, где у вас "задний end". У меня есть только передний.