вторник, 22 января 2019 г.

Оптимизация программ на PHP

Оптимизация программ на PHP

Автор: Дмитрий Бородин 
В этой статье на простых и очевидных примерах рассказано о некоторых способах оптимизировать любую (готовую) программу, не меняя ни одного алгоритма. Для такой оптимизации можно даже написать программу для автоматического выполнения всех рекомендаций, все они очень простые (правда, для начала придется написать парсер пхп-кода).

Выносите $переменные из "текстовых строк" - ускорение 25-40%

Одна и таже операция присваивания (либо echo/print для вывода на экран) в зависимости от того, заключены ли переменные в кавычеки или нет, сильно влияет на скорость. В первом и втором вариантах добавлены пробелы, чтобы выравнять размер общего кода для парсинга.
  1. {$x="test".$test;    }
  2. {$x="test $test";    }
  3. {$x="test";$x.=$test;}
Переменная $test содержит строку "1234567890".
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N113.59113.591100.0%70.9%
speedspeed
test N215.06165.061640.9%100.0%
speedspeed
test N314.98704.987038.9%98.5%
speedspeed
Итак, никогда не пишите $a="$b", ибо это затормозит программу (в этой строке) на 40%.
Однако, если у вас большая строка, где много текста и переменных, различия в скорости уменьшаются, т.к. общие затраты на парсинг становятся намного больше, чем разные по эффективности команды. Но почему бы и не увеличить скорость программы (строк присваивания) почти на четверть таким простым методом?
  1. {$x="test ".$test." test ".$test." test ".$test;                }
  2. {$x="test $test test $test test $test";                         }
  3. {$x="test ";$x.=$test;$x="test ";$x.=$test;$x="test ";$x.=$test;}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N117.68947.689400.0%66.0%
speedspeed
test N219.55159.551524.2%82.0%
speedspeed
test N3111.650611.650651.5%100.0%
speedspeed

Короткие переменные не более 7 символов - ускорение 15%

Как влияет длина имен переменных на скорость программы? Если использовать очень длинные переменные - очевидно, что весьма сильно. Однако и с короткими именеми не все просто:
  1. {$x=1;}
  2. {$x2=1;}
  3. {$x03=1;}
  4. {$x004=1;}
  5. {$x0005=1;}
  6. {$x00006=1;}
  7. {$x000007=1;}
  8. {$x0000008=1;}
  9. {$x000000010=1;}
  10. {$x00000000012=1;}
  11. {$x0000000000014=1;}
  12. {$x000000000000016=1;}
  13. {$x0000000000000000000000000000032=1;}
