Autor Zpráva
qteck
Profil
Ahoj,

mám tři tabulky article, tags a ref.tags

přidám článek, vyfiltruju tagy, a pak přidávám tag a hned za ním referenci jeden po jednom dokud tam nejsou všechny.

probém nastane když vkládám dva nebo více stejných tagů. v tabulce tags je na jednom sloupci (name) nastavený unique key.

pracouje to celé nazávislosti z last insert id a tak když vložim duplicitu a unique key to potlačí, dostanu prostě jenom ten nejvyšší id z tags kterej není správný. Následně se mi do ref.tags uloží odkaz na článek s odkazem na tag který neexistuje.

Řešením by bylo odebrat unique key to by ale zpomalilo celej systém a zkrátka se to nehodí. Jak toto vyřešit? Možná nějaký trigger?

Pro zpracování používám takový kód:
$tags = array('boobs','blueberries','food','boobs')

function hashTagInsert($tags) 
    {
        /// commit this as a transaction

        $lastInsertId = $this->db->lastId(); // last insert id of article

        foreach ($tags as $val)
        {
            $sql = 'INSERT INTO tags (tag, added) VALUES (:tag, NOW()) ON DUPLICATE KEY UPDATE occurrence = occurrence + 1';
            $this->db->boolQuery($sql, array(':tag' => $val));

            $sql2 = 'INSERT INTO tags_refs (article_id, tag_id) VALUES (:lastInsertId, LAST_INSERT_ID())';
            $this->db->boolQuery($sql2, array(':lastInsertId' => $lastInsertId));
        }
    }

a po tomto dostanu v db toto:
CREATE TABLE `tags` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tag` varchar(100) NOT NULL,
  `occurrence` int(11) NOT NULL,
  `added` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tag` (`tag`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO `tags` (`id`, `tag`, `occurrence`, `added`) VALUES
(18,    'boobs',    1,  '2015-05-29 16:46:44'),
(20,    'food', 0,  '2015-05-29 16:46:44'),
(19,    'blueberries',  0,  '2015-05-29 16:46:44');

and the reference one:

CREATE TABLE `tags_refs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `article_id` int(11) NOT NULL,
  `tag_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

INSERT INTO `tags_refs` (`id`, `article_id`, `tag_id`) VALUES
(24,    31, 18),
(25,    31, 19),
(26,    31, 20),
(27,    31, 26);

-- 2015-05-29 14:49:55
yFang
Profil
qteck:
Nepomohlo by použít před uložením na pole těch tagů array_unique?
Alphard
Profil
Doporučoval bych trochu předělat tu strukturu. Nesnažil bych se nové tagy automaticky vkládat (protože očekávám, že k jednomu tagu bude existovat mnoho článků), ale zkusil bych je prvně najít a teprve když se nenajde, vložit nový. Tohle by bylo v samostatné funkci, která by vracela id správného tagu, řekněme
function getTagId($tag) {
  if ($dbTag = $db->select($tag))  {
    return $dbTag->id;
  } else {
    $db->insert($tag);
    return $db->last_id
  }
}
a zjištěné id se dodá v parametru.
Tohle celé by možná šlo řešit na úrovní db nastavením vrácení správného id při updatu, ale návrhově mi tam ten on duplicate key nesedí.

yFang [#2]:
V tomto ukázkovém případě ano, ale obecně ne (tagy se sdílejí mezi články).


Doplňuji, že z pohledu konkurenčního přístupu by dávalo smysl prvně zkusit provést insert na tabulku s unikátním klíčem a teprve při selhání kvůli duplicitě hledat starší záznam. Výše popsaný postup je myslím srozumitelnější na vysvětlení, jako oprava stačí prohodit if a else větev v navržené funkci.
qteck
Profil
ale jo to byi dávalo smysl kdyby šlo vyloženě o to jedno pole. článků muže být vloženo více a paki když to pole s tagama nemá duplicity v databázi na ty duplicity narazit můžea pak se zase odehraje scénář který jsem popsal výše.

klíčem bybylo zjistit pokud je v db duplicita jaké řádky byly změněny.

Teď si nejsem jistej, ale napadlo mě, že byse možná dal použít affected rows
-----
affacted rows vrací jenom počet ovlivněných řádků, žádné další detaily. :(


Alphard:

moje představa je taková, že někdo napíše článek a pod něj pak napíše jenom tagy #tag1 #tag2 #tag3 a nebo přímo někde v toku textu toho článku. Já je z toho vytáhnu a pak uložím do db. Ty k teré neexistují vložím a ty které existují v db zvýrazním připočtením occurrence + 1 abych je potom mohl řadit podle četnosti.

Chtěl jsemse vyhnout jakémukoliv testování v phpku bo se trochu obávám rychlosti, pravděpodobně to ale udělám jakříkáš, něják mi došli inspirace :-).
qteck
Profil
Pro tentokrát vyřešeno takto:

    function hashTagInsert($tags) 
    {
        /// commit this as a transaction
        
        $lastInsertId = $this->db->lastId();
            
        foreach ($tags as $val)
        {
            $sql = 'INSERT INTO tags (tag, added) VALUES (:tag, NOW()) ON DUPLICATE KEY UPDATE occurrence = occurrence + 1, last_article_id = :lastInsertId';
            $stmt = $this->db->boolQuery($sql, array(':tag' => $val,
                                                     ':lastInsertId' => $lastInsertId));
            
            if(!$stmt)
            {
               $sql3 = 'SELECT id FROM tags WHERE tag = :tag';
               
               $existingTagId = $this->db->queryFetch($sql3, array(':tag' => $val));
            }
            
            $tagId = (isset($existingTagId)? $existingTagId : $this->db->lastId());
            
            $sql2 = 'INSERT INTO tags_refs (article_id, tag_id) VALUES (:lastInsertId, :tagId)';
            $this->db->boolQuery($sql2, array(':lastInsertId' => $lastInsertId,
                                              ':tagId' => $tagId));
            
            unset($existingTagId);
        }
    }
Alphard
Profil
Můj návrh je přiměřeně efektivní, přidávání článků není tak časté, aby bylo třeba to řešit. Navíc často má přehlednost kódů přednost; ta vaše podmínka založená na testování existence proměnné vytvořené v podmínce není vůbec hezká.

a ty které existují v db zvýrazním připočtením occurrence + 1
Tohle je podle mě zbytečná denormalizace, už jste řešil update článku? Počet použití tagu lze snadno spočítat z tabulky tags_refs.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: