Google+

MySQL шпаргалка ( MySQL Cheat Sheet) #

MySQL шпаргалка ( MySQL Cheat Sheet)

Краткий справочник для MySQL, включает в себя функции, типы, и простые запросы.
Смотреть PNG картинку

Большие обьемы данных MySQL (bigdata) и партиционирование таблиц #

Столкнулся с проблемой больших обьемов данных в MySQL. На помощь пришла относительно новая возможность mysql партицирование данных. Если коротко, то это разбиение таблиц на логические части по определенным критериям. И дает это прирост скорости работы с таблицами.

Смотрим поддерживается ли у нас:
HOW VARIABLES LIKE '%partition%';
have_partitioning | YES


Какие способы «разделения» данных предоставляет mySQL?
1. RANGE
По диапазону значений
PARTITION BY RANGE (store_id) (
 PARTITION p0 VALUES LESS THAN (10),
 PARTITION p1 VALUES LESS THAN (20),
 PARTITION p3 VALUES LESS THAN (30)
 );

2. LIST
По точному списку значений
 PARTITION BY LIST(store_id) (
 PARTITION pNorth VALUES IN (3,5,6,9,17),
 PARTITION pEast VALUES IN (1,2,10,11,19,20)
 )

3. HASH
Вы никак не управляете партицированием, просто указываете, по какому полю строить хеш и сколько «подтаблиц» создавать.
 PARTITION BY HASH(store_id)
 PARTITIONS 4;

4. KEY
Почти то же самое что и HASH, но более логично — по ключу.
 PARTITION BY KEY(s1)
 PARTITIONS 10;

Первый раз я попробовал разбить по RANGE DATETIME.
ALTER TABLE flows
PARTITION BY RANGE( TO_DAYS(date) ) (
 PARTITION y2013m10 VALUES LESS THAN( TO_DAYS('2013-10-01') ),
 PARTITION y2013m11 VALUES LESS THAN( TO_DAYS('2013-11-01') ),
 PARTITION y2013m12 VALUES LESS THAN( TO_DAYS('2013-12-01') )
);


Но получил:
[Error] 1503 - A PRIMARY KEY must include all columns in the table partitioning function


Вылез неприятный момент из за того что, поле по которому делаются партиции должно быть PRIMARY KEY, то есть уникальным.
А данные в таблицу пишутся не прерывно и вполне возможно что в 1 секунду писаться несколько значений. поэтому такое разделение мне не подойдет =(

Решено было найти id где заканчиваются года и сделать альтер по ним, как-то так. То есть разделение будет до 2013 года и после. При необходимости можно разбить мельче.
ALTER TABLE flows
PARTITION BY RANGE (id) (
        PARTITION y2013 VALUES LESS THAN( 231607217 ),
        PARTITION y2014 VALUES LESS THAN(MAXVALUE)
);

Что мы получаем? Первая «таблица» будет хранить данные за «архивный» период, до 2013 года, вторая — все остальное.

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

И ликбез: http://www.rldp.ru/mysql/mysqlpro/parts.htm

Удалить дублирующиеся записи в базе mysql (duplicate entry) #

Создаем чистую таблицу аналогичной то что у нас есть с неуниклаьными значениями
CREATE TABLE clean LIKE options

Создаем индексы для нужных полей
ALTER IGNORE TABLE clean ADD UNIQUE INDEX (add1, add2, add3)

Дальше вставляем данные из нашей старой базы в новую с параметром IGNORE
INSERT IGNORE INTO clean SELECT * FROM options

Удаляем старую таблицу
DROP TABLE options

И переименовываем новую в старую
RENAME TABLE clean TO options

Мощь MySQL #

ON DELETE CASCADE
CREATE TABLE test1 ( 
   id INTEGER NOT NULL, 
   parent INTEGER, 
   PRIMARY KEY (id), 
   FOREIGN KEY (parent) REFERENCES test1 (id) 
     ON UPDATE CASCADE 
     ON DELETE CASCADE 
 ); 
правило ON DELETE CASCADE, заданное для внешнего ключа по полю PARENT. Благодаря нему, при удалении узла будет автоматически удалено и все поддерево, для которого этот узел является корнем.

ON DUPLICATE KEY
// находим объект
 $row = query('SELECT * FROM table WHERE id=1');
 // проверяем есть ли такой объект
 if ($row) {
 // делаем апдейт
 query('UPDATE table SET column=column+1 WHERE id=1');
 }
 else {
 // делаем вставку
 query('INSERT INTO table SET column=1, id=1');
 }
Можно заменить на запрос без php
INSERT INTO table SET column = 1, id=1 ON DUPLICATE KEY UPDATE column = column + 1


INSERT IGNORE
// находим объект
 $row = query('SELECT * FROM table WHERE id=1');
 // если такого объекта нет, то вставляем новую запись
 if (!$row) {
 query('INSERT INTO table …');
 }
заменяем на запрос без php
INSERT IGNORE INTO table …

MySQL Event Scheduler #

MySQL Event Scheduler

Есть проект в котором при заходе юзера(каждого!) на страницу авторизации, а это первая(главная) страница сайта, выполняются запросы к бд:
- на очистку таблицы антибрут которым уже больше 1-го часа
- на очистку таблицы антиспам которым уже больше 1-го часа
- посчитать количество неудачных попыток с этого айпи
- посчитать количество отправленных писем с этого айпи
Хоть это легкие запросы, все же это напрягало, 4 запроса на то, что бы подсовывать капчу, или нет.

Логично что нужно избавится от первых 2 запросов(не зачем каждому юзеру выполнять их). Как решение, ставить скрипт по крону - можно, но оказывается - не нужно.
В бд MySQL есть свой Event Scheduler DAEMON!

1) Включаем MySQL Event Scheduler
SET GLOBAL event_scheduler = 1;

MySQL Event Scheduler


2) Создаем Event-ы
CREATE EVENT antibrut
ON SCHEDULE EVERY 1 HOUR
DO
DELETE FROM _antibrut WHERE logintime < DATE_SUB(NOW(),INTERVAL 1 HOUR)

CREATE EVENT antispam
ON SCHEDULE EVERY 1 HOUR
DO
DELETE FROM _antispam WHERE time < DATE_SUB(NOW(),INTERVAL 1 HOUR)


Дополнительно
Посмотреть Event-ы:
SHOW EVENTS;


Если хотить модифицирывать ваш Event, делать через ALTER, например:
ALTER antibrut 
ON SCHEDULE EVERY 5 HOUR 
STARTS TIMESTAMP + 3 HOUR 


Удалить Event:
DROP EVENT antispam;

Список товаров в категории ShopOS #

Список товаров в категории ShopOS

Немного теории:
http://www.getinfo.ru/article610.html - крутая статья!
http://phpclub.ru/detail/article/db_tree

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

Для начало нужно было получить вот такое меню:
Список товаров в категории ShopOS

Что бы при нажатии на главную категорию выпадал список товаров. Не долго мучаясь отписал на официальном форуме . Увы ответов не получил, готовых решений нет. Понял, что придётся самому.

С ЧПУ (вставить в index.html вашего шаблона):

            {php}        
            $sql = "SELECT cat.categories_id, cd.categories_name FROM vip_categories cat, vip_categories_description cd WHERE cat.categories_status = '1' AND cd.categories_id = cat.categories_id";
            $sql = osDBquery($sql);
            while ($row = os_db_fetch_array($sql)) {
                    $menu[$row['categories_id']] = $row['categories_name'];
            }
            foreach ($menu as $CatId => $CatName )
            {
                echo "
  • ".$CatName."";
                echo "
      ";
                      $query = "SELECT * FROM
                          vip_products p,
                          vip_products_description pd,
                          vip_products_to_categories p2c,
                          vip_categories c
                      WHERE
                          c.categories_status='1'
                          and p.products_status = '1'
                          and p.products_id = p2c.products_id
                          and p.products_id=pd.products_id
                          and p2c.categories_id = '".$CatId."'
                          and c.categories_id = '".$CatId."'
                      ORDER by p.products_date_added";
                      $query = osDBquery($query);
                      while ($row1 = os_db_fetch_array($query)) {
                          echo "
    • ".$row1['products_name']."
    • ";
                      }
                  echo "
  • ";
            }
            {/php}            


На выходе:


Оказалось что необходимость в данном меню отпадает, но появляется новая!
Список товаров в категории ShopOS

Класс! Теперь есть еще уровень вложенности.

С ЧПУ (вставить в index.html вашего шаблона):

    {php}
    $sql = "SELECT cat.categories_id, cat.parent_id, cat.categories_url, cd.categories_name FROM vip_categories cat, vip_categories_description cd WHERE cat.categories_status = '1' AND cd.categories_id = cat.categories_id ORDER BY cat.parent_id, cat.categories_id";
    $sql = osDBquery($sql);
    while ($row = os_db_fetch_array($sql)) {
            $menu[$row['parent_id']][$row['categories_id']] = $row['categories_name'];
    }
    foreach ($menu[0] as $CatId => $CatName )
    {
     echo "
  • ".$CatName."";
     if (isset($menu[$CatId])) {
            echo "
      ";
               foreach ($menu[$CatId] as $ItemId => $ItemName)
               {
                echo "
    • ".$ItemName."";
                echo "
        ";
                        $query = "SELECT * FROM
                                vip_products p,
                                vip_products_description pd,
                                vip_products_to_categories p2c,
                                vip_categories c
                        WHERE
                                c.categories_status='1'
                                and p.products_status = '1'
                                and p.products_id = p2c.products_id
                                and p.products_id=pd.products_id
                                and p2c.categories_id = '".$ItemId."'
                                and c.parent_id = '".$CatId."'
                        ORDER by p.products_date_added";
                        $query = osDBquery($query);
                        while ($row1 = os_db_fetch_array($query)) {
                                echo "
      • ".$row1['products_name']."
      • ";
                        }  
                   echo "
      ";
                echo "
    • ";
               }
              echo "
  • ";
     } else { echo "";}
    }
    {/php}


На выходе:



Мне ЧПУ не надо - БЕЗ ЧПУ (вставить в index.html вашего шаблона):


    {php}
    $sql = "SELECT cat.categories_id, cat.parent_id, cat.categories_url, cd.categories_name FROM vip_categories cat, vip_categories_description cd WHERE cat.categories_status = '1' AND cd.categories_id = cat.categories_id ORDER BY cat.parent_id, cat.categories_id";
    $sql = osDBquery($sql);
    while ($row = os_db_fetch_array($sql)) {
            $menu[$row['parent_id']][$row['categories_id']] = $row['categories_name'];
    }
    foreach ($menu[0] as $CatId => $CatName )
    {
     echo "
  • ".$CatName."";
     if (isset($menu[$CatId])) {
            echo "
      ";
               foreach ($menu[$CatId] as $ItemId => $ItemName)
               {
                echo "
    • ".$ItemName."";
                echo "
        ";
                        $query = "SELECT * FROM
                                vip_products p,
                                vip_products_description pd,
                                vip_products_to_categories p2c,
                                vip_categories c
                        WHERE
                                c.categories_status='1'
                                and p.products_status = '1'
                                and p.products_id = p2c.products_id
                                and p.products_id=pd.products_id
                                and p2c.categories_id = '".$ItemId."'
                                and c.parent_id = '".$CatId."'
                        ORDER by p.products_date_added";
                        $query = osDBquery($query);
                        while ($row1 = os_db_fetch_array($query)) {
                                //echo "
        ";
                                //print_r($row1);
                                //echo "
        ";
                                echo "
      • ".$row1['products_name']."
      • ";
                        }  
                   echo "
      ";
                echo "
    • ";
               }
              echo "
  • ";
     } else { echo "";}
    }
    {/php}


На выходе:

Получили что хотели, на не совсем так. Теперь я не могу попасть в раздел КАТЕГОРИИ, он то не кликабельный (я использую ява меню при клике на категорию выпадает вложенный UL), а это существенно и в ТЗ прописано.

Поэтому этот вариант мне не подходит, совсем. Будем работать с {$box_CATEGORIES}.

Идем в наш_сайт\themes\наш_шаблон\source\inc\show_category.inc.php. Проверяем что бы параметр $MaxLevel равен был 1, это значит, что по умолчанию будет показываться 1 уровень вложенности.
В том же файлике находим:
if ($old_level > $level) {

и после вставляем:
                        if ( isset($GLOBALS['cPath']) ) {
                            if ( !strpos($GLOBALS['cPath'], "_")===false ) {
                            $path = explode('_',$GLOBALS['cPath']);
                                $query = "SELECT * FROM
                                    vip_products p,
                                    vip_products_description pd,
                                    vip_products_to_categories p2c,
                                    vip_categories c
                                WHERE
                                    c.categories_status='1'
                                    and p.products_status = '1'
                                    and p.products_id = p2c.products_id
                                    and p.products_id=pd.products_id
                                    and p2c.categories_id = '".$path[1]."'
                                    and c.parent_id = '".$path[0]."'
                                ORDER by p.products_date_added";
                                $query = osDBquery($query);
                                $categories_string .= "
    ";
                                    while ($row1 = os_db_fetch_array($query)) {
                                        $categories_string .= "
  • ".$row1['products_name']."
  • ";
                                    }      
                                    $categories_string .= "
";
                            }
                        }

Итого: если есть параметр cPath (он показывает категорию текущую), то смотрим вложенная она, если да, то разбиваем и получаем айди категории и подкатегории. А дальше запрос - рисуем список.
Список товаров в категории ShopOS

Теперь, когда вы нажимаете на подкатегорию выпадает еще и список товаров. Оптимизируйте и ковыряйте -) Да и не забудьте в коде поменять префикс БД на свой.

UPD 02.09.11. для еще одного уровня + оптимизирован код.
вставить в оригинальный файл show_category.inc.php после ( $foo[$cid]['name'].$ProductsCount. ''; - 114 строчка)
                        if ($level == 3) {
                            if ( !empty($Aktiv)) {
                                //$categories_string .= $cid;
                                $query = "SELECT * FROM
                                    vip_products_to_categories p2c,
                                    vip_products p,
                                    vip_products_description pd
                                WHERE
                                    p2c.categories_id = '".$cid."'
                                    and p.products_status = '1'
                                    and p.products_id = p2c.products_id
                                    and p.products_id=pd.products_id
                                ORDER by p.products_date_added";
                                $query = osDBquery($query);
                                $categories_string .= "
    ";
                                    while ($row1 = os_db_fetch_array($query)) {
                                        $categories_string .= "
  • [url=/product_info.php?products_id={$row1[]".$row1['products_name']."[/url]";
                                    }      
                                    $categories_string .= "
";
                            }
                        }