выдает предсказуемый результат:
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N111.70001.700000.0%68.5%
speedspeed
test N211.70281.702800.2%68.6%
speedspeed
test N311.71821.718201.1%69.2%
speedspeed
test N411.72281.722801.3%69.4%
speedspeed
test N511.75361.753603.2%70.6%
speedspeed
test N611.75041.750403.0%70.5%
speedspeed
test N711.77991.779904.7%71.7%
speedspeed
test N811.96041.960415.3%78.9%
speedspeed
test N911.98651.986516.9%80.0%
speedspeed
test N1012.01192.011918.3%81.0%
speedspeed
test N1112.03022.030219.4%81.7%
speedspeed
test N1212.12882.128825.2%85.7%
speedspeed
test N1312.48352.483546.1%100.0%
speedspeed
Переменные от 32 символов могут тормознуть программу почти на половину.
Но если заполнять пробелами (" "), чтобы все строки "$x=1; ..." по длине занимали одно и тоже расстояние, то получается вот что:
  1. {$x=1;                                   }
  2. {$x2=1;                                  }
  3. {$x03=1;                                 }
  4. {$x004=1;                                }
  5. {$x0005=1;                               }
  6. {$x00006=1;                              }
  7. {$x000007=1;                             }
  8. {$x0000008=1;                            }
  9. {$x000000010=1;                          }
  10. {$x00000000012=1;                        }
  11. {$x0000000000014=1;                      }
  12. {$x000000000000016=1;                    }
  13. {$x0000000000000000000000000000032=1;    }
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N112.11672.116701.9%83.3%
speedspeed
test N212.07662.076600.0%81.7%
speedspeed
test N312.09372.093700.8%82.4%
speedspeed
test N412.08212.082100.3%81.9%
speedspeed
test N512.11452.114501.8%83.2%
speedspeed
test N612.09212.092100.7%82.3%
speedspeed
test N712.10762.107601.5%82.9%
speedspeed
test N812.30582.305811.0%90.7%
speedspeed
test N912.30462.304611.0%90.7%
speedspeed
test N1012.31072.310711.3%90.9%
speedspeed
test N1112.31112.311111.3%90.9%
speedspeed
test N1212.36802.368014.0%93.2%
speedspeed
test N1312.54182.541822.4%100.0%
speedspeed
Уж как комментировать тест переменной из одного символа (на 2% медленне самого быстрого) - не знаю... Наверно, тесты имеют большую погрешность. Предлагаю кому-нибудь запустить тест на час (исходники теста внизу страницы).
Одно ясно - при длине переменных в 8 и более символов происходит резкое снижение производительности, до 15%! А команд, включающих названия переменных, очень много. Еще один менее резкий скачек на переменных с именем 16 символов в длину и более. А в остальных случаях - чем больше, тем дольше, весьма линейная зависимость.
Вывод - не используйте переменны из 8 и более символов, выиграете 15% скорости (вернее, сэкономите).

Тормозят ли массивы в PHP? Вернее, как именно. Ускорение 40%.

А вот и не тормозят. Я где-то читал, якобы ассоциативные массивы в PHP жутко тормозят. Конечно, тест простой, но большой разницы между непрерывным простым (1), простым (2) и ассоциативным (3) массивами нет (элемент 0000 преобразуется в 0 - это же число, а не строка). И уж явно не тормозят "не ассоциативные не сплошные массивы".
  1. {$test[0000]=1;$test[0001]=1;$test[0002]=1;$test[0003]=1;$test[0004]=1;     }
  2. {$test[1000]=1;$test[1001]=1;$test[1002]=1;$test[1003]=1;$test[1004]=1;     }
  3. {$test["aa"]=1;$test["bb"]=1;$test["cc"]=1;$test["dd"]=1;$test["ee"]=1;     }
  4. {$test[aa]=1;  $test[bb]=1;  $test[cc]=1;  $test[dd]=1;  $test[ee]=1;       }
  5. {$test[0][0]=1;$test[0][1]=1;$test[0][2]=1;$test[0][3]=1;$test[0][4]=1;     }
  6. {$test[2][1]=1;$test[3][8]=1;$test[4][9]=1;$test[33][99]=1;$test[123][99]=1;}
  7. {$test[a][b]=1;$test[x][y]=1;$test[d][c]=1;$test[a][s]=1;$test[b][n]=1;     }
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N115.39245.392401.1%28.0%
speedspeed
test N215.33325.333200.0%27.7%
speedspeed
test N315.76515.765108.1%29.9%
speedspeed
test N417.65437.654343.5%39.7%
speedspeed
test N516.66496.664925.0%34.6%
speedspeed
test N616.62216.622124.2%34.3%
speedspeed
test N7119.282019.2820261.5%100.0%
speedspeed
Что тут можно комментировать.. все очевидно. Доступ к элементу одномерного ассоциативного массива по имени, не заключенному в кавычки, тормозит процесс на треть (относительно того же примера, но в кавычках). А вот в двухмерном массиве программа работает медленне аж в 2.5 раза! После такого теста хочешь не хочешь, а в любой программе пожертвуешь удобством - обращение к элементам массива по имени без кавычек.

Выносите многомерные массивы из "текстовых строк" - ускорение 25-30%. Одномерные можно не выносить.

