Задачи и решения для бойца PostgreSQL

  • Tutorial

Приветствую всех любителей SQL!

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

Представленные ответы подходят для PostgreSQL (большинство задач подойдут и для других СУБД, но результаты и решения могут быть иными. Даже интересно, где возникнут отличия)

Постарайтесь ответить самостоятельно, перед открытием спойлера.

Поехали!


Буду стараться помечать звездочкой, что-либо сугубо для PostgreSQL* (таких моментов не много)

1. Немного о числовых операциях


1.1 Выполнятся ли эти запросы? Какие результаты они вернут?

-- А) Начнем с простого запроса
SELECT 3/2;
-- Б)
SELECT min('Какой-то текст'::TEXT), avg('Какой-то текст'::TEXT);
-- В)* Почему данный запрос может вернуть FALSE, возможно ли такое поведение СУБД?
SELECT 7.2 = (3.8::FLOAT + 3.4)
-- Г)
SELECT (20/25)*25.0;


Ответы на 1.1
А) Ответ: 1
Будет показана только целая часть, т.к. при операции используются целые числа. Такое часто встречается и в других языках.

Б) Ответ: запрос не выполнится.

avg выдаст ошибку, т.к. принимает только числа и временные интервалы*

Однако функция min / max может выполняться на текстовых данных (в соответствии с алфавитной сортировкой в БД).
Иногда это может быть полезно, когда нужно хотя бы посмотреть на столбец, который не перечислен в GROUP BY
Или когда к числам нужно применить алфавитную сортировку, при которой '10' < '2'

В) Ответ: FALSE

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

Г) Ответ: 0. подвох в том, что выражение в скобках будет =0

SELECT (20/25.0)*25 отработал бы более корректно


1.2 Дана таблица "table_2" (с единственным столбцом "value"(INTEGER)) состоящая из следующих 5 строк:
value
5
5
NULL
5
5

Какой результат вернет запрос:
SELECT (avg(value)*count(*)) - sum(value) FROM table_2;

Варианты ответов
  • -4
  • 0
  • NULL
  • 5
  • Вызовет ошибку, т.к. не указан GROUP BY
  • Ни один из перечисленных


Ответ 1.2
ответ: 5

Агрегатные функции, примененные к конкретному столбцу, игнорируют NULL, однако count(*) посчитает все строки
5 * 5 — 20


2. Общие вопросы


2.1 В каких случаях запрос может вернуть не всё содержимое таблицы? (parent_id INTEGER, таблица наполнена разнообразными данными)

 SELECT * FROM any_table WHERE parent_id = parent_id;

А как поведет себя запрос ниже? Какие данные он выведет? *PostgreSQL

 SELECT * FROM any_table WHERE parent_id IS NOT DISTINCT FROM parent_id;

Ответы на 2.1
Первый запрос покажет все записи, кроме тех, где parent_id является NULL

Второй запрос покажет все записи таблицы. IS DISTINCT FROM по логике похож на оператор != в котором NULL идентичен NULL
IS NOT DISTINCT FROM логически обратит неравенство в равенство

2.2. Какой результат будет у запроса?

-- А)
SELECT * FROM (
    SELECT 1
    UNION ALL
    SELECT 1
    ) x(y)
UNION
(
    SELECT 2
    UNION ALL
    SELECT 2
);

Ответ на 2.2
Результатом будет 2 строки со значениями 1 и 2, UNION удалит все дубликаты в результирующей выборке, а не только между двумя объединяемыми таблицами. Замечал, что не для всех это очевидно.

2.3 Напишите запрос, который покажет завтрашнюю дату.

Ответ на 2.3
SELECT CAST((now()+ INTERVAL '1 DAY') AS DATE)

Не все часто работают с датами, но какой-то минимум освоить стоит
*Решение для Postgres, но думаю другие СУБД не сильно отличаются

Если работа с датами Вам в новинку, то советую поэкспериментировать с запросом
Например:
— заменить DAY на (week, month, year и т.д.)
— заменить +1 на -9000
— заменить DATE на TIME
— убрать CAST
— оставить только NOW()
и т.д.

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


2.4 Операторы UPDATE, DELETE , INSERT и MERGE созданы для манипулирования данными в таблицах. А является ли выполнение SELECT .. «безопасным»? Может ли какой-либо запрос повлиять на данные в таблице?
Ответ на 2.4
Вопрос может показаться примитивным, однако…

В самом начале изучения SQL, у меня складывалось мнение, что этот оператор может только показывать данные, но:

Помимо того, что SELECT способен заблокировать таблицу на изменение (BEGIN; SELECT… FOR UPDATE) *
SELECT способен вызывать функции, которые могут выполнять практически любые манипуляции.

Новичкам нужно это понимать сразу, а не после выполнения «маленького информационного» запроса на Production сервере


3. Only PostgreSQL


3.1 Опишите, что произойдет при выполнении данного запроса в SQL диалоге:

SELECT * INTO wtf FROM pg_stat_activity;

Ответ на 3.1
Обычно SELECT INTO используется в функциях plpgsql, для записи значения в переменную.

Вне plpgsql эффект команды будет аналогичен запросу ниже:

CREATE TABLE wtf AS
 SELECT * FROM pg_stat_activity;


3.2 что покажет данный «простой» запрос

SELECT wtf_ FROM pg_stat_activity AS wtf_ ;

Ответ на 3.2
pg_stat_activity системное представление (VIEW) активных процессов в базе.

Особенность запроса в том, что будет выведен один столбец со строками (ROW) имеющими TYPE pg_stat_activity (или другой таблицы). Знать это нужно скорее тем, кто пишет функции, подробнее можно почитать в мануале
Вопрос добавил потому, что новичок может легко по ошибке получить такой результат, и не понимать в чем дело

4. Работа с текстом. Регулярные выражения


Думаю нужно уметь не только строить запросы, но и представлять результаты в нужном виде.
Регулярные выражения — это отдельная огромная тема, со множеством качественных статей. Поэтому я лишь покажу примеры, без подробных объяснений.

4.1. Допустим, есть таблица "table_5" с текстовым столбцом "X" и множеством разнообразных строк. Каким запросом можно получить любые последние 10 символов каждой строки?

Ответ на 4.1
SQL позволяет придумать массу решений одной и той же задачи, к примеру:
самое простое, что приходит на ум — это right(X,10)
можно использовать регулярное выражение: substring( X, '.{0,10}$' )
можно даже накостылять «извертеться»(во всех смыслах) так: reverse(substring(reverse(X) for 10))


4.2 Имеется таблица «table_6» с текстовым столбцом «X». В таблице содержится одна строка(весь текст только на английском и русском языке):
'Lorem 3 Ipsum 23 standard 7 dummy 17 text Ultimate Answer of Life ?? 777'

А) Напишите запрос, который вернет символы с 42-го по 68-ый из этой строки
Б) Как вытащить только ЗАГЛАВНЫЕ (русские или английские) буквы в строке с помощью SQL?
В) Как посчитать сумму чисел (не цифр) в строке с помощью SQL

SQL набросок
WITH table_6(X) AS(
    SELECT 'Lorem 3 Ipsum 23 standard 7 dummy 17 text Ultimate Answer of Life ?? 777'::TEXT
    )
SELECT X FROM table_6

Ответы на 4.2
 -- ТУТ должен быть WITH из "SQL наброска"
-- А)
SELECT SUBSTRING(LEFT(X,68) FROM 42 ) FROM table_6 -- 1 вариант
SELECT SUBSTRING(X, 42, (68-42)+1) FROM table_6   -- 2 вариант
-- 3 вариант с Вас

-- Б) Все просто, заменяем всё кроме заглавных букв на пустоту
 SELECT regexp_replace(X,'[^A-ZА-ЯЁ]', '','g') FROM table_6 
-- Буква 'Ё' обычно не входит в диапазон А-Я
-- без параметра 'g' замена произойдет лишь 1 раз

-- В) Без регулярных выражений задача может показаться кошмаром
-- С помощью regexp_matches и жадного поиска** получаем массивы чисел в столбик, и суммируем вытащив из массива

SELECT sum(x[1]::INT) 
    FROM (
              SELECT regexp_matches(X,'[0-9]+','g') FROM table_6
              ) AS y(x)
-- * в других СУБД функции могут иначе называться
-- ** если уберем +, то получим сумму цифр (будет браться лишь 1 символ, а не вся максимальная последовательность)


4.3 Как заменить в тексте (ячейке таблицы) все двойные (тройные и более) пробелы на одинарный пробел? (по традиции: таблица "table_7" со столбцом "X") (P.S. достаточно будет написать SELECT возвращающий нужный результат, а не UPDATE table_7 ...)

