Чи є вибір * все ще великим ні-ні на SQL Server 2012?


41

Ще в часи минулих років це вважалося великим, що не потрібно робити select * from tableабо select count(*) from tableчерез хіт виступу.

Чи все-таки це стосується пізніших версій SQL Server (я використовую 2012 рік, але, мабуть, питання стосуватиметься 2008 - 2014 років)?

Редагувати: Оскільки люди, здається, мене тут трохи збивають, я дивлюся на це з орієнтиру / академічної точки зору, а не на те, чи це "правильна" річ (що, звичайно, це не так)

Відповіді:


50

Якщо ви, SELECT COUNT(*) FROM TABLEщо повертає лише один рядок (кількість), є відносно легким, і це спосіб отримати цю дату.

І SELECT *не є фізичним ні-ні, оскільки це законно і дозволено.

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

Так, так, це рекомендується проти загальної практики, оскільки це марно витрачає ресурси.

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

Аналогія: Якщо хтось використовує, SELECT *коли їм не потрібен кожен стовпець, чи він також буде використовувати SELECTбез WHERE(або якесь інше обмежувальне положення), коли їм не потрібен кожен рядок?


24

На додаток до вже наданого відповіді, я вважаю, що варто зазначити, що розробники часто лінуються під час роботи з сучасними ORM, такими як Entity Framework. Хоча DBA намагається уникнути їх SELECT *, найчастіше розробники записують семантично еквівалент, наприклад, в c # Linq:

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();

По суті, це призведе до наступного:

SELECT * FROM MyTable WHERE FirstName = 'User'

Також є додаткові накладні витрати, які ще не були покриті. Це ресурси, необхідні для обробки кожного стовпця в кожному рядку до відповідного об'єкта. Крім того, для кожного об'єкта, що зберігається в пам'яті, цей об'єкт повинен бути очищений. Якщо ви вибрали лише потрібні стовпці, ви зможете легко заощадити понад 100 Мбайт. Хоча сама по собі не є величезною сумою, її сукупний ефект з вивезення сміття тощо, що є стороною клієнта.

Так що так, принаймні для мене це є і завжди буде великим "ні". Нам також потрібно проінформувати про "приховані" витрати, щоб зробити це ще більше.

Додаток

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

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
                             .Select(entity => new { entity.FirstName, entity.LastNight });

13

Продуктивність: Запит з SELECT *, ймовірно , НЕ буде покриттям запитів ( Простий розмову пояснення , переповнення стека пояснення ).

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

Індексація: Якщо ви хочете, щоб ваші перегляди та функції з табличним значенням брали участь у індексації в SQL Server, тоді ці представлення та функції повинні бути створені за допомогою прив'язки схем, що забороняє використання SELECT *.

Найкраща практика : ніколи не використовуйте SELECT *у виробничому коді.

Для підзапитів я віддаю перевагу WHERE EXISTS ( SELECT 1 FROM … ).

Редагувати : Щоб звернутися до коментаря Крейга Янга нижче, використання "SELECT 1" в підзапиті не є "оптимізацією" - це так, що я можу встати перед своїм класом і сказати "не використовувати SELECT *, не виняток! "

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

Я можу прийняти виняток, що включає CTE та отримані таблиці, хоча я б хотів побачити плани виконання.

Зауважте, що я вважаю COUNT(*)винятком із цього, оскільки це різний синтаксичний спосіб використання "*".


10

У SQL Server 2012 (або будь-якій версії з 2005 р.) Використання SELECT *...лише можливої ​​проблеми продуктивності в операторі SELECT верхнього рівня запиту.

Так що це не проблема , в уявленнях (*), в підзапитів, в Exist пунктах, в КТР, ні і SELECT COUNT(*)..т.д., і т.д. Зверніть увагу, що це, ймовірно , також вірно і для Oracle і DB2, і може бути , Postgres (не впевнений) , але дуже ймовірно, що це все-таки проблема у багатьох випадках для MySql.

Щоб зрозуміти, чому (і чому це все ще може бути проблемою у вищому рівні SELECT), корисно зрозуміти, чому це коли-небудь було проблемою, тому що за допомогою SELECT *..засобів " повернути ВСІ стовпці ". Загалом це поверне набагато більше даних, ніж ви дійсно хочете, що, очевидно, може призвести до набагато більше IO, як на диску, так і на мережі.

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

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

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

Підсумком цього є те, що це вже не має значення, якщо ви використовуєте "SELECT * .." на нижньому / внутрішньому рівнях запиту. Натомість важливим є те, що знаходиться у списку стовпців SELECT верхнього рівня. Якщо ви не використовуєте SELECT *..вгорі, то це ще раз повинно припустити, що ви хочете ВСІ з стовпців, і тому не можете ефективно використовувати оптимізацію стовпців.

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


5

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


4
Це не має значення. Якщо ви отримуєте доступ до індексу стовпців за індексом стовпців у коді програми, то ви заслуговуєте на те, що програма не працює. Доступ до стовпців за назвою завжди створює набагато більш читабельний код програми, і це майже не є вузьким місцем продуктивності.
Lie Lie Ryan

3

Це фізично та проблематично дозволено використовувати select * from table, однак це погана ідея. Чому?

Перш за все, ви побачите, що повертаєте стовпці, які вам не потрібні (ресурс важкий).

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

