Как лучше хранить список стран в БД?

Тема в разделе "Базы данных", создана пользователем verfaa, 11 сен 2014.

Статус темы:
Закрыта.
Модераторы: latteo
  1. verfaa

    verfaa

    Регистр.:
    29 янв 2007
    Сообщения:
    371
    Симпатии:
    41
    Стоит задача хранить список ID стран в таблице БД MySQL. Т.е. значение в поле может быть одиночным, например 258, несколько стран - например 231 456 234 10 45 и 0 - т.е. все страны.

    Каким образом лучше хранить насколько стран в поле? Перечислять их через запятую: 231,456,234? И как тогда составить запрос, если нужно выбрать поля в которых присутствует страна, например, 234?
     
  2. Demian12

    Demian12 Создатель

    Регистр.:
    13 авг 2014
    Сообщения:
    12
    Симпатии:
    9
    я как-то делал такой вариант - ограничители спереди и сзади. Т.е. к примеру, #231##456##234#. Тогда поиск - "LIKE '%#234#%'. И не будет склеивания со страной 23 (поиском по стране 23). Можно и через запятую, но добивать нулями - 231,456,023. Запрос "LIKE '%023%'"
     
  3. latteo

    latteo Эффективное использование PHP, MySQL

    Moderator
    Регистр.:
    28 фев 2008
    Сообщения:
    1.403
    Симпатии:
    1.185
  4. Sergo_Sev

    Sergo_Sev Творец

    Регистр.:
    14 июн 2008
    Сообщения:
    571
    Симпатии:
    188
    Готовый пример
    Код:
    SELECT * FROM `table` WHERE `country` regexp '[[:<:]](234)[[:>:]]'
     
  5. verfaa

    verfaa

    Регистр.:
    29 янв 2007
    Сообщения:
    371
    Симпатии:
    41
    Я так понимаю, что "LIKE '%#234#%' будет работать быстрее, чем regexp '[[:<:]](234)[[:>:]]' и лучше выбрать его?
     
  6. Demian12

    Demian12 Создатель

    Регистр.:
    13 авг 2014
    Сообщения:
    12
    Симпатии:
    9
    Не могу точно сказать, надо проверять.
     
  7. bozhday

    bozhday Создатель

    Регистр.:
    15 сен 2013
    Сообщения:
    22
    Симпатии:
    18
    Вы же используете реляционную базу данных, так используйте ее по назначению: для хранения одной страны - одна запись в таблице. Если нужно сохранить несколько стран, то используете в таблице несколько записей. Построите кластерный индекс на таблицу и все летать будет + такое решение обслуживать легче, джойнить таблицу опять же удобно.
     
    konov и latteo нравится это.
  8. unpunk

    unpunk Писатель

    Регистр.:
    25 янв 2015
    Сообщения:
    5
    Симпатии:
    2
    Вообще, хранение ярлыков в реляционной базе - та ещё проблема :) Тут просится документоориентированное решение, но в большинстве случаев это местечковая задача, выходит стрельба из пушки по воробьям.

    Как выбрать все строки, которым принадлежат страны "Финляндия", "Португалия" и "Греция", но при этом не должно быть "Швеции" и "Люксембурга"?

    С денормализованной базой выходит более-менее понятно (хотя изобилие LIKE и % сигнализирует, что в рабочем окружении этот код неэффективен) :
    PHP:
    SELECT FROM `items`
    WHERE `countriesLIKE '%#Финляндия#%'
      
    AND `countriesLIKE '%#Португалия#%'
      
    AND `countriesLIKE '%#Греция#%'
      
    AND `countriesNOT LIKE '%#Швеция#%'
      
    AND `countriesNOT LIKE '%#Люксембург#%'
    Как это переписать в нормализованном виде?
     
  9. bozhday

    bozhday Создатель

    Регистр.:
    15 сен 2013
    Сообщения:
    22
    Симпатии:
    18
    Наверное мы с вами будем зря рассуждать, пока не увидим проблему целиком. Я имел в виду следующий сценарий:
    1. На клиенте есть комбобокс со странами которые надо выбрать (множественный выбор)
    2. На клиенте есть второй комбобокс со странами которые надо исключить (множественный выбор).
    3. После того как вы укажете значения и в том и в другом списках, вы передаете на сервер не названия стран, а ID этих стран как параметры-строки (Искать: 1,2,3; Исключить 4,5)
    4. На сервере в запросе, вы первым делом парсите эти строчки в 2 темповые таблицы @T1 и @T2 (1 строчка = 1 ID страны)
    5. Делаете сам запрос аля:
    PHP:
          Select Distinct TC.Name as CustomerName
          From Customer_Country 
    as TCC
               Join Customer 
    as TC on TC.ID TCC.CustomerID
          Where TCC
    .CountryID in (select ID from @T1)  
                   and 
    TCC.CountryID not in (select ID from @T2)
    Расставив индексы по этим 2 таблицам можно добиться, что этот запрос будет очень быстро работать на любых объемах данных.

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

    С одной стороны это довольно банальная задача, но с другой стороны, она просто взрывает мозг, когда сталкиваешься с требованием разработать систему типа Avito.ru. Вроде там все построено на простых справочниках типа Ключ-Значение, но с другой стороны когда в запрос заходит одновременно с десяток параметров типа "Включить в поиск список/Исключить из поиска список" + объявлений в базе пару миллионов + нужно чтобы запрос отдавал данные не более чем за 2-3 сек. вот тут-то и задумываешься как все же умудриться сделать хлопок одной ладонью :)))
     
    Последнее редактирование модератором: 27 янв 2015
  10. latteo

    latteo Эффективное использование PHP, MySQL

    Moderator
    Регистр.:
    28 фев 2008
    Сообщения:
    1.403
    Симпатии:
    1.185
    В чём профит от использования темп таблиц, по идее это 2 лишних запроса к базе, можно ведь добавить джойн Customer_Country на Country и в where перечислять нужные страны
    Код:
    TCO.country_name in ("Финляндия", "Португалия", "Греция") AND TCO.country_name NOT in ("Швеции", "Люксембург")
    В отдельных случаях есть смысл хранить country_id 2 country_name не только в бд, но и "под рукой" в мемкеше, тогда список id для условия IN на php сформируем.

    PS:Если стран о-о-очень много, не забывайте увеличить лимиты на длину запроса в mysql.
     
Статус темы:
Закрыта.