Ответ на 4.3
WITH table_7(X) AS (SELECT 'Lorem     3    Ipsum 23  standard  7     
  dummy    11
   text'::TEXT)
-- 1 вариант. Заменяем только пробелы (2 и более подряд)
SELECT regexp_replace(X, '( ){2,}', ' ', 'g') FROM table_7

-- 2 вариант. Заменяем все пробельные символы (табуляция, неразрывный пробел, перевод строки и т.д.) на один пробел, даже если эти символы чередуются
SELECT regexp_replace(X, '\s+', ' ', 'g') FROM table_7 

-- Отчаянный вариант! Думаю те, кто когда-либо искал решение подобной задачи, натыкались на такое "изящное" решение. Без использования регулярных выражений..
-- Работает весьма хитро, на любом количестве пробелов, главное, чтобы текст не содержал используемых подстановочных символов
-- Не рекомендую такое использовать, но для разминки ума стоит понять, как оно работает
SELECT replace(replace(replace(X, ' ', '<>'), '><', ''), '<>', ' ') FROM table_7


4.4 Имеется строка "X" в которой допущены опечатки. Вместо русских букв (е, о, с, С ) были использованы внешне похожие на них символы английского алфавита. Произведите замену данных символов с помощью SQL.

P.S. Строка должна содержать только русские символы, и переживать за возможное изменение английских слов не стоит.

(Если возникают трудности с заменой всех символов, то замените хотя бы один)

Пример строки:

X = 'Cтрoитeльствo или рeкoнcтрукция oбъeкта'

Ответ на 4.4
-- Несомненно, Replace(Replace(Replace(.. потрясающее решение, но
-- специальная функция гораздо изящнее для такого случая (1 символ на 1 символ)

SELECT TRANSLATE('Cтрoитeльствo или рeкoнcтрукция oбъeкта', 'Cceo', 'Ссео')

4.5 Напишите запрос, который преобразует строку:
'иВАнОв ИВан иВановиЧ' к виду 'Иванов Иван Иванович'

Ответ на 4.5
-- Все просто, когда имеется такая функция
SELECT initcap('иВАнОв  ИВан  иВановиЧ')
*Возможно в других СУБД имеются аналоги

Бонусное задание для тех, кто справился
Здорово, если есть готовая функция
А сможете преобразовать наоборот? (желательно не теряя отступов).
Возможно задача не типичная, но для развития будет полезна.

'иВАнОв ИВан иВановиЧ' преобразовать к 'иВАНОВ иВАН иВАНОВИЧ'
а инвертировать регистр?

Ответ на бонусное задание
SELECT string_agg(LOWER(LEFT(x,1)) || UPPER(SUBSTRING(x from 2)), ''  ORDER BY rn) 
    FROM 
  (SELECT * FROM regexp_split_to_table('  иВАнОв  ИВан  иВановиЧ
                           4 TesT', '\y') WITH ORDINALITY y(x, rn) ) AS z
-- *Решение для PostgreSQL, но смысл везде такой же
-- Придумал на скорую руку, без использования процедурного языка
-- возможно неуклюжее и не производительное решение, но для данного примера подходит
-- сохраняет все пробельные символы между словами.
-- WITH ORDINALITY нужен для сохранения порядка символов (доступен с версии 9.4)

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

5. Чуть-чуть о транзакциях


Транзакции очень важная вещь в СУБД, достаточно важно понимать основные моменты.

Попробую смоделировать пример:

Допустим, есть таблица «goods» с которой собираются работать два пользователя.
В ней имеется целочисленный столбец discount равный 10 для всех строк.
Настройки базы данных стандартные (READ COMMITTED — чтение зафиксированных данных).

Пользователь User_1 открывает транзакцию, выполняет следующий запрос:

BEGIN;
UPDATE goods
SET discount = discount + 5;

Секундой позже, другой пользователь (User_2)
Выполняет без открытия транзакции почти такой же запрос:
UPDATE goods
SET discount = discount + 10;

Как думаете, что произойдет при следующих раскладах:

А) Какой результат получит User_2, если User_1 оставит транзакцию открытой (т.е. не подтвердит транзакцию / не откатит изменения)?
Что увидит User_1 при запросе:

SELECT discount FROM goods LIMIT 1;

Б) Что произойдет, если User_1 сделает ROLLBACK? Какие результаты получит User_2?

В) Что произойдет, если User_1 сделает COMMIT? Какие результаты получит User_2?

Ответы
Насколько я знаю READ UNCOMMITTED не поддерживается в PostgreSQL, и «грязные» (не подтвержденные) данные прочитать не получится

Ответы будут следующими:

А) Запрос User_2 будет ожидать COMMIT или ROLLBACK от User_1. (запрос словно подвиснет)
User_1 в своей транзакции будет видеть свою версию снимка базы, где discount уже равняется 15

Б) Если User_1 сделает ROLLBACK, то значение discount останется прежним, а следом выполнится запрос User_2, который прибавит 10 к discount и discount будет равен 20

В) Если User_1 сделает COMMIT, то значение discount увеличится на 5, а следом выполнится запрос User_2, который прибавит 10 к discount и discount будет равен 25

Другая версия этой задачи
Немного другая версия задачи 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;
Какие данные окажутся в таблице?

Заключение


Думаю, что затронул достаточно интересные моменты.

Надеюсь, задачи помогут промотивировать начинающих, ведь скучно что-либо изучать без конкретных целей/задач/направлений.

