По какой причине не использовать select *?

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

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

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

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

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

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
137
0
28 064
20
Перейти к ответу Данный вопрос помечен как решенный

Ответы 20

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

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

dkretz 26.11.2008 22:55

Но все пишут код, который требует, чтобы программисты знали, какие данные возвращаются. Вы не можете Ctrl + F имя своего столбца, если оно скрыто в SELECT *.

Lotus Notes 07.05.2011 01:07

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

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

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

Хороший ответ, но я бы изменил «код сломается» на «код МОЖЕТ сломаться». Вот в чем настоящая проблема: использование "select *" не ВСЕГДА приводит к критическим изменениям. И когда разрыв действительно случается, это обычно сильно отделено от использования, которое в конечном итоге оказывается сломанным.

BQ. 26.11.2008 19:32

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

MusiGenesis 26.11.2008 19:41

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

dkretz 26.11.2008 22:54

@doofledorfer - никогда не говори никогда. Доступ к порядковым столбцам быстрее, и иногда это практично. Использование select * - большая ошибка, чем использование порядкового доступа.

Robert Paulson 27.11.2008 00:28

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

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

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

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

Причин несколько:

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

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

1 и 2 будут запахом кода, а 3 и 4 звучат как преждевременная оптимизация.

NikkyD 19.12.2016 19:47

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

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

Вопрос не в том, что «желательно ли * когда-либо», поэтому вторая часть вашего ответа не имеет значения. Вопрос гласит, что использование 'select *' должно быть предпочтительнее, что, конечно, полная чушь.

Robert Paulson 27.11.2008 00:31

Да, моя 2-я часть не имеет значения. OQ изменил вопрос, указав, что SELECT * предпочтительнее, и да, это какая-то чушь.

MusiGenesis 27.11.2008 01:15

Ах да, извините - вопрос изменил свое направление после вашего ответа.

Robert Paulson 27.11.2008 22:37

Все в порядке. Даже Моцарт был редактором (stackoverflow.com/questions/292682/…). В моем исходном сообщении говорилось, что использование SELECT * привело к каннибализму. :)

MusiGenesis 27.11.2008 23:30

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

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

Это нормально, когда ты делаешь exists(select * ...), потому что он никогда не расширяется. В противном случае это действительно полезно только при изучении таблиц с временными параметрами выбора или если у вас есть CTE, определенный выше, и вы хотите, чтобы каждый столбец не вводил их все снова.

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

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

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

РЕДАКТИРОВАТЬ

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

Это делает ваш код более неоднозначным и сложным в обслуживании; потому что вы добавляете в домен лишние неиспользуемые данные, и неясно, что вы намеревались, а какие нет. (Это также предполагает, что вы можете не знать или не заботиться.)

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

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

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

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

Ответ принят как подходящий

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

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


Так что выбор столбцов - это не преждевременная оптимизация. Кое-что не в моей голове ....

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

Следствие - использование select * ...

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

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

В вашем первом разделе пункт 5 должен гласить: «select * дает вам все столбцы из всех таблиц в объединении». Во втором разделе пункты №2 и №5 не обязательно верны, и их не следует указывать в качестве причин не использовать «select *».

jimmyorr 18.02.2009 17:50

@uglysmurf - спасибо за исправление, но что касается 2 и 5 - хотя они не обязательно могут быть верными для всех баз данных / dba во всех случаях, я считаю, что они важны и действительны для большинства случаев, и оставлю их. Использование 'select *' никогда не облегчало работу dba.

Robert Paulson 18.02.2009 23:45

Классный пост! В качестве примечания, я считаю, что единственное место, где SELECT * действительно действителен, - это внутри подзапросов EXISTS () (где он в любом случае оптимизирован для SELECT 1) и в коде отладки внутри хранимых процедур, который обычно никогда не запускается, если не передан специальный параметр.

Joe Pineda 19.02.2009 00:01

Я бы сказал, что № 3 (хрупкий код) на самом деле не соответствует действительности. В зависимости от реализации Select * может сделать его МЕНЬШЕ хрупким, но я не понимаю, как это может быть еще хуже.

JohnFx 19.02.2009 01:45

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

Robert Paulson 19.02.2009 02:10

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

mavnn 12.05.2009 17:33

@mavnn, w.r.t. Боюсь, что это проблема семантики, связанная с моим выбором слова «хрупкий». Мое последнее слово - сказать, что в любом случае это не имеет большого значения. Единственный сценарий - переименование / удаление столбцов. Вы просто перемещаете разрыв с момента выполнения sql (явным) по сравнению с разрывом, когда результаты потребляются. Способ использования результата запроса может различаться, и код может или не может автоматически завершиться ошибкой, но механизм выполнения sql определенно завершится с ошибкой с недопустимым sql. Так вам помог select *? Явный отказ IMO ближе к БД для проблемы с БД лучше. Спасибо

Robert Paulson 13.05.2009 01:56

А как насчет SELECT * FROM (SELECT TABLE1.id, TABLE1.count UNION SELECT TABLE2.id, TABLE2.count) AS AGG, где AGG.id и AGG.count используются позже? Если я не укажу эти столбцы вручную, отключит ли база данных оптимизацию для этого (части) запроса?

izogfif 26.12.2020 09:17

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

Robert Paulson 11.01.2021 06:53
  1. Окольными путями вы нарушаете правило модульности об использовании по возможности строгий набор текста. Явный почти всегда лучше.

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

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

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

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

В другой раз использовать SELECT * было бы, когда вы выполняете тестовые запросы с помощью клиента db.

cdmckay 18.02.2009 23:51

Это кажется странным исключением, учитывая контекст вопроса. В чем преимущество этого для тестовых запросов, кроме экономии времени на вводе текста?

JohnFx 19.02.2009 01:43

Также SELECT * FROM (SELECT a, b, c FROM table) в порядке.

kmkaplan 19.06.2018 13:37

Я не использую SELECT * просто потому, что приятно видеть и знать, какие поля я получаю.

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

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

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

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

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

NotMe 15.02.2012 02:52

Автор книг О'Рейли по PL / SQL. Попробуйте поискать в Google "feuerstein sql" вместо просто "feuerstein".

orbfish 21.02.2012 22:58

потому что "select *" тратит память, когда вам не нужны все поля. Но для sql server их производительность такая же.

ВЫБРАТЬ * не всегда зло. По крайней мере, на мой взгляд. Я довольно часто использую его для динамических запросов, возвращающих всю таблицу плюс некоторые вычисленные поля.

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

Пример:

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

    СОЗДАТЬ ТАБЛИЦУ мест (place_id integer, x numeric (10, 3), y numeric (10, 3), z numeric (10, 3), description varchar);

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

    ВСТАВИТЬ места (place_id, x, y, z, description) ЦЕННОСТИ (1, 2.295, 48.863, 64, Париж, площадь Этуаль),
    (2, 2.945, 48.858, 40, Париж, Эйфелева башня),
    (3, 0.373, 43.958, 90, «Презерватив, собор Сен-Пьер»);

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

    СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ПРОСМОТР places_points AS
    ВЫБРАТЬ *,
    GeomFromewkt ('SRID = 4326; POINT (' || x || '' || y || '' || z || ')')
    С мест;

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

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

    ВЫБРАТЬ * FROM places_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 хранит его внутри так:

ВЫБЕРИТЕ place.place_id, places.x, places.y, places.z, places.description, geomfromewkt (((((('SRID = 4326; POINT (' :: text || places.x) || '': : text) || places.y) || '' :: text) || places.z) || ')' :: text) AS geomfromewkt FROM places;

Другие вопросы по теме