В чем причина неработоспособности создания триггера в SQLite3?

Здравствуйте! Я начинающий разработчик и создаю бота для Telegram. Мне нужно реализовать базу данных для хранения некоторых данных этого бота. Я уже разобрался с созданием основных таблиц, но мне нужно добавить поля, в которые автоматически будут подставляться текущая дата или порядковый номер (автоинкремент).

Пожалуйста, не судите строго за код, так как я раньше не работал с SQL.

Вот мой текущий код: 
<pre><code class="python">import sqlite3 as sq
from conf_lib import Config

class DB:
    def __init__(self):
        self.db_file = Config().get('DATABASE', 'db_file')
        self.connection = sq.connect(self.db_file)
    
    def build_db(self):
        cursor = self.connection.cursor()
        
        user_tables = '''
CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(255),
    first_name VARCHAR(255),
    last_name VARCHAR(255),
    role TEXT DEFAULT 'user'
);
'''
        products_tables = '''
CREATE TABLE products (
    plu VARCHAR(255) PRIMARY KEY,
    name VARCHAR(255),
    barcodes TEXT
);
'''
        data_matrix_logs_tables = '''
CREATE TABLE data_matrix_logs (
    log_id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INT,
    request_type TEXT,
    tp_code TEXT,
    requested_count INT,
    request_date TEXT,
    processing_status TEXT CHECK (processing_status IN ('Created', 'Revert', 'Complete')),
    FOREIGN KEY (user_id) REFERENCES users(user_id)
);
'''

        trigger = '''
CREATE TRIGGER IF NOT EXISTS set_request_date_and_status
BEFORE INSERT ON data_matrix_logs
FOR EACH ROW
BEGIN
    SELECT datetime('now') INTO NEW.request_date;
    SET NEW.processing_status = 'Создано';
END;
'''
        cursor.execute(user_ta
  • 2 ноября 2024 г. 17:53
Ответы на вопрос 3
Судя по вашему коду, проблема с неработоспособностью создания триггера в SQLite заключается в том, что вы используете синтаксис, который не поддерживается в SQLite. Давайте рассмотрим, что именно неверно, и как это исправить.

1. **Синтаксис для установки значений в триггерах**: В SQLite синтаксис триггера не включает использование конструкции `SELECT ... INTO`. Вместо этого, вы можете напрямую присваивать значения полям через `SET NEW.field`, что также не поддерживается в таком виде. Для SQLite следует использовать обычное присваивание `NEW.field = value`.

2. **Форматирование даты**: Вы можете установить поле с текущей датой прямо в триггере, используя функцию `datetime('now')`.

Вот исправленный код триггера:

```sql
CREATE TRIGGER IF NOT EXISTS set_request_date_and_status
BEFORE INSERT ON data_matrix_logs
FOR EACH ROW
BEGIN
    SET NEW.request_date = datetime('now');
    SET NEW.processing_status = 'Создано';
END;
```

Но этот синтаксис также неверен в контексте SQLite. Простой способ задать данные в триггере будет такой:

```sql
CREATE TRIGGER IF NOT EXISTS set_request_date_and_status
BEFORE INSERT ON data_matrix_logs
FOR EACH ROW
BEGIN
    -- Устанавливаем текущую дату
    NEW.request_date = datetime('now');
    -- Устанавливаем статус
    NEW.processing_status = 'Создано';
END;
```

Теперь вы можете добавить этот триггер после создания таблицы `data_matrix_logs`, и он будет правильно работать, подставляя текущую дату и статус при каждом вставлении новой записи.

### Полный обновленный код

Как итог, вот полный код с исправленным триггером:

```python
import sqlite3 as sq
from conf_lib import Config

class DB:
    def __init__(self):
        self.db_file = Config().get('DATABASE', 'db_file')
        self.connection = sq.connect(self.db_file)
    
    def build_db(self):
        cursor = self.connection.cursor()
        
        user_table = '''
CREATE TABLE users (
    user_id INT PRIMARY KEY,
    username VARCHAR(255),
    first_name VARCHAR(255),
    last_name VARCHAR(255),
    role TEXT DEFAULT 'user'
);
'''
        products_table = '''
CREATE TABLE products (
    plu VARCHAR(255) PRIMARY KEY,
    name VARCHAR(255),
    barcodes TEXT
);
'''
        data_matrix_logs_table = '''
CREATE TABLE data_matrix_logs (
    log_id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INT,
    request_type TEXT,
    tp_code TEXT,
    requested_count INT,
    request_date TEXT,
    processing_status TEXT CHECK (processing_status IN ('Создано', 'Восстановлено', 'Завершено')),
    FOREIGN KEY (user_id) REFERENCES users(user_id)
);
'''

        trigger = '''
CREATE TRIGGER IF NOT EXISTS set_request_date_and_status
BEFORE INSERT ON data_matrix_logs
FOR EACH ROW
BEGIN
    SET NEW.request_date = datetime('now');
    SET NEW.processing_status = 'Создано';
END;
'''
        cursor.execute(user_table)
        cursor.execute(products_table)
        cursor.execute(data_matrix_logs_table)
        cursor.execute(trigger)

        self.connection.commit()
        cursor.close()
```

Обратите внимание, что у SQLite нет конструкции `SET`, поэтому вы должны просто использовать `NEW.field = value`. Если вы добавите триггер после создания таблицы и верно назначите значения, все должно заработать как задумано.
В этом случае вам не нужны триггеры. Просто прочтите о 
DEFAULT VALUES
Благодарю вас, теперь всё работает просто отлично!
Похожие вопросы