select * vs select column


124

Якщо мені просто потрібно 2/3 стовпців, і я запитую SELECT * замість надання цих стовпців у виборі запиту, чи є погіршення продуктивності щодо більш / менш вводу / виводу або пам'яті?

Мережеві накладні витрати можуть бути наявними, якщо я не виберу * без потреби.

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

Якщо він завжди тягне за собою кортеж, то накладні введення / вивільнення однакові.

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

Отже, якщо це так, виберіть деякийКолонка матиме більше витрат на пам'ять, ніж у вибору *


Чи є конкретні RDBMS, про які ви питаєте? Можливо, як SELECTвиконання / обробка запитів відрізняється від бази даних до бази даних.
Lèse majesté

10
На відміну від PostgreSQL, якщо ви скажете CREATE VIEW foo_view AS SELECT * FROM foo;, а потім додайте стовпці до таблиці Foo, ці стовпці автоматично не відображатимуться у foo_view, як очікувалося. Іншими словами, *в цьому контексті розширюється лише один раз (на час створення перегляду), а не на SELECT. Через ускладнення, що виникають із-за СТОЛІТНОГО ТАБЛИЦЯ, я б сказав, що (на практиці) *вважається шкідливим.
Joey Adams

@JoeyAdams - не лише PostgresQL, це також поведінка Oracle.
APC

1
@OMG Ponies: Я не знав про подібний пост. Однак це насправді не просто. @ Lèse majesté: Я говорю про Generic RDBMS. не про якогось конкретного постачальника @Joey Adams: Хм, я знаю, що * небезпечно. просто хочу обговорити питання щодо продуктивності.
Ніл Басу

Відповіді:


31

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

Він завжди тягне кортеж, тому що (у всіх постачальників RDBMS, з якими я знайомий), основна структура дискового зберігання для всього (включаючи дані таблиці) заснована на визначених Сторінках вводу / виводу (у SQL Server, наприклад, кожна сторінка є 8 кілобайт). І кожне читання або запис вводу-виводу - це Сторінка. Тобто, кожне записування чи читання - це повна Сторінка даних.

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

ВИЗНАЧЕННЯ. Єдине місце, де Select *все гаразд, - у підзапиті після пункту Existsабо Not Existsпредиката, як у:

   Select colA, colB
   From table1 t1
   Where Exists (Select * From Table2
                 Where column = t1.colA)

РЕДАКТУВАННЯ: Щоб звернутися до коментаря @Mike Sherer, так, це правда, як технічно, з певним визначенням для вашого особливого випадку, так і естетично. По-перше, навіть коли набір запитуваних стовпців є підмножиною тих, що зберігаються в якомусь індексі, процесор запитів повинен отримати кожний стовпець, що зберігається в цьому індексі, а не лише запитуваний, з тих же причин - ВСІ введення / виведення потрібно робити в сторінки та дані індексу зберігаються в IO-сторінках так само, як дані таблиці. Отже, якщо ви визначаєте "кортеж" для сторінки індексу як набір стовпців, що зберігаються в індексі, твердження все одно вірно.
і твердження є вірно естетичним, оскільки справа в тому, що він отримує дані на основі того, що зберігається на сторінці вводу / виводу, а не на тому, що ви запитуєте, і це правда, чи отримуєте ви доступ до сторінки вводу / виводу базової таблиці чи індексу Сторінка вводу / виводу.

З інших причин не використовувати Select *, див. Чому це SELECT *вважається шкідливим? :


"Це завжди тягне кортеж", ви впевнені? Хм Ок, значить, я мав рацію. якщо в такому випадку select *буде менше витрат на пам'ять, select columnале однакові накладні введення / виведення. тому Якщо ми залишимо мережу накладні. select *якщо менше накладних, ніж уselect column
Неель Басу

