По какой причине не использовать select *?
Я видел, как многие люди утверждали, что вы должны конкретно назвать каждый столбец, который вы хотите в вашем запросе выбора.
Если в любом случае я собираюсь использовать все столбцы, почему бы мне не использовать SELECT *
?
Даже рассматривая вопрос * SQL-запрос - Выберите * в представлении или выберите col1, col2,... colN в представлении*, я не думаю, что это точный дубликат, поскольку я подхожу к вопросу с несколько иной точки зрения.
Один из наших принципов - не оптимизировать раньше времени. Имея это в виду, кажется, что использование SELECT *
должен быть предпочтительным методом до тех пор, пока не будет доказано, что это проблема с ресурсами или схема в значительной степени заложена в камень. Что, как мы знаем, не произойдет, пока разработка не будет полностью завершена.
Тем не менее, есть основная проблема, чтобы не использовать SELECT *
?
20 ответов
Суть цитаты о преждевременной оптимизации заключается в том, чтобы перейти к простому и понятному коду, а затем использовать профилировщик, чтобы указать на горячие точки, которые затем можно оптимизировать для обеспечения эффективности.
Когда вы используете select *, вы делаете невозможным профилирование, поэтому вы не пишете четкий и понятный код и идете вразрез с духом цитаты. select *
это анти-шаблон.
Поэтому выбор столбцов не является преждевременной оптимизацией. Несколько вещей с моей головы....
- Если вы укажете столбцы в операторе SQL, механизм выполнения SQL выдаст ошибку, если этот столбец будет удален из таблицы и запрос будет выполнен.
- Вы можете легче сканировать код, где используется этот столбец.
- Вы всегда должны писать запросы, чтобы вернуть наименьшее количество информации.
- Как говорят другие, если вы используете порядковый доступ к столбцу, вы никогда не должны использовать select *
- Если ваш оператор SQL объединяет таблицы, выберите *, чтобы получить все столбцы из всех таблиц в объединении
Следствием является то, что с помощью select *
...
- Столбцы, используемые приложением, непрозрачны
- Администраторы баз данных и их профилировщики запросов не могут помочь в низкой производительности вашего приложения
- Код становится более хрупким, когда происходят изменения
- Ваша база данных и сеть страдают, потому что они возвращают слишком много данных (I/O)
- Оптимизация ядра СУБД минимальна, поскольку вы возвращаете все данные независимо (логично).
Написание правильного SQL так же просто, как написание Select *
, Таким образом, настоящий ленивый человек пишет правильный SQL, потому что он не хочет пересматривать код и пытаться вспомнить, что он делал, когда делал это. Они не хотят объяснять администратору базы данных каждый бит кода. Они не хотят объяснять своим клиентам, почему приложение работает как собака.
Если ваш код зависит от расположения столбцов в определенном порядке, ваш код сломается, когда в таблицу внесены изменения. Кроме того, при выборе * вы, возможно, слишком много извлекаете из таблицы, особенно если в таблице есть двоичное поле.
То, что вы сейчас используете все столбцы, не означает, что кто-то другой не собирается добавлять дополнительный столбец в таблицу.
Это также добавляет накладные расходы к кешированию выполнения плана, так как ему нужно извлечь метаданные о таблице, чтобы узнать, какие столбцы находятся в *.
Одна из основных причин заключается в том, что если вы когда-либо добавляете / удаляете столбцы из таблицы, любой запрос / процедура, выполняющая вызов SELECT *, теперь будет получать больше или меньше столбцов данных, чем ожидалось.
Обходным путем вы нарушаете правило модульности о строгой типизации, где это возможно. Явное почти всегда лучше.
Даже если вам сейчас нужен каждый столбец в таблице, позже можно будет добавить больше, которые будут сноситься каждый раз, когда вы выполняете запрос, что может снизить производительность. Это ухудшает производительность, потому что
- Вы тянете больше данных по проводам; а также
- Потому что вы можете потерять способность оптимизатора извлекать данные прямо из индекса (для запросов по столбцам, которые являются частью индекса), а не выполнять поиск в самой таблице.
Когда использовать выберите *
Когда вам явно НУЖЕН каждый столбец в таблице, в отличие от необходимости каждого столбца в таблице, ЧТО СУЩЕСТВУЕТ ВО ВРЕМЯ, ВЫ ЗАПИСАЛИ ЗАПРОС. Например, если вы писали приложение для управления БД, которое должно было отображать все содержимое таблицы (каким бы оно ни было), вы могли бы использовать этот подход.
Есть несколько причин:
- Если количество столбцов в базе данных изменяется, и ваше приложение ожидает, что будет определенное число...
- Если порядок столбцов в базе данных изменяется, и ваше приложение ожидает, что они будут в определенном порядке...
- Память накладных расходов. 8 ненужных столбцов INTEGER добавят 32 байта потраченной памяти. Звучит не так уж и много, но это для каждого запроса, и INTEGER - это один из типов маленьких столбцов... дополнительные столбцы, скорее всего, будут столбцами VARCHAR или TEXT, которые складываются быстрее.
- Сетевые накладные расходы. Связано с нехваткой памяти: если я выполняю 30000 запросов и имею 8 ненужных столбцов INTEGER, я трачу 960 КБ пропускной способности. Колонки VARCHAR и TEXT, вероятно, будут значительно больше.
Примечание: я выбрал INTEGER в приведенном выше примере, потому что они имеют фиксированный размер 4 байта.
Если ваше приложение получает данные с помощью SELECT * и структура таблицы в базе данных изменяется (скажем, столбец удален), ваше приложение не будет работать в каждом месте, где вы ссылаетесь на пропущенное поле. Если вместо этого вы включите все столбцы в свой запрос, ваше приложение сломается (надеюсь) в одном месте, где вы изначально получили данные, что упростит исправление.
При этом существует ряд ситуаций, в которых SELECT * желателен. Одна из них - это ситуация, с которой я сталкиваюсь все время, когда мне нужно скопировать всю таблицу в другую базу данных (например, с SQL Server на DB2). Другим является приложение, написанное для общего отображения таблиц (т.е. без каких-либо знаний о какой-либо конкретной таблице).
Я на самом деле заметил странное поведение, когда я использовал select *
в представлениях в SQL Server 2005.
Запустите следующий запрос, и вы поймете, что я имею в виду.
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
[id] [int] IDENTITY(1,1) NOT NULL,
[A] [varchar](50) NULL,
[B] [varchar](50) NULL,
[C] [varchar](50) NULL
) ON [PRIMARY]
GO
insert into dbo.starTest
select 'a1','b1','c1'
union all select 'a2','b2','c2'
union all select 'a3','b3','c3'
go
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vStartest]'))
DROP VIEW [dbo].[vStartest]
go
create view dbo.vStartest as
select * from dbo.starTest
go
go
IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vExplicittest]'))
DROP VIEW [dbo].[vExplicittest]
go
create view dbo.[vExplicittest] as
select a,b,c from dbo.starTest
go
select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicitTest
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
[id] [int] IDENTITY(1,1) NOT NULL,
[A] [varchar](50) NULL,
[B] [varchar](50) NULL,
[D] [varchar](50) NULL,
[C] [varchar](50) NULL
) ON [PRIMARY]
GO
insert into dbo.starTest
select 'a1','b1','d1','c1'
union all select 'a2','b2','d2','c2'
union all select 'a3','b3','d3','c3'
select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicittest
Сравните результаты двух последних утверждений. Я считаю, что то, что вы увидите, является результатом выбора столбцов Select * по индексу, а не по имени.
Если вы восстановите вид, он снова будет работать нормально.
РЕДАКТИРОВАТЬ
Я добавил отдельный вопрос: * "выбрать * из таблицы" против "выбрать colA, colB и т. Д. Из таблицы" - интересное поведение в SQL Server 2005 *, чтобы подробнее рассмотреть это поведение.
SELECT * не всегда зло. По моему, по крайней мере. Я использую его довольно часто для динамических запросов, возвращающих всю таблицу, плюс некоторые вычисляемые поля.
Например, я хочу вычислить географические геометрии из "нормальной" таблицы, то есть таблицы без какого-либо геометрического поля, но с полями, содержащими координаты. Я использую postgresql, и его пространственное расширение postgis. Но принцип применяется для многих других случаев.
Пример:
таблица мест с координатами, хранящимися в полях, помеченных x, y, z:
CREATE TABLE Places (place_id integer, x числовой (10, 3), y числовой (10, 3), z числовой (10, 3), описание varchar);
давайте накормим его несколькими примерами значений:
ВСТАВИТЬ INTO места (place_id, x, y, z, описание) VALUES
(1, 2.295, 48.863, 64, "Paris, Place de l\'Étoile"),
(2, 2.945, 48.858, 40, "Париж, Эйфелева башня"),
(3, 0.373, 43.958, 90, "Condom, Cathédrale St-Pierre");Я хочу иметь возможность отобразить содержимое этой таблицы, используя некоторый ГИС-клиент. Обычным способом является добавление поля геометрии в таблицу и построение геометрии на основе координат. Но я бы предпочел получить динамический запрос: таким образом, когда я меняю координаты (исправления, большую точность и т. Д.), Отображаемые объекты фактически перемещаются динамически. Итак, вот запрос с помощью SELECT *:
СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ПРОСМОТР place_points AS
ВЫБРАТЬ *,
GeomFromewkt ('SRID = 4326; ТОЧКА ('|| x || ' ' || y || ' ' || z || ')')
С места;Обратитесь к postgis, для использования функции GeomFromewkt().
Вот результат:
SELECT * FROM place_points;
place_id | х | у | z | описание | geomfromewkt ----------+-------+--------+--------+------------------------------+-------------------------------------------------------------------- 1 | 2,295 | 48,863 | 64.000 | Париж, Площадь Этуаль | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040 2 | 2.945 | 48,858 | 40.000 | Париж, Эйфелева башня | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440 3 | 0,373 | 43,958 | 90.000 | Презерватив, собор Сен-Пьер | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640 (3 лини)
Крайний правый столбец теперь может использоваться любой ГИС-программой для правильного отображения точек.
- Если в будущем некоторые поля будут добавлены в таблицу: не беспокойтесь, мне просто нужно снова запустить то же определение VIEW.
Я бы хотел, чтобы определение VIEW могло быть сохранено "как есть" с помощью *, но, с другой стороны, дело обстоит иначе: это то, как он хранится внутри postgresql:
ВЫБРАТЬ мест.позиция_ мест, мест.x, мест.й, мест.з, мест.десь, мест.десь, geomfromewkt(((((('SRID=4326; ТОЧКА ('::text || местах.х) || ' ':: текст) || местах.й) || ' ':: текст) || места.z) || ')':: текст) AS geomfromewkt ОТ мест;
Когда вы указываете столбцы, вы также привязываете себя к определенному набору столбцов и делаете себя менее гибким, заставляя Feuerstein переворачиваться, ну, где бы он ни был. Просто мысль.
Вы можете объединить две таблицы и использовать столбец A из второй таблицы. Если позже вы добавите столбец A к первой таблице (с тем же именем, но, возможно, с другим значением), вы, скорее всего, получите значения из первой таблицы, а не второй, как ранее. Этого не произойдет, если вы явно укажете столбцы, которые хотите выбрать.
Конечно, указание столбцов также иногда вызывает ошибки, если вы забыли добавить новые столбцы в каждое предложение select. Если новый столбец не нужен каждый раз, когда выполняется запрос, может пройти некоторое время, прежде чем ошибка будет замечена.
Я понимаю, куда вы идете в отношении преждевременной оптимизации, но на самом деле это только доходит до точки. Цель состоит в том, чтобы избежать ненужной оптимизации в начале. Ваши таблицы не проиндексированы? Вы бы использовали nvarchar(4000) для хранения почтового индекса?
Как уже отмечали другие, есть и другие плюсы при указании каждого столбца, который вы собираетесь использовать в запросе (например, ремонтопригодность).
Чтобы ответить на ваш вопрос напрямую: не используйте "SELECT *", когда он делает ваш код более уязвимым к изменениям в базовых таблицах. Ваш код должен ломаться только тогда, когда в таблицу вносятся изменения, которые напрямую влияют на требования вашей программы.
Ваше приложение должно использовать уровень абстракции, предоставляемый реляционным доступом.
Нормально когда делаешь exists(select * ...)
так как это никогда не расширяется. В противном случае это действительно полезно только при изучении таблиц с временными выбранными утверждениями или если у вас был определен CTE, указанный выше, и вы хотите каждый столбец без повторного ввода их всех.
Я не использую SELECT * просто потому, что приятно видеть и знать, какие поля я получаю.
Просто, чтобы добавить одну вещь, которую никто не упомянул. Select *
возвращает все столбцы, кто-то может позже добавить столбец, который вы не обязательно хотите, чтобы пользователи могли видеть, например, кто в последний раз обновил данные или отметку времени или заметки, которые должны видеть только все пользователи, не все пользователи и т. д.
Кроме того, при добавлении столбца следует проанализировать влияние на существующий код и рассмотреть вопрос о необходимости внесения изменений в зависимости от того, какая информация хранится в столбце. Используя select *
этот обзор часто пропускается, потому что разработчик будет предполагать, что ничего не сломается. И на самом деле ничто явно не может сломаться, но теперь запросы могут начать возвращать не ту вещь. Тот факт, что ничего явно не нарушается, не означает, что в запросах не должно быть изменений.
Даже если вы используете каждый столбец, но адрес строки массива по числовому индексу, у вас будут проблемы, если позже вы добавите еще одну строку.
Так что в основном это вопрос ремонтопригодности! Если вы не используете селектор *, вам не придется беспокоиться о своих запросах.
Выбор только тех столбцов, которые вам нужны, уменьшает размер набора данных в памяти и, следовательно, ускоряет работу приложения.
Кроме того, многие инструменты (например, хранимые процедуры) также кэшируют планы выполнения запросов. Если позже вы добавите или удалите столбец (особенно легко, если вы выбираете представление), инструмент часто выдает ошибку, если не возвращает ожидаемые результаты.
Это делает ваш код более двусмысленным и более сложным в обслуживании; потому что вы добавляете дополнительные неиспользуемые данные в домен, и не ясно, что вы намеревались, а какие нет. (Это также предполагает, что вы можете не знать или не заботиться.)
Как правило, неправильно использовать 'select *' внутри представлений, потому что вы будете вынуждены перекомпилировать представление в случае изменения столбца таблицы. Изменяя базовые столбцы таблицы представления, вы получите ошибку для несуществующих столбцов, пока не вернетесь и не перекомпилируете.
Потому что "select * " будет тратить память, когда вам не нужны все поля. Но для сервера sql их производительность одинакова.