<sect1 id="zend.db.select">

    <title>Zend_Db_Select</title>

    <sect2 id="zend.db.select.introduction">

        <title>Обзор</title>

        <para>
            Объект Zend_Db_Select представляет SQL-оператор <code>SELECT</code>.
            Класс имеет методы для добавления отдельных частей запроса. Вы
            можете определять некоторые части запроса, используя методы PHP и
            структуры данных, и класс формирует корректный синтаксис SQL.
            После того, как построен запрос, можно выполнять его так же, как
            если бы он был написан в виде строки.
        </para>

        <para>
            В число достоинств Zend_Db_Select входит:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    Объектно-ориентированные методы для "покусочного" построения
                    SQL-запросов;
                </para>
            </listitem>
            <listitem>
                <para>
                    Не зависящая от типа БД абстракция некоторых частей
                    SQL-запроса;
                </para>
            </listitem>
            <listitem>
                <para>
                    Автоматическое в большинстве случаев заключение в кавычки
                    идентификаторов метаданных для поддержки
                    идентификаторов, содержащих зарезервированные SQL слова и
                    спецсимволы;
                </para>
            </listitem>
            <listitem>
                <para>
                    Заключение в кавычки идентификаторов и значений для снижения
                    угрозы атак с использованием SQL-инъекций.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Использование Zend_Db_Select не обязательно. Для очень простых
            запросов SELECT обычно проще указать SQL-запрос целиком в виде
            строки и выполнить его, используя такие методы адаптера, как
            <code>query()</code> или <code>fetchAll()</code>. Использование
            Zend_Db_Select полезно, если нужно собрать запрос SELECT в процессе
            или основываясь на условной логике.
        </para>

    </sect2>

    <sect2 id="zend.db.select.creating">

        <title>Создание объекта Select</title>

        <para>
            Вы можете создавать экземпляр объекта Zend_Db_Select, используя
            метод <code>select()</code> объекта Zend_Db_Adapter_Abstract.
        </para>

        <example id="zend.db.select.creating.example-db">

            <title>Пример использования метода select() адаптера БД</title>

            <programlisting role="php"><![CDATA[
$db = Zend_Db::factory( ...опции... );
$select = $db->select();
]]>
            </programlisting>

        </example>

        <para>
            Другой способ состоит в создании объекта Zend_Db_Select через
            конструктор, при этом в качестве аргумента указывается адаптер БД.
        </para>

        <example id="zend.db.select.creating.example-new">

            <title>Пример создания нового объекта Select</title>

            <programlisting role="php"><![CDATA[
$db = Zend_Db::factory( ...опции... );
$select = new Zend_Db_Select($db);
]]>
            </programlisting>

        </example>

    </sect2>

    <sect2 id="zend.db.select.building">

        <title>Построение запросов Select</title>

        <para>
            При построении запроса вы можете добавлять по одному его
            предложения. Предложение - это часть SQL-оператора, не
            представляющая собой законченный оператор; например, предложение
            WHERE. Для каждого предложения есть свой метод Zend_Db_Select.
        </para>

        <example id="zend.db.select.building.example">

            <title>Пример использования методов для добавления предложений</title>

            <programlisting role="php"><![CDATA[
// Создание объекта Zend_Db_Select
$select = $db->select();

// Добавление предложения FROM
$select->from( ...определение таблицы и столбцов... )

// Добавление предложения WHERE
$select->where( ...определение критериев поиска... )

// Добавление предложения ORDER BY
$select->order( ...определение критериев сортировки... );
]]>
            </programlisting>

        </example>

        <para>
            Вы также можете использовать большинство методов объекта
            Zend_Db_Select в так называемом fluent interface.
            Fluent interface означает, что каждый метод возвращает ссылку на
            тот объект, в котором он был вызван, поэтому вы можете вызывать
            другой метод непосредственно за ним.
        </para>

        <example id="zend.db.select.building.example-fluent">

            <title>Пример использования fluent interface</title>

            <programlisting role="php"><![CDATA[
$select = $db->select()
    ->from( ...определение таблицы и столбцов... )
    ->where( ...определение критериев поиска... )
    ->order( ...определение критериев сортировки... );
]]>
            </programlisting>

        </example>

        <para>
            Этот пример демонстрирует применение fluent interface, но в любом
            случае вы можете использовать стандартный подход. Часто
            бывает необходимо использовать стандартный подход - например, когда
            нужно выполнить некоторую логику до добавления предложения в запрос.
        </para>

        <sect3 id="zend.db.select.building.from">

            <title>Добавление предложения FROM</title>

            <para>
                Указывайте таблицу для запроса, используя метод
                <code>from()</code>. Вы можете задавать имя таблицы в виде
                простой строки, Zend_Db_Select добавит идентификатор, заключив в
                кавычки имя таблицы, поэтому вы можете использовать специальные
                символы.
            </para>

            <example id="zend.db.select.building.from.example">

                <title>Пример использования метода from()</title>

                <programlisting role="php">