При использовании многомерных массивов в строках наблюдается заметное снижение скорости Из-за многомерности нужно заключать переменные в парные фигурные скобки.
  1. {$x="test ".$myarray["name"]["second"][1]." test";       }
  2. {$x="test {$myarray[name][second][1]} test";             }
  3. {$x="test ";$x.=$myarray["name"]["second"][1];$x=" test";}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N113.53693.536900.0%69.9%
speedspeed
test N215.06055.060543.1%100.0%
speedspeed
test N314.60174.601730.1%90.9%
speedspeed
Тот же пример с ассоциативным 3-х мерным массивом, но с обращением к элементу по его индексному номеру:
  1. {$x="test ".$myarray[3][2][0]." test";       }
  2. {$x="test {$myarray[3][2][0]} test";         }
  3. {$x="test ";$x.=$myarray[3][2][0];$x=" test";}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N113.30123.301200.0%73.1%
speedspeed
test N214.16674.166726.2%92.3%
speedspeed
test N314.51454.514536.8%100.0%
speedspeed
Разница в 1 и 2 вариантах очень мала. Это говорит, что потери на не эффективное использование кавычек не слишком большое, чем доступ к массивам (см. тесты в первой главе).
  1. {$x="test".$myarray["test"]."test";}
  2. {$x="test$myarray[test]test";      }
  3. {$x="test{$myarray[test]}test";    }
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N112.84952.849500.0%80.9%
speedspeed
test N212.95192.951903.6%83.9%
speedspeed
test N313.52023.520223.5%100.0%
speedspeed
А теперь, на основании всех трех тестов, однозначный вывод: использовать фигурные скобки для обозначения границ имени элемента многомерного массива НЕЛЬЗЯ. Это сильно снижает скорость работы - 25-30% (в третьем варианте от простого добавления скобок скорость понизилась на четверть). Не использовать скобки нельзя. Следовательно, единственный способ не терять 30% скорости - выносить многомерные массивы из скобок.
Такой же тест, но для одномерных:
  1. {$x="test".$myarray["test"]."test".$myarray["test"]."test".$myarray["test"];   }
  2. {$x="test$myarray[test]testtest$myarray[test]testtest$myarray[test]test";      }
  3. {$x="test{$myarray[test]}testtest{$myarray[test]}testtest{$myarray[test]}test";}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N116.58436.584300.0%78.0%
speedspeed
test N216.77706.777002.9%80.3%
speedspeed
test N318.44068.440628.2%100.0%
speedspeed
Сравнивая два последних теста очевидно, что одномерные массивы можно и не выносить, потери всего 3-4% ( а вот на простых переменных - потери 25-40%!).

Регулярные выражения: PHP(POSIX) vs Perl.

PHP поддерживает регулярные выражения стандарта POSIX/eger*/ и PERL/preg*/-ориентированные (об их различии тут - php.spb.ru/regular_expression.html). Кто из них работает быстрее?
Хочу заранее предупредить любителей Перла, чтобы не радовались: хоть перловые реги и круче пхпышных, только ничто и никто не мешает использовать в PHP перловые реги! Наверно, потому их и встроили в PHP, что уж больно тормоза большие... :-)
Итак, простейший текст. Поиск простого выражения в тексте, который состоит из многократного повторения данной статьи (получается размер переменной $text в 3 Мб).
Тест вызывает всего 1 раз, ибо реги имеют встроенное средство для кеширования результатов компиляции. Т.е. перед запуском проиходит компиляции, а повторные реги не компилируются. Это особенности регулярных выражений. Разные языки программирования в состоянии хранить разное число откомпилированных выражений, что вызывались (в порядке вызова в программе). И в данном тесте как раз нет эффекта от компиляции, т.к. функция вызывается всего один раз.
  1. {eregi("МаС+иВ",$text);}
  2. {preg_match("/МаС+иВ/im",$text);}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N111.33391.3339107.8%100.0%
speedspeed
test N210.64170.641700.0%48.1%
speedspeed
  1. {eregi("(ма[a-zа-я]{1,20})",$text);}
  2. {preg_match("/(ма[a-zа-я]{1,20})/im",$text);}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N110.45210.452176.9%100.0%
