В чем причина не использовать select *? - PullRequest
131 голосов
/ 26 ноября 2008

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

Если я все равно собираюсь использовать все столбцы, почему бы мне не использовать SELECT *?

Даже с учетом вопроса * SQL-запрос - выберите * в представлении или выберите col1, col2, ... colN в представлении *, я не думаю, что это точный дубликат при приближении к проблеме с немного другой точки зрения.

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

Тем не менее, есть ли основная проблема не использовать SELECT *?

Ответы [ 20 ]

161 голосов
/ 27 ноября 2008

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

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


Таким образом, выбор столбцов не является преждевременной оптимизацией. Несколько вещей с моей головы ....

  1. Если вы укажете столбцы в операторе SQL, механизм выполнения SQL выдаст ошибку, если этот столбец будет удален из таблицы и запрос будет выполнен.
  2. Вы можете легче сканировать код, где используется этот столбец.
  3. Вы всегда должны писать запросы, чтобы вернуть наименьшее количество информации.
  4. Как и другие отмечают, что если вы используете порядковый доступ к столбцу, вы никогда не должны использовать select *
  5. Если ваш оператор SQL объединяет таблицы, выбор * дает вам все столбцы из всех таблиц в объединении

Следствие состоит в том, что select * ...

  1. Используемые приложением столбцы непрозрачны
  2. Администраторы баз данных и их профилировщики запросов не могут помочь в низкой производительности вашего приложения
  3. Код становится более хрупким, когда происходят изменения
  4. Ваша база данных и сеть страдают, потому что они возвращают слишком много данных (I / O)
  5. Оптимизация ядра СУБД минимальна, поскольку вы возвращаете все данные независимо (логично).

Написание правильного SQL так же просто, как написание Select *. Поэтому настоящий ленивый человек пишет правильный SQL, потому что он не хочет пересматривать код и пытаться вспомнить, что он делал, когда делал это. Они не хотят объяснять администратору базы данных каждый бит кода. Они не хотят объяснять своим клиентам, почему приложение работает как собака.

42 голосов
/ 26 ноября 2008

Если ваш код зависит от расположения столбцов в определенном порядке, ваш код сломается, когда в таблицу внесены изменения. Кроме того, вы можете извлечь слишком много из таблицы при выборе *, особенно если в таблице есть двоичное поле.

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

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

23 голосов
/ 26 ноября 2008

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

16 голосов
/ 05 декабря 2008
  1. Окружным способом вы нарушаете правило модульности об использовании строгое печатание везде, где это возможно. Явный почти универсально лучше.

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

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

Когда использовать, выберите *

Когда вам явно НУЖДАЕТСЯ каждый столбец в таблице, а не каждый столбец в таблице, ЧТО СУЩЕСТВУЕТ В ТО ВРЕМЯ, ЧТО ВЫ ЗАПИШИЛИ ЗАПРОС. Например, если вы писали приложение для управления БД, которое должно было отображать все содержимое таблицы (каким бы оно ни было), вы могли бы использовать этот подход.

12 голосов
/ 26 ноября 2008

Есть несколько причин:

  1. Если количество столбцов в базе данных изменяется, и ваше приложение ожидает, что будет определенное число ...
  2. Если порядок столбцов в базе данных изменяется, и ваше приложение ожидает, что они будут в определенном порядке ...
  3. Память накладных расходов. 8 ненужных столбцов INTEGER добавят 32 байта потраченной памяти. Это звучит не так уж и много, но это для каждого запроса, и INTEGER - это один из небольших типов столбцов ... дополнительные столбцы, скорее всего, будут столбцами VARCHAR или TEXT, которые складываются быстрее.
  4. Сетевые издержки. Связано с нехваткой памяти: если я выполняю 30 000 запросов и имею 8 ненужных столбцов INTEGER, я трачу 960 КБ пропускной способности. Колонки VARCHAR и TEXT, вероятно, будут значительно больше.

Примечание: я выбрал INTEGER в приведенном выше примере, потому что они имеют фиксированный размер 4 байта.

7 голосов
/ 26 ноября 2008

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

При этом существует ряд ситуаций, в которых SELECT * желателен. Одна из них - это ситуация, с которой я сталкиваюсь все время, когда мне нужно скопировать всю таблицу в другую базу данных (например, с SQL Server на DB2). Другим является приложение, написанное для общего отображения таблиц (т.е. без знания какой-либо конкретной таблицы).

