21 ошибка программиста PHP. Часть 2 - Непродуманная работа с данными: бд и sql

ОГЛАВЛЕНИЕ

13. Непродуманная работа с данными: бд и sql

Забавно иногда наблюдать, сколько разных уловок находят люди для организации доступа к базам данных и получения выборки результатов. Среди прочих особенно выделяются комбинации из веток if, циклов do..while, множественных запросов и вызовов функции sql_result() внутри цикла for.

Чем, на их взгляд, они занимаются?

Код, основанный на методе научного тыка, говорит о недостаточно ясно определённой организации работы с БД. Те, кто прилагают все свои усилия на написание кода, а не на написание правильного кода, рискуют больше потерять, чем заработать. Некорректная выборка данных - яркий тому пример. Некоторые программисты не уделяют достаточно времени на тщательное продумывание этого момента. Естественно, в реальной жизни может и не оказаться того "единственно верного" способа выборки данных, но всегда найдётся тысяча "неверных", это точно.

Ошибки в организации выборки данным можно разделить на три класса:

Неправильное использование функций обращения к БД

Один из PHP-исходников предлагал следующий способ получения выборки из БД (приведённый ниже код в проекте находится после сгенерированных SQL-запросов):

<?php
if (!($row = sql_fetch_row ($result))) {
    print
"Ошибка: не найдено ни одного ряда";
    exit;
}

do {
    print
"$row[0]: $row[1]\n<br>\n";
}
while (
$row = sql_fetch_row ($result));
?>

Примечание: в данном и последующих примерах $result является дескриптором выборки или указателем на неё. Другими словами, был произведён запрос и получено определённое множество рядов. Примеры демонстрируют методы эффективной обработки этого множества.

В этом отрезке кода есть две ошибки:

  • проверка на "ноль рядов" - это попытка получить хотя бы один.
  • полученные данные не хранятся в ассоциативном массиве.

Проверка на "ноль рядов" ($result): неправильный подход

Задействовав функцию sql_fetch_row(), данный кусок кода предлагает косвенную проверку выборки на наличие хотя бы одного ряда данных. Но ведь существует прямой способ - это подсчёт количества рядов в выборке $result функцией sql_num_rows(), как показано ниже:

<?php

if (sql_num_rows ($result) <= 0) {
    print
"Ошибка: не найдено ни одного ряда";
    exit;
}

while (
$row = sql_fetch_row ($result)){
    print
"$row[0]: $row[1]\n<br>\n";
}
?>

Избавляемся от do..while

Прежде всего, исчезает необходимость в использовании давно уже поднадоевшего do..while, ибо для проверки на "ноль рядов" функция sql_num_row() не выдёргивает первый рядв $row, и указатель по-прежнему установлен на начало.

В PHP Source как-то был представлен подобный фрагмент кода. Если выборка не была нулевой, то функция sql_fetch_row() внутри условного блока доставляла первый ряд. Для получения остальных приходилось прибегать к do..while, потому что получение ряда из выборки ("to fetch" - принести, доставить// Прим. перев.) смещает указатель в ней. Таким образом, сначала вам придётся обработать уже полученный ряд ("do"), только потом получить второй ряд и так далее.

Так чем же do..while так провинился?

  • в данном примере внутри цикла do..while помещён только один оператор: простой вывод. Теперь представим, что там может оказаться не один, а десять операторов. Тогда редактору кода придётся искать условие while после оператора do и целого блока действий внутри цикла. Занятие не из приятных.
  • условие while обычно располагается в начале блока, а не в конце его. Поэтому редактору кода нужно будет уделять этому особое внимание при чтении, чтобы не спутать цикл do..while с предварительным условием while обычного цикла.

Делаем всё просто и понятно

В случае получения нулевой выборки, функция sql_num_row() в отличие от sql_fetch_row() делает именно то, что вам нужно сделать:

  • действие sql_fetch_row(): "При попытке получить первый ряд не найдено ни одного ряда. Это может означать, что в данной выборке их нет".
  • Действие sql_num_row(): "Количество рядов в выборке равно нулю".

Но как это отражается на написании кода?

Рассмотрим следующий пример, где операторы внутри условия записаны псевдокодом:

  • if(!($row = sql_fetch_row($result))){Print Error}:
  • Получаем первый ряд из выборки.
  • Если выборка пустая, то переменной $row приписываем 0; ноль логически выражается False; отсюда !(0)=True; выводим сообщение об ошибке.
  • Иначе, если выборка не пустая, получаем первый ряд, приписываем его переменной $row; $row не равно нулю, то есть True; !(True)=False; выходим на цикл do..while.
  • If(sql_num_rows($result)<=0){Print Error}:
  • Подсчёт рядов в выборке.
  • Если их меньше или равно нулю, выводим сообщение об ошибке.
  • Иначе - идём дальше.

Итак, какое из двух выражений проще и быстрее понять? Безусловно, подсчёт рядов - более прямой и короткий путь.

Каково всё же практическое преимущество второго способа? Невелика разница, что мы поместим внутри этого условия - многого тут не выиграть.

Однако на протяжении 10 000 строк вашего кода продуманные, а потому просто и ясно изложенные идеи сэкономят кучу времени редактору кода (вот и первое преимущество). Есть и другие преимущества: разработка скриптов заметно ускоряется и становится более размеренной.

Если ваша СУБД не поддерживает sql_num_row()

Действительно, некоторые СУБД могут не поддерживать эту функцию. Отнесёмся с сочувствием ко всем владельцам таких систем. Им придётся проверять выборки "на ноль рядов" путем запроса первого ряда. Однако и здесь, рекомендуем использовать булевские переменные:

<?php
$found
= false;

while (
$row = sql_fetch_array($result)){
    
$found = true;
}

if (!
$found){
    print
"Ошибка";
}
?>