speedspeed
test N210.25560.255600.0%56.5%
speedspeed
Пример для другого выражения и 30-мегабайтного текста (все те же повторы статьи, что вы сейчас читаете):
  1. {eregi("(ма[a-zа-я]{1,20})",$text);}
  2. {preg_match("/(ма[a-zа-я]{1,20})/im",$text);}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N111.34301.343060.6%100.0%
speedspeed
test N210.83650.836500.0%62.3%
speedspeed
Я еще писал штук пять разных выражений, но тенденция не меняется. Скорость может меняться, но Pelr обгоняет POSIX минимум на половину. Этого достаточно, чтобы похоронить функции регулярных выражений от PHP (POSIX). Для всех функций есть аналогичные Perl-ориентированные (все они встроены в PHP).
Далее один очень показательный пример на этой же статье (увеличение до 28Мб). Пример ищет в тексте e-mail. По свойству "жадности" регулярных выражений будет найден самый большой и наболее близкий к левому краю адрес.
Этот пример огорчит любителей перла. Приятно их огорчать :-)
  1. {eregi("([a-z_-]+@([a-z][a-z-]*\.)+([a-z]{2}|com|mil|org|net|gov|edu|arpa|info|biz))",$text);}
  2. {preg_match("/([a-z_-]+@([a-z][a-z-]*\.)+([a-z]{2}|com|mil|org|net|gov|edu|arpa|info|biz))/im",$text);}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N1111.682811.6828680.3%100.0%
speedspeed
test N211.49731.497300.0%12.8%
speedspeed
Из одного теста делать вывод сложно, но, видимо, чем сложнее регулярное выражение, тем больше POSIX отстает от Perl.
А теперь тот же пример, но только в статье (увеличение до 28Мб) нет НИ ОДНОГО символа "@" (я специально сделал копию статьи и стер эти символы):
  1. {eregi("([a-z_-]+@([a-z][a-z-]*\.)+([a-z]{2}|com|mil|org|net|gov|edu|arpa|info|biz))",$text,$ok); echo $ok[1];}
  2. {preg_match("/([a-z_-]+@([a-z][a-z-]*\.)+([a-z]{2}|com|mil|org|net|gov|edu|arpa|info|biz))/im",$text,$ok); echo $ok[1];}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N110.58540.585400.0%10.2%
speedspeed
test N215.76715.7671885.2%100.0%
speedspeed
Что мы видим?.. Ничто в этом мире не совершенно. Конечно, это очень не оптимизированное выражение для поиска email'ов, но всё же все те, кто кричал мне в форуме "ereg - отстой", на этом и похожих примерах могут отдыхать. Бывает же, что в тексте нет ни одной собачки :-)
Итак, вывод о скорости с примерами был дан выше. Вывод однозначный - надо использовать Perl-ориентированные регулярные выражения. В начеле главы я упоминал о кешировании откомпилированных копий регов. Если в программе одно и тоже выражение встречается неоднократно, производительность может отличаться не просто многократно, а в 10-100-1000 раз!
Селдующий пример вызывается 200 раз подряд над текстом в 250Кб:
  1. {eregi("МаС+иВ",$text);}
  2. {preg_match("/МаС+иВ/im",$text);}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N1117.638417.638472381.4%100.0%
speedspeed
test N210.02430.024300.0%00.1%
speedspeed
Что такое кеш - знают все. Видимо именно с кешем в PHP проблеммы... Кстати, ради примера, отключите в BIOSе вашего комптьютера кеш процессора и попробуйте загрузить Windows 2000... Не дождетесь! (Кажется, их называют L1 и L2 - два разных кеша для кода и данных первого и второго уровня, какой то из них можно отключить.)

Циклы: for, foreach, while, count/sizeof() - ускорение 15%-30%