<![CDATA[
// Строит запрос:
//   SELECT *
//   FROM "products"

$select = $db->select()
             ->from( 'products' );
]]>
                </programlisting>

            </example>

            <para>
                Вы можете также задавать корреляционное (называемое также
                "псевдонимом") имя таблицы. В этом случае вместо простой строки
                используйте ассоциативный массив корреляционных имен и
                действительных имен таблиц. В других предложениях SQL-запроса
                используйте это корреляционное имя. Если в вашем запросе
                объединяется более одной таблицы, то Zend_Db_Select генерирует
                уникальные корреляционные имена, основанные на именах таблиц,
                для всех таблиц, для которых не были заданы корреляционные
                имена.
            </para>

            <example id="zend.db.select.building.from.example-cname">

                <title>Пример задания корреляционного имени таблицы</title>

                <programlisting role="php">
<![CDATA[
// Строит запрос:
//   SELECT p.*
//   FROM "products" AS p

$select = $db->select()
             ->from( array('p' => 'products') );
]]>
                </programlisting>
            </example>

            <para>
                Некоторые СУРБД поддерживают спецификатор схемы для таблицы. Вы
                можете задавать имя таблицы в виде
                "<code>имяСхемы.имяТаблицы</code>". Zend_Db_Select заключит в
                кавычки все части по отдельности.
            </para>

            <example id="zend.db.select.building.from.example-schema">

                <title>Пример задания имени схемы</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT *
//   FROM "myschema"."products"

$select = $db->select()
             ->from( 'myschema.products' );

// или

$select = $db->select()
             ->from('products', '*', 'myschema');
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.building.columns">

            <title>Добавление колонок</title>

            <para>
                Во втором аргументе метода <code>from()</code> вы можете
                задавать столбцы для выборки из соответствующей таблицы. Если вы
                не укажете столбцы для выборки, то по умолчанию будет подставлен
                метасимвол "<code>*</code>", означающий "все столбцы".
            </para>

            <para>
                Вы можете перечислить столбцы в простом массиве строк или
                в ассоциативном массиве псевдонимов столбцов и их имен. Если в
                запросе нужен только один столбец, и к нему не нужен псевдоним,
                то можно передать строку с его именем вместо массива.
            </para>

            <para>
                Если вы передали пустой массив в качестве аргумента для
                столбцов, то ни один столбец из соответствующей таблицы не будет
                включен в результат выборки. См.
                <link linkend="zend.db.select.building.join.example-no-columns">пример
                кода</link> в разделе про метод <code>join()</code>.
            </para>

            <para>
                Вы можете указать имя столбца как
                "<code>корреляционноеИмя.имяСтолбца</code>". Zend_Db_Select
                заключит в кавычки все части по отдельности. Если вы не укажете
                корреляционное имя для столбца, то используется корреляционное
                имя таблицы, указанной в текущем вызове метода
                <code>from()</code>.
            </para>

            <example id="zend.db.select.building.columns.example">

                <title>Пример указания столбцов</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'));

// Строит тот же запрос с указанием корреляционных имен:
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('p.product_id', 'p.product_name'));

// Строит тот же запрос с псевдонимом для одного столбца:
//   SELECT p."product_id" AS prodno, p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('prodno' => 'product_id', 'product_name'));
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.building.columns-expr">

            <title>Добавление столбцов выражений</title>

            <para>
                Столбцы в SQL-запросах иногда представляют собой выражения, а не
                просто столбцы из таблицы. Выражения не должны иметь
                корреляционные имена и заключаться в кавычки. Если строка,
                обозначающая столбец запроса, содержит скобки, то Zend_Db_Select
                распознает ее как выражение.
            </para>

            <para>
                Вы можете также явно создавать объект типа Zend_Db_Expr для
                того, чтобы предотвратить ошибочное распознавание строки как
                имени столбца. Zend_Db_Expr - мини-класс, содержащий
                только строку. Zend_Db_Select распознает объекты типа
                Zend_Db_Expr и преобразует их обратно в строку, но без таких
                действий, как заключение в кавычки и добавление корелляционных
                имен.
            </para>

            <note>
                <para>
                    Использование Zend_Db_Expr не обязательно, если ваше
                    выражение содержит скобки. Zend_Db_Select распознает
                    выражение по скобкам и оперирует строкой как выражением,
                    пропуская такие действия, как заключение в кавычки и
                    добавление корреляционных имен.
                </para>
            </note>

            <example id="zend.db.select.building.columns-expr.example">

                <title>Пример указания столбцов, содержащих выражения</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", LOWER(product_name)
//   FROM "products" AS p
// Выражение со скобками неявно преобразуется в Zend_Db_Expr.

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'LOWER(product_name)'));

// Строит запрос:
//   SELECT p."product_id", (p.cost * 1.08) AS cost_plus_tax
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id',
                          'cost_plus_tax' => '(p.cost * 1.08)')
                   );

