Вчера я обсуждал с программистом «хобби» (я сам профессиональный программист). Мы сталкивались с некоторыми из его работ, и он сказал, что он всегда запрашивает все столбцы в своей базе данных (даже на / в производственном сервере / коде).
Я пытался убедить его не делать этого, но пока не получилось. На мой взгляд, программист должен запрашивать только то, что действительно нужно, ради «красивости», эффективности и трафика. Я ошибаюсь с моей точки зрения?
Ответы:
Подумайте о том, что вы получаете, и как вы связываете это с переменными в вашем коде.
Теперь подумайте, что происходит, когда кто-то обновляет схему таблицы, чтобы добавить (или удалить) столбец, даже тот, который вы не используете напрямую.
Использование select *, когда вы печатаете запросы вручную, не подходит, когда вы пишете запросы для кода.
источник
Изменения схемы
foo
, а другая таблица в запросе добавляет столбецfoo
, то, как это обрабатывается, может вызвать проблемы при попытке получить правильныйfoo
столбец.В любом случае, изменение схемы может вызвать проблемы с извлечением данных.
Далее рассмотрим, удаляется ли использованный столбец из таблицы. Все
select * from ...
еще работает, но выдает ошибки при попытке извлечь данные из набора результатов. Если в запросе указан столбец, запрос выдаст ошибку, вместо этого дается четкое указание на то, в чем и где проблема.Затраты на данные
С некоторыми столбцами может быть связано значительное количество данных. Выбор назад
*
потянет все данные. Да, вот чтоvarchar(4096)
на 1000 строк, которые вы выбрали, дает вам дополнительные 4 мегабайта данных, которые вам не нужны, но все равно отправляются по проводам.В связи с изменением схемы, varchar может не существовать там, когда вы впервые создали таблицу, но теперь она там.
Неспособность передать намерение
Когда вы выбираете обратно
*
и получаете 20 столбцов, но вам нужно только 2 из них, вы не передаете смысл кода. Глядя на запрос, который делает,select *
никто не знает, каковы его важные части. Могу ли я изменить запрос на использование этого другого плана, чтобы ускорить его, не включая эти столбцы? Я не знаю, потому что цель того, что возвращает запрос, не ясна.Давайте посмотрим на некоторые скрипты SQL, в которых рассматриваются эти изменения схемы .
Во-первых, исходная база данных: http://sqlfiddle.com/#!2/a67dd/1
DDL:
SQL:
И столбцы вы получаете обратно являются
oneid=1
,data=42
,twoid=2
, иother=43
.Что произойдет, если я добавлю столбец к первой таблице? http://sqlfiddle.com/#!2/cd0b0/1
И мои результаты от того же самого запроса , как и раньше являются
oneid=1
,data=42
,twoid=2
, иother=foo
.Изменение в одной из таблиц нарушает значения a,
select *
и внезапно ваша привязка 'other' к int приведет к ошибке, и вы не знаете почему.Если вместо этого ваш оператор SQL был
Изменение в таблице один не нарушило бы ваши данные. Этот запрос выполняется одинаково до изменения и после изменения.
индексирование
Когда вы делаете a,
select * from
вы вытягиваете все строки из всех таблиц, которые соответствуют условиям. Даже таблицы, которые вам действительно безразличны. Хотя это означает, что передается больше данных, существует еще одна проблема с производительностью, которая скрывается в стеке.Индексы. (связано с SO: Как использовать индекс в операторе выбора? )
Если вы извлекаете много столбцов, оптимизатор плана базы данных может игнорировать использование индекса, потому что вам все равно потребуется извлекать все эти столбцы, и потребуется больше времени, чтобы использовать индекс, а затем извлечь все столбцы в запросе. чем было бы просто сделать полное сканирование таблицы.
Если вы просто выбираете, скажем, фамилию пользователя (которую вы много делаете, и поэтому у вас есть индекс), база данных может выполнять сканирование только по индексу ( postgres wiki index only scan , mysql full table scan vs full сканирование индекса , индекс-Only Scan: Избежание таблицы Access ).
Существует довольно много оптимизаций относительно чтения только из индексов, если это возможно. Информация может быть получена быстрее на каждой странице индекса, потому что вы также извлекаете ее меньше - вы не используете все остальные столбцы для
select *
. При сканировании только по индексу возможно возвращать результаты в 100 раз быстрее (источник: Select * is bad ).Это не говорит о том, что полное индексное сканирование - это хорошо, это все же полное сканирование, но это лучше, чем полное сканирование таблицы. Как только вы начинаете преследовать все способы, которые
select *
ухудшают производительность, вы продолжаете находить новые.Связанное чтение
источник
select *
?Еще одна проблема: если это
JOIN
запрос, и вы извлекаете результаты запроса в ассоциативный массив (как это может быть в PHP), он подвержен ошибкам.Дело в том, что
foo
имеет столбцыid
иname
bar
имеет столбцыid
иaddress
,SELECT * FROM foo JOIN bar ON foo.id = bar.id
угадайте, что происходит, когда кто-то добавляет столбец
name
вbar
таблицу.Код внезапно перестанет работать должным образом, потому что теперь
name
столбец появляется в результатах дважды, и если вы сохраняете результаты в массиве, данные из secondname
(bar.name
) будут перезаписывать firstname
(foo.name
)!Это довольно неприятная ошибка, потому что это очень неочевидно. Чтобы понять это, может потребоваться некоторое время, и человек, добавляющий в таблицу еще один столбец, никак не мог предвидеть такой нежелательный побочный эффект.
(Правдивая история).
Поэтому не используйте
*
, управляйте тем, какие столбцы вы извлекаете, и используйте псевдонимы, где это необходимо.источник
SELECT
предложению, и это, когда вы надеетесь найти имя не уникальным. Кстати, я не думаю, что это так редко в системах с большими базами данных. Как я уже сказал, однажды я потратил пару часов на охоту за этой ошибкой в большом коде PHP-кода. И я нашел другой случай только сейчас: stackoverflow.com/q/17715049/168719Во многих случаях запросы к каждому столбцу могут быть вполне законными.
Всегда запрашивать не каждый столбец.
Это больше работы для вашего механизма базы данных, который должен отключиться и покопаться в своих внутренних метаданных, чтобы определить, с какими столбцами ему нужно иметь дело, прежде чем он сможет приступить к реальной работе по фактическому получению данных и их отправке обратно вам. ОК, это не самая большая нагрузка в мире, но системные каталоги могут стать заметным узким местом.
Это больше работы для вашей сети, потому что вы вытягиваете любое количество полей, когда вам может понадобиться только одно или два из них. Если кто-то [еще] идет и добавляет пару дюжин дополнительных полей, каждое из которых содержит большие куски текста, ваша пропускная способность внезапно падает через пол - без видимой причины. Это усугубляется, если ваше предложение «где» не особенно хорошо, и вы также вытягиваете много строк - это потенциально много данных, разбирающихся с вами по сети (т.е. это будет медленно).
Это больше работы для вашего приложения, необходимость откатить и сохранить все эти дополнительные данные, которые, вероятно, не заботятся.
Вы рискуете изменить порядок столбцов. Хорошо, вам не нужно беспокоиться об этом (и вы не будете беспокоиться об этом, если выберете только нужные вам столбцы), но, если вы идете, получите их все сразу, и кто-то [еще] решит изменить порядок столбцов в таблице. Этот тщательно продуманный CSV-экспорт, который вы даете на счета по коридору, внезапно превращается в неудачу - опять же, без видимой причины.
Кстати, я сказал «кто-то [еще]» пару раз выше. Помните, что базы данных по своей сути многопользовательские; Вы можете не иметь контроля над ними, как вы думаете.
источник
TOP
ограничение; Я не уверен, насколько это важно, если код читает столько раз, сколько нужно для отображения, а затем удаляет запрос. Я думаю, что ответы на запросы обрабатываются несколько лениво, хотя я не знаю деталей. В любом случае, я думаю, что вместо того, чтобы говорить, что это "не законно", было бы лучше сказать "... законно гораздо меньше"; По сути, я бы суммировал законные случаи как случаи, когда у пользователя было бы лучшее представление о том, что является значимым, чем программист.Краткий ответ: это зависит от того, какую базу данных они используют. Реляционные базы данных оптимизированы для извлечения данных вам нужны в быстром, надежном и атомном пути. В больших наборах данных и сложных запросах это намного быстрее и, вероятно, безопаснее, чем SELECTing *, и делает эквивалент соединений на стороне «кода». Хранилища ключей-значений могут не иметь таких реализованных функций или быть недостаточно зрелыми для использования в производстве.
Тем не менее, вы по-прежнему можете заполнять любую структуру данных, которую вы используете, с помощью SELECT * и обрабатывать все остальное в коде, но вы найдете узкие места в производительности, если захотите масштабировать.
Самое близкое сравнение - сортировка данных: вы можете использовать быструю сортировку или пузырьковую сортировку, и результат будет правильным. Но не будет оптимизирован, и определенно будут проблемы, когда вы вводите параллелизм и должны сортировать атомарно.
Конечно, дешевле добавлять ОЗУ и ЦП, чем вкладывать средства в программиста, который может выполнять запросы SQL и даже имеет смутное представление о том, что такое JOIN.
источник
Customer customer = this._db.Customers.Where( “it.ID = @ID”, new ObjectParameter( “ID”, id ) ).First();
см. « Время обижаться» на стр. 2.var cmd = db.CreateCommand(); cmd.CommandText = "SELECT TOP 1 * FROM Customers WHERE ID = @ID"; cmd.Parameters.AddWithValue("@ID", id); var result = cmd.ExecuteReader();
...., а затем приступить к созданию Customer из каждой строки. LINQ бьет штаны от этого.var customer = _db.Customers.Where(it => it.id == id).First();
.ИМО, о том, чтобы быть явным против неявного. Когда я пишу код, я хочу, чтобы он работал, потому что я заставил его работать, а не только потому, что все его части оказались там. Если вы запрашиваете все записи и ваш код работает, то у вас будет тенденция двигаться дальше. Позже, если что-то изменится, и теперь ваш код не работает, это большая проблема для отладки множества запросов и функций, ищущих значение, которое должно быть там, и единственная ссылка на значения - *
Также в N-уровневом подходе все еще лучше изолировать сбои схемы базы данных на уровне данных. Если ваш уровень данных переходит * в бизнес-логику и, скорее всего, в уровень представления, вы расширяете область отладки в геометрической прогрессии.
источник
select *
гораздо хуже!потому что если таблица получает новые столбцы, вы получаете все эти столбцы, даже если они вам не нужны. с
varchars
этим может стать много лишних данных, которые нужно перемещать из БДнекоторые оптимизации БД могут также извлекать записи не фиксированной длины в отдельный файл, чтобы ускорить доступ к частям фиксированной длины, используя select *, что побеждает цель этого
источник
Помимо накладных расходов, чего вы в первую очередь хотите избежать, я бы сказал, что как программист вы не зависите от порядка столбцов, определенного администратором базы данных. Вы выбираете каждый столбец, даже если вам нужны все.
источник
Я не вижу причин, по которым вы не должны использовать его для целей сборки - извлечь все столбцы из базы данных. Я вижу три случая:
Столбец добавляется в базу данных, и вы хотите его в коде также. a) С * произойдет сбой с правильным сообщением. б) Без * будет работать, но не будет делать то, что вы ожидаете, что довольно плохо.
Столбец добавляется в базу данных, и вы не хотите его в коде. а) С * не удастся; это означает, что * больше не применяется, так как его семантика означает «получить все». б) без * будет работать.
Столбец удален. Код не работает в любом случае.
Теперь наиболее распространенным случаем является случай 1 (поскольку вы использовали *, что означает все, что вы, скорее всего, хотите всего); без * вы можете иметь код, который работает нормально, но не делает то, что ожидалось, что намного, гораздо хуже, чем код, который завершается ошибкой с правильным сообщением об ошибке .
Я не принимаю во внимание код, который извлекает данные столбца на основе индекса столбца, который на мой взгляд подвержен ошибкам. Гораздо логичнее получать его по имени столбца.
источник
Select *
было задумано скорее как удобство для специальных запросов, а не для целей разработки приложений. Или для использования в статистических конструкциях, подобныхselect count(*)
которым позволяет обработчику запросов решать, использовать ли индекс, какой индекс использовать и т. Д., И вы не возвращаете никаких фактических данных столбца. Или для использования в таких предложениях, какwhere exists( select * from other_table where ... )
, что опять-таки является приглашением в механизм запросов самостоятельно выбирать наиболее эффективный путь, а подзапрос используется только для ограничения результатов основного запроса. И т.д.select *
имеет семантику извлечения всех столбцов; если ваше приложение действительно нуждается в этом, я не вижу причин, почему бы не использовать его. Можете ли вы указать на какую-то ссылку (Oracle, IBM, Microsoft и т. Д.), В которой упоминается, что цельюselect *
сборки была не получение всех столбцов?select *
существует для извлечения всех столбцов ... в качестве удобной функции, для специальных запросов, а не потому, что это отличная идея в производственном программном обеспечении. Причины уже достаточно хорошо освещены в ответах на этой странице, поэтому я не создал свой собственный подробный ответ: •) Проблемы с производительностью, многократное распределение данных по сети, которые вы никогда не используете, •) проблемы с алиасами столбцов, •) ошибки оптимизации плана запросов (в некоторых случаях отказ от использования индексов), •) неэффективный серверный ввод-вывод в случаях, когда ограниченный выбор мог использовать только индексы и т. Д.select *
в реальном производственном приложении, но природа крайнего случая состоит в том, что это не общий случай. :-)select *
; то, что я говорил, если вам действительно нужны все столбцы, я не вижу причин, почему вы не должны их использоватьselect *
; хотя немногие должны быть сценарии, где нужны все столбцы.Подумайте об этом так ... если вы запрашиваете все столбцы из таблицы, в которой есть всего несколько небольших строковых или числовых полей, то это 100 000 данных. Плохая практика, но она сработает. Теперь добавьте одно поле, которое содержит, скажем, изображение или документ размером 10 МБ. теперь ваш быстродействующий запрос немедленно и загадочно начинает работать плохо, просто потому, что в таблицу было добавлено поле ... вам может не понадобиться этот огромный элемент данных, но, поскольку вы это сделали,
Select * from Table
вы все равно получите его.источник