Система импорта товаров из *.csv

Discussion in 'PHP' started by FindeR, 13 Nov 2010.

  1. FindeR

    FindeR Elder - Старейшина

    Joined:
    15 Nov 2006
    Messages:
    623
    Likes Received:
    138
    Reputations:
    20
    Доброго времени суток!
    На сайте (интернет-магазин) используется система импорта товаров из csv.

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

    Смысл таков:
    проверяется артикул - если присутствует в БД, у товара обновляются остатки, цена и т.д.

    Если отсутствует - проверяем, есть ли вообще такой бренд. Если нет - создаём и кладём туда товар. Если есть - кладём в существующий бренд товар.

    Система, в принципе, рабочая.
    Но прайсы по 15 000 наименований, на каждую позицию выходит от 2 до 4 запросов в БД. Итого - получаем нехилую нагрузку на базу при каждом обновлении товаров.

    Может, кто-то реализовывал подобное, но другими способами? Интересует, как можно оптимизировать скрипт.
    Буду благодарен за советы, возможно, примеры реализации (более гуманные для мускула).
     
  2. -=Zhenek=-

    -=Zhenek=- Elder - Старейшина

    Joined:
    31 Dec 2007
    Messages:
    271
    Likes Received:
    77
    Reputations:
    1
    Может выложите уже существующий код?
    Попробуйте ставить на крон выполнение.. У крона нет ограничений по времени запроса,нагрузке и т.д
     
    1 person likes this.
  3. tty0

    tty0 Member

    Joined:
    24 Oct 2010
    Messages:
    21
    Likes Received:
    8
    Reputations:
    10
    Всё, что приходит в голову сходу - это сделать dump базы, потом локально (или если проблема в железе, то на другом железе) сравнивать дамп и поступления новые, и если показания не сходяться, то обновлять дамп и бд.

    Или можно делать дамп бд перед каждым обновлением.
    Мои 2 копейки.
     
    1 person likes this.
  4. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    tty0 это, как раз, как не надо делать. Если не очевидно - мы делаем более чем в 2 раза больше действий. Вместо просто заливки 15000 записей - мы вначале загружаем их все, а потом все равно заливаем 15к записей

    FindeR можно обойтись максимум 2 запросами на каждое наименование. А если бренды сгруппированы - то еще меньше:
    Code:
    INSERT IGNORE INTO `brands`(`brand_name`) VALUES ('Название бренда');
    REPLACE `articles`(`article_id`, `value`, `items_left`) VALUES ('123456', '100', '1');
    
    1 запрос - Если бренд существует - запись не будет вставлена. 2 запрос - если запись существует - обновить данные, если записи не существует - вставить её.

    "Существование" определяется по колонкам с флагами UNIQUE и PRIMARY KEY. Со вторым запросом следует быть внимательным - если существует несколько колонок с индексом UNIQUE - то может быть 'обновлена' более чем одна запись в таблице, перед выполнением следует проверить структуру базы

    ~30000 запросов - это не так много, тем более что данная операция, скорее всего, не так часто выполняется, и обновление можно производить, когда СУБД не так сильно загружена.

    Для *никс систем и, если СУБД расположена на той же машине, что и скрипт - лучше использовать локальный Unix-сокет (напр. '/var/mysql/mysql.sock').
     
    _________________________
    1 person likes this.
  5. FindeR

    FindeR Elder - Старейшина

    Joined:
    15 Nov 2006
    Messages:
    623
    Likes Received:
    138
    Reputations:
    20
    -=Zhenek=-, кода много, вряд ли его кто-то будет читать. Крон тут не в тему, админ сам прайсы загружает, всегда разные. Да и отключить таймаут всегда и так можно, не в этом дело.

    tty0, база там на 30 000 наименований примерно, с прайсов не все обновляются.

    Gifts, спасибо, интересные мысли. Но сделаю пару уточнений.
    По поводу INSERT INGNORE - тут поле brand_name не является UNIQUE.
    Запрос на проверку выглядит так
    Code:
    SELECT id FROM brands WHERE brand_name = 'name_from_price' AND enable=1
    
    Т.е. бренд должен быть активен (enable=1), чтобы считаться "существующим".

    Артикул товара - также не является уникальным. Могут одинаковые встретиться.
    Я правильно понимаю, что в таком случае REPLACE table некорректно себя поведёт?
    Или можно условие WHERE или ON дописать?

    А вот слова "А если бренды сгруппированы - то еще меньше" натолкнули на мысль.
    Я так понимаю, сначала сравнивать бренды текущей и предыдущей итерации, если совпадают - избавляемся от запросов бренда.
     
  6. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    FindeR уникальный индекс может висеть на нескольких столбцах - и уже по этой комбинации будет срабатывать обновление

    По какому принципу тогда вы вообще обновляете позиции товара?
    В принципе - да, а если брендов в базе немного - то можно делать выборку всех брендов, заносить их в массив и проверять условие уже внутри ПХП
     
    _________________________
  7. FindeR

    FindeR Elder - Старейшина

    Joined:
    15 Nov 2006
    Messages:
    623
    Likes Received:
    138
    Reputations:
    20
    По артикулу и обновляются. Но т.к. прайсы присылаются различными поставщиками, возможен вариант совпадения артикулов у разных товаров. В таком случае уникальность определяется articul+brand_name.
    Требование заказчика :rolleyes:
     
  8. Gifts

    Gifts Green member

    Joined:
    25 Apr 2008
    Messages:
    2,494
    Likes Received:
    807
    Reputations:
    614
    FindeR ну и поставьте уникальный индекс на два поля:
    Code:
    ALTER TABLE `brands`
    ADD UNIQUE INDEX `uniq` (`articul`, `brand_name`) ;
    и если при вставке с помощью REPLACE - эти два поля в какой то строке одновременно совпадут - строка будет обновлена
     
    _________________________
    1 person likes this.
  9. FindeR

    FindeR Elder - Старейшина

    Joined:
    15 Nov 2006
    Messages:
    623
    Likes Received:
    138
    Reputations:
    20
    Да, так и сделаю.

    Благодарю за полезные советы!