// Построение того же запроса с явным использованием Zend_Db_Expr:
//   SELECT p."product_id", p.cost * 1.08 AS cost_plus_tax
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id',
                          'cost_plus_tax' =>
                              new Zend_Db_Expr('p.cost * 1.08'))
                    );
]]>
                </programlisting>

            </example>

            <para>
                В примерах выше Zend_Db_Select не модифицирует строку для
                добавления корреляционных имен или заключения идентификаторов в
                кавычки. Если эти изменения необходимы для исключения
                неоднозначности, то вы должны произвести их в строке вручную.
            </para>

            <para>
                Если имена столбцов являются ключевыми словами SQL или содержат
                специальные символы, то следует использовать метод адаптера
                <code>quoteIdentifier()</code> и включать результат в строку.
                Метод <code>quoteIdentifier()</code> использует заключение в
                кавычки для ограничения идентификатора, тем самым обозначается,
                что это идентификатор таблицы или столбца, а не другая часть
                синтаксиса SQL.
            </para>

            <para>
                Ваш код будет более независимым от типа БД, если вы будете
                использовать метод <code>quoteIdentifier()</code> вместо
                непосредственного добавления кавычек в строке, поскольку
                некоторые БД используют нестандартные символы вместо кавычек.
                Метод <code>quoteIdentifier()</code>
                использует нужные символы, основываясь на типе
                адаптера. <code>quoteIdentifier()</code> также экранирует
                символы кавычек в самом идентификаторе.
            </para>

            <example id="zend.db.select.building.columns-quoteid.example">

                <title>Пример заключения в кавычки столбцов в выражении</title>

                <programlisting role="php"><![CDATA[
// Строится следующий запрос, при этом имя столбца "from" в выражении
// заключается в кавычки:
//   SELECT p."from" + 10 AS origin
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('origin' =>
                              '(p.' . $db->quoteIdentifier('from') . ' + 10)')
                   );
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.building.columns-atomic">

            <title>Добавление столбцов в существующую таблицу FROM или JOIN</title>

            <para>
                Может потребоваться добавить столбцы в таблицу FROM или JOIN
                после того, как были вызваны соответствующие методы. Метод
                <code>columns()</code> позволяет добавлять определенные столбцы
                в любой момент до того, как будет выполнен запрос. Вы можете
                передавать столбцы в виде строки, объекта
                <code>Zend_Db_Expr</code> или массива этих элементов. Второй
                аргумент этого метода может быть опущен, это значит, что столбцы
                должны быть добавлены в таблицу FROM, иначе должно
                использоваться существующее корреляционное имя.
            </para>

            <example id="zend.db.select.building.columns-atomic.example">

                <title>Пример добавления столбцов через метод <code>columns()</code></title>

                <programlisting role="php"><![CDATA[
// Строится следующий запрос:
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'), 'product_id')
             ->columns('product_name');

// Строится тот же запрос с указанием корреляционных имен:
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->from(array('p' => 'products'), 'p.product_id')
             ->columns('product_name', 'p');
             // Можно также писать: columns('p.product_name')]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.building.join">

            <title>Добавление в запрос другой таблицы c помощью JOIN</title>

            <para>
                Многие полезные запросы используют <code>JOIN</code> для
                объединения строк из разных таблиц. Вы можете добавлять таблицы
                в запрос, используя метод <code>join()</code> объекта
                Zend_Db_Select. Использование этого метода похоже на
                использование <code>from()</code> за тем исключением, что в
                большинстве случаев вам нужно будет также указать условие
                объединения.
            </para>

            <example id="zend.db.select.building.join.example">

                <title>Пример использования метода join()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", p."product_name", l.*
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id');
]]>
                </programlisting>
            </example>

            <para>
                Второй аргумент метода <code>join()</code> является строкой,
                содержащей условие объединения. Это выражение объявляет условие,
                при выполнении которого строки в одной таблице объединяются со
                сроками в другой таблице. Вы можете использовать корреляционные
                имена в этом выражении.
            </para>

            <note>
                <para>
                    К выражению, указанному вами как условие объединения, не
                    применяется автоматическое заключение в кавычки. Если нужно
                    заключить в кавычки имена столбцов, то используйте
                    <code>quoteIdentifier()</code> при формировании строки
                    условия объединения.
                </para>
            </note>

            <para>
                Третий аргумент метода <code>join()</code> - массив имен
                столбцов, как и в методе <code>from()</code>. По умолчанию
                используется "<code>*</code>" вместо столбцов,
                поддерживаются корреляционные имена, выражения и Zend_Db_Expr -
                так же, как и в случае массива столбцов для метода
                <code>from()</code>.
            </para>

            <para>
                Если из таблицы не нужно выбирать столбцы, то используйте пустой
                массив вместо перечисления столбцов. Это работает и для метода
                <code>from()</code>, но обычно требуется извлечь часть столбцов
                из основной таблицы в запросе, при этом может не требоваться
                получение столбцов из присоединяемой таблицы.
            </para>

            <example id="zend.db.select.building.join.example-no-columns">

                <title>Пример извлечения без столбцов</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array() ); // empty list of columns
]]>
                </programlisting>

                <para>
                    Обратите внимание на пустой <code>array()</code> в примере
                    выше вместо списка столбцов из присоединяемой таблицы.
                </para>

            </example>

            <para>
                SQL имеет несколько типов объединений. Ниже приведен список
                методов для поддержки различных типов объединений в
                Zend_Db_Select.
            </para>

            <itemizedlist>
                <listitem>
                    <para>
                        <command>INNER JOIN</command>, методы
                        <code>join(table, join, [columns])</code>
                        и <code>joinInner(table, join, [columns])</code>.
                    </para>
                    <para>
                        Возможно, наиболее часто используемый тип объединения.
                        Строки из каждой таблицы сравниваются с
                        использованием условия сравнения. Результат включает в
                        себя только те строки, которые удовлетворяют условию
                        объединения. Результат может быть пустым, если ни одна
                        строка не удовлетворяет этому условию.
                    </para>
                    <para>
                        Все СУРБД поддерживают этот тип объединения.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <command>LEFT JOIN</command>, метод <code>joinLeft(table, condition, [columns])</code>.
                    </para>
                    <para>
                        В результат входят все строки из таблицы слева и все
                        соответствующие строки из таблицы справа. Если нет
                        соответствующих строк из таблицы справа, то
                        соответствующие столбцы в результате заполняются NULL.
                    </para>
                    <para>
                        Все СУРБД поддерживают этот тип объединения.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <command>RIGHT JOIN</command>, метод <code>joinRight(table, condition, [columns])</code>.
                    </para>
                    <para>
                        Правое внешнее объединение дополняет левое внешнее
                        объединение. В результат входят все строки из таблицы
                        справа и все соответствующие строки из таблицы слева.
                        Если нет соответствующих строк из таблицы слева, то
                        соответствующие столбцы в результате заполняются NULL.
                    </para>
                    <para>
                        Некоторые СУРБД не поддерживают этот тип объединения,
                        но, как правило, любое правое объединение может быть
                        заменено на левое посредством изменения порядка таблиц
                        на обратный.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <command>FULL JOIN</command>, метод
                        <code>joinFull(table, condition, [columns])</code>.
                    </para>
                    <para>
                        Полное внешнее объединение является как бы комбинацией
                        левого и правого объединений. Все строки из обоих таблиц
                        входят в результат, при этом объединяются друг с другом
                        в одну строку результата, если соответствуют условию
                        объединения, иначе объединяются с NULL вместо значений
                        столбцов из другой таблицы.
                    </para>
                    <para>
                        Некоторые СУРБД не поддерживают этот тип объединения.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <command>CROSS JOIN</command>, метод
                        <code>joinCross(table, [columns])</code>.
                    </para>
                    <para>
                        Перекрестное объединение является декартовым
                        произведением. Каждая строка в первой таблице
                        объединяется с со всеми строками во второй таблице.
                        Таким образом, количество строк в результате будет равно
                        произведению числа строк в обоих таблицах. Вы можете
                        фильтровать результат, используя условие в предложении
                        WHERE, в этом случае перекрестное объединение подобно
                        старому синтаксису объединений в SQL-89.
                    </para>
                    <para>
                        Метод <code>joinCross()</code> не имеет параметров для
                        определения условий объединения. Некоторые СУРБД не
                        поддерживают этот тип объединения.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <command>NATURAL JOIN</command>, метод
                        <code>joinNatural(table, [columns])</code>.
                    </para>
                    <para>
                        Естественное объединение сравнивает столбцы, имеющие
                        одинаковые имена в обоих таблицах. Проверка производится
                        на равенство; проверка на неравенство не является
                        естественным объединением.
                        Данным API поддерживаются только внутренние
                        естественные объединения, даже если SQL поддерживает
                        внешние естественные объединения.
                    </para>
                    <para>
                        Метод <code>joinNatural()</code> не имеет параметров для
                        определения условий объединения.
                    </para>
                </listitem>
            </itemizedlist>

            <para>
                В дополнение к этим методам объединения вы можете упростить свои
                запросы, используя методы JoinUsing. Вместо предоставления
                полного условия объединения вы можете просто передавать имя
                столбца, по которому производится объединение, и Zend_Db_Select
                допишет условие объединения за вас.
            </para>

            <example id="zend.db.select.building.joinusing.example">

                <title>Пример использования метода joinUsing()</title>

                <programlisting role="php"><![CDATA[
// Строится запрос:
//   SELECT *
//   FROM "table1"
//   JOIN "table2"
//   ON "table1".column1 = "table2".column1
//   WHERE column2 = 'foo'

$select = $db->select()
             ->from('table1')
             ->joinUsing('table2', 'column1')
             ->where('column2 = ?', 'foo');]]>
                </programlisting>

            </example>

            <para>
                Каждый метод объединения из перечисленных выше имеет соответствующий ему метод JoinUsing.
            </para>

            <itemizedlist>
                <listitem>
                    <para>
                        <code>joinUsing(table, join, [columns])</code> и
                        <code>joinInnerUsing(table, join, [columns])</code>
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <code>joinLeftUsing(table, join, [columns])</code>
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <code>joinRightUsing(table, join, [columns])</code>
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <code>joinFullUsing(table, join, [columns])</code>
                    </para>
                </listitem>
            </itemizedlist>

        </sect3>

        <sect3 id="zend.db.select.building.where">

            <title>Добавление предложения WHERE</title>

            <para>
                Вы можете задавать условия для ограничения строк в результате
                выборки, используя метод <code>where()</code>. Первым аргументом
                этого метода является SQL-выражение, которое используется в
                предложении <code>WHERE</code> в данном запросе.
            </para>

            <example id="zend.db.select.building.where.example">

                <title>Пример использования метода where()</title>

                <programlisting role="php"><![CDATA[
// Строится запрос:
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE price > 100.00

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price > 100.00');]]>
                </programlisting>

            </example>

            <note>
                <para>
                    К выражениям для методов <code>where()</code> или
                    <code>orWhere()</code> не применяется автоматическое
                    заключение в кавычки. Если необходимо, чтобы имена столбцов
                    были заключены в кавычки, то используйте метод
                    <code>quoteIdentifier()</code> при формировании строки
                    условия.
                </para>
            </note>

            <para>
                Второй аргумент метода <code>where()</code> является
                опциональным. Это значение подставляется в выражение.
                Zend_Db_Select заключает это значение в кавычки и подставляет
                вместо знака вопроса ("<code>?</code>") в выражении.
            </para>

            <para>
                Этот метод принимает только один параметр. Если в выражение
                подставляется несколько значений, то нужно сформировать строку
                вручную, вставляя переменные и заключая их в кавычки
                самостоятельно.
            </para>

            <example id="zend.db.select.building.where.example-param">

                <title>Пример параметра в методе where()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price > 100.00)

$minimumPrice = 100;

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price > ?', $minimumPrice);
]]>
                </programlisting>

            </example>

            <para>
                Вы можете вызывать метод <code>where()</code> несколько раз на
                одном и том же объекте Zend_Db_Select. Результирующий запрос
                объединяет в себе все термы с добавлением <code>AND</code>
                между ними.
            </para>

            <example id="zend.db.select.building.where.example-and">

                <title>Пример нескольких вызовов метода where()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price > 100.00)