В начале программы создается массив $test из целых чисел (100 000 элементов). Потом один раз запускаются приведенные ниже примеры. Цикл проходит данный массив 3-мя способами (разными циклами) и выполняет кое-какие операции. Не выполнять в цикле ничего нельзя, ибо это будет уже совсем не реальный тест.
  1. {$x=0; foreach($test as $n)                          { $x=sprintf("test%08i",$i);        }}
  2. {$x=0; for ($it=0; $it<100000; $it++)                { $x=sprintf("test%08i",$i);        }}
  3. {$x=0; $it=0; while($it<100000)                      { $x=sprintf("test%08i",$i); $it++; }}
  4. {$x=0; for ($it=0; $it<count($test); $it++)          { $x=sprintf("test%08i",$i);        }}
  5. {$x=0; $it=0; while($it<count($test))                { $x=sprintf("test%08i",$i); $it++; }}
  6. {$x=0; $co=count($test); for ($it=0; $it<$co; $it++) { $x=sprintf("test%08i",$i);        }}
  7. {$x=0; $co=count($test); $it=0; while($it<$co)       { $x=sprintf("test%08i",$i); $it++; }}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N1112.031312.0313154.4%100.0%
speedspeed
test N214.72904.729000.0%39.3%
speedspeed
test N314.77124.771200.9%39.7%
speedspeed
test N4110.284710.2847117.5%85.5%
speedspeed
test N5110.346610.3466118.8%86.0%
speedspeed
test N619.12719.127193.0%75.9%
speedspeed
test N719.14099.140993.3%76.0%
speedspeed
Почему sprintf, а не реальное echoecho использовать нельзя, т.к. от него будет немерянный буфер (OUTPUT в браузер или консоль).
Теперь о деле. Бесспорный вывод - использование foreach сильно тормозит дело, а между for и while большой разницы нет. (На голом тесте for/while/foreach {..} тормоза foreach - 30%). Это не удивительно, т.к. foreach делает копию массива, на что тратиться масса времени (хотя это только слухи).
Вывод с count() не столь очевиден, потому что от разного текста в цикле % тормознутости от самого быстрого варианта резко возрастает... Я взял цикл с небольшой нагрузкой - проход по огромному массиву $test + форматирование функцией sprintf. Как видите, варинты с count() и заменяющей эту функцию перемнной $co различаются на 10% по скорости между собой (не смотрите на варинант с константой в 100000, заранее знать кол-во элементов невозможно).
Вывод о не ассоциативных массивах: 1) foreach существенно замедляет работу 2) использование count() в простых циклах - замедленение 10%. Но на сложных циклах потери от лишних запусков count() будут абсолютно незаметны, так что ситуация не очевидна.
Сравнение count() и sizeof().
Судя по мануалу - это алиасы. Об этом написано на страницах самих функций и дополнительной странице "Appendex => Aliases list". Что же мы видим на массиве в 100000 элементов:
  1. {$x=0; for ($it=0; $it<count($test); $it++)  { $x=sprintf("test%08i",$test[$it]);}}
  2. {$x=0; for ($it=0; $it<sizeof($test); $it++) { $x=sprintf("test%08i",$test[$it]);}}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N113.00873.008715.7%100.0%
