Нито едно от двете предложени решения вероятно не е оптимално, НО решение 1 е НЕПРЕДСКАЗИМО и следователно СЪЩНОСТНО НЕДОСТАТОЧНО!
Едно от първите неща, които научавате, когато работите с големи бази данни, е, че „най-добрият начин“ за извършване на заявка често зависи от фактори (наричани мета-данни) в базата данни:
- Колко реда има.
- Колко таблици правите заявка.
- Размерът на всеки ред.
Поради това е малко вероятно да има сребърно решение за вашия проблем. Вашата база данни не е същата като моята, ще трябва да сравните различни оптимизации, ако имате нужда от най-добрата налична производителност.
Вероятно ще откриете, че прилагане и изграждане на правилни индекси (и разбирането на естествената реализация на индекси в MySQL) във вашата база данни прави много повече за вас.
Има някои златни правила със заявките, които рядко трябва да се нарушават:
- Не ги правете в циклични структури . Колкото и често да е изкушаващо, разходите за създаване на връзка, изпълнение на заявка и получаване на отговор са големи.
- Избягвайте
SELECT *
освен ако не е необходимо . Избирането на повече колони значително ще увеличи разходите за вашите SQL операции. - Познайте своите индекси . Използвайте
EXPLAIN
функция, така че да виждате кои индекси се използват, да оптимизирате заявките си, за да използвате наличното, и да създавате нови.
Поради това от двете бих отишъл за втората заявка (заменящ SELECT *
само с колоните, които искате), но вероятно има по-добри начини за структуриране на заявката, ако имате време за оптимизиране.
Скоростта обаче трябва да НЕ бъдете единственото ви съображение в това, има ГОЛЯМА причина да не използвате едно предложение:
ПРЕДСКАЗИМОСТЬ:защо заключванията за четене са добро нещо
Един от другите отговори предполага, че заключването на таблицата за дълъг период от време е лошо нещо и че следователно решението с множество заявки е добро.
Бих казал, че това не може да бъде по-далеч от истината . Всъщност бих твърдял, че в много случаи предсказуемостта на изпълнение на едно заключване SELECT
query е по-голям аргумент ЗА изпълнението на тази заявка, отколкото предимствата за оптимизация и скорост.
На първо място, когато стартираме SELECT
(само за четене) към база данни MyISAM или InnoDB (системи по подразбиране за MySQL), това, което се случва, е, че таблицата е заключена за четене. Това предотвратява извършването на операции WRITE на масата, докато блокирането за четене не бъде предадено (или нашия SELECT
заявката е завършена или неуспешна). Друго SELECT
заявките не са засегнати, така че ако изпълнявате многонишково приложение, те ще продължат да работят.
Това забавяне е ДОБРО. Защо, може да попитате? Цялост на релационни данни.
Да вземем пример:изпълняваме операция, за да получим списък с елементи, които в момента са в инвентара на група потребители на игра, така че правим това присъединяване:
SELECT * FROM `users` JOIN `items` ON `users`.`id`=`items`.`inventory_id` WHERE `users`.`logged_in` = 1;
Какво се случва, ако по време на тази операция на заявка потребител изтъргува артикул на друг потребител? Използвайки тази заявка, виждаме състоянието на играта такова, каквото е било, когато сме стартирали заявката:елементът съществува веднъж, в инвентара на потребителя, който го е имал, преди да изпълним заявката.
Но какво ще стане, ако го изпълним в цикъл?
В зависимост от това дали потребителят го е търгувал преди или след като прочетем неговите данни и в какъв ред четем инвентара на двамата играчи, има четири възможности:
- Елементът може да бъде показан в инвентара на първия потребител (сканиране на потребител B -> сканиране на потребител A -> търгуван артикул ИЛИ сканиране на потребител B -> сканиране на потребител A -> търгуван артикул).
- Елементът може да бъде показан в инвентара на втория потребител (търгуван артикул -> сканиране потребител A -> сканиране потребител B ИЛИ търгуван артикул -> сканиране потребител B -> сканиране потребител A).
- Елементът може да се показва в и двете инвентари (сканиране на потребител A -> търгуван артикул -> сканиране на потребител B).
- Елементът може да бъде показан в нито едно от двете от инвентара на потребителя (сканиране на потребител B -> търгувани артикули -> сканиране на потребител A).
Това означава, че няма да можем да предвидим резултатите от заявката или да гарантираме релационна цялост .
Ако планирате да дадете $5,000 на човека с артикул ID 1000000 в полунощ във вторник, надявам се, че имате $10k на ръка. Ако вашата програма разчита на това, че уникалните елементи са уникални, когато се правят моментни снимки, вероятно ще предизвикате изключение с този вид заявка.
Заключването е добро, защото повишавапредвидимостта и защитава целота на резултатите.
Забележка:Можете да принудите цикъл да се заключи с транзакция , но щесе бъдете по-бавни.
О, и накрая, ИЗПОЛЗВАЙТЕ ПОДГОТОВЛЕНИ ИЗЯВЛЕНИЯ!
Не трябва никога има изявление, което изглежда така:
mysqli_query("SELECT * FROM Table2 WHERE ColumnAId=" . $row['ColumnAId'], $con);
mysqli
има поддръжка за подготвени изявления
. Прочетете за тях и ги използвайте, те ще ви помогнат да избегнете нещо ужасно да се случи с вашата база данни
.