//     AND (price < 500.00)

$minimumPrice = 100;
$maximumPrice = 500;

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price > ?', $minimumPrice)
             ->where('price < ?', $maximumPrice);
]]>
                </programlisting>

            </example>

            <para>
                Если вам нужно объединить термы с использованием
                <code>OR</code>, то используйте метод <code>orWhere()</code>.
                Этот метод используется так же, как и метод
                <code>where()</code>, за тем исключением, что определенный в
                этом вызове терм добавляется вместе с <code>OR</code> вместо
                <code>AND</code>.
            </para>

            <example id="zend.db.select.building.where.example-or">

                <title>Пример использования метода orWhere()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price < 100.00)
//     OR (price > 500.00)

$minimumPrice = 100;
$maximumPrice = 500;

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where('price < ?', $minimumPrice)
             ->orWhere('price > ?', $maximumPrice);
]]>
                </programlisting>

            </example>

            <para>
                Zend_Db_Select автоматически заключает в скобки все выражения,
                которые вы добавляете через методы <code>where()</code> или
                <code>orWhere()</code>. Это позволяет быть уверенным в том, что
                приоритет булевых операторов не приведет к другому результату
                вместо ожидаемого.
            </para>

            <example id="zend.db.select.building.where.example-parens">

                <title>Пример заключения булевых выражений в скобки</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT product_id, product_name, price