speedspeed
test N212.59982.599800.0%86.4%
speedspeed
Пусть тесты будут иметь погрешности... Но результат один - count() заметно отстает по скорости от sizeof()! Хм, я бы к записи в мануале сделал приписку: "The sizeof() function is an alias for count(), but последний сильно тормозит!"
Если кол-во элементов в массиве меньше 65000 (64К), то эти функции по скорости практически не различимы. Тут вывод простой - переходим на использование sizeof(), как ускоренного алиаса count(). Это принесет свои результаты на огромных массивах.
Ассоциативные массивы: тестирование разных способов перебора
С ними наблюдается таже проблема: на разных по величине массивах разные функции эффективны, но лучше всех foreach!
Массив в 200 элементов и 1000 повторов программы:
  1. {$x=0; foreach($test as $k=>$v) { $x=sprintf("%s=>%s\n",$k,$v);                                                           }}
  2. {$x=0; reset($test); while (list($k, $v) = each($test)) { $x=sprintf("%s=>%s\n",$k,$v);                                   }}
  3. {$x=0; $k=array_keys($test); $co=sizeof($k); for ($it=0; $it<$co; $it++) { $x=sprintf("%s=>%s\n",$k[$it],$test[$k[$it]]); }}
  4. {$x=0; reset($test); while ($k=key($test)) { $x=sprintf("%s=>%s\n",$k,current($test)); next($test);                       }}
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N118.12228.122200.0%78.7%
speedspeed
test N2110.322110.322127.1%100.0%
speedspeed
test N319.79219.792120.6%94.9%
speedspeed
test N418.97118.971110.5%86.9%
speedspeed
Тоже самое, но массив в 5000 элементов и 200 повторов:
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N1114.447314.447300.0%67.2%
speedspeed
test N2118.680118.680129.3%86.9%
speedspeed
test N3121.505621.505648.9%100.0%
speedspeed
test N4115.851415.851409.7%73.7%
speedspeed
Опять тоже самое, но массив в 100 000 элементов и без повторов:
счетчиккол-во
вызовов
общее
вpемя
сpеднее
вpемя
% от min% от maxобщее
время
test N113.51163.511600.0%82.8%
speedspeed
test N213.97243.972413.1%93.6%
speedspeed
test N314.24364.243620.8%100.0%
speedspeed
test N414.00264.002614.0%94.3%
speedspeed
Другие тесты на холостых циклах тоже показывают преимущество foreach.
Резюме:
  • sizeof() лучше, чем count()
  • в циклах sizeof лучше вообще заменить на переменную
  • for и while практически не отличимы
  • для перебора простых индексных массивов нужно использовать for или while
  • для перебора ассоциативных массивов нужно использотьва foreach

Для чтения файла file() быстрее, чем fopen+цикл - ускорение 40%

Чтобы прочитать в массив $x файл размером 1Мб (100 000 строк по 10 байт) можно воспользоваться двумя вариантами: чтение файла с помощью file(), либо традиционным методом fopen/fgets. Разумеется, для файлов разного объема и содержимого скорость может меняться. Но в данном примере статистика такова: file("1Mb_file.txt") работает на 40%быстрее, чем:
   $f=fopen("1Mb_file.txt","r") or die(1);
   while($x[]=fgets($f,1000));
   fclose($f);
Аналогичные варианты
   $f=fopen("1Mb_file.txt","r") or die(1);
   while($s=fgets($f,1000)) $x[]=$s;
   fclose($f);
или
   $f=fopen("1Mb_file.txt","r") or die(1);
   while(!feof($f))) $x[]=fgets($f,1000);
   fclose($f);
работают еще медленнее (во втором случае лишняя функция feof() заметно снижает скорость). Тот же тест, но на 15Мб файле (100 000 строк по 150 байт) показывает разницу в 50%, в пользу file(). Тест проводился так, чтобы исключить фоновый своппинг во время работы из-за предшествующих команд создания/чтения таких больших файлов. Подсчитать тоже самое на очень маленьких файлах в 1-2 Кб не представляется возможным, т.к. операцию чтения нельзя повторять в течении одного теста, операции чтения будут кешироваться...

Скачать примеры для проверки приведенных фактов

Архив RAR: speed.exe (42 Кб) .

понедельник, 21 января 2019 г.

SQL - запросы и их обработка с помощью PHP

SQL - запросы и их обработка с помощью PHP

В данной статье мы рассмотрим способы обращения к таблицам баз данный MySQL с помощью языка запросов SQL. SQL - это аббревиатура, которая так и "раскрывается" - структуризированный язык запросов.
В языке PHP для это цели существует целый ряд функций с префиксом "mysql". Нам для рассмотрения запросов понадобится не так много из них. Функция, без которой в языке PHP выполнение SQL-запросов было бы просто невозможным:
resource mysql_query(запрос)
Данная функция посылает запрос к базе данных и возвращает в случае успешного обращения идентификатор ресурса.
Для того чтобы подключиться к базе данных MySQL необходимо выполнить следующую последовательность:
$host='localhost'; // имя хоста (уточняется у провайдера)
$database='db_name'; // имя базы данных, которую вы должны создать
$user='user_name'; // заданное вами имя пользователя, либо определенное провайдером
$pswd='your_pass'; // заданный вами пароль
 
