SQL - використання псевдоніму в групі


143

Просто цікаво про синтаксис SQL. Тож якщо я маю

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY itemName, FirstLetter

Це було б неправильно, оскільки

GROUP BY itemName, FirstLetter 

насправді повинно бути

GROUP BY itemName, substring(itemName, 1,1)

Але чому ми не можемо просто використати колишній для зручності?


13
це дозволено в Postgresql
Майкл Буен

7
MySQL також дозволяє це
Kip

1
про які rdbms ви говорите?
Shiwangini

Відповіді:


292

SQL реалізується так, ніби запит виконувався в такому порядку:

  1. З пункту
  2. ДЕ ЗАКЛАД
  3. Стаття GROUP BY
  4. ПЕРЕГЛЯД ДІЯЛЬНОСТІ
  5. Пункт SELECT
  6. ЗАМОВЛЕННЯ за пунктом

Для більшості реляційних систем баз даних цей порядок пояснює, які імена (стовпці чи псевдоніми) є дійсними, оскільки вони повинні бути введені на попередньому кроці.

Отже, в Oracle і SQL Server ви не можете використовувати термін у пункті GROUP BY, який ви визначаєте в пункті SELECT, оскільки GROUP BY виконується перед пропозицією SELECT.

Однак є винятки: MySQL і Postgres, здається, мають додаткову розумність, яка це дозволяє.


3
Мені подобається це пояснення. Хоча я не можу припустити, як важко додати його до двигуна як синтаксичний цукор.
Haoest

11
Будь-яка ідея, якщо БД достатньо розумний, щоб реалізувати той самий вираз, є в пунктах SELECT і GROUP BY без повторної оцінки виразів? тобто, якщо є GROUP BY substring(itemName, 1,1), чи достатньо розумною є база даних, щоб не сприймати результативність повторного обчислення підрядка в пункті SELECT?
Кіп

10
У пункті SELECT запиту з групуванням ви маєте доступ лише до виразів GROUP BY та агрегованих значень. Тож справа не в тому, щоб бути розумним; це має бути реалізовано таким чином, щоб групування працювало. (І цього вимагає стандарт SQL). Але навіть у більш тривіальних випадках (наприклад, той самий вираз у WHERE та пункт SELECT), сучасні системи баз даних, безумовно, обчислять його лише один раз. Така оптимізація називається загальним усуненням суб-виразів .
Кодо

6
Що має відношення до наказу про виконання? Це не так, як запитувач намагався згрупуватися по COUNT (). Насправді, запит, який ви задали, працює чудово в MySQL та, ймовірно, PostgreSQL, як зазначено в коментарях.

1
Для mysql, sql_modeне враховуючи ONLY_FULL_GROUP_BY в біт-масці, оптимізатор має шанс надати кращі результати при різноманітному / різному використанні псевдоніму в HAVINGпункті.
Дрю

28

Ви завжди можете використовувати підзапит, щоб ви могли використовувати псевдонім; Звичайно, перевірте працездатність (можливо, сервер db запустить і те, і інше, але ніколи не завадить перевірити):

SELECT ItemName, FirstLetter, COUNT(ItemName)
FROM (
    SELECT ItemName, SUBSTRING(ItemName, 1, 1) AS FirstLetter
    FROM table1
    ) ItemNames
GROUP BY ItemName, FirstLetter

2
Слід уникати суб-запитів, коли це можливо через погану ефективність. Використання копії функції набагато краще, оскільки вона, звичайно, виявляється оптимізатором бази даних і робиться лише один раз.
Роланд

1
@Roland, але план виконання не відрізняється в цьому випадку. Чи є якесь інше врахування ефективності?
Гвідо Моча

@Roland, Співвіднесені підзапити або інший синтаксис, який призводить до циклів чи поведінки рядків за рядком, слід уникати, і обмеження щодо глибини ви повинні пройти з вкладеними підзапити, але, як правило, невірно, що підзапроси ведуть до поганих показників. У цьому випадку, як сказав Кріс, ви можете перевірити план виконання (план запитів AKA, пояснити план), порівнюючи і підзапит, і без нього, і побачити, чи дійсно є якась різниця. Практично кожен двигун бази даних перепише ваш запит, щоб ви не повністю контролювали те, що виконується. У цьому суть декларативного синтаксису.
Давос