//   FROM "products"
//   WHERE (price < 100.00 OR price > 500.00)
//     AND (product_name = 'Apple')

$minimumPrice = 100;
$maximumPrice = 500;
$prod = 'Apple';

$select = $db->select()
             ->from('products',
                    array('product_id', 'product_name', 'price'))
             ->where("price < $minimumPrice OR price > $maximumPrice")
             ->where('product_name = ?', $prod);
]]>
                </programlisting>

            </example>

            <para>
                В примере выше результаты могут отличаться от тех, что
                получаются без скобок, потому что <code>AND</code> имеет больший
                приоритет, чем <code>OR</code>. Zend_Db_Select добавляет скобки,
                Таким образом, результатом является то, что каждое выражение,
                добавленное в успешных вызовах <code>where()</code> более
                связанно, чем <code>AND</code>, объединяющее эти выражения.
            </para>

        </sect3>

        <sect3 id="zend.db.select.building.group">

            <title>Добавление предложения GROUP BY</title>

            <para>
                В языке SQL выражение <code>GROUP BY</code> позволяет ограничить
                количество строк в результатах запроса до одной стоки на каждое
                уникальное значение в столбцах, перечисленных в предложении
                <code>GROUP BY</code>.
            </para>

            <para>
                В Zend_Db_Select вы можете задавать столбцы, используемые для
                определения групп строк, через метод <code>group()</code>.
                Аргументом этого метода является столбец или массив столбцов для
                подстановки в предложение <code>GROUP BY</code>.
            </para>

            <example id="zend.db.select.building.group.example">

                <title>Пример использования метода group()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", COUNT(*) AS line_items_per_product
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id
//   GROUP BY p.product_id

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array('line_items_per_product' => 'COUNT(*)'))
             ->group('p.product_id');
]]>
                </programlisting>

            </example>

            <para>
                Как и для массива столбцов в методе <code>from()</code>, вы
                можете использовать корреляционные имена в именах столбцов,
                столбцы заключаются в кавычки в качестве идентификаторов, если
                строка не содержит скобок или является объектом типа
                Zend_Db_Expr.
            </para>

        </sect3>

        <sect3 id="zend.db.select.building.having">

            <title>Добавление предложения HAVING</title>

            <para>
                В SQL предложение <code>HAVING</code> применяет условие
                ограничения к группам строк. Это подобно тому, как предложение
                <code>WHERE</code> применяет условие ограничения к строкам.
                Но эти предложения не являются идентичными, поскольку условия
                <code>WHERE</code> применяются до группировки, в то время как
                условия <code>HAVING</code> применяется после группировки.
            </para>

            <para>
                В Zend_Db_Select вы можете определять условия ограничения
                групп через метод <code>having()</code>. Его использование
                аналогично использованию метода <code>where()</code>. Первый
                аргумент является строкой, содержащей SQL-выражение.
                Опциональный второй аргумент - значение, которое используется
                для подстановки вместо метки заполнения в SQL-выражении.
                Выражения, переданные в нескольких вызовах метода
                <code>having()</code>, объединяются через булевый оператор
                <code>AND</code> или через булевый оператор
                <code>OR</code>, если вы используете метод
                <code>orHaving()</code>.
            </para>

            <example id="zend.db.select.building.having.example">

                <title>Пример использования метода having()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", COUNT(*) AS line_items_per_product
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id
//   GROUP BY p.product_id
//   HAVING line_items_per_product > 10

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array('line_items_per_product' => 'COUNT(*)'))
             ->group('p.product_id')
             ->having('line_items_per_product > 10');
]]>
                </programlisting>

            </example>

            <note>
                <para>
                    К выражениям, переданным через методы <code>having()</code>
                    или <code>orHaving()</code>, не применяется автоматическое
                    заключение в кавычки. Если у вас есть имена столбцов,
                    которые требуется заключить в кавычки, то используйте
                    <code>quoteIdentifier()</code> при формировании строки
                    условия.
                </para>
            </note>

        </sect3>

        <sect3 id="zend.db.select.building.order">

            <title>Добавление предложения ORDER BY</title>

            <para>
                В SQL предложение <code>ORDER BY</code> задает один или более
                столбцов (или выражений), по которым сортируется результат
                запроса. Если перечислено несколько столбцов, то вторичные
                столбцы используются для принятия решения в ситуации "ничьи":
                если первичные столбцы содержат идентичные значения, то порядок
                сортировки определяется через вторичные столбцы. По умолчанию
                сортировка производится от меньших значений к большим. Вы можете
                также производить для данного столбца сортировку от больших
                значений к меньшим, указав ключевое слово <code>DESC</code>
                после этого столбца.
            </para>

            <para>
                В Zend_Db_Select вы можете использовать метод
                <code>order()</code> для определения столбца или массива
                столбцов, по которым производится сортировка. Каждый элемент
                массива является строкой с именем столбца, опционально строка
                может содержать ключевое слово <code>ASC</code> или
                <code>DESC</code> после имени столбца и отделенное от него
                пробелом.
            </para>

            <para>
                Как и в случае методов <code>from()</code> и
                <code>group()</code>, имена столбцов заключаются в кавычки в
                качестве идентификаторов, если они не содержат скобки и не
                являются объектами Zend_Db_Expr.
            </para>

            <example id="zend.db.select.building.order.example">

                <title>Пример использования метода order()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", COUNT(*) AS line_items_per_product
