За последние 24 часа нас посетили 17759 программистов и 1606 роботов. Сейчас ищут 1705 программистов ...

Кривой запрос

Тема в разделе "PHP и базы данных", создана пользователем rustrek, 3 авг 2015.

  1. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    В общем проблема как всегда начинается когда на сайте появляется много данных, да ещё и запросы кривые.

    Помогаю одному человеку, но, что то застрял на одном запросе.

    Код (PHP):
    1. SELECT SQL_CACHE 9 AS test,N.*,
    2.                (SELECT NAME FROM news_regions where id = N.id_region) AS region,
    3.                (SELECT NAME FROM news_category where id = N.id_category) AS category,
    4.                (SELECT NAME FROM news_sources where id = N.id_source) AS source
    5.                FROM news_news N
    6.                 WHERE 1=1
    7.                  AND N.id_category = 2   
    8.                    ORDER BY N.date DESC
    9.                    LIMIT 12600,36; 
    при таком вот запросе выдаёт Отображает строки 0 - 29 (36 всего, запрос занял 3.0455 сек.) что не есть гуд.

    записей в категории 2 где-то около полутора миллионов, вот и буксует.

    помогите пожалуйста кто чем может )

    PHP, JavaScript, SQL и другой код пишите внутри тегов
    Код ( (Unknown Language)):
    1. [b]php][/b]Тут код[b][/[/b][b]code][/b][/color]
     
  2. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    А почему отказались от LEFT JOIN? И в вашем случае кеш наверное лучше использовать не для всего запроса, а для подзапросов. И зачем нужна таблица news_news?

    Я бы сделал так
    Код (PHP):
    1. SELECT N.*, news_regions.NAME as region, news_category.NAME as category, news_sources.NAME as source
    2. FROM N
    3. LEFT JOIN news_regions, news_category, news_sources
    4. ON (news_regions.id = N.id_region AND news_category.id = N.id_category AND news_sources.id = N.id_source)
    5. WHERE 1=1 AND N.id_category = 2
    6. ORDER BY N.date DESC
    7. LIMIT 12600,36;
     
  3. VLK

    VLK Старожил

    С нами с:
    15 дек 2013
    Сообщения:
    3.010
    Симпатии:
    58
    Не силен в MySQL, а тут запрос идет в несколько таблиц?
     
  4. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.115
    Симпатии:
    1.244
    Адрес:
    там-сям
    LIMIT 12600,36 — эта штука всегда будет чертовски медленной, особенно когда у тебя есть подзапросы.
    так устроен движок БД: всякий раз данные запроса будут вычисляться с первой "страницы", только 12600 записей будет промотано вхолостую прежде чем будет выдан ответ.

    Можно попробовать оптимизировать разбив запрос на два:

    - первый добывает только news_news.id и использует LIMIT. без подзапросов! также надо постараться чтобы этот запрос использовал индексы. смотреть EXPLAIN и делать выводы.
    - второй с джойнами куда надо и WHERE id IN:)ids) — использует вычисленные в первом запросе айдишники

    Другая стратегия оптимизации:

    Переписать запрос так, чтобы он использовал не LIMIT m,n, а только LIMIT n. При этом первая строка "страницы" должны определяться через WHERE. Типа WHERE `date` >= :d
     
  5. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Я вижу Вы нашли применение этой таблице?)))
    Полностью согласен
     
  6. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    в ней сами новости.

    кстати ваш запрос выдаёт ошибку ON ссылаясь на эту строку (news_regions.id = N.id_region AND news_cat' at
     
  7. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Смотря когда Вы ее скопировали(Последний раз редактировалось Maputo Пн авг 03, 2015 12:30, всего редактировалось 1 раз.). Там была запятая вместо AND
    Только из нее почему-то данные не выводятся в исходном запросе...
    Или весь фокус в такой записи
    Я, честно, не знаю как это интерпритируется
    Я может просто с таким не сталкивался, и ли должно это выглядеть так:
    Код (PHP):
    1. FROM news_news AS N
    или
    Код (PHP):
    1. FROM news_news, N
    Таблица N вообще существует?

    Проверил - это эквивалент "as N".
    Тогда чтоб мой вариант сработал вам нужно добавить nesw_news в строку:
    Код (PHP):
    1. FROM news_news AS N
     
  8. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    Maputo

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

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Код (PHP):
    1. SELECT N.*, news_regions.NAME as region, news_category.NAME as category, news_sources.NAME as source
    2. FROM news_news AS N
    3. LEFT JOIN news_regions, news_category, news_sources
    4. ON (news_regions.id = N.id_region AND news_category.id = N.id_category AND news_sources.id = N.id_source)
    5. WHERE 1=1 AND N.id_category = 2
    6. ORDER BY N.date DESC
    7. LIMIT 12600,36; 
    А вообще не мешало бы выложить структуру таблицы news_news и для начала попробуйте с LIMIT 0,36
    А потом
     
  10. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    структура.

    Код (PHP):
    1. CREATE TABLE IF NOT EXISTS `news_news` (
    2.   `id` int(11) NOT NULL auto_increment,
    3.   `tid` int(11) NOT NULL default '0',
    4.   `id_category` int(11) NOT NULL default '0',
    5.   `id_source` int(11) NOT NULL default '1',
    6.   `id_region` int(11) NOT NULL default '1',
    7.   `date` datetime NOT NULL default '0000-00-00 00:00:00',
    8.   `title` varchar(255) NOT NULL default '',
    9.   `text` longtext NOT NULL,
    10.   `link` varchar(255) NOT NULL default '',
    11.   `image` varchar(255) NOT NULL,
    12.   `counter` int(11) NOT NULL default '0',
    13.   `comment` int(11) NOT NULL default '0',
    14.   `seo_title` varchar(255) NOT NULL,
    15.   `user_id` int(11) NOT NULL,
    16.   `poll` int(11) NOT NULL default '0',
    17.   `hits` int(11) NOT NULL default '0',
    18.   PRIMARY KEY  (`id`),
    19.   KEY `id_category` (`id_category`),
    20.   KEY `image` (`image`),
    21.   KEY `category_image` (`id_category`,`image`),
    22.   KEY `id_region` (`id_region`),
    23.   KEY `date(`date`),
    24.   KEY `id_source` (`id_source`),
    25.   KEY `counter` (`counter`),
    26.   KEY `region_category` (`id_category`,`id_region`),
    27.   KEY `category_date_source` (`id_category`,`date`,`id_source`),
    28.   KEY `link(`link`),
    29.   KEY `source_region` (`id_source`,`id_region`),
    30.   KEY `user_id` (`user_id`),
    31.   KEY `hits` (`hits`),
    32.   KEY `counter_cat` (`id_category`,`counter`),
    33.   KEY `hits_cat` (`id_category`,`hits`),
    34.   KEY `cat_date` (`id_category`,`date`),
    35.   KEY `region_date` (`id_region`,`date`),
    36.   KEY `region_category_source_date` (`id_region`,`id_category`,`id_source`),
    37.   KEY `hits_date` (`hits`,`date`),
    38.   KEY `cat_reg_date` (`id_category`,`id_region`,`date`),
    39.   FULLTEXT KEY `FULLTEXT` (`text`,`title`)
    40. ) ENGINE=MyISAM  DEFAULT CHARSET=cp1251 AUTO_INCREMENT=4116217 ;
    41.  
    PHP, JavaScript, SQL и другой код пишите внутри тегов
    Код ( (Unknown Language)):
    1. [b]php][/b]Тут код[b][/[/b][b]code][/b][/color]
     
  11. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Я думаю, если это вынести в отдельную таблицу - обработка будет значительно быстрее производиться (поправьте меня, если я не прав)
    Код (PHP):
    1. `text` longtext NOT NULL, 
    Никогда не использовал этот тип данных, но неужели тут не надо указывать размер поля?
    Я например для текста использую только CHAR(например 240), если текст больше - режу по пробелам на куски меньшие 240.
    Вот статейка https://dev.mysql.com/doc/refman/5.6/en/innodb-fulltext-index.html Тут почему-то нет longtext


    P.S.: Сомневаюсь, что текст новости может превышать 65000 символов и исчисляться в миллионах символов.
     
  12. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    Maputo так же как и прежде выдаёт ошибку

    Код (PHP):
    1. #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' news_category, news_sources
    2. ON (news_regions.id = N.id_region AND news_categor' at line 3
    PHP, JavaScript, SQL и другой код пишите внутри тегов
    Код ( (Unknown Language)):
    1. [b]php][/b]Тут код[b][/[/b][b]code][/b][/color]
     
  13. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    А так?
    Код (PHP):
    1. SELECT N.*, news_regions.NAME as region, news_category.NAME as category, news_sources.NAME as source
    2. FROM news_news AS N
    3. LEFT JOIN (news_regions, news_category, news_sources)
    4. ON (N.id_region = news_regions.id AND N.id_category = news_category.id AND N.id_source = news_sources.id)
    5. WHERE 1=1 AND N.id_category = 2
    6. ORDER BY N.date DESC
    7. LIMIT 12600,36; 
    Кстати, еще для оптимизации поменяйте движок на InnoDB
    https://www.percona.com/blog/2006/05/29/join-performance-of-myisam-and-innodb/
     
  14. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    не, к сожалению ошибка осталась

    Код (PHP):
    1. #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' news_category, news_sources
    2. ON (N.id_region = news_regions.id AND N.id_categor' at line 3
    PHP, JavaScript, SQL и другой код пишите внутри тегов
    Код ( (Unknown Language)):
    1. [b]php][/b]Тут код[b][/[/b][b]code][/b][/color]
     
  15. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    После LEFT JOIN имена таблиц в скобки. В посте выше поправил
     
  16. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    спасибо. сейчас запрос прошёл, но время выполнения осталось таким же

    Код (Text):
    1. Отображает строки 0 - 29 (36 всего, запрос занял 3.0978 сек.)
     
  17. Chushkin

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

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Код (PHP):
    1. select sql_cache n.*, r.`name` region, c.`name` category, s.`name` source
    2.   from news_news n
    3.     left join news_regions r on r.id = n.id_region
    4.     left join news_category c on c.id = n.id_category
    5.     left join news_sources s where s.id = n.id_source
    6.   where n.id_category = 2   
    7.   order by n.id_category desc, n.date desc
    8.   limit 12600,36
    Должно быть: Innodb + индекс (id_category, date) + правильные настройки движка (кеш включён, буфер >= размеру таблиц с индексами). И всё будет "летать".
    Первый запрос (НЕ из кеша) будет заметно меньше секунды, второй такой же (из кеша) - ~0.001 сек.
    Когда не из кеша, при первом параметре limit-а 0 (первая страница) запрос будет выполнятся меньше 0.01 сек. По мере роста числа будет стремится ближе к секунде (у меня на ~20 млн. последняя страница считывается ~0.6 сек.)
    Если не достигнет таких параметров - используйте метод, который подсказал artoodetoo. Т.е. разбейте запрос на два: в первом получаете только ИД, во втором запрашиваете/загружаете данные для этих ИД из всех 4-х таблиц. Это может быть быстрее, за счёт того, что первый запрос будет использовать только индекс.
    Как-то так...
     
  18. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    спасибо, но ксожалению тоже выдаёт ошибку

    Код (Text):
    1. #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where s.id = n.id_source
    2.       where n.id_category = 2  
    3.       order by n.id_' at line 5
     
  19. Chushkin

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

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    rustrek, голова Вам дана не только шапку носить ;) Включите мозги и поработайте ими хотя бы чуть-чуть, пару секунд.
    Неужели не видно, что там опечатка - вместо "on" написано "where"?
     
  20. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Вот это всё надо сделать InnoDB обязательно:
     
  21. Chushkin

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

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Maputo
    Первое не надо - разбивка на страницы будет кривой.
     
  22. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    да не варит она сейчас, два бессонных дня, да действительно не посмотрел, а просто скопировал и всё

    ну а по запросу, к сожалению так же - Отображает строки 0 - 29 (36 всего, запрос занял 3.0247 сек.)
     
  23. Maputo

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

    С нами с:
    30 июл 2015
    Сообщения:
    1.136
    Симпатии:
    173
    Если постараться - можно сделать ровно...

    Chushkin, а что Вы порекомендуете делать с этим?
    Код (PHP):
    1. `text` longtext NOT NULL, 
     
  24. Chushkin

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

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    Если всё сделали так, как я сказал, то ... дурите с вероятностью 99.9%. Или себя или нас. :(
    Сделайте дамп таблиц и дайте мне ссылку на скачивание (в личку) - я проверю за сколько выполнится на моём домашнем компе, не поленюсь.

    Добавлено спустя 4 минуты 49 секунд:
    "Знаем, плавали!" (с) не мой
    На некоторое время - да. Но по закону Подлости, всё поедет в самый неподходящий момент.
    По сути, на небольших таблицах (до 10-30 млн. не имеет смысла извращаться)

    Тип поля зависит от максимально допустимого размера текста (в байтах). До 8К лучше пользовать "text". и т.д. Но не критично и на скорость влияет мало.
     
  25. rustrek

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

    С нами с:
    17 дек 2012
    Сообщения:
    23
    Симпатии:
    0
    сегодня сделаю дамп скину, много информации только правда