выберите * vs выберите столбец

124

Если мне просто нужны 2/3 столбца, и я запрашиваю SELECT *вместо предоставления этих столбцов в запросе выбора, есть ли какое-либо снижение производительности в отношении большего / меньшего количества операций ввода-вывода или памяти?

Если я выберу * без необходимости, то могут возникнуть накладные расходы сети.

Но в операции выбора всегда ли движок базы данных извлекает атомарный кортеж с диска или извлекает только те столбцы, которые запрашиваются в операции выбора?

Если он всегда извлекает кортеж, то накладные расходы ввода-вывода такие же.

В то же время может потребоваться память для удаления запрошенных столбцов из кортежа, если он извлекает кортеж.

Поэтому, если это так, select someColumn будет иметь больше накладных расходов на память, чем select *

Нил Басу
источник
Есть ли конкретная СУБД, о которой вы спрашиваете? Возможно, что то, как SELECTвыполняются / обрабатываются запросы, отличается от базы данных к базе данных.
Lèse majesté 05
10
Кстати, в PostgreSQL, если вы скажете CREATE VIEW foo_view AS SELECT * FROM foo;, а затем добавьте столбцы в таблицу foo позже, эти столбцы не будут автоматически отображаться в foo_view, как ожидалось. Другими словами, *в этом контексте раскрывается только один раз (во время создания представления), а не для каждого SELECT. Из-за сложностей, связанных с ALTER TABLE, я бы сказал, что (на практике) *считается вредным.
Joey Adams
@JoeyAdams - это не только PostgresQL, но и поведение Oracle.
APC
1
@OMG Ponies: Мне не было известно о подобном посте. Однако это не совсем аналог. @ Lèse majesté: Я говорю об общей СУБД. не о каком-то конкретном вендоре @Joey Adams: Хм, я знаю, что * небезопасно. просто хочу обсудить проблемы с производительностью.
Neel Basu
3
возможный дубликат Почему SELECT * считается вредоносным?
Аарон Бертран

Ответы:

31

Он всегда извлекает кортеж (за исключением случаев, когда таблица была сегментирована по вертикали - разбита на части по столбцам), поэтому, чтобы ответить на заданный вами вопрос, это не имеет значения с точки зрения производительности. Однако по многим другим причинам (ниже) вы всегда должны выбирать именно те столбцы, которые вам нужны, по имени.