//   FROM "products" AS p JOIN "line_items" AS l
//     ON p.product_id = l.product_id
//   GROUP BY p.product_id
//   ORDER BY "line_items_per_product" DESC, "product_id"

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id'))
             ->join(array('l' => 'line_items'),
                    'p.product_id = l.product_id',
                    array('line_items_per_product' => 'COUNT(*)'))
             ->group('p.product_id')
             ->order(array('line_items_per_product DESC',
                           'product_id'));
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.building.limit">

            <title>Добавление предложения LIMIT</title>

            <para>
                Некоторые СУРБД расширяют язык SQL предложением, известным как
                предложение <code>LIMIT</code>. Это предложение ограничивает
                количество строк в результате запроса до заданного вами
                количества. Вы можете также задать количество пропускаемых до
                начала вывода строк. Эта возможность облегчает выборку
                подмножества строк результата - например, для постраничного
                вывода результатов запроса.
            </para>

            <para>
                В Zend_Db_Select вы можете использовать метод
                <code>limit()</code> для задания количества строк в выборке и
                количества пропускаемых строк. Первым аргументом этого метода
                является желаемое количество строк в результате запроса. Вторым
                аргументом - количество пропускаемых строк
            </para>

            <example id="zend.db.select.building.limit.example">

                <title>Пример использования метода limit()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p
