У них там что, нет компьютеров общего пользования, в ихних америках, что ли? Или домашних компьютеров, за которыми сидит кто успел - и все под одной учёткой? Или опенспейсов с "готовыми к употреблению" рабочими местами? А если родитель 40 лет от роду ставит ось на комп, купленный для 13-летнего чада, то чей возраст он должен вводить? не, не по логике, а по тексту закона...
Согласен ли ты, что при создании новой not null колонки со значением по умолчанию, значения в строках таблицы не изменяются.
Да, согласен. Изменение метаданных не затрагивает область данных. Я вроде об этом говорил.
А при чтении строк созданных до добавления колонки, значение для колонки возвращается из attmissingval?
И с этим согласен. И об этом я тоже говорил.
я рассуждал с точки зрения потребителя
Ну тогда надо чётко делить - вон там я говорил о внешнем эффекте, а вот тут о технической стороне вопроса. Иначе и возникают подобные непонимания.
Читаю таблицу, получаю одно значение. Меняю метаданные, читаю таблицу ещё раз, получают другое значение.
Вот опять. Читаешь таблицу, и НЕ получаешь поля, о котором говоришь, ибо оно не создано, и его в структуре нет. Меняешь метаданные, создавая поле, читаешь таблицу, и теперь получаешь запись другой структуры, со свежесозданным полем. Это не ДРУГОЕ значение. Потому что твоего "одного значения" - не было, а получение этого "одного значения" - галлюцинация из-за неаккуратной формулировки.
На практике - я не смог обновить attmissingval.
Конечно, это же запрещено. Нет, можно остановить Постгресс, влезть грязными лапами через какой-нить бин-редактор в файл метаданных, поправить значение, снова запустить Постгресс и посмотреть, скушает ли он это изменение и покажет ли теперь изменённое значение. Я, правда, сомневаюсь, и сильно надеюсь, что Постгресс по каким-нибудь внутренним контрольным суммам сумеет обнаружить стороннее изменение и завопит о разрушении данных, но надо проверять.
Блин. Поля НЕ БЫЛО! Ты его только что создал. Если не было поля - не было и значения в этом поле. Вот объясни мне, дураку, как нечто не существующее может ПОМЕНЯТЬСЯ? Поменяться может только существующее значение - некое конкретное или признак отсутствия ака NULL. Было старое значение, стало новое - это называется поменяться. А у тебя значение ПРИСВОЕНО. Что называется, "с нуля". Раньше не было, теперь есть.
И это косвенно подтверждается тем, что значение, записанное в attmissingval, не изменяется при изменении DEFAULT. Потому что либо запись существовала на момент создания поля, и тогда ему было присвоено начальное DEFAULT, но оно не записано в тело записи, и его надо брать из attmissingval, либо запись не существовала и была создана позднее - тогда в тело данных записи сразу было записано значение этого поля, взятое из column_default. И для записи, которая существовала, при любом её изменении будет записано новое состояние этой записи, на сей раз уже с внесённым туда полем и его значением, взятым из attmissingval. И уже по телу записи невозможно определить, была ли запись на момент создания поля, или была вставлена позже.
Ну да ну да. Вот то есть сеть сделана по уму, есть даже сервер удалённой загрузки, и коммутатор где-то имеется на этот класс, причём явно не на ученическом столе, а маршрутизатор (кстати, ROAS) находится посередь класса и включен в клиентскую розетку. При том, что ему (как и коммутатору) в компьютерном классе возле рабочего места вообще нечего делать - он должен находится как минимум в той точке, куда выведены все провода и где стоИт коммутатор..
Я бы сказал, что автору надо бы принять пару таблеточек нетрындина.
ИИ ж не думает, ему тупо нечем. Ему никто не сказал, а сам он не в жисть не догадается, что NOT NULL в описании поля - это просто синтаксический сахар для CONSTRAINT constraint_name CHECK (column_name IS NOT NULL). Вот и галлюцинирует.
Абсолютно всегда. Вы просто как-то валите в одну кучу описание данных и сами данные, чего делать категорически нельзя.
Для новой колонки со значением по умолчанию, "значения поля в конкретной записи" определяется как раз таки метаданными.
Именно. Метаданные отвечают на вопрос "какое значение присвоить, если создаётся новая запись, а значение для поля не указано". тогда как данные отвечают на вопрос "какое значение хранится в данном поле у данной конкретной уже существующей записи".
Теоретически, мы можем поменятьattmissingval и у миллиона строк поменяется значение за O(1)
Нет, нет и ещё раз нет! Значение НЕ поменяется. Потому что и запись, и соответственно значение поля, УЖЕ существуют и УЖЕ хранятся в теле данных таблицы. И им ну совершенно сиренево, что там происходит в метаданных.
Весь вопрос был в том, что это за метаданные, т.к. точно не DEFAULT.
Почему я и сказал, что раскопки в метаданных - это интересно.
DEFAULT-значение поля таблицы - это метаданные таблицы, и хранится это значение в структуре. А вот значение поля в конкретной записи - это уже данные. И хранится это значение в теле таблицы. Поэтому никакие манипуляции с DEFAULT (с метаданными) не могут изменить данные уже существующих записей.
А если мы поле, которое не имело ограничения NOT NULL, угостим таким ограничением, то все существующие записи будут проверены на соответствие ограничению. И при обнаружении не соответствующих ограничению записей в изменении структуры нам будет отказано.
Так что "колонка NOT NULL может вернуть NULL" - это галлюцинация ИИ, в принципе невозможная на практике. Можно было даже не экспериментировать..
А вот раскопки с метаданными - это по делу и интересно.
Порой не помогает от слова "вообще", причём даже в простейших ситуациях. Например, вот в такой:
Таблица, в ней имеется индексированное поле ch_short VARCHAR(16), хранит текстовое шестнадцатиричное представление 128-битных значений, распределение по значениям достаточно равномерное. Плюс куча других полей, в т.ч. достаточно длинных. Всего записей порядка 200 тысяч. Выполняется простейший запрос на поиск значений по первым двум Hex-цифрам (к слову, таких значений менее 1%), причём без отсеивания дубликатов и даже без сортировки:
SELECT ch_short
FROM test
WHERE ch_short >= 'A1' AND ch_short < 'A2';
Статистика обновлена непосредственно перед выполнением запроса. Но вместо Index Only Seek Постгресс упорно выполняет Bitmap Index Scan on idx по Index Cond, потом зачем-то Recheck Cond, и финально Bitmap Heap Scan on public.test. И вот хоть застрелись. Почему ты игнорируешь индекс? Зачем перепроверяешь условие? Зачем лезешь в тело таблицы? Фиг знает.
Если N — это количество групп (ключей), то мы не можем определить сложность, тк не знаем размеры списков
Ну не сказать что корректная фраза. Тем более что пунктом ниже вы сложность-таки определяете. Правильнее, вероятно, сказать, что мы не можем определить сложность при таком выборе N (т.е. не можем определить сложность в зависимости именно от количества групп).
Вот знаете... если человек освоил выборку и фильтрацию, но ни уха ни рыла в группировке - то практически ничего он и не освоил. Это вообще самые основные основы. Или вы всерьёз полагаете, что на проверке валидности данных в таблице БД работа тестировщика заканчивается? А такой фигнёй как "выбрать топ-3 из имеющихся данных" тестировщик имхо вообще не должен заниматься. Я даже представить не могу, как подобная задача коррелирует хоть с каким-то тестированием, только если это не тестирование кандидата на знание основ SQL.
Между тем, что перечислено в статье, и тем, что перечислил я, есть вполне зримая квалификационная граница. А между двумя списками, что в статье, имхо границы нет вообще. Разница между ними лишь в том, дочитан ли букварь по SQL до конца или заброшен на середине.
Любое сравнение с NULL даёт NULL (не TRUE и не FALSE).
Ну-ну... а, между прочим, СУБД у вас в тегах не указана. Так что берём, значит, MySQL (можно и MariaDB), спрашиваем
SELECT NULL <=> NULL AS compare_two_nulls;
и получаем единичку, а вовсе даже не обещанный NULL.
Эту задачу дают постоянно — выучите наизусть.
WHERE NOT EXISTS как минимум не хуже, чем LEFT JOIN WHERE IS NULL. А порой - намного эффективнее.
Задача с собеседования: «Кто потратил больше 10 000₽?»
Незачёт. Вас спросили кто, но не спрашивали сколько.
На собеседовании лучше показать оба способа и объяснить разницу — это покажет глубину понимания.
На собеседовании надо не показывать оба способа (к слову, их куда как больше, чем два), а уточнить задачу, и только после снятия всех неоднозначностей начинать написание запроса.
То же относится и к Задача 4: «Топ-3 покупателя»
Найдите email, которые встречаются больше одного раза.
Решение вообще-то как минимум спорное. Если email может дублироваться, то не вижу, почему не может дублироваться и набор естественных идентифицирующих полей. Различие будет только в значении первичного синтетического ключа.
И это не баг, как вы пишете, а ошибка проектирования. Если email должен быть уникален, то должен существовать соответствующий constraint. А при его существовании обнаружение дублирования уже не просто не баг, а разрушение таблицы данных.
Найдите сумму второго по величине заказа.
Решение - ошибочное. Первые два заказа могут иметь одинаковую сумму.
Задача 3: «Пользователи без заказов за последний месяц»
Найдите пользователей, которые зарегистрированы больше месяца назад, но не сделали ни одного заказа.
Заголовок и условие - это две разные задачи. Зарегистрировался 3 месяца назад, сделал последнюю покупку 2 месяца назад - соответствует условию в названии и не соответствует условию в тексте.
Правило простое: всё, что не внутри агрегатной функции (COUNT, SUM...) — должно быть в GROUP BY.
В некоторых диалектах (и таких становится всё больше) группировка по первичному ключу делает необязательным указание в выражении группировки других полей той же таблицы, даже если они не агрегируются в списке вывода.
Но SQL выполняет в другом порядке:
Я понимаю, что полный порядок формального выполнения запроса вы не нашли - его просто пока вроде бы никто не озаботился опубликовать. Но можно было бы найти и скопипастить хотя бы наиболее полный из имеющихся.
Middle — то, что отличает от джуна
Всё, что ниже - это ещё джун. Middle - это как минимум табличные и оконные функции, хранимые объекты, латеральные запросы и рекурсивные CTE. А по-хорошему - уровни изоляции, блокировки, репликация, хотя бы на уровне понимания основ.
Если мы имеем "идеальную гантелю", и фиксируем пространственные координаты так, что центр находится в центре одного шарика, а плюс икс - в направлении другого, то шарики притягиваются только потому, что мы наблюдаем гантелю не в моменте, а во времени? так, что ли?
А вот тут у вас не лучшее, ну или во всяком случае не универсальное, решение. Потому что вы заявляете "больше", но используете хардкод - ровно три, ни больше ни меньше. В то время как я лично ожидал увидеть универсальное решение - подходящее и для двух, и для трёх, и для четырёх, а в оптимуме - ещё и одновременно. Это было бы красиво...
Жаль, что у вас не Постгресс, он позволяет создать такое универсальное решение достаточно эффективным - на основании данных из отдельных транзакций сгенерировать все возможные сочетания (скажем, в формате массива идентификаторов продуктов), затем по вхождению связать, агрегировать, и провести анализ показателей для полученных групп. Для SQLite и SQL Server придётся делать то же через CSV или там JSON, что будет не так эффективно.
В некоторых культурах это уже 14 лет. А 13 началось в 12 лет и 1 секунду.
У них там что, нет компьютеров общего пользования, в ихних америках, что ли? Или домашних компьютеров, за которыми сидит кто успел - и все под одной учёткой? Или опенспейсов с "готовыми к употреблению" рабочими местами? А если родитель 40 лет от роду ставит ось на комп, купленный для 13-летнего чада, то чей возраст он должен вводить? не, не по логике, а по тексту закона...
Да, согласен. Изменение метаданных не затрагивает область данных. Я вроде об этом говорил.
И с этим согласен. И об этом я тоже говорил.
Ну тогда надо чётко делить - вон там я говорил о внешнем эффекте, а вот тут о технической стороне вопроса. Иначе и возникают подобные непонимания.
Вот опять. Читаешь таблицу, и НЕ получаешь поля, о котором говоришь, ибо оно не создано, и его в структуре нет. Меняешь метаданные, создавая поле, читаешь таблицу, и теперь получаешь запись другой структуры, со свежесозданным полем. Это не ДРУГОЕ значение. Потому что твоего "одного значения" - не было, а получение этого "одного значения" - галлюцинация из-за неаккуратной формулировки.
Конечно, это же запрещено. Нет, можно остановить Постгресс, влезть грязными лапами через какой-нить бин-редактор в файл метаданных, поправить значение, снова запустить Постгресс и посмотреть, скушает ли он это изменение и покажет ли теперь изменённое значение. Я, правда, сомневаюсь, и сильно надеюсь, что Постгресс по каким-нибудь внутренним контрольным суммам сумеет обнаружить стороннее изменение и завопит о разрушении данных, но надо проверять.
Блин. Поля НЕ БЫЛО! Ты его только что создал. Если не было поля - не было и значения в этом поле. Вот объясни мне, дураку, как нечто не существующее может ПОМЕНЯТЬСЯ? Поменяться может только существующее значение - некое конкретное или признак отсутствия ака NULL. Было старое значение, стало новое - это называется поменяться. А у тебя значение ПРИСВОЕНО. Что называется, "с нуля". Раньше не было, теперь есть.
И это косвенно подтверждается тем, что значение, записанное в attmissingval, не изменяется при изменении DEFAULT. Потому что либо запись существовала на момент создания поля, и тогда ему было присвоено начальное DEFAULT, но оно не записано в тело записи, и его надо брать из attmissingval, либо запись не существовала и была создана позднее - тогда в тело данных записи сразу было записано значение этого поля, взятое из column_default. И для записи, которая существовала, при любом её изменении будет записано новое состояние этой записи, на сей раз уже с внесённым туда полем и его значением, взятым из attmissingval. И уже по телу записи невозможно определить, была ли запись на момент создания поля, или была вставлена позже.
Ну да ну да. Вот то есть сеть сделана по уму, есть даже сервер удалённой загрузки, и коммутатор где-то имеется на этот класс, причём явно не на ученическом столе, а маршрутизатор (кстати, ROAS) находится посередь класса и включен в клиентскую розетку. При том, что ему (как и коммутатору) в компьютерном классе возле рабочего места вообще нечего делать - он должен находится как минимум в той точке, куда выведены все провода и где стоИт коммутатор..
Я бы сказал, что автору надо бы принять пару таблеточек нетрындина.
ИИ ж не думает, ему тупо нечем. Ему никто не сказал, а сам он не в жисть не догадается, что
NOT NULLв описании поля - это просто синтаксический сахар дляCONSTRAINT constraint_name CHECK (column_name IS NOT NULL). Вот и галлюцинирует.Абсолютно всегда. Вы просто как-то валите в одну кучу описание данных и сами данные, чего делать категорически нельзя.
Именно. Метаданные отвечают на вопрос "какое значение присвоить, если создаётся новая запись, а значение для поля не указано". тогда как данные отвечают на вопрос "какое значение хранится в данном поле у данной конкретной уже существующей записи".
Нет, нет и ещё раз нет! Значение НЕ поменяется. Потому что и запись, и соответственно значение поля, УЖЕ существуют и УЖЕ хранятся в теле данных таблицы. И им ну совершенно сиренево, что там происходит в метаданных.
Почему я и сказал, что раскопки в метаданных - это интересно.
DEFAULT-значение поля таблицы - это метаданные таблицы, и хранится это значение в структуре. А вот значение поля в конкретной записи - это уже данные. И хранится это значение в теле таблицы. Поэтому никакие манипуляции с DEFAULT (с метаданными) не могут изменить данные уже существующих записей.
А если мы поле, которое не имело ограничения NOT NULL, угостим таким ограничением, то все существующие записи будут проверены на соответствие ограничению. И при обнаружении не соответствующих ограничению записей в изменении структуры нам будет отказано.
Так что "колонка NOT NULL может вернуть NULL" - это галлюцинация ИИ, в принципе невозможная на практике. Можно было даже не экспериментировать..
А вот раскопки с метаданными - это по делу и интересно.
Порой не помогает от слова "вообще", причём даже в простейших ситуациях. Например, вот в такой:
Таблица, в ней имеется индексированное поле ch_short VARCHAR(16), хранит текстовое шестнадцатиричное представление 128-битных значений, распределение по значениям достаточно равномерное. Плюс куча других полей, в т.ч. достаточно длинных. Всего записей порядка 200 тысяч. Выполняется простейший запрос на поиск значений по первым двум Hex-цифрам (к слову, таких значений менее 1%), причём без отсеивания дубликатов и даже без сортировки:
Статистика обновлена непосредственно перед выполнением запроса. Но вместо Index Only Seek Постгресс упорно выполняет Bitmap Index Scan on idx по Index Cond, потом зачем-то Recheck Cond, и финально Bitmap Heap Scan on public.test. И вот хоть застрелись. Почему ты игнорируешь индекс? Зачем перепроверяешь условие? Зачем лезешь в тело таблицы? Фиг знает.
Модель - вот тут.
Ну не сказать что корректная фраза. Тем более что пунктом ниже вы сложность-таки определяете. Правильнее, вероятно, сказать, что мы не можем определить сложность при таком выборе N (т.е. не можем определить сложность в зависимости именно от количества групп).
Я бы всё же использовал WITH TIES. И подогнал данные так, чтобы показать, что это даёт.
Из полученных данных этого не видно без дополнительного расчёта в уме. Следовало бы применить WITH ROLLUP.
Вот знаете... если человек освоил выборку и фильтрацию, но ни уха ни рыла в группировке - то практически ничего он и не освоил. Это вообще самые основные основы. Или вы всерьёз полагаете, что на проверке валидности данных в таблице БД работа тестировщика заканчивается? А такой фигнёй как "выбрать топ-3 из имеющихся данных" тестировщик имхо вообще не должен заниматься. Я даже представить не могу, как подобная задача коррелирует хоть с каким-то тестированием, только если это не тестирование кандидата на знание основ SQL.
Между тем, что перечислено в статье, и тем, что перечислил я, есть вполне зримая квалификационная граница. А между двумя списками, что в статье, имхо границы нет вообще. Разница между ними лишь в том, дочитан ли букварь по SQL до конца или заброшен на середине.
Ну-ну... а, между прочим, СУБД у вас в тегах не указана. Так что берём, значит, MySQL (можно и MariaDB), спрашиваем
и получаем единичку, а вовсе даже не обещанный NULL.
WHERE NOT EXISTSкак минимум не хуже, чемLEFT JOIN WHERE IS NULL. А порой - намного эффективнее.Незачёт. Вас спросили кто, но не спрашивали сколько.
На собеседовании надо не показывать оба способа (к слову, их куда как больше, чем два), а уточнить задачу, и только после снятия всех неоднозначностей начинать написание запроса.
То же относится и к Задача 4: «Топ-3 покупателя»
Решение вообще-то как минимум спорное. Если email может дублироваться, то не вижу, почему не может дублироваться и набор естественных идентифицирующих полей. Различие будет только в значении первичного синтетического ключа.
И это не баг, как вы пишете, а ошибка проектирования. Если email должен быть уникален, то должен существовать соответствующий constraint. А при его существовании обнаружение дублирования уже не просто не баг, а разрушение таблицы данных.
Решение - ошибочное. Первые два заказа могут иметь одинаковую сумму.
Заголовок и условие - это две разные задачи. Зарегистрировался 3 месяца назад, сделал последнюю покупку 2 месяца назад - соответствует условию в названии и не соответствует условию в тексте.
В некоторых диалектах (и таких становится всё больше) группировка по первичному ключу делает необязательным указание в выражении группировки других полей той же таблицы, даже если они не агрегируются в списке вывода.
Я понимаю, что полный порядок формального выполнения запроса вы не нашли - его просто пока вроде бы никто не озаботился опубликовать. Но можно было бы найти и скопипастить хотя бы наиболее полный из имеющихся.
Всё, что ниже - это ещё джун. Middle - это как минимум табличные и оконные функции, хранимые объекты, латеральные запросы и рекурсивные CTE. А по-хорошему - уровни изоляции, блокировки, репликация, хотя бы на уровне понимания основ.
Форматирование - вообще никакое. Неужели трудно посмотреть, что публикуете?
Нечитабельно.
Ну то есть операции "скобка" == "конвертировать в скалярное значение" в Постгрессе нет. Обидно, хотя и ожидаемо.
Чёта какта...
Если мы имеем "идеальную гантелю", и фиксируем пространственные координаты так, что центр находится в центре одного шарика, а плюс икс - в направлении другого, то шарики притягиваются только потому, что мы наблюдаем гантелю не в моменте, а во времени? так, что ли?
If - это, знаете ли, сравнение...
А операция скобка в Постгре не работает? т.е. просто
(o.price * o.quantity) AS v1(subtotal),?Непонятно. Нужно именно указать, какое из них, a или b, больше, или просто получить значение бОльшего из них?
Да, кстати, а числа-то какого хоть типа? каким боком к ним можно прислонять битовые операции?
А вот тут у вас не лучшее, ну или во всяком случае не универсальное, решение. Потому что вы заявляете "больше", но используете хардкод - ровно три, ни больше ни меньше. В то время как я лично ожидал увидеть универсальное решение - подходящее и для двух, и для трёх, и для четырёх, а в оптимуме - ещё и одновременно. Это было бы красиво...
Жаль, что у вас не Постгресс, он позволяет создать такое универсальное решение достаточно эффективным - на основании данных из отдельных транзакций сгенерировать все возможные сочетания (скажем, в формате массива идентификаторов продуктов), затем по вхождению связать, агрегировать, и провести анализ показателей для полученных групп. Для SQLite и SQL Server придётся делать то же через CSV или там JSON, что будет не так эффективно.