$dbh = mysql_connect($host, $user, $pswd) or die("Не могу соединиться с MySQL.");
mysql_select_db($database) or die("Не могу подключиться к базе.");
Итак mysql_connect() - функция для подключения к серверу MySQL на Вашем хостинге.
А mysql_select_db() выбирает базу данных на сервере для подключения.
Иными словами подключаемся к серверу, выбираем базу и начинаем работать.
Функция die() вызывается в случае ошибки и выводит в окно браузера сообщение, которое вы указали.
Для завершения работы с базами данных используется функция:
mysql_close ($ ДВГ);
Здесь $dbh - дескриптор, которые при соединении возвратила функция mysql_connect.
Закончив стартовый обзор, начнем рассмотрение собственно SQL-запросов.
Для этого прежде всего вам необходимо создать базу данных с определенном именем. А в ней создать таблицу, тоже с конкретным именем. В наших примерах будем обращаться к таблице my_sql_table. Чтобы создать эту таблицу давайте выполним в phpmyadmin нашего localhost следующий запрос:
CREATE TABLE `my_sql_table` (
`id` INT NOT NULL ,    // идентификатор будущих записей таблицы
`firstname` VARCHAR( 50 ) NOT NULL , // текстовое поле VARCHAR
`surname` VARCHAR( 50 ) NOT NULL , // max длиной 50 символов
PRIMARY KEY ( `id` ) // первичный ключ - идентификатор id
);
Итак таблица создана. Выполним первый запрос, который сразу оформим в виде PHP-кода:
<? PHP
// Файл firstsql.php
$host='localhost'; // имя хоста (уточняется у провайдера)
$database='db_name'; // имя базы данных, которую вы должны создать
$user='user_name'; // заданное вами имя пользователя, либо определенное провайдером
$pswd='your_pass'; // заданный вами пароль
 