10
Це не правда. Один з прикладів у верхній частині моєї голови - це коли ви бажаєте лише значення індексованого стовпця в MySQL (наприклад, просто перевірити наявність рядка), і ви використовуєте двигун зберігання даних MyISAM, він захопить дані MYI-файл, який міг би бути в пам'яті, а навіть не переходити на диск!
Майк Шеров

Так, якщо запитуваний набір кортежів знаходиться в пам'яті, не буде вводу / виводу, але це окремий випадок. Отже, що таке літній. Якщо я виберу якийсь індексований стовпець, то весь кортеж не читається? інакше читається весь кортеж?
Ніл Басу

Я не точно впевнений, як MySql робить кешування, але в SQL Server і в Oracle, навіть коли дані знаходяться в кеш-пам'яті, він все ще отримує доступ до нього за допомогою тієї ж структури сторінки, що і під час доступу до неї з диска. це означає, що він вимагає одного вводу / виводу пам'яті на кожну сторінку даних ... точно так само, як і з диска. (за винятком пам'яті, введення / виведення пам'яті набагато швидше, ніж звичайно дисковий введення / вивід). Дійсно, це мета кешування дизайну, щоб зробити процес доступу абсолютно незалежним від розташування даних.
Чарльз Бретана

2
Чи можете ви прописати більше "з багатьох інших причин"? Тому що мені це було не зрозуміло. Якщо продуктивність не має значення, навіщо дбати про запит імен стовпців?
Денніс

111

Є кілька причин, які ви ніколи не повинні (ніколи не використовувати) SELECT *у виробничому коді:

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

  • якщо вам потрібно лише 2/3 стовпців, ви вибираєте 1/3 занадто багато даних, які потрібно отримати з диска та надіслати по мережі

  • якщо ви почнете покладатися на певні аспекти даних, наприклад, порядок повернення стовпців, ви можете отримати неприємний сюрприз, коли таблиця буде реорганізована та додані нові стовпці (або видалені існуючі)

  • у SQL Server (не впевнений у інших базах даних), якщо вам потрібна підмножина стовпців, завжди є ймовірність, що некластеризований індекс може охоплювати цей запит (містять усі необхідні стовпці). З SELECT *, ви відмовляєтесь від цієї можливості прямо з початку роботи. У цьому конкретному випадку дані будуть витягнуті з індексних сторінок (якщо вони містять усі необхідні стовпці), і таким чином дискові введення / виведення та накладні дані будуть набагато меншими порівняно з SELECT *....запитом.

Так, спочатку потрібно трохи більше вводити текст (такі інструменти, як SQL-підказка для SQL Server, навіть вам там допоможуть) - але це справді один випадок, коли існує правило без жодного винятку: ніколи не використовуйте SELECT * у виробничому коді. ВСЕ.


