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

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

Возможно, чуть более простое решение бонусного задания:

CREATE FUNCTION invert_char(s text) RETURNS text AS $$
  SELECT CASE WHEN s  ~ '[[:upper:]]' THEN lower(s) ELSE upper(s) END;
$$ LANGUAGE sql;

SELECT string_agg(invert_char(t),'')
FROM regexp_split_to_table(initcap('иВАнОв ИВан иВановиЧ'),'') AS t;

Case можно и в запрос вставить, но Постгрес с этим, к счастью, сам справляется.

Да, здорово!
Выходит, что в данном примере не обязательно выводить порядок для string_agg? Чтобы ничего не сдвинулось без принудительной сортировки.

Да, тут просто негде порядку нарушиться.

Можно еще для инвертирования регистра использовать функцию translate, тогда решением бонусного задания будет:


select translate(initcap(‘иВАнОв ИВан иВанович’),’АБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯабвгдеёжзиклмнопрстуфхцчшщьыъэюя’,‘абвгдеёжзиклмнопрстуфхцчшщьыъэюяАБВГДЕЁЖЗИКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ‘) as t;
Если ограничиться русским языком, то конечно (:
Почему только русский язык?)
Так это не баг, а фича:)
Меня всегда умиляли подобные подходы к решению
На какие только велосипеды можно не пойти, когда не важно КАК, если главное сейчас и верить что это одноразово
Вот это гениально даже!
SELECT reverse(substring(reverse('start long striiing 12345 end') for 10));

SELECT replace(replace(replace('s   p       a c  e', ' ', '<>'), '><', ''), '<>', ' ');


Case можно и в запрос вставить, но Постгрес с этим, к счастью, сам справляется.

Еще немного добавлю
В этой фразе заложен смысл, который не каждый поймет.
О том что в план запроса, вызвавший функцию, будет встроен текст запроса из нее
Как будто мы сами прописали этот CASE в запросе, с меньшими накладными расходами, чем «настоящий» вызов функции
Именно так.
Еще вариант решения бонусного задания
select 
    array_to_string(array_agg(substring(a from 1 for 1) || upper(substring(a from 2))), ' ')
from
  (select unnest(string_to_array(lower('иВАнОв ИВан иВановиЧ'), ' ')) as a) t
1.1 Любой начинающий программист, думаю, когда первый раз столкнулся с явлением разной обработки чисел с плавающей точкой, в зависимости от микросхемы сопроцессора на его материнке, сломал себе моСк, пока не вычитал, или математически не увидел проблему чисел на разных ПК :) Я столкнулся в басике еще, когда начинал писать, дошел до решения сам, просто сравнив простой пример арифметический на двух разных ПК и увидел что числа разные — тогда и вычитал в фидо каком-то что по разному сопроцессоры работают. С тех пор, все условия равенства только нечеткие и обязательное округление, с указанием точных свойств поведения при округлении чисел с плавающей точкой.

В MySQL и SQL Server нет такой х**ни как целый числа в операциях, поэтому SELECT 3/2 выдает как положено 1.5. Если в Postgre это не так — в голову гвоздь забить разрабу, который это придумал.

2.3 NOW() на сколько я знаю, во ВСЕХ языках возвращает не дату, а дату и время. Дату всегда возвращает функция с текстом DATE в названии (в MySQL это CURDATE()). У любой функции в любом языке есть значение инкремента по умолчанию — для CURDATE это 1 день. Поэтому, запрос следующего дня всегда будет SELECT CAST(CURDATE() + 1 AS DATE) без всяких указаний интервалов и прочей чепушины.