По-третє, виконання цього фактично ускладнює розробника. Як часто вам потрібно перевертати назад і назад від SSMS до VS, щоб отримати всі назви стовпців?

По-четверте, це ознака лінивого програмування, і я не думаю, що жоден розробник не захоче такої репутації.


Ваш другий аргумент у цій нинішній формі має невеликі помилки. По-перше, всі RDBMS кешують схему таблиць, здебільшого тому, що схема буде завантажена в будь-якому випадку на етапі розбору запитів, щоб визначити, який стовпець існує або відсутній у таблиці в запиті. Отже, аналізатор запитів вже запитував список імен стовпців самостійно і миттєво замінює * списком стовпців. Тоді більшість двигунів RDBMS намагаються кешувати все, що може, тому якщо ви випустите таблицю SELECT * FROM, складений запит буде кешований, щоб розбір не відбувся кожного разу. І розробники лінуються :-)
Габор Гарамі

Що стосується вашого другого аргументу, це поширена помилка - проблема з SELECT * не полягає у пошуку метаданих, оскільки якщо ви дасте ім’я стовпцям, SQL Server все одно повинен перевірити їхні імена, перевірити типи даних тощо
Aaron Bertrand

@Gabor Одне з проблем із SELECT * трапляється, коли ви поміщаєте це в перегляд. Якщо змінити основну схему, подання може заплутатися - тепер воно має інше поняття схеми таблиці (власну), ніж сама таблиця. Я говорю про це тут .
Аарон Бертран

3

Якщо ви помістите Select * ...код у програму, це може бути проблемою , оскільки, як зазначалося раніше, база даних може змінюватися з часом і мати більше стовпців, ніж те, що ви очікували, коли ви писали запит. Це може призвести до відмови програми (найкращий випадок), або програма може продовжити свій веселий шлях і пошкодити деякі дані, оскільки вона дивиться на значення поля, які не було написано для обробки. Коротше кажучи, виробничий код повинен ЗАВЖДИ вказувати поля, які потрібно повернути у SELECT.

Сказавши це, у мене менше проблем, коли Select *це частина EXISTSпункту, оскільки все, що буде повернуто програмі, є булевим показником, що вказує на успіх чи невдачу вибору. Інші можуть не погодитися з цією позицією, і я поважаю їх думку з цього приводу. МОЖЕ бути трохи менш ефективним, Select *ніж кодувати, ніж кодувати "Вибрати 1" у EXISTSпункті, але я не думаю, що в будь-якому випадку існує небезпека пошкодження даних.


Власне, так, я мав на увазі посилання на пункт EXISTS. Моя помилка.
Марк Росс

2

Дуже багато відповідей, чому select *це неправильно, тому я розкрию, коли відчуваю, що це правильно або принаймні гаразд.

1) В EXISTS вміст SELECT частини запиту ігнорується, тому ви навіть можете писати, SELECT 1/0і він не помилиться. EXISTSпросто підтверджує, що деякі дані повертаються та повертають булеві дані на основі цього.

IF EXISTS(
    SELECT * FROM Table WHERE X=@Y
)

2) Це може почати вогневий штурм, але мені подобається використовувати select *тригери в таблиці історії. До select *, запобігає основну таблицю від отримання нової колонки без додавання стовпця в таблицю історії , як добре він відразу error'ing при вставці / оновлено / видалення в основну таблицю. Це заважало не раз розробникам додавати стовпці і забували додати їх до таблиці історії.


3
Я все ще вважаю за краще, SELECT 1оскільки він, очевидно, сповіщає майбутніх утримувачів коду про ваші наміри. Це не вимога , але якщо я бачу, ... WHERE EXISTS (SELECT 1 ...)це досить очевидно оголошує себе як перевірку на правду.
swasheck

1
@zlatanMany користуються SELECT 1на основі міфу, що ефективність буде кращою SELECT *. Однак обидва варіанти цілком прийнятні. Немає відмінностей у продуктивності через те, як оптимізатор працює з EXISTS. Немає різниці в читанні через слово "ІСНУЄТЬСЯ", яке чітко оголошує тест на істинність.
Розчарований

У пункті №2 я розумію ваші міркування, але все ж є ризики. Дозвольте мені "намалювати сценарій для вас" ... Розробник додає Column8до основної таблиці забувши таблицю історії. Розробник пише купу коду, переробленого у колонку 8. Потім додає Column9до основної таблиці; цього разу запам'ятовування також додасть до історії. Пізніше під час тестування він розуміє, що забув додати Column9до історії (завдяки вашій техніці виявлення помилок), і швидко додає її. Зараз тригер, здається, працює, але дані в стовпцях 8 і 9 змішуються в історії. : S
Розчарований

Проти ... Справа в тому, що вищезгаданий "придуманий" сценарій - це лише один із багатьох, що може призвести до того, що ваш трюк виявлення помилок вийде з ладу і насправді погіршить ситуацію. В основному вам потрібна краща техніка. Той, який не покладається на ваш тригер, робить припущення про порядок стовпців таблиці, яку ви вибираєте. Пропозиції: - огляди персонального коду з контрольними списками ваших поширених помилок. - Огляди експертних кодів. - Альтернативна техніка відстеження історії (особисто я вважаю, що механізми, що базуються на спрацьовуванні, є реактивними, а не активними, а тому схильними до помилок)
Розчарований

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