За последние 24 часа нас посетили 55325 программистов и 1725 роботов. Сейчас ищут 867 программистов ...

Девелопинг форума - прочитанные сообщения

Тема в разделе "Прочие вопросы по PHP", создана пользователем Psih, 18 янв 2007.

  1. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Девелоплю для проэкта форум, смесь PHPBB, vBulletin и прочих - крч вбираю всё лучшее из всех форумов :)

    Столкнгулся с такой вещью как "Отметить форумы как прочитанное" - появилась загвоздка...

    КАК это реализуют по умному и при минимум ресурсов (особенно SQL запроса).

    собственно есть таблицы вида:

    PHP:
    1.  
    2. CREATE TABLE forums_areas (
    3.   far_id int(11) unsigned NOT NULL auto_increment,
    4.   far_title char(50) NOT NULL default '',
    5.   far_creation_date datetime NOT NULL,
    6.   PRIMARY KEY  (far_id),
    7.   KEY far_creation_date (far_creation_date)
    8. ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
    9.  
    10. CREATE TABLE forums (
    11.   for_id int(11) unsigned NOT NULL auto_increment,
    12.   for_far_id int(11) unsigned NOT NULL,
    13.   for_title char(50) NOT NULL default '',
    14.   for_creation_date datetime NOT NULL,
    15.   for_descr char(200) default NULL,
    16.   for_topics int(11) unsigned NOT NULL default '0',
    17.   for_messages int(11) unsigned NOT NULL default '0',
    18.   for_last_frm_id int(11) unsigned NOT NULL default '0',
    19.   PRIMARY KEY  (for_id,for_far_id),
    20.   UNIQUE KEY for_id (for_id),
    21.   KEY for_last_frm_id (for_last_frm_id),
    22.   KEY for_creation_date (for_creation_date),
    23.   KEY for_far_id (for_far_id)
    24. ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
    25.  
    26. CREATE TABLE forums_topics (
    27.   frt_id int(11) unsigned NOT NULL auto_increment,
    28.   frt_prf_id int(11) unsigned NOT NULL,
    29.   frt_for_id int(11) unsigned NOT NULL,
    30.   frt_creation_date datetime NOT NULL,
    31.   frt_title char(50) NOT NULL default '',
    32.   frt_closed enum('no','yes') NOT NULL default 'no',
    33.   frt_sticky enum('yes','no') NOT NULL default 'no',
    34.   frt_replyes int(11) unsigned NOT NULL default '0',
    35.   frt_views int(11) unsigned NOT NULL default '1',
    36.   frt_last_frm_id int(11) unsigned NOT NULL,
    37.   PRIMARY KEY  (frt_id,frt_prf_id,frt_for_id),
    38.   KEY frt_creation_date (frt_creation_date),
    39.   KEY frt_last_frm_id (frt_last_frm_id),
    40.   KEY frt_replyes (frt_replyes),
    41.   KEY frt_views (frt_views)
    42. ) ENGINE=InnoDB AUTO_INCREMENT=56 DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
    43.  
    44. CREATE TABLE forums_messages (
    45.   frm_id int(11) unsigned NOT NULL auto_increment,
    46.   frm_msg text,
    47.   frm_prf_id int(11) unsigned NOT NULL,
    48.   frm_creation_date datetime NOT NULL,
    49.   frm_frt_id int(11) unsigned NOT NULL,
    50.   frm_for_id int(11) unsigned NOT NULL,
    51.   PRIMARY KEY  (frm_id),
    52.   KEY frm_creation_date (frm_creation_date),
    53.   KEY frm_frt_id (frm_frt_id),
    54.   KEY frm_for_id (frm_for_id)
    55. ) ENGINE=InnoDB AUTO_INCREMENT=711 DEFAULT CHARSET=utf8;
    56.  
    57. CREATE TABLE forums_last_readed (
    58.   flr_for_id int(11) unsigned NOT NULL,
    59.   flr_frt_id int(11) unsigned NOT NULL,
    60.   flr_prf_id int(11) unsigned NOT NULL,
    61.   flr_frm_id int(11) unsigned NOT NULL,
    62.   PRIMARY KEY  (flr_for_id,flr_frm_id,flr_prf_id)
    63. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED;
    64.  
    *_prf_id - связка на ID пользователя. Остальное я думаю опытному глазу понятно будет.

    Идея такова, что в forums_last_readed запись о прочтении топика вноситься когда пользователь посещает конкретный топик, поэтому если мы отмечаем всё форумы как прочитанные приходиться учитывать что для части топиков записи уже есть, им нужно сделать UPDATE, а вот большей части не будет, поэтому им надо делать INSERT. Да да, я уже слышу ваши крики про REPLACE, вообщем то для одной части я так и сделал - у меня есть один большой форум и аналог Yahoo Groups, для них я сделал так (поля DB немного подругому называються):
    PHP:
    1.  
    2. /*
    3. * $this->group_id - ID группы
    4. */
    5. $result = mysql_query('SELECT gfo_id, grt_id, MAX(grm_id)
    6.                     FROM groups
    7.                     LEFT JOIN groups_areas ON grp_id = gar_grp_id
    8.                     LEFT JOIN groups_forums ON gfo_gar_id = gar_id
    9.                     LEFT JOIN groups_topics ON grt_gfo_id = gfo_id
    10.                     LEFT JOIN groups_messages ON grm_grt_id = grt_id
    11.                     WHERE grp_id = '.(int)$this->group_id.' GROUP BY grt_id', $database);
    12.             $rows = array();
    13.             while ($row = mysql_fetch_row($result)){
    14.                 $rows[] = '('.$row[0].', '.$row[1].', '.$session['auth'].', '.$row[2].')';
    15.             }
    16.             mysql_query('REPLACE groups_last_readed (lsr_gfo_id, lsr_grt_id, lsr_prf_id, lsr_grm_id) VALUES'.implode(',', $rows), $database);
    Для небольших групп это годиться, но стоит форуму разрастить хотя бы до 100 топиков (что очень быстро), я уже не говорю про большой общий форум, где это дело недели - представляете какого размера будет REPLACE запрос и как он будет долго выполняться... Очень быстро это всё отвалиться, поэтому мне срочно нужна альтернатива, возможно изменение структуры базы, что бы это можно было сделать в один небольшой запрос (можно мега-сложный, главное что бы быстро работало) или поменять структуру так, что бы на каждую пару ФОРУМ-ТОПИК-ЮЗЕР не надо было свою запись - хотя помоему это почти невозможно..
    Думаю так же о процедуре так же..

    Ваши идеи/предложения. Принимаються любые варианты (кроме смены БД) от лёгкой модификации до полного перелопачивания. Смеха ради попрошу не писать.
     
  2. Ti

    Ti Активный пользователь

    С нами с:
    3 июл 2006
    Сообщения:
    2.378
    Симпатии:
    1
    Адрес:
    d1.ru, Екатеринбург
    REPLACE нет в стандарте и, вроде, нет в PostgreSQL.

    делать выборку и вставку(REPLACE) можно одним запросом.
     
  3. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Это понятно, но это большой портал, так что совместимость нам полностью побоку.

    Вообщем народ, я нашел способ:
    PHP:
    1.  
    2. mysql_query('REPLACE INTO forums_last_readed
    3.                     SELECT for_id, frt_id, '.$session['auth'].', MAX(frm_id)
    4.                     FROM forums
    5.                     LEFT JOIN forums_topics ON frt_for_id = for_id
    6.                     LEFT JOIN forums_messages ON frm_frt_id = frt_id
    7.                     GROUP BY frt_id', $database);
    8.  
    Работает на ура :)

    P.S. В других базах где нету аналога REPLACE я чуствую такой запрос станет большой головной болью :)
     
  4. Ti

    Ti Активный пользователь

    С нами с:
    3 июл 2006
    Сообщения:
    2.378
    Симпатии:
    1
    Адрес:
    d1.ru, Екатеринбург
    replace эмулировать не сложно, достаточно добавить в запросе таблицу и условие
     
  5. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Очень бы хотелось бы видеть такой запрос... что ли типо IF(bla bla, UPDATE... , INSERT...) ?
     
  6. Anonymous

    Anonymous Guest

    Триггеры.
     
  7. Ti

    Ti Активный пользователь

    С нами с:
    3 июл 2006
    Сообщения:
    2.378
    Симпатии:
    1
    Адрес:
    d1.ru, Екатеринбург
    LEFT JOIN table ON ... WHERE ISNULL(table.row)...