Статьи

Самая недостающая особенность языка SQL

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

Но язык SQL, пожалуй, самый красивый язык программирования. У него так много предостережений, что такие люди, как я, становятся смехотворно богатыми, продавая консалтинговые услуги, просто чтобы объяснить их семантику . Одним из лучших примеров того, как искажен язык SQL, является статья Алекса Боленка о SQL NULL на Tech.Pro .

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

1
2
3
4
5
SELECT   first_name || ' ' || last_name
FROM     customers
WHERE    first_name || ' ' || last_name LIKE 'L% E%'
GROUP BY first_name || ' ' || last_name
ORDER BY first_name || ' ' || last_name

Вы можете написать другой запрос для того же, конечно. Но даже если вы этого не сделали, вас беспокоит, что вряд ли есть какой-либо способ повторно использовать first_name || ' ' || last_name first_name || ' ' || last_name конкатенационное выражение first_name || ' ' || last_name ?

Ну, вы можете, по крайней мере, повторно использовать его в предложении ORDER BY :

1
2
3
4
5
SELECT   first_name || ' ' || last_name AS name
FROM     customers
WHERE    first_name || ' ' || last_name LIKE 'L% E%'
GROUP BY first_name || ' ' || last_name
ORDER BY name

А в MySQL вы также можете использовать его в предложении GROUP BY , хотя мы считаем, что это плохая идея :

1
2
3
4
5
SELECT   first_name || ' ' || last_name AS name
FROM     customers
WHERE    first_name || ' ' || last_name LIKE 'L% E%'
GROUP BY name
ORDER BY name

Стандартные решения SQL для повторного использования столбцов

Конечно, вы можете создать производную таблицу:

1
2
3
4
5
6
7
8
SELECT   name
FROM (
  SELECT first_name || ' ' || last_name
  FROM   customers
) c(name)
WHERE    name LIKE 'L% E%'
GROUP BY name
ORDER BY name

… Или общее табличное выражение, которое в основном является производной таблицей многократного использования, или локальное представление:

1
2
3
4
5
6
7
8
9
WITH c(name) AS (
  SELECT first_name || ' ' || last_name
  FROM   customers
)
SELECT   name
FROM     c
WHERE    name LIKE 'L% E%'
GROUP BY name
ORDER BY name

Но почему мне даже нужно создать таблицу для повторного использования / переименования простого выражения столбца? Почему я не могу просто использовать что-то вроде выражения общего столбца ? Как это?

1
2
3
4
5
6
7
8
-- Common column expression that can be appended
-- to any table expression.
SELECT   name
FROM     customers
WITH     name AS first_name || ' ' || last_name
WHERE    name LIKE 'L% E%'
GROUP BY name
ORDER BY name

Обратите внимание, что выражение общего столбца будет ограничено источником таблицы. Другими словами, это имеет смысл только в контексте предложения FROM и таблиц, указанных в предложении FROM . В каком-то смысле следующие два выражения будут в точности эквивалентны:

01
02
03
04
05
06
07
08
09
10
-- Proposed syntax
FROM     customers
WITH     name AS first_name || ' ' || last_name
 
-- Existing syntax
FROM (
  SELECT customers.*,
         first_name || ' ' || last_name AS name
  FROM   customers
) AS customers

Предложенный синтаксис может также применяться к объединенным таблицам:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
-- Proposed syntax
FROM customers
  AS c
WITH name AS c.first_name || ' ' || c.last_name
JOIN addresses
  AS a
WITH address AS a.street || '\n' || a.city
  ON c.address_id = a.address_id
WITH full_address AS c.name || '\n' || a.address
 
-- Or alternatively, if you don't need to tightly
-- scope these common column expressions to their
-- corresponding tables:
FROM customers AS c
JOIN addresses AS a
  ON c.address_id = a.address_id
WITH name AS c.first_name || ' ' || c.last_name,
     address AS a.street || '\n' || a.city,
     full_address AS name || '\n' || address

Таким образом, в простом английском языке, новое предложение WITH может быть добавлено к любому типу табличного выражения. С круглыми скобками и комментариями первый из приведенных выше примеров будет выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
FROM (
  customers AS c
  -- The "name" column is appended to "c"
  WITH name AS c.first_name || ' ' || c.last_name
)
JOIN (
  addresses AS a
  -- The "address" column is appended to "a"
  WITH address AS a.street || '\n' || a.city
) ON c.address_id = a.address_id
-- The "full_address" column is appended to the
-- above joined table expression
WITH full_address AS c.name || '\n' || a.address

Приведенный выше синтаксис снова будет в точности эквивалентен этому:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
FROM (
  SELECT
    c.*, a.*,
    c.name || '\n' || a.address AS full_address
  FROM (
    SELECT c.*,
           c.first_name || ' ' || c.last_name
    FROM customers AS c
  ) c
  JOIN (
    SELECT a.*,
           a.street || '\n' || a.city
    FROM addresses AS a
  ) a
  ON c.address_id = a.address_id
)

Самая недостающая языковая особенность SQL

Начиная с SQL: 1999 , у нас, к счастью, есть общие табличные выражения — предложение WITH , которое может быть добавлено перед любым SELECT (и другими операторами DML в некоторых диалектах). Используя общие табличные выражения, мы можем избежать повторения часто используемых производных таблиц.

Но такое не возможно для колонн. Таким образом, особенность SQL, которая наиболее необходима, — это обычные выражения столбцов .

Справка: наиболее недостающая функция языка SQL от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ .