В чем причина не использовать select *? - программирование
Подтвердить что ты не робот

В чем причина не использовать select *?

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

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

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

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

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

4b9b3361

Ответ 1

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

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


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

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

Следствием является то, что используя select *...

  • Колонки, используемые приложением, непрозрачны
  • DBA и их профилировщики запросов не могут помочь вашему приложению иметь низкую производительность.
  • Код становится более хрупким, когда происходят изменения.
  • Ваша база данных и сеть страдают, потому что они возвращают слишком много данных (I/O)
  • Оптимизация движка базы данных минимальна, поскольку вы возвращаете все данные независимо (логически).

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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

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

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

Я действительно заметил странное поведение, когда использовал 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. Я считаю, что вы увидите результат Выбрать *, ссылаясь на столбцы по индексу вместо имени.

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

ИЗМЕНИТЬ

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

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

Ответ 11

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

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

Пример:

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

    CREATE TABLE (целое число place_id, x числовое (10, 3), y числовое (10, 3), z числовое (10, 3), описание varchar);

  • дайте ему несколько значений:

    INSERT INTO places (place_id, x, y, z, description) ЗНАЧЕНИЯ
    (1, 2,295, 48,863, 64, 'Paris, Place de l''Étoile'),
    (2, 2.945, 48.858, 40, "Париж, Эйфелева башня" ),
    (3, 0,373, 43,958, 90, "Презерватив, Cathédrale St-Pierre" );

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

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

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

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

    SELECT * 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:

SELECT places.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;

Ответ 12

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

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

Ответ 13

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

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

Ответ 14

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

Ответ 15

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

Ответ 16

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

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

Ответ 17

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

Ответ 18

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

Ответ 19

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

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

Ответ 20

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