Он всегда извлекает кортеж, потому что (в РСУБД всех поставщиков, с которыми я знаком), базовая структура хранения на диске для всего (включая данные таблиц) основана на определенных страницах ввода-вывода (в 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, да, это правда, как технически, с небольшим определением для вашего особого случая, так и эстетически. Во-первых, даже когда запрошенный набор столбцов является подмножеством тех, которые хранятся в каком-либо индексе, обработчик запросов должен получить каждый столбец, хранящийся в этом индексе, а не только запрошенные, по тем же причинам - ВСЕ операции ввода-вывода должны выполняться в страниц, а данные индекса хранятся на страницах ввода-вывода так же, как данные таблицы. Поэтому, если вы определяете «кортеж» для страницы индекса как набор столбцов, хранящихся в индексе, утверждение все равно остается верным.
и утверждение истинно эстетически, потому что дело в том, что он извлекает данные на основе того, что хранится на странице ввода-вывода, а не на основе того, что вы запрашиваете, и это верно независимо от того, обращаетесь ли вы к странице ввода-вывода базовой таблицы или к индексу. Страница ввода / вывода.

По другим причинам, чтобы не использовать Select *, см. Почему SELECT *считается вредным? :

Чарльз Бретана
источник
«Он всегда вытаскивает кортеж», вы уверены? Хм, ладно. Итак, я был прав. в этом случае select *накладные расходы на память будут меньше, чем select columnнакладные расходы ввода-вывода. Итак, если мы оставим сетевые накладные расходы. select *если накладные расходы меньше, чем уselect column
Нил Басу
10
Это неправда. Один из примеров, который мне не нравится, - это когда вам нужно только значение индексированного столбца в MySQL (например, просто для проверки существования строки), и вы используете механизм хранения MyISAM, он будет получать данные из MYI-файл, который может находиться в памяти и даже не попадать на диск!
Майк Шеров
Да, если запрошенный набор кортежей находится в памяти, ввода-вывода не будет, но это особый случай. Итак, что такое лето. Если я выберу какой-нибудь индексированный столбец, тогда весь кортеж не будет прочитан? иначе читается весь кортеж?
Neel Basu
Я не совсем уверен, как MySql выполняет кэширование, но в SQL Server и в Oracle, даже когда данные находятся в кеше в памяти, он по-прежнему обращается к ним, используя ту же структуру страницы, что и при доступе к ним с диска. Это означает, что для каждой страницы данных потребуется один ввод-вывод в память ... точно так же, как и с диска. (за исключением того, что ввод / вывод памяти, конечно, намного быстрее, чем ввод / вывод диска). В самом деле, цель дизайна кэширования - сделать процесс доступа полностью независимым от местоположения данных.
Чарльз Бретана 05
2
Можете ли вы подробнее объяснить «по многим другим причинам»? Потому что мне это было непонятно. Если производительность не имеет значения, зачем запрашивать имена столбцов?
Деннис
111

Есть несколько причин, по которым вы никогда (никогда) не должны использовать SELECT *в производственном коде:

  • поскольку вы не даете своей базе данных никаких подсказок относительно того, что вы хотите, сначала нужно будет проверить определение таблицы, чтобы определить столбцы в этой таблице. Этот поиск будет стоить некоторое время - не так много в одном запросе - но со временем он накапливается.

  • если вам нужно только 2/3 столбца, вы выбираете на 1/3 слишком много данных, которые необходимо получить с диска и отправить по сети

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

  • в SQL Server (не уверен в других базах данных), если вам нужно подмножество столбцов, всегда есть вероятность, что некластеризованный индекс может покрывать этот запрос (содержать все необходимые столбцы). С a SELECT *вы отказываетесь от этой возможности с самого начала. В этом конкретном случае данные будут извлекаться из страниц индекса (если они содержат все необходимые столбцы), и, таким образом, дисковый ввод-вывод и накладные расходы на память будут намного меньше по сравнению с выполнением SELECT *....запроса.

Да, сначала требуется немного больше ввода (такие инструменты, как SQL Prompt для SQL Server даже помогут вам в этом), но это действительно тот случай, когда есть правило без каких-либо исключений: никогда не используйте SELECT * в своем производственном коде. КОГДА-ЛИБО.

marc_s
источник
13
соглашаясь с вами на практике, вы, безусловно, правы во всех случаях, когда получаете данные столбца из таблицы, поскольку этот вопрос касается), годовой акцент на КОГДА-ЛИБО заставляет меня указать, что эти правила не являются общими для ВСЕХ запросов Sql .. В частности, он используется в подзапросе после предиката EXISTS, (как в Where Exists (Select * From ...) использование, Select *безусловно, не является проблемой и в некоторых кругах считается лучшей практикой.
Чарльз Бретана 05
3
@Charles Bretana: да, IF EXISTS(SELECT *...это особый случай - поскольку там данные на самом деле не извлекаются, но это всего лишь проверка на существование, SELECT * здесь не проблема ...
marc_s
1
Что насчет того, если я разрабатываю API, который позволяет извлекать данные из одной из моих таблиц. Поскольку я не знаю, какие данные интересуют пользователя, я полагаю, что SELECT * будет приемлемым?
Саймон Бенгтссон
1
@SimonBengtsson: Я все равно буду возражать против этого - предположим, у вас есть какие-то "административные" данные в определенных столбцах вашей таблицы, которые вы не хотите раскрывать клиенту? Я бы всегда явно указывал список столбцов для выборки
marc_s
1
Это правда. Как насчет запроса представления, которое было специально настроено для использования с API?
Саймон Бенгтссон
21

Вы должны всегда только selectстолбцы , которые вы на самом деле нужно. Никогда не менее эффективно выбирать меньше вместо большего, и вы также сталкиваетесь с меньшим количеством неожиданных побочных эффектов, таких как доступ к столбцам результатов на стороне клиента по индексу, а затем эти индексы становятся неверными из-за добавления нового столбца в таблицу.

[править]: имел в виду доступ. Глупый мозг все еще просыпается.

Donnie
источник
3
+1 для крайнего случая, который, я думаю, не многие подумают с первого взгляда - индексы на стороне клиента и добавленные / измененные столбцы.
Tomas Aschan 05
1
Да, но так ли распространено использование числовых индексов для столбцов? Я всегда обращался к данным столбца, используя строковые ключи или имена свойств при использовании ORM.
Lèse Majesté 05
11
видел это давным-давно, младший программист выбирал * из таблицы и делал предположения о порядке столбцов; весь его код сломался, как только кто-то изменил таблицу. Какое веселье у нас было.
Пол Маккензи
7
Вероятно, использовать порядок столбцов в целом просто для удобства чтения - плохая идея, вдвойне плохая идея использовать SELECT *с ним.
Lèse Majesté 05
2
Вау, доступ к столбцам по индексу в клиентском коде кажется феноменально плохой идеей. Для этого вопроса, опираясь на порядок , в котором столбцы появляются в наборе результатов в любом случае чувствует себя очень грязно мне.
Мэтт Петерсон
7

Если вы не храните большие двоичные объекты, производительность не имеет значения. Основная причина не использовать SELECT * заключается в том, что если вы используете возвращаемые строки как кортежи, столбцы возвращаются в том порядке, который указан в схеме, и если это изменится, вам придется исправить весь свой код.

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

gxti
источник
6

Это сразу заставляет меня думать о таблице, которую я использовал, которая содержала столбец типа blob; обычно он содержал изображение в формате JPEG размером несколько Mbсекунд.

Излишне говорить, что я не писал SELECTэту колонку, если она мне действительно не нужна. Распространение этих данных - особенно когда я выбрал несколько строк - было просто проблемой.

Однако я признаю, что в противном случае я обычно запрашиваю все столбцы в таблице.

Ричард Дж. П. Ле Гуен
источник
20
Столбцы LOB всегда являются моим любимым примером опасностей SELECT *. Итак, я собирался проголосовать за вас, пока не прочитал третий абзац. Тск, цк. Что произойдет, если какой-либо другой разработчик добавит большой двоичный объект в таблицу, в которой в настоящее время нет такого столбца?
APC
1
@APC, я хотел бы больше проголосовать за ваш комментарий. Подумайте о своем бедном коллеге, который просто хочет добавить столбец, не вызывая резкого падения производительности! Подумайте, как они разозлятся, когда через несколько часов обнаружат вашего невинно выглядящего избранника *.
Майк Шеров
1
@ user256007, да, даже без BLOB ... BLOB просто иллюстрирует крайний пример. Посмотрите мой ответ Чарльзу, бывают случаи, когда выбор определенных столбцов может позволить вам получить данные из памяти, даже не переходя на диск!
Майк Шеров
1
@Richard, я думаю, они отлично подходят, когда оптимизация производительности БД не является вашей главной заботой, а это в 99% случаев. Как и большинство фреймворков, они склонны обобщать вещи, чтобы ускорить разработку, жертвуя чистой производительностью. Как сказал Кнут: «Преждевременная оптимизация - корень всех зол». Когда вы дойдете до точки, когда вам нужно беспокоиться о производительности столбцов select по сравнению с select * (спросите Twitter о RoR), вы можете беспокоиться об этом и оптимизировать его. Если фреймворк недостаточно надежен для поддержки этого, я бы сказал, что вы используете неправильный фреймворк.
Майк Шеров
1
@ user256007 - общее правило - «не используйте SELECT *». Ответ от marc_s содержит все причины, почему это так.
APC
6

Во время выбора SQL база данных всегда будет ссылаться на метаданные для таблицы, независимо от того, является ли это SELECT * для SELECT a, b, c ... Почему? Потому что именно здесь находится информация о структуре и расположении таблицы в системе.

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

Теперь очевидно, что метаданные БД кэшируются в системе, но их еще нужно обработать.

Затем метаданные используются для создания плана запроса. Это также происходит каждый раз при компиляции оператора. Опять же, это работает с кешированными метаданными, но это всегда делается.

Единственный раз, когда эта обработка не выполняется, - это когда БД использует предварительно скомпилированный запрос или кэширует предыдущий запрос. Это аргумент в пользу использования параметров привязки, а не буквального SQL. «SELECT * FROM TABLE WHERE key = 1» - это другой запрос, чем «SELECT * FROM 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);

Затем, если вы выполните «ВЫБРАТЬ id, имя FROM customer WHERE id = 1», весьма вероятно, что ваша БД будет извлекать эти данные из индекса, а не из таблиц.

Зачем? Он, скорее всего, в любом случае будет использовать индекс для удовлетворения запроса (в отличие от сканирования таблицы), и даже если 'name' не используется в предложении where, этот индекс по-прежнему будет лучшим вариантом для запроса.

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

Это краткое объяснение конкретного метода оптимизации, используемого некоторыми базами данных. У многих есть несколько методов оптимизации и настройки.

В конце концов, SELECT * полезен для динамических запросов, которые вам нужно вводить вручную, я бы никогда не использовал его для «реального кода». Идентификация отдельных столбцов дает БД больше информации, которую она может использовать для оптимизации запроса, и дает вам лучший контроль в вашем коде от изменений схемы и т. Д.

Уилл Хартунг
источник
Уилл, я отклонил ваш ответ только потому, что вы используете NOT NULL вместе с PRIMARY KEY. Есть ли у вас веская причина так писать?
Learner
4

Я думаю, что на ваш вопрос нет точного ответа, потому что вы задумываетесь о производительности и возможности поддержки своих приложений. Select columnявляется более производительным select *, но если вы разрабатываете ориентированную объектную систему, вам понравится использование, object.propertiesи вам могут понадобиться свойства в любой части приложений, тогда вам нужно будет написать больше методов для получения свойств в особых ситуациях, если вы не использовать select *и заполнить все свойства. Ваши приложения должны иметь хорошую производительность при использовании, select *и в некоторых случаях вам понадобится использовать столбец выбора для повышения производительности. Тогда у вас будет лучшее из двух миров: возможность писать и поддерживать приложения и производительность, когда вам нужна производительность.

M.Torres
источник
4

Принятый ответ здесь неверен. Я столкнулся с этим, когда другой вопрос был закрыт как дубликат этого (пока я все еще писал свой ответ - grr - следовательно, SQL ниже ссылается на другой вопрос).

Вы всегда должны использовать атрибут SELECT, атрибут .... NOT SELECT *

Это в первую очередь для проблем с производительностью.

ВЫБЕРИТЕ имя ИЗ пользователей ГДЕ name = 'John';

Не очень полезный пример. Вместо этого подумайте:

SELECT telephone FROM users WHERE name='John';

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

Кроме того, предположим, что в таблице есть большой двоичный объект, содержащий изображение пользователя, загруженное резюме и электронную таблицу ... использование SELECT * перенесет всю эту информацию обратно в буферы СУБД (вытесняя другую полезную информацию из кеша). Затем все это будет отправлено клиенту, используя время в сети и память на клиенте для данных, которые являются избыточными.

Это также может вызвать функциональные проблемы, если клиент получает данные в виде перечислимого массива (например, PHP mysql_fetch_array ($ x, MYSQL_NUM)). Может быть, когда код был написан, «телефон» был третьим столбцом, возвращаемым SELECT *, но затем кто-то приходит и решает добавить адрес электронной почты в таблицу, помещенную перед «телефоном». Требуемое поле теперь перемещено в 4-й столбец.

symcbean
источник
2

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

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

С другой стороны, жесткий список столбцов дает вам возможность проверить на уровне приложения, что схемы db не изменились определенным образом, и это может быть полезно. (Я провожу такие проверки на другом уровне.)

Что касается производительности, я обычно использую представления и хранимые процедуры, возвращающие типы (а затем список столбцов внутри хранимой процедуры). Это дает мне контроль над возвращаемыми типами.

Но имейте в виду, что я использую SELECT * обычно для уровня абстракции, а не для базовых таблиц.

Крис Трэверс
источник
2

Ссылка взята из этой статьи:

Без SELECT *: когда вы используете «SELECT *» в это время, вы выбираете больше столбцов из базы данных, и некоторые из этих столбцов могут не использоваться вашим приложением. Это приведет к дополнительным затратам и нагрузке на систему базы данных, и больше данных будет перемещаться по сети.

С помощью SELECT *: если у вас есть особые требования и создана динамическая среда, при добавлении или удалении столбца автоматически обрабатывается кодом приложения. В этом особом случае вам не нужно изменять код приложения и базы данных, и это автоматически повлияет на производственную среду. В этом случае вы можете использовать «ВЫБРАТЬ *».

Anvesh
источник
0

Просто чтобы добавить нюанс к обсуждению, которого я здесь не вижу: с точки зрения ввода-вывода, если вы используете базу данных с хранилищем, ориентированным на столбцы, вы можете делать НАМНОГО меньше операций ввода-вывода, если вы запрашиваете только определенные колонны. По мере перехода на твердотельные накопители преимущества могут быть немного меньше по сравнению с хранилищем, ориентированным на строки, но есть а) чтение только блоков, содержащих столбцы, которые вам нужны, б) сжатие, которое обычно значительно уменьшает размер данных на диске и, следовательно, объем данных, прочитанных с диска.

Если вы не знакомы с хранилищем, ориентированным на столбцы, одна реализация 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