3 голосов
/ 26 ноября 2008

Я действительно заметил странное поведение, когда я использовал 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

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

Если вы перестроите вид, он снова будет работать нормально.

EDIT

Я добавил отдельный вопрос, * «выбрать * из таблицы» против «выбрать colA, colB и т. Д. Из таблицы», интересное поведение в SQL Server 2005 *, чтобы подробнее рассмотреть это поведение .

2 голосов
/ 26 ноября 2008

Вы можете объединить две таблицы и использовать столбец A из второй таблицы. Если позже вы добавите столбец A к первой таблице (с тем же именем, но, возможно, с другим значением), вы, скорее всего, получите значения из первой таблицы, а не второй, как ранее. Этого не произойдет, если вы явно укажете столбцы, которые хотите выбрать.

Конечно, указание столбцов также иногда вызывает ошибки, если вы забыли добавить новые столбцы в каждое предложение select. Если новый столбец не нужен при каждом выполнении запроса, может пройти некоторое время, прежде чем ошибка будет замечена.

2 голосов
/ 21 декабря 2012

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

Например, я хочу вычислить географические геометрии из «нормальной» таблицы, то есть таблицы без какого-либо геометрического поля, но с полями, содержащими координаты. Я использую postgresql, и его пространственное расширение postgis. Но принцип применим ко многим другим случаям.

Пример:

  • таблица мест с координатами, хранящимися в полях, помеченных x, y, z:

    CREATE TABLE Places (place_id integer, x цифра (10, 3), y цифра (10, 3), z цифра (10, 3), описание varchar);

  • давайте добавим несколько примеров значений:

    INSERT INTO place (place_id, x, y, z, описание) ЗНАЧЕНИЯ
    (1, 2,295, 48,863, 64, «Париж, площадь Этуаль»),
    (2, 2,945, 48,858, 40, «Париж, Эйфелева башня»),
    (3, 0,373, 43,958, 90, "Condom, Cathédrale St-Pierre");

  • Я хочу иметь возможность отобразить содержимое этой таблицы, используя некоторый ГИС-клиент. Обычным способом является добавление поля геометрии в таблицу и построение геометрии на основе координат. Но я бы предпочел получить динамический запрос: таким образом, когда я меняю координаты (исправления, большую точность и т. Д.), Отображаемые объекты фактически перемещаются динамически. Итак, вот запрос с SELECT *:

    СОЗДАТЬ ИЛИ ЗАМЕНИТЬ VIEW place_points AS
    SELECT *,
    GeomFromewkt ('SRID = 4326; ТОЧКА (' || x || '' || y || '' || z || ')')
    С места;

    Обратитесь к postgis, для использования функции GeomFromewkt ().

  • Вот результат:

    SELECT * FROM place_points;

 place_id |   x   |   y    |   z    |         description          |                            geomfromewkt                            
----------+-------+--------+--------+------------------------------+--------------------------------------------------------------------  
        1 | 2.295 | 48.863 | 64.000 | Paris, Place de l'Étoile     | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 | 2.945 | 48.858 | 40.000 | Paris, Tour Eiffel           | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 | 0.373 | 43.958 | 90.000 | Condom, Cathédrale St-Pierre | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3 lignes)

Крайний правый столбец теперь может использоваться любой ГИС-программой для правильного отображения точек.

  • Если в будущем некоторые поля будут добавлены в таблицу: не беспокойтесь, мне просто нужно снова запустить то же определение VIEW.

Хотелось бы, чтобы определение VIEW могло быть сохранено "как есть" с помощью *, но пока дело не в этом: это то, как оно внутренне хранится в postgresql:

ВЫБОР мест '' :: text) || place.y) || '' :: text) || place.z) || ')' :: text) AS geomfromewkt ОТ мест;

2 голосов
/ 26 ноября 2008

Я понимаю, куда вы идете в отношении преждевременной оптимизации, но это действительно только доходит до точки Цель состоит в том, чтобы избежать ненужных оптимизаций в начале. Ваши таблицы не проиндексированы? Вы бы использовали nvarchar (4000) для хранения почтового индекса?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...