13
Хоча, погоджуючись з вами на практиці, ви, безумовно, правильні у всіх випадках, коли ви отримуєте дані стовпців із таблиці, як це питання стосується), все ж акцент на EVER все-таки спонукає мене зазначити, що ці правила не є загальними для ВСІХ запитів Sql. конкретно, це використання в підзапиті після предиката EXISTS, (як і в Where Exists (Select * From ...) використання Select *, звичайно, не є проблемою, а в деяких колах вважається найкращою практикою.
Чарльз Бретана

3
@Charles Bretana: так, IF EXISTS(SELECT *...це особливий випадок - оскільки там жодних даних не витягується, але це лише перевірка на існування, SELECT * - це не проблема ...
marc_s

1
Що робити, якщо я розробляю API, який дозволяє отримати дані з однієї з моїх таблиць. Оскільки я не знаю, які дані цікавлять користувача, я вважаю, що SELECT * буде прийнятним?
Саймон Бенгтссон

1
@SimonBengtsson: Я все одно заперечуватиму проти цього - припустимо, у вас є деякі "адміністративні" дані в конкретних стовпцях таблиці, які ви не хочете виставляти клієнту? Я завжди чітко вказуватиму список стовпців для отримання
marc_s

1
Це правда. А як щодо запиту представлення даних, яке було спеціально налаштоване для використання в API?
Саймон Бенгтссон

21

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

[редагувати]: мається на увазі доступ. Дурний мозок все ще прокидається.


3
+1 для кращого випадку, який, на мою думку, мало хто придумає на перший погляд - індекси на стороні клієнта та додані / змінені стовпці.
Томаш Ашан

1
Так, але чи поширене використання числових індексів для стовпців? Я завжди отримував доступ до даних стовпців за допомогою рядкових ключів або імен властивостей, якщо використовував ORM.
Lèse majesté

11
побачив це давно, молодший програміст вибирав * із таблиці та робив припущення щодо порядку стовпців; весь його код зламався, як тільки хтось інший змінив стіл. Що нам було весело.
Пол Макензі

7
Це, мабуть, погана ідея використовувати порядок стовпців взагалі лише для читабельності коду, вдвічі поганіше їх використовувати SELECT *.
Lèse majesté

2
Нічого, доступ до стовпців за індексом у коді клієнта здається феноменально поганою ідеєю. З цього приводу, покладаючись на порядок, у якому стовпці відображаються в наборі результатів, якимось чином відчуває мене дуже брудно.
Метт Петерсон

7

Якщо ви не зберігаєте великі краплі, продуктивність не викликає побоювань. Найбільшою причиною не використовувати SELECT * є те, що якщо ви використовуєте повернуті рядки як кортежі, стовпці повертаються в будь-якому порядку, який буде вказано в схемі, і якщо ці зміни вам доведеться виправити весь код.

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


6

Це негайно змушує мене думати про таблицю, яку я використовував, яка містила стовпець типу blob; Зазвичай воно містило зображення JPEG, розміром декілька Mbс.

Потрібно сказати, що я не робив SELECTцю колонку, якщо мені справді не потрібен. Мати ці дані, що плавали навколо, особливо коли я вибирав багаторядні рядки - було просто клопотом.

Однак я визнаю, що в іншому випадку я зазвичай запитую всі стовпці таблиці.


20
Стовпці LOB - це мій улюблений приклад небезпеки SELECT *. Тож я збирався підтримати вас, поки не прочитав третього абзацу. Цск, цк. Що станеться, якщо якийсь інший розробник додасть BLOB до таблиці, в якій зараз немає такого стовпця?
APC

1
@APC, я б хотів, щоб я міг більше підтримати ваш коментар. Подумайте про свого бідного співробітника, який просто хоче додати стовпець, не викликаючи величезного спаду продуктивності! Подумайте, наскільки вони будуть розлючені, коли через кілька годин виявлять ваш невинно виглядаючий вибір *.
Майк Шеров

1
@ user256007, так, навіть без BLOB ... BLOB просто ілюструє крайній приклад. Перевірте мою відповідь на Чарльза, є випадки, коли вибрані конкретні стовпці дозволять вам захопити дані з пам'яті, навіть не переходячи на диск!
Майк Шеров

1
@Richard, я думаю, що вони чудово підходять для оптимізації продуктивності БД - це не ваша головна проблема, яка займає 99% часу. Як і в більшості фреймворків, вони, як правило, узагальнюють речі, щоб забезпечити швидший розвиток, при цьому приносячи в жертву чисті результати. Як сказав Кнут: "Передчасна оптимізація - корінь усього зла". Коли ви доберетеся до того, що вам потрібно потурбуватися про ефективність вибраних стовпців проти select *, (запитайте у Twitter про RoR), ви можете потурбуватися про це та оптимізувати його потім. Якщо рамка не є достатньо надійною, щоб підтримати це, то я б сказав, що ви використовуєте неправильну рамку.
Майк Шеров

1
@ user256007 - загальне правило - "не використовувати SELECT *". Відповідь від marc_s має всі підстави, чому це так.
APC

6

Під час вибору SQL БД завжди буде посилатися на метадані таблиці, незалежно від того, чи це SELECT * для SELECT a, b, c ... Чому? Тому що тут інформація про структуру та компонування таблиці в системі.

Він повинен прочитати цю інформацію з двох причин. Один, щоб просто скласти заяву. Для цього потрібно переконатися, що ви принаймні вказали існуючу таблицю. Також структура бази даних, можливо, змінилася з моменту останнього виконання заяви.

Тепер, очевидно, метадані БД є кешованими в системі, але це все ще обробка, що потрібно зробити.

Далі метадані використовуються для генерації плану запитів. Це відбувається кожного разу, коли також складається компіляція. Знову ж таки, це працює проти кешованих метаданих, але це завжди робиться.

Єдиний час, коли ця обробка не виконується, це коли БД використовує попередньо складений запит або кешує попередній запит. Це аргумент для використання параметрів прив'язки, а не буквального SQL. "SELECT * OF TABLE WHERE key = 1" - це інший запит, ніж "SELECT * OF TABLE WHERE key =?" і "1" прив'язується до дзвінка.

БД сильно покладаються на кешування сторінок для роботи там. Багато сучасних БД є досить маленькими, щоб повністю вміститись у пам'яті (або, можливо, слід сказати, сучасна пам'ять достатньо велика, щоб вмістити багато БД). Тоді ваша основна вартість вводу-виводу на зворотному кінці - це ведення журналів та перегляд сторінки.

Однак, якщо ви все ще натискаєте на диск для своєї БД, першочерговою оптимізацією багатьох систем є покладання даних на індекси, а не на самі таблиці.

Якщо у вас є:

CREATE TABLE customer (
    id INTEGER NOT NULL PRIMARY KEY,
    name VARCHAR(150) NOT NULL,
    city VARCHAR(30),
    state VARCHAR(30),
    zip VARCHAR(10));

CREATE INDEX k1_customer ON customer(id, name);

Тоді, якщо ви робите "SELECT id, name FROM customer WHERE id = 1", велика ймовірність, що ви DB буде витягувати ці дані з індексу, а не з таблиць.

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

Тепер у базі даних є всі необхідні дані, щоб задовольнити запит, тому немає ніяких причин звертатися до самих сторінок таблиці. Використання індексу призводить до зменшення дискового трафіку, оскільки у вас більша щільність рядків в індексі порівняно з таблицею загалом.

Це хвилеподібне пояснення конкретної методики оптимізації, яка використовується деякими базами даних. Багато хто має кілька методів оптимізації та настройки.

Зрештою, SELECT * корисний для динамічних запитів, які потрібно вводити вручну, я б ніколи не використовував його для "реального коду". Ідентифікація окремих стовпців надає БД більше інформації, яку він може використовувати для оптимізації запиту, і дає вам кращий контроль у вашому коді щодо змін схеми тощо.


Вілл, я спростував вашу відповідь, тільки тому, що ви використовуєте НЕ НУЛЬНИЙ разом з ПЕРВИЧНИМ КЛЮЧАМ. Чи є для вас вагома причина писати так?
Учень

4

Я думаю, що немає точного відповіді на ваше запитання, тому що ви обдумуєте продуктивність та можливості підтримувати свої програми. Select columnє більш продуктивним select *, але якщо ви розробляєте орієнтовану систему об'єктів, вам сподобається використання, object.propertiesі вам потрібні властивості в будь-якій частині додатків, тоді вам потрібно буде написати більше методів, щоб отримати властивості в особливих ситуаціях, якщо ви цього не зробите використовувати select *та заселяти всі властивості. select *Щоб покращити ефективність, ваші додатки повинні мати високу ефективність, а в деяких випадках вам потрібно буде використовувати стовпець Select. Тоді у вас буде кращий з двох світів, можливість писати та підтримувати програми та продуктивність, коли вам потрібна продуктивність.


4

Тут прийнята відповідь - неправильна. Я натрапив на це, коли інше питання було закрито як дублікат цього (поки я ще писав свою відповідь - grr - отже, SQL нижче посилається на інше питання).

Ви завжди повинні використовувати атрибут SELECT, атрибут .... NOT SELECT *

Це насамперед для питань продуктивності.

ВИБІРТЕ ім’я від користувачів, де ім'я = 'Джон';

Не дуже корисний приклад. Розглянемо замість цього:

SELECT telephone FROM users WHERE name='John';

Якщо в ньому є індекс (ім’я, телефон), то запит можна вирішити без необхідності шукати відповідні значення з таблиці - є індекс покриття .

Крім того, припустимо, що у таблиці є BLOB, що містить зображення користувача, і завантажене резюме, і електронну таблицю ... за допомогою SELECT * буде повернено всю цю інформацію в буфери СУБД (витісняючи іншу корисну інформацію з кеша). Тоді всі вони будуть відправлені клієнтові, використовуючи час роботи в мережі та пам'ять клієнта для надмірних даних.

Це також може викликати функціональні проблеми, якщо клієнт отримує дані у вигляді переліченого масиву (наприклад, mysql_fetch_array PHP ($ x, MYSQL_NUM)). Можливо, коли в коді було написано "телефон", це третя колонка, яку слід повернути SELECT *, але тоді хтось приходить разом і вирішує додати адресу електронної пошти до таблиці, розташованої перед "телефоном". Бажане поле тепер переміщується до 4-ї колонки.


2

Для того, щоб робити справи, є причини. Я дуже часто використовую SELECT * на PostgreSQL, оскільки у PostgreSQL є багато речей, які ви можете зробити з SELECT *, що ви не можете зробити з явним списком стовпців, особливо, коли це зберігається. Аналогічно в Informix, SELECT * над успадкованим деревом таблиці може надавати вам нерівні рядки, тоді як явний список стовпців не може, оскільки додаткові стовпці в дочірніх таблицях також повертаються.

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

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

Що стосується продуктивності, я, як правило, використовую VIEW та збережені процедури, що повертають типи (а потім список стовпців всередині збереженої процедури). Це дає мені контроль над тим, які типи повертаються.

Але майте на увазі, що я використовую SELECT *, як правило, проти абстракційного шару, а не базових таблиць.


2

Довідка взята з цієї статті:

Без SELECT *: Коли ви використовуєте "SELECT *" у той час, ви вибираєте більше стовпців із бази даних, і частина цього стовпця може не використовуватися вашою програмою. Це створить додаткові витрати та навантаження на систему баз даних та більше подорожей даних по мережі.

З SELECT *: Якщо у вас є спеціальні вимоги та створено динамічне середовище, коли додавання або видалення стовпця автоматично обробляється кодом програми. У цьому спеціальному випадку вам не потрібно змінювати код програми та бази даних, і це автоматично вплине на виробниче середовище. У цьому випадку ви можете використовувати “SELECT *”.


0

Просто додати нюанс до дискусії, яку я тут не бачу: З точки зору вводу-виводу, якщо ви використовуєте базу даних із орієнтованим на стовпчик сховищем ви можете зробити набагато менше вводу-виводу, якщо ви запитуєте лише певні стовпчики. Коли ми переходимо до SSD, переваги можуть бути трохи меншими порівняно з рядковим сховищем, але є: а) лише зчитування блоків, що містять стовпці, про які ви дбаєте, б) стиснення, що, як правило, значно зменшує розмір даних на диску, а отже, і обсяг даних, прочитаних з диска.

Якщо ви не знайомі зі стовпчиком, орієнтованим на зберігання, одна реалізація для Postgres надходить із Citus Data, інша - Greenplum, інша Paraccel, інша (слабко кажучи) Amazon Redshift. Для MySQL існує Infobright, нині неіснуючий InfiniDB. Інші комерційні пропозиції включають Vertica від HP, Sybase IQ, Teradata ...


-1
select * from table1 INTERSECT  select * from table2

рівний

select distinct t1 from table1 where Exists (select t2 from table2 where table1.t1 = t2 )

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