Могу порадоваться за тех, кому было легко ответить на все вопросы. А те, у кого возникали сложности, надеюсь, получили пинок направление развития. Те кто мало что понял, но хочет освоить SQL, приглашаю на свою прошлую статью Курс молодого бойца PostgreSQL.

Жду каких-либо дополнений, решений особо интересных задач(можно своих) и прочих комментариев!

Спасибо за внимание! Желаю успехов в изучении SQL!

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

Была ли полезна статья?
Поделиться публикацией

Похожие публикации

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

    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 можно и в запрос вставить, но Постгрес с этим, к счастью, сам справляется.
      0

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

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

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


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


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

            Еще немного добавлю
            В этой фразе заложен смысл, который не каждый поймет.
            О том что в план запроса, вызвавший функцию, будет встроен текст запроса из нее
            Как будто мы сами прописали этот CASE в запросе, с меньшими накладными расходами, чем «настоящий» вызов функции
              +1
              Именно так.
            +1
            Еще вариант решения бонусного задания
            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
              –5
              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) без всяких указаний интервалов и прочей чепушины.

              Да, вообще вся ваша статья какие-то надуманные грабли и костыли, которых нет в нормальных языках. Просто лень уже стало писать опровержения на каждый ваш пункт — просто пшите на правильных языках и инструментах, и тогда не придется выдумывать себе проблемы на свою… голову.
                +1
                Завтрашний день можно посчитать и как SELECT current_date + 1. Другое дело, что в Постгресе это смотрится как кривоватое исключение, сделанное совместимости ради. Интервалы гораздо приятнее, на самом деле.
                  0
                  Что значит «писать на правильных языках?»
                  Приобрести MS SQL из-за “select 3/2” = 1.5?
                    +1
                    Ну да, ну да, по этому столько крупных компаний использует Postgres.
                      0
                      Какие же еще опровержения вы собрались писать?
                      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 арбузов…
                      Я сам долго ленился учить регулярки, пока не понял, что все достаточно просто.
                        +1
                        Потрясающе!) думаю с этого надо было начать пост! с того как подходить к Вашим примерам.
                        Спасибо за статью! работаю с PostgreSQL давно, и для опытного DWH само собой ничего нового, но я бы гораздо быстрее освоился в начале карьеры, будь тогда такой материал.

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

                          0
                          Спасибо, возможно! Добавил в пример.
                        +2
                        В 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))
                          –2
                          На счет MS SQL — вы правы, просто у меня в настройках стоит коррекция формата SQL, и этот запрос выполняется в моем MS SQL как надо, и возвращает дробь (по дефолту со стандартом мелкомягких — там такой же трабл). Что еще раз говорит о том, что нужно использовать правильный софт и инструменты (или до-настраивать бажные, чтобы не получать такое вот чудо арифметики).

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

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

                            При этом, в том же VB6, который тоже писали ребята из мелкомягких, целочисленное деление, если оно вдруг понадобилось программисту, было реализовано как положено во всех нормальных языках — обратным слэшем \, чтобы явно отличаться от общей операции деления по всем арифметическим правилам. Вот и пойми их там в долине, кто в каком культе состоял и продолжает состоять по сей день.
                              +2
                              Так что в итоге то вы хотели доказать?))
                              Так сильно распинались, кому то гвоздь вбить хотели…
                              +1
                              Нет, ничем подобным 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)
                              Но тем, кто помоложе, гадостям учиться не надо. Не окупается.
                          +1
                          Спасибо! Пинок и правда есть, но достаточно нейтральный, чтобы не опустить руки :)
                          Насколько Вам кажется, что нужна теория по устройству СУБД, чтобы писать нормальные запросы?
                            0
                            Благодарю!
                            Я бы посоветовал начать с теории (и последующей практики):
                            построить таблицы с разными связями, понять зачем нужна нормализация/денормализация и т.п.
                            Так автоматически станет понятно как строить запросы
                            +1
                            Замечательная статья, пришлось поломать голову над некоторыми вопросами. Бонусные задания решил иначе. Автор, скажите, а когда появится новая публикация в таком же стиле, как и эта? Хочется чего-то вроде и серьёзного, но с другой стороны и веселого.
                              0
                              Спасибо!
                              Есть ещё темы, которыми хочется поделиться, и которых хватит не на одну публикацию.
                              Осталось найти время и желание…
                              Если решили принципиально иначе — выкладывайте :)
                              +1
                              Спасибо за публикацию. По задаче 2.1 помнил, что есть особенности с NULL. Пришлось перечитать документацию. Пиши ещё.
                                0
                                Благодарю! Если есть мысли о том, что будет интересно осветить, то пишите. Сюда или в лс:)
                                0
                                Немного другая версия задачи 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;

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

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

                                  Самое читаемое