//   LIMIT 10, 20

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->limit(10, 20);
]]>
                </programlisting>

            </example>

            <note>
                <para>
                    Ситаксис <code>LIMIT</code> поддерживается не всеми СУРБД.
                    Некоторые СУРБД используют другой синтаксис для поддержки
                    аналогичной функциональности. Каждый класс
                    Zend_Db_Adapter_Abstract включает в себя метод для генерации
                    SQL, присущего данной СУРБД.
                </para>
            </note>

            <para>
                Используйте метод <code>limitPage()</code> в качестве
                альтернативного способа указания количества строк и смещения.
                Этот метод позволяет ограничить набор возвращаемых результатов
                подмножеством фиксированной длины, который является одним из
                последовательности подмножеств, составляющих весь набор
                результатов запроса.
                Другими словами, вы указываете длину "страницы" результатов в
                строках и порядковый номер страницы для извлечения. Номер
                страницы является первым аргументом в методе
                <code>limitPage()</code>, а длина страницы - вторым. Оба
                аргумента являются обязательными и не имеют значений по
                умолчанию.
            </para>

            <example id="zend.db.select.building.limit.example2">

                <title>Пример использования метода limitPage()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p."product_id", p."product_name"
//   FROM "products" AS p
//   LIMIT 10, 20

$select = $db->select()
             ->from(array('p' => 'products'),
                    array('product_id', 'product_name'))
             ->limitPage(2, 10);
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.building.distinct">

            <title>Добавление модификатора DISTINCT</title>

            <para>
                Метод <code>distinct()</code> дает возможность добавлять
                ключевое слово <code>DISTINCT</code> в ваш запрос.
            </para>

            <example id="zend.db.select.building.distinct.example">

                <title>Пример использования метода distinct()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT DISTINCT p."product_name"
//   FROM "products" AS p

$select = $db->select()
             ->distinct()
             ->from(array('p' => 'products'), 'product_name');
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.building.for-update">

            <title>Добавление модификатора FOR UPDATE</title>

            <para>
                Метод <code>forUpdate()</code> дает возможность добавлять
                модификатор <code>FOR UPDATE</code> в ваш запрос.
            </para>

            <example id="zend.db.select.building.for-update.example">

                <title>Пример использования метода forUpdate()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT FOR UPDATE p.*
//   FROM "products" AS p

