LINUX.ORG.RU

SQL Update or Insert

 , , ,


1

1

Уважаемые форумчане! Требуется ваша помощь!

Подскажите, как правильно? Задача простая, нужно записать визит пользователя в базу данных.

  1. Подключаемся к базе данных.
  2. Обновляем запись о посетителе в таблице.
  3. А вдруг посетитель впервые и его записи нет, значит вставляем запись.
  4. Для развёртывания (для первого запуска): А вдруг таблицы нет, значит создаём таблицу.

Вопрос 1: Беспокоит то, что каждый раз при обновлении (update) куча холостых операций (create, insert), может это как то иначе решается, подскажите.

Вопрос 2: А как вы решаете задачи которые требуются однократно при первом запуске?

Вопрос 3: Подключение к базе данных. Может в другой файл переместить? Но тогда при перемещении файла будут проблемы, а если оставить - то постоянно открывается и закрывается подключение к базе данных в разных файлах.

$db = new PDO('sqlite:file.sqlite');

$db -> exec("CREATE TABLE IF NOT EXISTS 'tableName' (

id PRIMARY KEY AUTOINCREMENT,
user TEXT UNIQUE NOT NULL,
visitCounter INTEGER DEFAULT '0'

)");

$db -> exec("INSERT OR IGNORE INTO 'tableName' (user) VALUES ('user1')");

$db -> exec("UPDATE 'tableName' SET visitCounter = visitCounter + 1 WHERE user = 'user1'");

unset($db);

Работу на прямую с базой данных перенеси в отдельный класс, а в других файлах используй её объекты.

Создание таблицы выполняй только один раз, при первом создании файла. Можешь создать собственный bin/db_init.php файл, или как там тебе его удобнее назвать, который будешь сам вызывать в ручную перед запуском проекта на новой машине.

Если не нравится два запроса, можно сделать один, на подобии:

INSERT  INTO 'newVisits' (user) VALUES ('user1');
а записи в этой таблице newVisits обрабатывать и стирать кроном (только сначала MAX(ID) забери, до которого будешь брать и удалять данные, чтоб новые инсерты не конфликтовали с работой крона).

Но лучше меняй саму логику программы, чтоб пользователи при регистрации визита должны были уже присутствовать в таблице. Как то странно, что у тебя кто угодно может придти и заполучить запись с визитом...

anonymous ()

А вдруг таблицы нет, значит создаём таблицу.

Создай заранее и будь уверен, что таблица есть.

А вдруг посетитель впервые и его записи нет, значит вставляем запись.

Тут два варианта. Обычно данные пользователя читаются сразу при обработке запроса через select, т.е. ты сразу знаешь есть пользователь, или нет. Второй вариант - просто всегда добавляй данные - нужно узнать visitCounter делай select count(*)

unset($db);

Зачем? Используй глобально.

А вообще, возьми лучше DBAL и любой DI-контейнер. Если уж не хочешь универсальный ORM, то сооруди хотя-бы DAO.

no-such-file ★★★★★ ()

1. Убери создание таблицы в скрипт инициализации, который будешь руками запускать на сервере - это один из вариантов, но он точно лучше твоего подхода. 2. Запись в БД твоим способом - не атомарная операция, возможно состояние race-condition, прочитай за INSERT ON DUPLICATE. 3. Делать из текстовое поле уникальным - больновато для производительности БД, но не критично. 4. Не понятно зачем ты удаляешь переменную, которая хранит ссылку на инстанс PDO в конце скрипта, не нужно.

c3ph ()

Insert ... on duplicate разве не подойдет?

Deleted ()

Для развёртывания (для первого запуска): А вдруг таблицы нет, значит создаём таблицу.

Перед первым реквестом чекать есть ли вообще бд и таблички.

ggrn ★★★★★ ()
Ответ на: комментарий от Deleted

Insert ... on duplicate разве не подойдет?

this. но исходя из вопроса ТС, uq index'a там может и не быть.

Anoxemian ★★★★★ ()

Вопрос 1: Беспокоит то, что каждый раз при обновлении (update) куча холостых операций (create, insert), может это как то иначе решается, подскажите.

Use UPSERT, Luke!

Вопрос 2: А как вы решаете задачи которые требуются однократно при первом запуске?

google: php database migrations

Вопрос 3: Подключение к базе данных. Может в другой файл переместить? Но тогда при перемещении файла будут проблемы, а если оставить - то постоянно открывается и закрывается подключение к базе данных в разных файлах.

Мартин Фаулер - Архитектура корпоративных программных приложений.

hippi90 ★★★★ ()
Ответ на: комментарий от anonymous

а записи в этой таблице newVisits обрабатывать и стирать кроном

Изощрённо и необычно :)

Как то странно, что у тебя кто угодно может придти и заполучить запись с визитом...

Это упрощённо, для краткости кода, чтобы зерно задачи было лучше видно, в реальной ситуации по другому будет

В любом случае спасибо за ответ!

tonchikp ()
Ответ на: комментарий от no-such-file

Используй глобально.

Не понял что имеется ввиду

А вообще, возьми лучше DBAL и любой DI-контейнер. Если уж не хочешь универсальный ORM, то сооруди хотя-бы DAO

DBAL, ORM, DAO к сожалению с текущим уровнем знаний не умею, изучу позже

tonchikp ()
Ответ на: комментарий от c3ph

INSERT ON DUPLICATE

А оно есть в SQLite3? В документации и в сети не нашёл, только для MySQL

tonchikp ()
Ответ на: комментарий от ggrn

Перед первым реквестом чекать есть ли вообще бд и таблички.

Чекать это if(file_exists)? Ну база есть в любом случае, потому что php+sqlite создают файл при первом обращении.

tonchikp ()
Ответ на: комментарий от tonchikp

Пардон, не увидел в тегах sqlite. Тогда не знаю, я с sqlite особо не работал, может кто другой что подскажет.

По остальной части вопроса, всё-таки настоятельно рекомендую прочитать Фаулера, он рассказывает и про DAL и про общие принципы организации архитектуры. Если вкратце - работа с БД должна быть сосредоточена в одном логическом слое (в простом приложении это может быть один класс), только он знает как работать с БД, все остальные слои работают через DAL. DAO-класс может выставить наружу интерфейс типа save(), get(), remove() и т.д.. Соответственно в этом классе у тебя есть поле, хранящее соединение с БД, оно создается один раз при старте приложения и закрывается при его остановке. Если таких классов много, то тут уже сложнее, тут придется использовать какой-нибудь пул соединений, но если у тебя встает такая задача, то писать свои велосипеды - последнее дело, это всё уже реализовано в различных фреймворках или библиотеках, и надо только выбрать подходящий инструмент.

hippi90 ★★★★ ()
Ответ на: комментарий от tonchikp

Чекать это if(file_exists)? Ну база есть в любом случае, потому что php+sqlite создают файл при первом обращении.

Ну если сам файл создается автоматически, чекай на существование таблиц. С php и sqlite оч давно не имел дел

ggrn ★★★★★ ()

Вставка или обновление это классическая проблема sql.

Либо делай процедуру с транзакцией либо выпендривайся конструкциями типа

insert into ...
select ...
where not exist ....

anonymous ()

Такое впечатление, что пыхыпе живет в какой-то изоляции. Там, ммммм, миграции, ормки, всякие тулзы для деплоинга, неужели ничего этого нет?

nikolnik ★★★ ()

2к17, не андроид, зачем ты взял sqlite?
Это дерьмо естественно не поддерживает православный UPSERT, так что BEGIN TRANSACTION и понеслось, и только так, все остальное от лукавого.

Deleted ()
Ответ на: комментарий от anonymous

Либо делай процедуру с транзакцией либо выпендривайся конструкциями типа

insert into ...
select ...
where not exist ....

Как это воплотить применительно к моей задаче не знаю пока, но подумаю

tonchikp ()
Ответ на: комментарий от tonchikp

А я и не увидел что тебе для SQLite, прости. Поищи аналог, должно что то быть.

c3ph ()
Ответ на: комментарий от Deleted

2к17, не андроид, зачем ты взял sqlite?

Не знай, мне эта база нравится, маленькая, удобненькая, «для маленькой такой компании»(с) :) Да и хостера практически любого переживёт, мне нравится вобщем

так что BEGIN TRANSACTION и понеслось, и только так, все остальное от лукавого

Это применяю, спасибо

tonchikp ()
Ответ на: комментарий от c3ph

А я и не увидел что тебе для SQLite, прости

Да ладно, бывает :)

tonchikp ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.