$dbh = mysql_connect($host, $user, $pswd) or die("Не могу соединиться с MySQL.");
mysql_select_db($database) or die("Не могу подключиться к базе.");
$ query = "SELECT * FROM` my_sql_table` ";
$ res = mysql_query ($ query);
while ($ row = mysql_fetch_array ($ res))
{
echo "Номер:". $ row ['id']. "<br> \ n";
echo "Имя:". $ row ['firstname']. "<br> \ n";
echo "Фамилия:". $ row ['фамилия']. "<br> <hr> \ n";
}
?>
Разберем PHP-код файла firstsql.php. Начнем с собственно запроса к таблицам базы данных (БД).
$ query = "SELECT * FROM` my_sql_table` ";
Данный запрос можно расшифровать так: выбрать из таблицы my_sql_table БД все записи из всех полей. Таким образом знак * после слова SELECT означает "выбрать абсолютно все". Итак, запрос сформирован. Теперь его надо выполнить:
$ res = mysql_query ($ query); 
В случае успешного выполнения запроса функция mysql_query() вернет нам идентификатор ресурса $res.
Его мы должны передать в качестве параметра в функцию mysql_fetch_array(). Название этой функции говорит само за себя. Т.е. она формирует и выдает массив по выборке из таблицы БД. В случае нашей таблицы массив будет состоять из числа элементов, равных количествам записей (строк) в таблице и содержать значения id, firstname, surname для каждой строки таблицы. Следовательно, следующий код:
while ($ row = mysql_fetch_array ($ res))
{
echo "Номер:". $ row ['id']. "<br> \ n";
echo "Имя:". $ row ['firstname']. "<br> \ n";
echo "Фамилия:". $ row ['фамилия']. "<br> <hr> \ n";
}
можно прокомментировать так: пока введенная нами переменная $row получает не нулевые результаты работы функции mysql_fetch_row следует выдать в броузер значение полей $row['id'], $row['firstname'], $row['surname'] с помощью echo.
Если запрос выполнить так:
$ query = "SELECT firstname FROM` my_sql_table` ";
то это будет означать, что из всех строк выбирается только значения поля firstname.
Следовательно предыдущий код следует переписать как:
$ res = mysql_query ($ query);
while ($ row = mysql_fetch_array ($ res))
{
echo "Имя:". $ row ['firstname']. "<br> \ n";
}
Если Вы хотите выбрать строки таблицы с конкретным значением id где фамилия (surname) будет Петров, то запрос перепишется следующим образом:
$ query = "SELECT id FROM` my_sql_table`, где фамилия = 'Петров' ";
А вот если потребуется узнать фамилию того, кто находится под номером, к примеру, 5, то запрос будет таким:
$ query = "SELECT фамилия ОТ` my_sql_table`, где id = 5 ";
В этом случае Вы знаете, что результатом запроса будет всего одна строка из таблицы. Т.е. нет смысла организовывать цикл с использованием while. И обработка запроса будет следующей
$ res = mysql_query ($ query);
$ row = mysql_fetch_row ($ res);
echo "Фамилия пятого человека в списке:  ".$row[0]."\n";
Здесь вместо mysql_fetch_array() мы применили mysql_fetch_row(). Т.е. получить значение поля (или полей) конкретной строки. Поскольку поле у нас было одно -  surname - мы можем обратиться к единственному элементу массива $row как $row[0];.
Далее мы не будем столь подробно останавливаться на обработке запросов, а будем рассматривать наиболее типичные примеры собственно запросов.
Итак, рассмотрим наиболее типичные примеры запросов MySQL. Рассмотрение проведем на базе таблицы my_sql_table:
1. Добавим в таблицу my_sql_table поле middle_name (отчество) после surname:
$ query = "ALTER TABLE` my_sql_table` ADD `middle_name` 
  VARCHAR (50) NOT NULL ПОСЛЕ` фамилии` ";
2. Теперь удалим поле surname из таблицы my_sql_table:
$ query = "ALTER TABLE` my_sql_table` DROP `фамилия`"; 
3. Удаляем записи из таблицы my_sql_table с фамилией Сидоров :
$ query = "УДАЛИТЬ ИЗ` my_sql_table`, где фамилия = 'Сидоров' "; 
4. Помимо знаков равенства,  также "больше" или "меньше", в языке MySQL запросов существует понятие "похоже на". Выберем записи из таблицы my_sql_table, где в фамилии встречается "дор" :
$ query = "SELECT * FROM` my_sql_table`, где фамилия похожа на '% дор%' "; 
Здесь наличие "%" в начале и конце 'дор' и означает, что запрос будет искать именно 'дор', причем не важно в начале, конце, или середине фамилии он находится. Рассмотрим следующий пример
5. Выберем записи из таблицы my_sql_table с фамилией, которая начинается на П. Обратите внимание на расположение "%":
$ query = "SELECT * FROM` my_sql_table`, где фамилия похожа на 'П%' ";
6. Вычислим максимальное значение id:
$ query = "SELECT MAX (id) FROM` my_sql_table` ";
7. Вычислим количество полей в my_sql_table с фамилией, которая начинается на П.
$ query = "SELECT COUNT (*) FROM` my_sql_table`, где фамилия похожа на 'П%' ";
8. Удаление таблицы  my_sql_table:
$ query = "DROP TABLE` my_sql_table` ";
Для запросов 1-3 на языке PHP достаточно просто выполнить запрос:
mysql_query ($ запроса);
Мы рассмотрели наиболее характерные примеры запросов. Полагаю, с их помощью, следуя элементарной логике, Вы сможете выполнять более сложные запросы к созданным Вами таблицам баз данных MySQL.