forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   PHP / MySQL (http://forum.boolean.name/forumdisplay.php?f=135)
-   -   Добавить запись если не существует. (http://forum.boolean.name/showthread.php?t=17836)

pax 05.02.2013 15:09

Добавить запись если не существует.
 
Подскажите лучший вариант реализации такого сабжа, есть ли возможность записать это в один запрос, а не делать сначала select а потом insert или update (MySQL)?

Randomize 05.02.2013 22:46

Ответ: Добавить запись если не существует.
 
http://dev.mysql.com/doc/refman/5.0/...duplicate.html

Например:
Код:

INSERT INTO users
SET
  `id` = 1,
  `name` = "ololosha",
  `pass` ="somepass"
ON DUPLICATE KEY UPDATE
  `name` = "ololosha",
  `pass`  = "somepass"

id - PRIMARY KEY (или UNIQUE)

moka 05.02.2013 22:54

Ответ: Добавить запись если не существует.
 
Код:

INSERT IGNORE INTO table
IGNORE - не вставит если произойдёт коллизия каких либо Primary или Unique значений.

Копипаст от сюда: http://stackoverflow.com/questions/1...xists-in-mysql

pax 05.02.2013 22:57

Ответ: Добавить запись если не существует.
 
А если не знаешь primary key?

moka 05.02.2013 22:58

Ответ: Добавить запись если не существует.
 
А как ты собирался проверить что запись уже существует?

UPD
Если например у тебя таблица с аккаунтами [id(primary), email(unique), name, pass, ...], и ты проверяешь по email'у, получается у тебя email - это Unique значение.
Пометь его в mysql как Unique, и затем при INSERT IGNORE возникнет коллизия по полю email, и оно вставлено не будет.

pax 05.02.2013 23:52

Ответ: Добавить запись если не существует.
 
У меня таблица параметров игрока, уникальна связка id игрока и id параметра, есть еще id записи. Получается надо создать связанный ключ из первых двух, а id записи выкинуть?

UPD: т.е. примерно такая таблица:

moka 06.02.2013 00:12

Ответ: Добавить запись если не существует.
 
Если у тебя будет несколько параметров на игрока но id параметра не будет повторяться на игрока. То я бы сделал 3 колонки смело.
Первая ID игрока - индексированная. Т.к. ты будешь часто по этому данному часто искать.
ID параметра - тут можно уже не индексировать, главное в поиске искать сперва ID игрока, а потом уже по параметру.
А вот третья колонка - уникальный Primary Key, по которому если тебе нужно получить конкретный параметр ты будешь иметь возможность сразу найти.
Он состоит из ID игрока + ID параметра.
Например:
123_456
Где 123 - ID игрока, и 456 - ID параметра.
Таким образом у тебя есть уникальный адрес на каждый параметр для любого игрока. Быстрый доступ - и точка коллизии для вставки используя IGNORE атрибут.

Всё правильно ниже.

pax 06.02.2013 00:14

Ответ: Добавить запись если не существует.
 
Т.е. так не норм?
PHP код:

$sth $this->dbh->prepare'INSERT INTO user_params (user, parameter, value) VALUES (:user, :parameter, :value1)
ON DUPLICATE KEY UPDATE  value = value + :value2'
);
            
$sth->execute(array(
                
':user' => intval($id),
                
':parameter' => intval($parameter),
                
':value1' => intval($value),
                
':value2' => intval($value)
            )); 

И получается что по user поиск будет медленный?

moka 06.02.2013 00:15

Ответ: Добавить запись если не существует.
 
Если нету записи - вставит, если есть, обновит значение, прибавив к прошлому новое.

pax 06.02.2013 00:16

Ответ: Добавить запись если не существует.
 
по user будет поиск медленный с таким ключем? типа select * from user_params where user= :user

moka 06.02.2013 00:22

Ответ: Добавить запись если не существует.
 
Цитата:

Сообщение от pax (Сообщение 251836)
по user будет поиск медленный с таким ключем? типа select * from user_params where user= :user

http://dev.mysql.com/doc/refman/5.0/...n-indexes.html

Будет шустро по первому индексу. Или по обоим, но не по правым.

pax 06.02.2013 00:23

Ответ: Добавить запись если не существует.
 
Значит мне подходит, спасибо.

moka 06.02.2013 00:23

Ответ: Добавить запись если не существует.
 
Я тоже кое-что выучил..

pax 06.02.2013 00:32

Ответ: Добавить запись если не существует.
 
Такой запрос тоже нормально должен сработать ведь?
PHP код:

'SELECT p.name, up.value FROM user_params up JOIN parameters p ON up.parameter = p.id WHERE up.user = ?' 

по user ведь поиск получется...

moka 06.02.2013 02:59

Ответ: Добавить запись если не существует.
 
Будет искать по user да, индекс сработает. Но учти что JOIN вернёт только если будет совмещение.
Например если есть запись в user_params а записи в parameters нету (по id как ты соединяешь), то JOIN ничего не возвратит.

Твой запрос возвратит все параметры определённого юзверя.

pax 06.02.2013 07:36

Ответ: Добавить запись если не существует.
 
Да мне так и надо. Теперь я думаю что мне надо искать по параметрам тоже... Т.е. получить список пользователей отфильтрованный по какому либо параметру и отсортированный по значению параметра - таблица рекордов например. Пока появилась мысль дублировать поле parameter и добавить ему индекс, а для сортировки тоже нужен индекс для значения параметра?

moka 06.02.2013 14:52

Ответ: Добавить запись если не существует.
 
По сути не нужны индексы для сортировки.
Единственная разница в том что если ты будешь делать запросы к данным, заместо прямого доступа по индексу, бд будет листать все данные логически отфильтровывая.
Следственно, тут если нужна хорошая скорость, либо использовать индексы (но и они не всегда спасают), либо завезти отдельную таблицу которая будет обновляться в бд периодически, и выдавать её - получается кеш. Это самый шустрый вариант.

Просто когда речь будет идти о десятках тысяч игроков, то не индексированный поиск и сортировка не будет быстрой затеей.

pax 06.02.2013 15:13

Ответ: Добавить запись если не существует.
 
Отдельная таблица - хорошо, сделаю раз N минут обновление. Но индексы я создам. Создал индекс на поле parameter. Первый раз почему-то ругалось. Теперь норм)

