За последние 24 часа нас посетили 20087 программистов и 1002 робота. Сейчас ищут 305 программистов ...

MySQL приемы создания правильных БД и запросов к ним

Тема в разделе "MySQL", создана пользователем Danil, 17 дек 2008.

  1. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Всем привет! Я думаю у многих начинающих программистов рано или поздно возникают вопросы по поводу того на сколько правильно я создаю базы данных и формирую запросы к ним. В этой теме предлагаю разобрать наиболее часто встречающиеся проблемы и пути их решения. Всем откликнувшимся заранее благодарен!
     
  2. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Тут же предлагаю следующую ситуацию:
    Есть 2 таблицы: 1 - таблица отелей; 2 - таблица (с параметрами) например: джакузи в номере, телефон, телевизор короче не суть важно (их около сотни).

    Внимание вопрос! :)

    Каким образом наиболее правильно сформировать связь между этими таблицами так чтобы при добавлении нового отеля можно было выбрать несколько параметров и сохранить эту информацию.
     
  3. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Мое видение (может оно идиотское) но пока другого не пришло :)

    При выборе параметров я формирую из id (параметров) строку с разделителями и сохраняю в таблицу с отелями. все бы ни чего, но когда доходит дело до поиска по отелям с заданными параметрами - дело плохо, ничего умнее как формировать запрос с кучей
    [sql] SELECT * FROM `hotels` WHERE params LIKE '%,1,%' AND params LIKE '%,20,%'[/sql]
    и т.д. но параметров много и этот запрос превращается в километровую строку.

    У меня такое чувство что что-то здесь не так :)
    Всем спасибо за помощь.
     
  4. Dagdamor

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

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Danil
    Добавлять поля jacuzzi, phone, tv и т. д. (с типом TINYINT(1)) в таблицу отелей. 100 полей - это конечно сильно, но все равно лучше других решений типа дополнительных таблиц или поиска через LIKE. Запросы будут выглядеть так:
    [sql]SELECT * FROM hotels WHERE phone=1 AND tv=1[/sql]
    И самому понятнее, и скорость выборки по максимуму (особенно если на наиболее популярные параметры навесить индексы).
     
  5. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Спасибо, но дело в том что количество параметров может увеличиваться. Как с этим быть добавлять поля в таблицу? Можно конечно, но мне кажется возможно лучшее решение (жаль только я его не знаю :( )
     
  6. Dagdamor

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

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Danil
    Да, разумеется. Запросы ALTER TABLE не просто так придуманы :)
    Лучшее ли - не знаю... можно завести таблицу, связывающую отели и параметры. С полями hotelid, paramid. И условиться: если в этой таблице есть строка с некими hotelid и paramid - значит, данный параметр у данного отеля включен. Так можно чуть ускорить твою выборку (см. ниже). Но и у этого метода полно недостатков.
    [sql]SELECT * FROM hotels WHERE id IN
    (SELECT DISTINCT(hotelid) FROM hotelparams WHERE paramid IN (1,20))[/sql]
     
  7. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Ладно попробуем :)
     
  8. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Про это я тоже уже думал, но почему-то выбрал метод со строкой :)
     
  9. Dagdamor

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

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    Упс, запрос выше - не для твоего случая. Это если устраивает любой из параметров.
    Ладно, для случая AND пусть кто-нибудь другой запрос напишет ;)
     
  10. Военный

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

    С нами с:
    15 янв 2007
    Сообщения:
    70
    Симпатии:
    0
    Тоже есть вопрос.

    Есть сайт с объявлениями о продаже недвижимости.
    Человек заходит и хочет сделать запрос и чтобы по этому запросу потом обрабатывались новые объявления.
    В запросе, в том числе, будут указаны станции метро, на которых интерисует квартира.
    Как лучше хранить эти станции метро?
    Я видел вариант (и сейчас с ним работаю) когда id - шники метро хранятся в текстовом поле таблицы filtr с фильтрами объявлений, например [sql],12,45,112,62,[/sql] и потом идет выборка (в триггере, при добавлении нового объявления смотрим есть ли подходящие фильтры) [sql]WHERE filtr.idmetro LIKE CONCAT("%,",new.idmetro,",%")[/sql].
    Вроде все работает и довольно быстро. И добавить можно хоть вообще все станции.
    Но, неправильнее ли будет сделать отдельную таблицу, где будут храниться все эти id шники метро?
     
  11. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    о существовании like надо забыть.
    все эти задачи делаются без него.
     
  12. Военный

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

    С нами с:
    15 янв 2007
    Сообщения:
    70
    Симпатии:
    0
    Так как тогда быть?
    Создавать новую таблицу, в которой будет
    id_filter, id_metro

    Но тогда, получается:
    30 заявок, в каждой по 10 станций метро = 300 записей в таблице.
    А так, просто в таблице filtr будет поле TEXT - в которой все idшники будут храниться через запятую
    Как лучше?
     
  13. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    кросс-таблица лучше.
    поиск в текстовом поле будет только перебором.
     
  14. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    задачи для баз в 10000 записей вообще можно делать как угодно, хоть через жопу, хоть в FULLTEXT все пихать. MySQL съест все это не поперхнувшись.

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


    имеем. 100000 записей о людях/персонах и 100 о станциях метро
    нам нжно установить связь с теми станциями метро где челове живет и работает (2-4 стнции)

    стандартным решением является НОРМАЛИЗАЦИЯ базы

    персона id,name
    связь idname,idmetro
    метро id,metro

    тогда SQL выборки бдут стандатрными. обычный JOIN или как-то так, НО обратим внимание на объемы котоые мускулю нужно будет обработать при таких зпросах.

    100 000*100 = 100 000 000

    это уже МНОГОВА-то, а если нам надо на странице отобразить всего 20 человек, то лоптить такие объемы совсем не катит.

    делаем просто поле text и пихаем туда сериализованный массив со станциями метро.

    имеем:

    одн таблицу.
    однопроходную выборку с LIKE.

    прирост производиительности в РАЗЫ. Проверено на реальных объемах.

    прикинте объемы при 2-3-4 таких связях, а в нормальных ьбазах это как минимум и те обюъемы записей которые будет mysql лопатить для вывода 20 персон.

    =)))))))))))))))
     
  15. Военный

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

    С нами с:
    15 янв 2007
    Сообщения:
    70
    Симпатии:
    0
    Спасибо за ответы :)
     
  16. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    это быстрее поиска по индексу?
     
  17. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Когда у меня была та же проблема выбора нашел вот такую функцию может кому пригодится

    [sql]FIND_IN_SET(str,strlist)[/sql]

    Возвращает значение от 1 до N, если строка str присутствует в списке strlist, состоящем из N подстрок. Список строк представляет собой строку, состоящую из подстрок, разделенных символами ','. Если первый аргумент представляет собой строку констант, а второй является столбцом типа SET, функция FIND_IN_SET() оптимизируется для использования двоичной арифметики! Возвращает 0, если str отсутствует в списке strlist или если strlist является пустой строкой. Если один из аргументов равен NULL, возвращается 0. Данная функция не будет корректно работать, если первый аргумент содержит символ ',':

    [sql]mysql> SELECT FIND_IN_SET('b','a,b,c,d');
    -> 2[/sql]
     
  18. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    она есть, но это все равно ПЕРЕБОР.
     
  19. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    я те 100 0примеров риведу когда индексы ТОРМОЗЯТ. Когда что бы все работло сначла индексы удаляют, потом производят операции, а потом опять их создают.
    =)

    не надо вбивать себе в голову,
    что небо синее, а вода мокрая.
    Надо посмотреть на небо и потрогать воду.

    Перебор 100 000 000 записей (это для одной связи) хоть с десятком индексов куда как медленее простого перебора 100 000. уж поверь. да сам подумай да посчитай.
     
  20. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    от запроса зависит. если у тебя уже есть перебор - тогда да.
     
  21. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    от понимания процесса это зависит, а не от того, что "индексы бстро". нихрена не быстро при определенных задачах.
     
  22. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    угу. в твоем примере так имеет смысл, пока надо искать только с одной стороны.
    Как только потребуется найти людей близких к этому метро - ты умрешь.
     
  23. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    неа. я бессмертен.
     
  24. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Вот еще один вопросик. Думаю должен быть актуальным.
    Пусть есть таблица людей или фирм кого угодно, которые размещают объявления с оплатой на какое-то количество дней и датой публикации. Необходимо сформировать запрос который выводит эти объявления одновременно проверяя не просрочены ли они и если просрочены то сбрасывает их (не удаляет, а просто отмечает что их показывать не надо)
     
  25. Danil

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

    С нами с:
    17 дек 2008
    Сообщения:
    13
    Симпатии:
    0
    Адрес:
    бы ни был!
    Во вроде бы что-то типа такого должно сработать
    Если в поле pubdate хранится дата публикации, а в days количество оплаченных дней
    pay - это просто флаг который нужен для последующей выборки

    [sql]UPDATE company_table SET pay = TO_DAYS(NOW()) - TO_DAYS(pubdate) < days[/sql]