$select = $db->select()
             ->forUpdate()
             ->from(array('p' => 'products'));
]]>
                </programlisting>

            </example>

        </sect3>

    </sect2>

    <sect2 id="zend.db.select.execute">

        <title>Произведение запросов на выборку</title>

        <para>
            Этот раздел объясняет, как производить запрос, представленный
            объектом Zend_Db_Select.
        </para>

        <sect3 id="zend.db.select.execute.query-adapter">

            <title>Произведение запросов на выборку из адаптера БД</title>

            <para>
                Вы можете производить запрос, представленный объектом
                Zend_Db_Select, посредством передачи его в качестве первого
                аргумента методу <code>query()</code> объекта
                Zend_Db_Adapter_Abstract. Используйте объекты Zend_Db_Select
                вместо строк запроса.
            </para>

            <para>
                Метод <code>query()</code> возвращает объект Zend_Db_Statement
                или PDOStatement, в зависимости от типа адаптера.
            </para>

            <example id="zend.db.select.execute.query-adapter.example">

                <title>Пример использования метода query() адаптера БД</title>

                <programlisting role="php"><![CDATA[
$select = $db->select()
             ->from('products');

$stmt = $db->query($select);
$result = $stmt->fetchAll();
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.execute.query-select">

            <title>Произведение запросов на выборку через объект Select</title>

            <para>
                Вместо метода <code>query()</code> объекта адаптера можно
                использовать метод <code>query()</code> объекта Zend_Db_Select.
                Оба метода возвращают объект типа Zend_Db_Statement или
                PDOStatement, в зависимости от типа адаптера.
            </para>

            <example id="zend.db.select.execute.query-select.example">

                <title>Пример использования метода query() объекта Select</title>

                <programlisting role="php"><![CDATA[
$select = $db->select()
             ->from('products');

$stmt = $select->query();
$result = $stmt->fetchAll();
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.execute.tostring">

            <title>Преобразование объекта Select в SQL-строку</title>

            <para>
                Если нужно получить доступ к строковому представлению
                SQL-запроса, соответствующего данному объекту Zend_Db_Select, то
                используйте метод <code>__toString()</code>.
            </para>

            <example id="zend.db.select.execute.tostring.example">

                <title>Пример использования метода __toString()</title>

                <programlisting role="php"><![CDATA[
$select = $db->select()
             ->from('products');

$sql = $select->__toString();
echo "$sql\n";

// Выводится будет строка:
//   SELECT * FROM "products"
]]>
                </programlisting>

            </example>

        </sect3>

    </sect2>

    <sect2 id="zend.db.select.other">

        <title>Другие методы</title>

        <para>
            Этот раздел описывает другие методы класса Zend_Db_Select, которые
            не были охвачены ранее: <code>getPart()</code> и
            <code>reset()</code>.
        </para>

        <sect3 id="zend.db.select.other.get-part">

            <title>Получение частей объекта Select</title>

            <para>
                Метод <code>getPart()</code> возвращает представление
                определенной части вашего запроса. Например, вы можете
                использовать этот метод для получения строки выражения для
                предложения <code>WHERE</code>, массива столбцов, перечисленных
                для получения выборки по ним, значений количества и смещения для
                предложения <code>LIMIT</code>.
            </para>

            <para>
                Возвращаемое значение не является строкой, содержащей фрагмент
                запроса SQL. Возвращаемое значение является внутренним
                представлением в объекте, обычно это массив, содержащий значения
                и выражения. Каждая часть запроса имеет различную структуру.
            </para>

            <para>
                Единственным аргументом метода <code>getPart()</code> является
                строка, идентифицирующая часть запроса, которую требуется
                вернуть. Например, строка <code>'from'</code> соответствует той
                части запроса, которая хранит информацию о таблицах в
                предложении <code>FROM</code>, включая присоединяемые таблицы.
            </para>

            <para>
                Zend_Db_Select определяет константы, которые вы можете
                использовать для частей SQL-запроса. Вы можете использовать эти
                константы или литеральные строки.
            </para>

            <table id="zend.db.select.other.get-part.table">
                <title>Константы, используемые методами getPart() и reset()</title>
                <tgroup cols="2">
                    <thead>
                        <row>
                            <entry>Константа</entry>
                            <entry>Строковое значение</entry>
                        </row>
                    </thead>
                    <tbody>
                        <row>
                            <entry><code>Zend_Db_Select::DISTINCT</code></entry>
                            <entry><code>'distinct'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::FOR_UPDATE</code></entry>
                            <entry><code>'forupdate'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::COLUMNS</code></entry>
                            <entry><code>'columns'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::FROM</code></entry>
                            <entry><code>'from'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::WHERE</code></entry>
                            <entry><code>'where'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::GROUP</code></entry>
                            <entry><code>'group'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::HAVING</code></entry>
                            <entry><code>'having'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::ORDER</code></entry>
                            <entry><code>'order'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::LIMIT_COUNT</code></entry>
                            <entry><code>'limitcount'</code></entry>
                        </row>
                        <row>
                            <entry><code>Zend_Db_Select::LIMIT_OFFSET</code></entry>
                            <entry><code>'limitoffset'</code></entry>
                        </row>
                    </tbody>
                </tgroup>
            </table>

            <example id="zend.db.select.other.get-part.example">

                <title>Пример использования метода getPart()</title>

                <programlisting role="php"><![CDATA[
$select = $db->select()
             ->from('products')
             ->order('product_id');

// Вы можете использовать строковой литерал для указания части запроса
$orderData = $select->getPart( 'order' );

// Можно использовать константу для указания той же части
$orderData = $select->getPart( Zend_Db_Select::ORDER );

// Возвращаемое значение может быть массивом, а не строкой
// Каждая часть имет свою структуру
print_r( $orderData );
]]>
                </programlisting>

            </example>

        </sect3>

        <sect3 id="zend.db.select.other.reset">

            <title>Сброс частей запроса в объекте Select</title>

            <para>
                Через метод <code>reset()</code> вы можете очистить
                определенную часть SQL-запроса или все части запроса, если
                опустить аргумент.
            </para>

            <para>
                Единственный аргумент метода является опциональным. Вы можете
                указать часть запроса для очистки, используя те же строки,
                которые используются для метода <code>getPart()</code>. Та часть
                запроса, которую вы указали, сбрасывается в состояние по
                умолчанию.
            </para>

            <para>
                Если вы опустите этот параметр, то <code>reset()</code>
                сбрасывает все части запроса в состояние по умолчанию. Это
                делает объект Zend_Db_Select эквивалентным новому объекту - так
                же, как если бы вы его только что инстанцировали его.
            </para>

            <example id="zend.db.select.other.reset.example">

                <title>Пример использования метода reset()</title>

                <programlisting role="php"><![CDATA[
// Строит запрос:
//   SELECT p.*
//   FROM "products" AS p
//   ORDER BY "product_name"

$select = $db->select()
             ->from(array('p' => 'products')
             ->order('product_name');

// Changed requirement, instead order by a different columns:
// Теперь нужна сортировка по другому столбцу:
//   SELECT p.*
//   FROM "products" AS p
//   ORDER BY "product_id"

// Очищаем соответствующую часть, теперь можно переопределить ее
$select->reset( Zend_Db_Select::ORDER );

// Задаем другой столбец
$select->order('product_id');

// Очищаем все части запроса
$select->reset();
]]>
                </programlisting>

            </example>

        </sect3>

    </sect2>

</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