16

Принаймні, у PostgreSQL ви можете використовувати номер стовпця в наборі результатів у пункті GROUP BY:

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY 1, 2

Звичайно, це починає боліти, якщо ви робите це інтерактивно, і ви редагуєте запит, щоб змінити кількість чи порядок стовпців у результаті. Але все ж.


GROUP BY FirstLetterдозволено в Postgresql. Для дотепності спробуйте запустити це у Postgresql: виберіть підрядник (ім'я таблиці, 1,2) як tname з групи information_schema.tables за tname
Майкл Буен

1
@MichaelBuen Мені здається потенційно проблематичним. З швидкого тесту виглядає так, ніби є псевдонім і стовпець базової таблиці з такою ж назвою, останній отримує пріоритет? SQL Fiddle . Тож якщо покладатися на цю групу псевдонімом, пізніша зміна схеми може мовчки порушити ваш запит і змінити семантику.
Мартін Сміт

@MartinSmith тільки зараз знав, що це ґутча, утримається від використання цього, дякую. Зважаючи на те, що PostgreSQL дозволяє цей ярлик, вони повинні надавати пріоритет псевдоніму, інакше вони взагалі не повинні допускати цей ярлик.
Майкл Буен

Це була жахлива ідея дизайнерів PostgreSQL. Це заплутано, як тільки ви намагаєтесь GROUP BYбудь-який вираз, що містить сукупні функції або функції вікна, який "очевидно" не працює.
Лукас Едер

13

SQL Server не дозволяє посилатися на псевдонім у пункті GROUP BY через логічний порядок обробки. Запит GROUP BY обробляється перед пропозицією SELECT, тому псевдонім невідомий, коли оцінюється пункт GROUP BY. Це також пояснює, чому ви можете використовувати псевдонім у пункті ЗАМОВЛЕННЯ ПО.

Ось одне джерело інформації про фази логічної обробки SQL Server .


8

Я не відповідаю, чому це так, але лише хотів показати спосіб подолання цього обмеження в SQL Server, використовуючи CROSS APPLYдля створення псевдонім. Потім ви використовуєте це в GROUP BYпункті, як-от так:

SELECT 
 itemName as ItemName,
 FirstLetter,
 Count(itemName)
FROM table1
CROSS APPLY (SELECT substring(itemName, 1,1) as FirstLetter) Alias
GROUP BY itemName, FirstLetter

4

Застережте, що використання псевдоніму в групі By (для служб, які його підтримують, таких як постгреси) може мати непередбачувані результати. Наприклад, якщо ви створили псевдонім, який вже існує у внутрішньому висловлюванні, група "Вибір" обрать ім'я внутрішнього поля.

-- Working example in postgres
select col1 as col1_1, avg(col3) as col2_1
from
    (select gender as col1, maritalstatus as col2, 
    yearlyincome as col3 from customer) as layer_1
group by col1_1;

-- Failing example in postgres
select col2 as col1, avg(col3)
from
    (select gender as col1, maritalstatus as col2,
    yearlyincome as col3 from customer) as layer_1
group by col1;

3

Деякі СУБД дозволять вам використовувати псевдонім замість того, щоб повторювати весь вираз.
Терадата є одним із таких прикладів.

Я уникаю порядкового позначення положення, як рекомендував Білл, з причин, зафіксованих у цьому питанні .

Проста та надійна альтернатива - завжди повторювати вираз у пункті GROUP BY.
DRY НЕ застосовується до SQL.


1

Остерігайтеся використання псевдонімів при групуванні результатів з представлення даних у SQLite. Ви отримаєте несподівані результати, якщо ім’я псевдоніму буде таким самим, як ім'я стовпця будь-яких базових таблиць (до представлень.)


0

Ще в той день, коли я виявив, що Rdb, колишній DEC-продукт, який зараз підтримується Oracle, дозволив псевдонім стовпців використовувати в GROUP BY. Основний Oracle через версію 11 не дозволяє використовувати псевдонім стовпців у групі BY. Не впевнені, що Postgresql, SQL Server, MySQL тощо дозволять чи не дозволять. YMMV.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.