pax 06.02.2013 15:29

Ответ: Добавить запись если не существует.
 
Еще интересно, такие запросы это очень плохо?
PHP код:

SELECT FROM `user_params
WHERE user 49 AND parameter 
      (
SELECT id FROM parameters WHERE name 'frags' LIMIT 1


moka 06.02.2013 19:32

Ответ: Добавить запись если не существует.
 
Это называется Subquery, и если он находится во внешней части запроса (WHERE), то это будет произведено один раз перед главным запросом, и затем будет использовано.
А вот если ты сделаешь подобное до FROM - для значения, то такой Subquery будет запущен для каждой строки которая будет выведена (учитывая возможный LIMIT).

По сути это ок.
Вопрос, у тебя как понимаю параметр это не только имя, и id, а что-то ещё, типо тип значения, минимальное / максимальное значение? Если это всё же что-то простое, то я не вижу необходимости иметь отдельную таблицу. Если же сложное - то оправдано.

pax 06.02.2013 20:20

Ответ: Добавить запись если не существует.
 
Я тут возможно подумаю о преобразовании поля parameter в тип enum, чтобы не получать id параметров из другой таблицы по имени. Сейчас таблица parameters просто список параметров с их id.

moka 06.02.2013 22:39

Ответ: Добавить запись если не существует.
 
Цитата:

Сообщение от pax (Сообщение 251945)
Я тут возможно подумаю о преобразовании поля parameter в тип enum, чтобы не получать id параметров из другой таблицы по имени. Сейчас таблица parameters просто список параметров с их id.

А почему тебе просто не использовать в таблице user_params в поле param сразу имя параметра типо: armor, health, speed и т.п.?
Они индексируются хорошо, и поиск нормально по ним тоже если индексированы.
Тем самым и читаемы, и одна таблица заместо двух.

pax 07.02.2013 07:14

Ответ: Добавить запись если не существует.
 
да хотел не тратить лишней памяти

Randomize 07.02.2013 08:36

Ответ: Добавить запись если не существует.
 
Кстати при таком подходе и производительность будет выше, а затрачиваемой памяти будет куда меньше.
Хотя главное тут не память, а количество обращений к файловой системе.

pax 08.02.2013 07:50

Ответ: Добавить запись если не существует.
 
В общем решил отказаться от Enum вот почему:
http://www.mysqlperformanceblog.com/...hat-is-faster/
Цитата:

The problem we had with ENUM-fields; When we wanted to add a ‘type’ to an ENUM-field (or change the definition of the field in any way), MySQL often did a complete rewrite of that table. For some of our bigger tables (think tens of millions of records), this could take up to a few hours sometimes, which meant downtime. So for a few cases we decided to switch from ENUM’s to TINYINT’s because this eliminates the need for a change in table definition.
и пришел к выводу что лучше сделать в php вот так:
PHP код:

$config = array(
        ...,
        
'user_params' => array(
            
'frags' => 1,
            
'deaths' => 2,
            
'wins' => 3,
            
'fails' => 4,
            
'xp' => 5,
            
'money' => 6,
            
'golds' => 7
        
),
       ); 

и поле объявить как tinyint

Randomize 08.02.2013 11:20

Ответ: Добавить запись если не существует.
 
Цитата:

Сообщение от pax (Сообщение 252107)
PHP код:

$config = array(
        ...,
        
'user_params' => array(
            
'frags' => 1,
            
'deaths' => 2,
            
'wins' => 3,
            
'fails' => 4,
            
'xp' => 5,
            
'money' => 6,
            
'golds' => 7
        
),
       ); 


O__o
Если я правильно понял это константы.
PHP код:

<?
class UserParams{
     const FRAGS = 1;
     const DEATHS = 2;
     const WINS = 3;
}
// ну и юзать так
UserParams::MONEY
?>


pax 08.02.2013 13:09

Ответ: Добавить запись если не существует.
 
чтобы понятнее было чего я хочу добиться:

PHP код:

$config = array(
...
    
'user_params' => array(
        
'frags' => 1,
        
'deaths' => 2,
        
'wins' => 3,
        
'fails' => 4,
        
'xp' => 5,
        
'money' => 6,
        
'golds' => 7
    
),
    
'user_params_invert' => array(
        
=> 'frags',
        
=> 'deaths',
        
=> 'wins',
        
=> 'fails',
        
=> 'xp',
        
=> 'money',
        
=> 'golds'
    
),
); 

PHP код:

public function user_updateParameter($id$parameterName$value)
    {
        
$param $this->config['user_params'][$parameterName];
        if (isset(
$param)) {
            
$sth $this->dbh->prepare('INSERT INTO user_params (user, parameter, value) VALUES (:user, :parameter, :value1) ON DUPLICATE KEY UPDATE  value = value + :value2');
            
$sth->execute(array(
                
':user' => intval($id),
                
':parameter' => $param,
                
':value1' => intval($value),
                
':value2' => intval($value)
            ));
        }
    } 

PHP код:

public function user_getParameters($id)
    {
        
$sth $this->dbh->prepare('SELECT parameter, value FROM user_params WHERE user = ?');
        
$sth->execute(array(intval($id)));
        
$params $sth->fetchAll(PDO::FETCH_NUM);
        
$nameValue = array();
        
$pi = &$this->config['user_params_invert'];
        foreach (
$params as $keyValue) {
            
$nameValue[$pi[$keyValue[0]]] = $keyValue[1];
        }
        return 
$nameValue;
    } 



Часовой пояс GMT +4, время: 23:53.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot