[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Коррекция функционала, если сотни тысяч записей
isergi
У меня такая вот проблема, можете кто нибудь подсказать решение?

Проблема:

У меня к примеру есть 200 тыс. продуктов.
Всё это храниться в нескольких таблицах
products
products_descriptions
products_categories
products_prices


Связь через айдишники продуктов.

Поиск я по максимум заоптимизировал, проставив ключи и по корректному поставив джоины.
Т.е. если мы что то ищем, то нормально.

Функционал сайта разработан таким образом, что когда мы переходим на страницу Products, то срабатывает поиск с пустыми параметрами. Т.е. все джоины объеденяют эти 200 тыс записей, получаеться большая масса, которая оочень долго работает unsure.gif выводяться продукты конечно с паджинацией (LIMIT 10) по дефолту. Но всё равно копать приходиться по всем записям, чтобы отобрать этот LIMIT который нужно показать.
В общем запрос занимает приличное время.

Над этим списком продуктов, висит секция поиска, куда можно ввести параметры, и время запроса сократиться, из за введённых параметров поиска.

Так вот у меня вопрос такой, как можно это всё по проще сделать, чтобы когда мы переходили на Products, не ждали так долго.

В основном решения встречал, когда список категорий выводится, по нажатию на которую отображаются продукты из выбранной категории. (Да, это быстро, но по архитектуре сайта, должны отображаться все продукты...независимо от категории) dry.gif

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


Можете какое нибудь решение подсказать, как это всё реализовать.
Задача в итоге такова:
Куча записей в таблицах, и при переходе на Products происходит выборка продуктов по LIMIT`у, но из всех записей.


P.S. Простите за многабукаф smile.gif



Спустя 13 минут, 48 секунд (23.07.2009 - 13:43) sergeiss написал(а):
Я вот что не понял. Почему при входе на страницу должны показываться все записи? Почему бы просто "тупо" вообще не делать никакую выборку товара, когда идет первоначальных вход на страницу?
Зашел - выбрал параметры - получил выборку.

Либо, задать какие-то параметры поиска, в случае, если все поля поиска пустые. Хотя бы просто выбор нескольких произвольных записей.

Иными словами, надо просто изменить вот этот алгоритм, отказаться от него
Цитата (isergi @ 23.07.2009 - 14:29)
Функционал сайта разработан таким образом, что когда мы переходим на страницу Products, то срабатывает поиск с пустыми параметрами.


Спустя 11 минут, 16 секунд (23.07.2009 - 13:54) isergi написал(а):
Цитата (sergeiss @ 23.07.2009 - 10:43)

Почему при входе на страницу должны показываться все записи?


Изначально небыло предусмотренно на такую нагруженность, поэтому когда поиск был по 500 продуктам, работало всё быстро и без проблем.

Щас вот понадобилось всё это оптимизировать под большое количество записей.

По поводу
Цитата (sergeiss @ 23.07.2009 - 10:43)

надо просто изменить вот этот алгоритм, отказаться от него


Придеться переписывать общую иерархию сайта, но я не отрицаю что к этому и всё придется похоже.

Вот думаю может есть какие нибудь ещё альтернативные решения?
Может кто нибудь видел как организована работа в проектах с огромным количеством записей?

Спустя 13 минут, 14 секунд (23.07.2009 - 14:07) sergeiss написал(а):
Цитата (isergi @ 23.07.2009 - 14:54)
в проектах с огромным количеством записей?

"Огромное" это сколько? smile.gif Ну у меня вот миллионы (а в некоторых таблицах десятки миллионов) записей. Это много? Но у меня всякие выборки идут только по запросу пользователя, никаких таких вот "автоматических" не делается.

Я вот что не понял. А зачем переписывать общую иерархию сайта, если можно просто поставить условие: если в полях поиска вообще ничего нету (как это происходит при начальном входе), то просто поиск не производится. И всё. Тупо обходится блок выборки данных. Остальное всё остаётся, что там есть.

Спустя 11 минут, 10 секунд (23.07.2009 - 14:18) isergi написал(а):
Цитата (sergeiss 23.07.2009 - 11:07)

"Огромное" это сколько?


Да, чет я по поводу количества не уточнил smile.gif. В общем по поводу Миллионов это слишком. Тут огромоное количество считается уже 500 тысяч.
Вот поэтому то и конечно по хорошему конечно же организовать сначала выборку.

Цитата (sergeiss 23.07.2009 - 11:07)

Я вот что не понял. А зачем переписывать общую иерархию сайта, если можно просто поставить условие:


Объясняю smile.gif Сначала тож думал, выдерну просто из одной таблицы Products LIMIT 10, и всё. Но понял что это не выход, т.к. там есть сортировка по полям, например Price, Description, и .т.д., который находятся уже в других таблицах.

Т.е. если например сделать ORDER BY products_prices.price, а потом надо будет ORDER BY products_descriptions.name, или ещё по чему нибудь.

Сортировка по функционалу добавлялась к общему запросу поиска, поэтому выборка продуктов происходит через поиск.
Если отлавливать на "не передаваемость параметров поиска" то это уже получится стремное (прошу прощение за выражение) вставление так называемых костылей.
Сортировки по определенным полям. Т.е. например если сортировать по прайс, то лезть в таблицу prices, если по найму то лезть в таблицу descriptions.
Это не будет соответвовать общему принципу иерархии.
Вот как то так smile.gif

Спустя 20 минут, 58 секунд (23.07.2009 - 14:39) sergeiss написал(а):
Цитата (isergi @ 23.07.2009 - 15:18)
Если отлавливать на "не передаваемость параметров поиска" то это уже получится стремное (прошу прощение за выражение) вставление так называемых костылей.

Не согласен категорически! Это называется (примерно) "проверка данных на корректность". Если рассматривать незаполненные поля поиска как некорректность, то всё встаёт на свои места. И это должно быть единственное глобальное условие, никаких описанных тобой ЛИМИТов по отдельным таблицам не надо!!!
Пустые поля - поиска нету.
Есть данные - есть поиск.

Вопрос: а ты вообще хоть какую-то проверку переданных параметров поиска делаешь или так их "лепишь", как они приходят?

Спустя 11 минут, 41 секунда (23.07.2009 - 14:51) isergi написал(а):
Цитата (sergeiss 23.07.2009 - 11:39)

Вопрос: а ты вообще хоть какую-то проверку переданных параметров поиска делаешь или так их "лепишь", как они приходят?


Выглядит это примерно вот так, т.е. формируется строка если есть чета (если мы нажали поиск), и это передается в функцию.
PHP
$params = $_REQUEST;
list(
$products, $search, $product_count) = function_products($params, $elements_count);


В этой функции соответвенно идет проверка на данные:
вот например одна из проверок.
PHP
if (!empty($params['name'])) {
                $tmp .= db_quote(" OR descr1.product LIKE ?l", "%$params[name]%");
}


Ну в принципе походу так и придеться делать обходить поиск проверкой. Была такая мысля, думаю мож че ещё есть такое smile.gif

Спустя 5 минут, 39 секунд (23.07.2009 - 14:57) isergi написал(а):
Цитата (sergeiss 23.07.2009 - 11:39)

Пустые поля - поиска нету.
Есть данные - есть поиск.


Кстати по этмоу поводу есть маленькое недопонимание.
Например мы зашли на страницу, поиска небыло показали 10 продуктов первые, и тут я решил отсортировать по цене, то как выбрать записи не использую поиска, и не используя LIMIT?

Спустя 7 минут, 50 секунд (23.07.2009 - 15:04) sergeiss написал(а):
Цитата (isergi @ 23.07.2009 - 15:57)
Цитата (sergeiss 23.07.2009 - 11:39)

Пустые поля - поиска нету.
Есть данные - есть поиск.


Кстати по этмоу поводу есть маленькое недопонимание.
Например мы зашли на страницу, поиска небыло показали 10 продуктов первые, и тут я решил отсортировать по цене, то как выбрать записи не используя поиска, и не используя LIMIT?

Не понял идею...

Спустя 8 минут, 10 секунд (23.07.2009 - 15:13) isergi написал(а):
Цитата (sergeiss 23.07.2009 - 12:04)

Не понял идею...


Мы заходим на страницу Products, по твоей логике обходим поиск, и выдираем просто 10 продуктов из таблицы Products.

А вот затем я всю эту кучу хочу отсортировать по цене, но цена храниться уже в таблице product_prices.

Соответвенно уже надо лезть в другую таблицу.
И опять же Лимит, и всё такое.
Т.е. одна таблица products не прокатит. Приходиться использовать поиск по пустым параметрам, которому добавить сортировку по prices.

Спустя 9 минут, 38 секунд (23.07.2009 - 15:22) sergeiss написал(а):
По моей логике было вообще пропустить какую бы то ни было выборку при отсутствии параметров.
Ежели тебе хочется что-то выбрать, то и выбери с десяток произвольных продуктов... Я что-то не вижу проблем.

Кстати. Покажи запрос(ы), которым(и) делается выборка. Ты уверен, что они оптимальные? Твои высказывания типа "и выдираем просто 10 продуктов из таблицы Products. А вот затем я всю эту кучу хочу отсортировать по цене, но цена храниться уже в таблице product_prices." наводят меня на мысли, что запросы у тебя не совсем оптимальные. Какая куча, если ты уже отсортировал и выбрал???

И еще. Я что-то "тормознул" сначала smile.gif Проверку можно делать не по отсутствию параметров, а по тому, нажата ли кнопка "Поиск".

Спустя 11 минут, 41 секунда (23.07.2009 - 15:34) isergi написал(а):
В общем вот запрос по пустым параметрам:

SQL
SELECT SQL_NO_CACHE SQL_CALC_FOUND_ROWS products.product_id, descr1.product as product, products.tracking, products.feature_comparison, products.zero_price_action, products.product_type, products.tax_ids, GROUP_CONCAT(IF(products_categories.link_type = 'M', CONCAT(products_categories.category_id, 'M'), products_categories.category_id)) as category_ids, min_qty, max_qty, products.qty_step, products.list_qty_count, avail_since, buy_in_advance, products.product_code, products.amount, MIN(prices.price) as price, products.status, products.list_price, descr1.short_description, IF(descr1.short_description = '', descr1.full_description, '') as full_description, products.is_edp, IF(products.age_verification = 'Y', 'Y', IF(categories.age_verification = 'Y', 'Y', categories.parent_age_verification)) as age_verification, IF(products.age_limit > categories.age_limit, IF(products.age_limit > categories.parent_age_limit, products.age_limit, categories.parent_age_limit), IF(categories.age_limit > categories.parent_age_limit, categories.age_limit, categories.parent_age_limit)) as age_limit FROM products as products LEFT JOIN product_descriptions as descr1 ON descr1.product_id = products.product_id AND descr1.lang_code = 'EN' LEFT JOIN product_prices as prices ON prices.product_id = products.product_id AND prices.lower_limit = 1 INNER JOIN products_categories as products_categories ON products_categories.product_id = products.product_id INNER JOIN categories ON categories.category_id = products_categories.category_id WHERE 1 AND prices.membership_id IN (0) GROUP BY products.product_id ORDER BY descr1.product asc LIMIT 0, 10


SQL_NO_CACHE - нужен для проверки сколько это всё будет фигачица без кэша
SQL_CALC_FOUND_ROWS - эта фича мне нужна для дальнейших действий.

Десятку произвольных продуктов не прокатит выбрать sad.gif

Спустя 7 минут, 23 секунды (23.07.2009 - 15:41) isergi написал(а):
Запрос громозкий...и все поля и параметры по которым идет поиск и выборка, это всё надо smile.gif

Спустя 8 минут, 2 секунды (23.07.2009 - 15:49) FatCat написал(а):
У нас в форуме похожая ситуация с главной страницей, а точнее с выводом на главной информации об именинниках. Пробовал такой запрос на базе в 200 000 пользователей, 4 секунды, что совершенно недопустимо для главной страницы.
У нас пользователей поменьше, запрос отнимает 0.15 секунды, но это тоже очень много
Плюс довольно нагрузочные запросы колонки новостей.
Плюс куча мелких запросов по статичной информации.
На круг вышло 15 запросов к БД, из которых один тяжелый.

Проблему решил кешированием в файл; имя файла содержит 10 цифр, получаемых по time().

При запросе главной страницы сценарий считывает из директории кеша кешированный файл и сверяет его время с текущим. Если лимит превышен - файл удаляется, и тогда гонится чехарда запросов, на выходе создается новый файл кеша.
На отдачу информация идет из кеша.

У нас здесь ради колонки новостей и ради списка "кто в онлайне за последние 15 минут" кеширование сделано через 15 минут; Вам же наверное можно сделать кеширование раз в сутки...

Спустя 9 минут, 46 секунд (23.07.2009 - 15:59) FatCat написал(а):
PHP
// Начали проверку файлов для кеширования
        $time_next = Array();
        $cache_dir="cache";
        $open_dir=opendir($cache_dir);
        while($tmp_name=readdir($open_dir))
        {
            if(stristr($tmp_name,".cache.board.tmp"))
                $time_next[] = intval( str_replace(".cache.board.tmp","",$tmp_name) );
        }
        if( count($time_next) == 0 ) // Нет кеша главной
        {
            $time_next = 0;
            $cached_in_file = "recach";
        }
        elseif( count($time_next) > 1 ) // Несколько кешей
        {
            rsort($time_next);
            unlink($ibforums->vars['base_dir'].$cache_dir."/".$time_next[1].".cache.board.tmp");
            $time_next = $time_next[0];
        }
        else // Кеш есть
        {
            $time_next = $time_next[0];
        }
        $time_delim = time() - $time_next;
        
        if
( $time_delim > 900 or $time_delim < 0 )
        {
            $cached_in_file = "recach";
            if(file_exists($ibforums->vars['base_dir'].$cache_dir."/".$time_next.".cache.board.tmp"))
                unlink($ibforums->vars['base_dir'].$cache_dir."/".$time_next.".cache.board.tmp");
        }
        else
        
{
            $cached_in_file = "cached";
        }
// Закончили проверку файлов
// Начали кешировать
        if( $cached_in_file == "recach")
        {
            // Запросы к БД, генерация кода
            
            ignore_user_abort
(TRUE);
            $time_next = time();
            $fh = fopen("cache/".$time_next.".cache.board.tmp", "w");
            fwrite($fh, $stats_html);
            fclose($fh);
        }
        else{
            $stats_html = file_get_contents( $ibforums->vars['base_dir'].$cache_dir."/".$time_next.".cache.board.tmp" );
        }

$stats_html дальше используется в сборке страницы.

Спустя 1 минута, 16 секунд (23.07.2009 - 16:00) sergeiss написал(а):
МоЩЩЩно задвинул... Я попытался понять твой запрос и не понял, смог я это сделать или не смог smile.gif Хотя у меня есть подозрение, что всё это можно сделать проще намного.
Давай упростим твой запрос, чтобы он был нагляднее. Так, чтобы проще просто не было:


SQL
select .... from products LEFT JOIN product_descriptions as descr1 on ...
LEFT JOIN product_prices as prices ON ...
INNER JOIN products_categories as products_categories ON ...
INNER JOIN categories ON ...
where group by ....
order by ....
LIMIT 0, 10

Правильно? Так он выглядит? Иными словами, ты выбираешь ВСЕ продукты, цепляешь к ним нужные параметры, а потом только делаешь ограничения.

А теперь начинаем думать. При отсутствии ограничений идет выборка по всей таблице, а ты хочешь выбрать несколько произвольных продуктов...
Ну так и выбери их изначально!!! Используй подзапросы, которые будут создавать временный таблицы малого размера, и общее время выполнения запроса будет уменьшаться.
SQL
select *
( select .... from products order by rand() LIMIT 0, 10 ) as products
LEFT JOIN product_descriptions as descr1 on ...
LEFT JOIN product_prices as prices ON ...
INNER JOIN products_categories as products_categories ON ...
INNER JOIN categories ON ...
where group by ....
order by ....

Подзапрос
SQL
select .... from products order by rand() LIMIT 0, 10
выберет тебе 10 произвольных продуктов, к которым уже будут цепляться нужные атрибуты. Если надо, то ты можешь еще тут сделать какие-то условия.
Самое главное в том, что ты будешь дальше работать в этом запросе не с 200000 записей, а только с 10.


И еще, также существенно. Индексы у тебя есть по всем нужным полям?


PS. И кэширование сделать тоже можно... Но это уже будет другая задача smile.gif

Спустя 29 минут, 47 секунд (23.07.2009 - 16:30) isergi написал(а):

Спасибо FatCat

Хм..по поводу кэширования всей этой бодяги хорошая идея. dry.gif

Спасибо за предоставленный код.
Так...надо разобраться во всём этом smile.gif

Если будут решения ещё кроме - буду рад изучить эту информацию rolleyes.gif

Спустя 19 минут, 48 секунд (23.07.2009 - 16:50) isergi написал(а):
Огромное спасибо sergeiss

Я же говорил что запрос такой громозкий. Мозг ломает с первого взгляда =)

Индексы я там проставил, всё нормально, до этого ваще там минутыми запрос выполнялся.

Я так понимаю предлагаешь использовать вложенные запросы?

Оч хорошая идея. Надо тока обдумать как их организовать.

Спасибо ещё раз.

Спустя 49 минут, 42 секунды (23.07.2009 - 17:40) isergi написал(а):
Возвращаюсь всё со своей же бедой...
Вложенные запросы не помогли..наоборот они утяжелили запрос. Он стал дольше выполняться.
Вполне вероятно что при каких то критериях поиска, он будет гораздо быстрее работать, но основной проблеме это никак не поможет (

Спустя 3 часа, 12 минут, 19 секунд (23.07.2009 - 20:52) sergeiss написал(а):
Вот еще один тормоз в этом запросе:
SQL
WHERE 1 AND prices.membership_id IN (0)

Во-первых, непонятно, зачем тут "1 AND".
А во-вторых, ежели у тебя величина membership может принимать в данном случае только одну величину, равную нулю, то так и пиши:
SQL
WHERE prices.membership_id = 0

На этом можешь съэкономить много времени!!!

Далее.
Такие конструкции, как
SQL
LEFT JOIN product_descriptions as descr1 ON descr1.product_id = products.product_id AND descr1.lang_code = 'EN'

я бы упростил до
SQL
LEFT JOIN product_descriptions as descr1 ON descr1.product_id = products.product_id

а условие перенес бы вниз, в WHERE. Причем, все условия, которые не нужны непосредственно для связки таблиц. Тогда там, в условии, получится:
SQL
WHERE prices.membership_id = 0 and descr1.lang_code = 'EN' and prices.lower_limit = 1 and

а во всех джойнах будут только в чистом виде условия для связывания таблиц.


Попробуй сделать всё вышесказанное...

Спустя 55 минут, 44 секунды (23.07.2009 - 21:48) isergi написал(а):
Спасибо sergeiss, пряма выручаешь smile.gif

Я маленько поясню:

SQL
WHERE 1 AND prices.membership_id IN (0)


Не прокатит заменить на
SQL
WHERE prices.membership_id = 0


Т.к. туда через implode добавляется перечесление если они есть из массива. Поэтому там может быть

SQL
WHERE 1 AND prices.membership_id IN (1,2,3,4)


На даже если убрать IN и поставить = то не сильно во времени выиграю по сравнению с копанием в большой кучей.

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

Thx! wink.gif

Спустя 6 минут, 50 секунд (23.07.2009 - 21:55) sergeiss написал(а):
Цитата (isergi @ 23.07.2009 - 22:48)
На даже если убрать IN и поставить = то не сильно во времени выиграю по сравнению с копанием в большой кучей

Тут много раз этот вопрос поднимался и делались сравнения на живых серверах. Лучше, по возможности, без ИНов обходиться.

Цитата (isergi @ 23.07.2009 - 22:48)
Т.к. туда через implode добавляется перечесление если они есть из массива

Ну так и флаг тебе в руки, и барабан на шею!!! smile.gif Если одно значение, то пиши " = величина", а если список, то "IN (......)".


Спустя 2 часа, 24 минуты, 29 секунд (24.07.2009 - 00:19) PandoraBox2007 написал(а):
может поигратся с конфигом демона СУБД

5.1 быстрее 5.0 и закончен контейнер транзакций проблема юникода решена и т.д.

Спустя 3 минуты, 56 секунд (24.07.2009 - 00:23) PandoraBox2007 написал(а):
кинь структуру таблиц попробую оптимизировать запросы и таблицы

запрос громозкий а триггеров для выборки нет, на одном запросе не выкрутится rolleyes.gif

Спустя 3 дня, 9 часов, 28 минут, 53 секунды (27.07.2009 - 09:52) isergi написал(а):
Извините за задержку с ответим cool.gif

Цитата (PandoraBox2007 23.07.2009 - 21:19)

может поигратся с конфигом демона СУБД


Неа...тож не прокатит smile.gif Эта штука ставиться на разных серваках, к которым я даже доступ не имею.

По поводу структуры таблиц, ВО:

SQL
CREATE TABLE `products` (
`product_id` mediumint(8) unsigned NOT NULL auto_increment,
`product_code` varchar(32) NOT NULL default '',
`product_type` char(1) NOT NULL default 'P',
`owner_id` mediumint(8) unsigned NOT NULL default '0',
`status` char(1) NOT NULL default 'A',
`list_price` decimal(9,2) NOT NULL default '0.00',
`amount` mediumint(8) NOT NULL default '0',
`min_amount` mediumint(8) unsigned NOT NULL default '0',
`weight` decimal(12,2) NOT NULL default '0.00',
`length` mediumint(8) unsigned NOT NULL default '0',
`width` mediumint(8) unsigned NOT NULL default '0',
`height` mediumint(8) unsigned NOT NULL default '0',
`shipping_freight` decimal(9,2) NOT NULL default '0.00',
`low_avail_limit` mediumint(8) unsigned NOT NULL default '0',
`timestamp` int(11) unsigned NOT NULL default '0',
`is_edp` char(1) NOT NULL default 'N',
`edp_shipping` char(1) NOT NULL default 'N',
`unlimited_download` char(1) NOT NULL default 'N',
`tracking` char(1) NOT NULL default 'B',
`free_shipping` char(1) NOT NULL default 'N',
`feature_comparison` char(1) NOT NULL default 'N',
`zero_price_action` char(1) NOT NULL default 'R',
`is_pbp` char(1) NOT NULL default 'N',
`is_op` char(1) NOT NULL default 'N',
`is_oper` char(1) NOT NULL default 'N',
`supplier_id` mediumint(8) unsigned NOT NULL default '0',
`is_returnable` char(1) NOT NULL default 'Y',
`return_period` int(11) unsigned NOT NULL default '10',
`avail_since` int(11) unsigned NOT NULL default '0',
`buy_in_advance` char(1) NOT NULL default 'N',
`localization` varchar(255) NOT NULL default '',
`min_qty` smallint(5) NOT NULL default '0',
`max_qty` smallint(5) NOT NULL default '0',
`qty_step` smallint(5) NOT NULL default '0',
`list_qty_count` smallint(5) NOT NULL default '0',
`tax_ids` varchar(255) NOT NULL default '',
`age_verification` char(1) NOT NULL default 'N',
`age_limit` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`product_id`),
KEY `age_verification` (`age_verification`,`age_limit`),
KEY `status` (`status`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `product_descriptions` (
`product_id` mediumint(8) unsigned NOT NULL default '0',
`lang_code` varchar(2) NOT NULL default 'EN',
`product` varchar(255) NOT NULL default '',
`shortname` varchar(255) NOT NULL default '',
`short_description` text NOT NULL,
`full_description` text NOT NULL,
`meta_keywords` varchar(255) NOT NULL default '',
`meta_description` varchar(255) NOT NULL default '',
`search_words` text NOT NULL,
`page_title` varchar(255) NOT NULL default '',
`age_warning_message` text NOT NULL,
PRIMARY KEY (`product_id`,`lang_code`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `product_prices` (
`product_id` mediumint(8) unsigned NOT NULL default '0',
`price` decimal(12,2) NOT NULL default '0.00',
`lower_limit` smallint(5) unsigned NOT NULL default '0',
`membership_id` mediumint(8) unsigned NOT NULL default '0',
UNIQUE KEY `product_id` (`product_id`,`lower_limit`,`membership_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `products_categories` (
`product_id` mediumint(8) unsigned NOT NULL default '0',
`category_id` mediumint(8) unsigned NOT NULL default '0',
`link_type` char(1) NOT NULL default 'M',
`position` smallint(5) unsigned NOT NULL default '0',
PRIMARY KEY (`category_id`,`product_id`),
KEY `link_type` (`link_type`),
KEY `position` (`position`),
KEY `pt` (`product_id`,`link_type`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `categories` (
`category_id` mediumint(8) unsigned NOT NULL auto_increment,
`parent_id` mediumint(8) unsigned NOT NULL default '0',
`id_path` varchar(255) NOT NULL default '',
`owner_id` mediumint(8) unsigned NOT NULL default '0',
`membership_id` mediumint(8) unsigned NOT NULL default '0',
`status` char(1) NOT NULL default 'A',
`product_count` mediumint(8) unsigned NOT NULL default '0',
`position` smallint(5) unsigned NOT NULL default '0',
`timestamp` int(11) unsigned NOT NULL default '0',
`is_op` char(1) NOT NULL default 'N',
`localization` varchar(255) NOT NULL default '',
`age_verification` char(1) NOT NULL default 'N',
`age_limit` tinyint(4) NOT NULL default '0',
`parent_age_verification` char(1) NOT NULL default 'N',
`parent_age_limit` tinyint(4) NOT NULL default '0',
PRIMARY KEY (`category_id`),
KEY `c_status` (`membership_id`,`status`,`parent_id`),
KEY `position` (`position`),
KEY `parent` (`parent_id`),
KEY `id_path` (`id_path`),
KEY `localization` (`localization`),
KEY `age_verification` (`age_verification`,`age_limit`),
KEY `parent_age_verification` (`parent_age_verification`,`parent_age_limit`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


Вродь не забыл никакие таблицы smile.gif


_____________
Рекурсивный салат - огурцы, помидоры, салат.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.