Да, вообще вся ваша статья какие-то надуманные грабли и костыли, которых нет в нормальных языках. Просто лень уже стало писать опровержения на каждый ваш пункт — просто пшите на правильных языках и инструментах, и тогда не придется выдумывать себе проблемы на свою… голову.
Завтрашний день можно посчитать и как SELECT current_date + 1. Другое дело, что в Постгресе это смотрится как кривоватое исключение, сделанное совместимости ради. Интервалы гораздо приятнее, на самом деле.
Что значит «писать на правильных языках?»
Приобрести MS SQL из-за “select 3/2” = 1.5?
Ну да, ну да, по этому столько крупных компаний использует Postgres.
Какие же еще опровержения вы собрались писать?
1.1 Я как пользователь не причем, что PostrgeSQL так работает. Но показываю это, чтобы осветить нюанс.
2.2 Хочу напомнить, что статья не называется «Делай так, как я показываю», а рассчитана на обучение.
Иначе, я бы писал «показать текущую дату можно так „SELECT current_date“, как вы. Это строка, из которой ничего не выжать (кроме current_time, об этом ниже)

Видимо не все понимают посыл статьи, и готовы цепляться за ерунду.
Я не поленюсь пояснить.

На мой взгляд, чтобы обучаться SQL, надо выполнять запросы и „играться“ с ними.

Мой пример прост для понимания и дает место для фантазии.
SELECT CAST((now()+ INTERVAL '1 DAY') AS DATE)

Поясню, как бы я его воспринимал, если бы никогда не работал с датами, но желал бы немного разобраться.
Я бы задался вопросом „А что будет, если?“:
- заменю DAY на (week, month, year и т.д.)
- заменю +1 на -9000
- заменю DATE на TIME
- уберу CAST
- оставлю только NOW()
и т.д.


И, вдохновившись какими-то результатами, отправлюсь читать MANUAL, т.к. там все темы подробно раскрыты…

Еще мне писали:
"Не представляю, для чего может пригодиться считать количество ЗАГЛАВНЫХ (английских) букв?"
Все для того, же… Это просто задача, как в школе о покупке 10 арбузов…
Я сам долго ленился учить регулярки, пока не понял, что все достаточно просто.
Потрясающе!) думаю с этого надо было начать пост! с того как подходить к Вашим примерам.
Спасибо за статью! работаю с PostgreSQL давно, и для опытного DWH само собой ничего нового, но я бы гораздо быстрее освоился в начале карьеры, будь тогда такой материал.

Заголовок спойлера
Даже зарегистрироваля ради того, чтоб лайк поставить, но нельзя(

Спасибо, возможно! Добавил в пример.
В MySQL и SQL Server нет такой х**ни как целый числа в операциях, поэтому SELECT 3/2 выдает как положено 1.5. Если в Postgre это не так — в голову гвоздь забить разрабу, который это придумал.

Это ложь.
Select 3/2 в MSSqlserver вернет 1.
С чего бы возвращать что-то еще, если оба операнда — целочисленные?

2.3 NOW() на сколько я знаю, во ВСЕХ языках возвращает не дату, а дату и время. Дату всегда возвращает функция с текстом DATE в названии (в MySQL это CURDATE()). У любой функции в любом языке есть значение инкремента по умолчанию — для CURDATE это 1 день. Поэтому, запрос следующего дня всегда будет SELECT CAST(CURDATE() + 1 AS DATE) без всяких указаний интервалов и прочей чепушины.

Не нужно говорить за всех.
Для MSSQLSERVER это будет звучать как:
Select dateadd(day, 1, cast(CURRENTTIMESTAMP AS DATE))
На счет MS SQL — вы правы, просто у меня в настройках стоит коррекция формата SQL, и этот запрос выполняется в моем MS SQL как надо, и возвращает дробь (по дефолту со стандартом мелкомягких — там такой же трабл). Что еще раз говорит о том, что нужно использовать правильный софт и инструменты (или до-настраивать бажные, чтобы не получать такое вот чудо арифметики).

На счет даты — в MSSQL инкрементом дневной даты обладает GETDATE(), так, что её можно использовать без суффиксов (но она плохо оптимизируется, вроде, как).
Вообще, мелокмягкие со своими стандартами SQL, меня достали еще в детстве, в своем косом MSAccess. Я тогда уже их люто невзлюбил за реализацию SQL запросов. И они десятилетяими так и продолжают топтать грабли своих косых разработчиков (впрочем, как и с их IE это у них, наверное какой-то культ там).
На счет MS SQL — вы правы, просто у меня в настройках стоит коррекция формата SQL, и этот запрос выполняется в моем MS SQL как надо, и возвращает дробь (по дефолту со стандартом мелкомягких — там такой же трабл). Что еще раз говорит о том, что нужно использовать правильный софт и инструменты (или до-настраивать бажные, чтобы не получать такое вот чудо арифметики).

На счет даты — в MSSQL инкрементом дневной даты обладает GETDATE(), так, что её можно использовать без суффиксов (но она плохо оптимизируется, вроде, как).
Вообще, мелокмягкие со своими стандартами SQL, меня достали еще в детстве, в своем косом MSAccess. Я тогда уже их люто невзлюбил за реализацию SQL запросов. И они десятилетяими так и продолжают топтать грабли своих косых разработчиков (впрочем, как и с их IE это у них, наверное какой-то культ там).

При этом, в том же VB6, который тоже писали ребята из мелкомягких, целочисленное деление, если оно вдруг понадобилось программисту, было реализовано как положено во всех нормальных языках — обратным слэшем \, чтобы явно отличаться от общей операции деления по всем арифметическим правилам. Вот и пойми их там в долине, кто в каком культе состоял и продолжает состоять по сей день.
Так что в итоге то вы хотели доказать?))
Так сильно распинались, кому то гвоздь вбить хотели…
Нет, ничем подобным GETDATE() — не обладает.
docs.microsoft.com/ru-ru/sql/t-sql/functions/getdate-transact-sql?view=sql-server-2017

