← Назад к справке

Администрирование

SQL-запросы

Рабочая справка по прямым SQL-запросам в коробочном Bitrix24: Application::getConnection, SELECT, UPDATE, транзакции, SQL helper и логирование.

Прямой SQL лучше использовать для диагностики, отчётов и служебных операций. Если есть ORM или нормальное API сущности, сначала стоит рассмотреть их: так меньше риск обойти события, права и бизнес-логику.

Общее понимание

SQL полезен, но в Bitrix24 он легко обходит часть логики модулей.

Когда нужен прямой SQL

  • быстрая диагностика данных;
  • сложный отчёт по нескольким таблицам;
  • поиск проблемных записей;
  • служебная миграция данных;
  • проверка производительности запроса.

Когда лучше ORM

Если нужно работать с сущностью модуля, у которой есть ORM-таблица или API, лучше начинать с них. Например, CRM-карточки менять через фабрики или методы CRM, а не через прямой UPDATE по таблицам.

Выполнение запросов

Основная точка входа — Bitrix\Main\Application::getConnection().

Application::getConnection

<?php

use Bitrix\Main\Application;

/**
 * Получает соединение с базой данных.
 */
function fetchDatabaseConnection()
{
    return Application::getConnection();
}

$connection = fetchDatabaseConnection();

SELECT

<?php

use Bitrix\Main\Application;

/**
 * Получает последние сообщения чата через SQL.
 */
function fetchLastChatMessages(int $chat_id, int $limit): array
{
    $connection = Application::getConnection();
    $sql_helper = $connection->getSqlHelper();

    $chat_id = (int) $chat_id;
    $limit = max(1, min((int) $limit, 100));

    $sql = "
        SELECT
            ID,
            CHAT_ID,
            AUTHOR_ID,
            MESSAGE,
            DATE_CREATE
        FROM b_im_message
        WHERE CHAT_ID = {$chat_id}
        ORDER BY ID DESC
        LIMIT {$limit}
    ";

    $messages = [];
    $result = $connection->query($sql);

    while ($message = $result->fetch()) {
        $messages[] = $message;
    }

    return $messages;
}

$messages = fetchLastChatMessages($chat_id, 50);

print_r($messages);

INSERT, UPDATE, DELETE

Для изменяющих запросов используется queryExecute(). Такие запросы особенно опасны: они могут обойти проверки, события, таймлайн, историю и роботов.

<?php

use Bitrix\Main\Application;

/**
 * Обновляет служебное поле в своей таблице.
 */
function updateCustomItemStatus(int $item_id, string $status): void
{
    $connection = Application::getConnection();
    $sql_helper = $connection->getSqlHelper();

    $item_id = (int) $item_id;
    $safe_status = $sql_helper->forSql($status);

    $connection->queryExecute("
        UPDATE b_custom_item
        SET STATUS = '{$safe_status}'
        WHERE ID = {$item_id}
    ");
}

updateCustomItemStatus($item_id, 'done');

Безопасные значения

Строки нужно экранировать через SqlHelper, числа приводить к числам.

<?php

use Bitrix\Main\Application;

/**
 * Ищет пользователей по логину.
 */
function fetchUsersByLogin(string $login): array
{
    $connection = Application::getConnection();
    $sql_helper = $connection->getSqlHelper();

    $safe_login = $sql_helper->forSql($login, 255);

    $sql = "
        SELECT ID, LOGIN, EMAIL
        FROM b_user
        WHERE LOGIN = '{$safe_login}'
    ";

    $users = [];
    $result = $connection->query($sql);

    while ($user = $result->fetch()) {
        $users[] = $user;
    }

    return $users;
}

$users = fetchUsersByLogin($login);

print_r($users);

Транзакции и диагностика

Если меняется несколько таблиц, лучше использовать транзакцию.

Транзакция

<?php

use Bitrix\Main\Application;

/**
 * Выполняет несколько SQL-операций в транзакции.
 */
function updateCustomDataInTransaction(int $item_id, string $status): bool
{
    $connection = Application::getConnection();
    $sql_helper = $connection->getSqlHelper();

    $item_id = (int) $item_id;
    $safe_status = $sql_helper->forSql($status);

    try {
        $connection->startTransaction();

        $connection->queryExecute("
            UPDATE b_custom_item
            SET STATUS = '{$safe_status}'
            WHERE ID = {$item_id}
        ");

        $connection->queryExecute("
            INSERT INTO b_custom_item_log (ITEM_ID, MESSAGE)
            VALUES ({$item_id}, 'Status changed')
        ");

        $connection->commitTransaction();

        return true;
    } catch (\Throwable $exception) {
        $connection->rollbackTransaction();

        AddMessage2Log(
            [
                'message' => $exception->getMessage(),
                'item_id' => $item_id,
            ],
            'sql_error'
        );

        return false;
    }
}

$is_updated = updateCustomDataInTransaction($item_id, 'done');

var_dump($is_updated);

Логирование SQL

Для ручной диагностики проще сначала логировать сам SQL и параметры, а уже потом выполнять запрос.

<?php

/**
 * Пишет SQL в лог.
 */
function writeSqlLog(string $sql, array $parameters = []): void
{
    AddMessage2Log(
        [
            'sql' => $sql,
            'parameters' => $parameters,
        ],
        'sql_debug'
    );
}

Источники