Вообще, в MSSQLSERVER — все манипуляции с датой и временем — только через функции даты и времени.
Нет, застарелые олдфаги еще помнят, что datetime можно привести к float, и что-то там делать, например — округлить:
SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) AS DATETIME)
Но тем, кто помоложе, гадостям учиться не надо. Не окупается.
Спасибо! Пинок и правда есть, но достаточно нейтральный, чтобы не опустить руки :)
Насколько Вам кажется, что нужна теория по устройству СУБД, чтобы писать нормальные запросы?
Благодарю!
Я бы посоветовал начать с теории (и последующей практики):
построить таблицы с разными связями, понять зачем нужна нормализация/денормализация и т.п.
Так автоматически станет понятно как строить запросы
Замечательная статья, пришлось поломать голову над некоторыми вопросами. Бонусные задания решил иначе. Автор, скажите, а когда появится новая публикация в таком же стиле, как и эта? Хочется чего-то вроде и серьёзного, но с другой стороны и веселого.
Спасибо!
Есть ещё темы, которыми хочется поделиться, и которых хватит не на одну публикацию.
Осталось найти время и желание…
Если решили принципиально иначе — выкладывайте :)
Спасибо за публикацию. По задаче 2.1 помнил, что есть особенности с NULL. Пришлось перечитать документацию. Пиши ещё.
Благодарю! Если есть мысли о том, что будет интересно осветить, то пишите. Сюда или в лс:)
Немного другая версия задачи 13 от пользователя kirill_petrov на особенность READ COMMITTED
-- Имеется таблица с двумя строками
CREATE TABLE goods (discount) AS
 (SELECT 10::INT
 UNION ALL
 SELECT 15);

-- 1. User_1 выполняет запрос (транзакция остается открыта):
BEGIN;
UPDATE goods
SET discount = discount + 5;

--2. User_2 выполняет запрос:
UPDATE goods
SET discount = discount + 100
WHERE discount = 15

--3. User_1 выполняет 
COMMIT;

Какие значения окажутся в таблице?
Хороший пример! Думаю тут мало кто даст верный ответ
Да, замечательная задача. Вот таких бы ещё!
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории