Зіткнувся з завданням, є дві змінні
цифри завжди шестизначні і можуть бути довільними але start завжди менше ніж finish в базі даних в таблиці є два стовпці id і number
- id ідентифікатор з атрибутом автоінкремент
- number зберігають шестизначні числа
Завдання таке занести в базу даних числовий проміжок між start і finish. але так що б якщо в базі вже є рядок з збігається числом, воно не реєструвалося повторно.
Я звичайно можу налаштувати громіздку конструкцію з циклу for в якому буде спочатку здійснюватися вибірка SELECT і перевірка на кількість обраних стовпців, і в разі якщо num_rows повертає 0 то робити INSERT але як розумієте це просто супер ресурсовитратності буде і громозкой - може є якась спрощує функція ніби як DISTINCT при вибірці що б все спростити.
Варіанти не дуже правильні
Шестизначні числа, значить максимум 1 млн записів - це насправді не дуже дофіга. Можна віднімати з таблиці все по where number between: start and: end в простий масив number => true. займе скільки-то мегабайт пам'яті в гіршому випадку. Потім в for за допомогою isset перевіряти, чи треба таке число у списку, якщо немає - то накопичувати в окремий масив і кожні, так, 1000 елементів скидати в одним запитом до бази. Чи не занадто правильна думка, але можна.
Можна рівно так само робити, але вичитувати не все, а розбивши вихідний діапазон на кілька блоків, наприклад, по 50 тис чисел - для повного обходу потрібно 20 запитів до бази на читання.
Або все той же спосіб, але ще один варіант, що дозволяє контролювати і витрата пам'яті в гіршому випадку і мінімальне число запитів в кращому: вичитувати в масив where number> =: start order by number limit 10000. Якщо вам повернулося менше ніж limit записів - дієте за першим сценарієм, ви вичитали всі використовувані числа, всі інші від start до end вам потрібно додати в базу. Якщо повернулося рівно то число записів, що зазначено в limit - то, можливо, у вас є ще дані в таблиці. Тоді ви запам'ятовуєте останній number з результату запиту, і коли ваш for дійшов до цього числа - робите ще запит до бази підставивши ваш останній зустрінуте число where number> =: last_number order by number limit 10000 і пересоздаёте ваш масив з наявними id в базі. Це буде добре працювати, якщо зазвичай у вашій таблиці небагато записів і пропуски зустрічаються часто.
знову варіанти
Більш дивні, але більш продуктивні. Якщо потрібно таке завдання виконувати більш-менш часто, то створіть таблицю, наприклад:
Це все. Серйозно.
Звичайно, у всьому варіантах по tablename.number повинен бути побудований індекс.
Варіант з генерацією послідовності на льоту силами величезного Джойна - на жаль працює адекватно тільки на невеликих діапазонах. Зазначений в питанні діапазон у мене порахувався за півтори секунди - це тільки діапазон.
Якщо є велика таблиця поруч (абсолютно будь-яка, аби в ній було досить рядків), то можна генерувати послідовності за допомогою користувальницької змінної:
Це буде бігати веселіше, але потрібна якась табличка. Для кінцевого діапазону, досить маленького до того ж, за все 1 млн рядків - простіше заздалегідь створити таблицю з числами.
За кадром залишився, начебто, тільки варіант з збереженої процедурою. Можна написати хранімку, яка буде робити саме наївний цикл for. але через близькість до даних робити це більш ефективно, ніж з додатка. Але один спеціалізований запит буде ефективніше, а збережена логіка в mysql - річ не сама безпроблемна. Вельми неприємний, наприклад, тихий автоматичний Комміт транзакції при виклику хранімкі.
У гіршому випадку у вас 1 000 000 рядків в послідовності. Потрібно згенерувати таку кількість рядків.
Генерувати їх будемо з розумом. Спочатку обчислимо скільки нам реально потрібно рядків. Потім будемо множити таблички по 10 рядків, поки не отримаємо потрібну кількість.
Виглядає це приблизно так:
Зверніть увагу на умови вигляду cnt> = 1000. Воно буде спрацьовувати (відбуватися чергове перемноження з табличкою 0..9), тільки якщо наше число більше або дорівнює 1000. Тобто якщо нам потрібно, наприклад 5000 рядків. Цей запит згенерує 10000 і відфільтрує зайві 5000, а не мільйон! Це дуже важливий момент, який сильно підвищує продуктивність.
Таким чином ми отримали послідовність з необхідною кількістю елементів від $ start до $ finish. При цьому це буде працювати швидко для невеликих діапазонів. Тобто з точки зранія оптимізації все зроблено по розуму.
Далі треба вставити в таблицю відсутні значення. Виглядати це буде наприклад так:
Як робити вставку відсутніх значень - як кому подобається. У прикладі EXISTS, може бути NOT IN або LEFT JOIN. Я розповів як швидко згенерувати послідовність свідомо невідомого розміру одним запитом до MySQL.
Найчастіше при додавання в таблицю, що має UNIQUE індекс або PRIMARY KEY, нового рядка, дуже корисним буває синтаксис INSERT IGNORE. Використання даного синтаксису зручно в разі випадкового дублювання ключа при вставці, тобто сама вставка нічого очікувати зроблена, при цьому не буде припинено виконання. Звичайний алгоритм:
- перевірити наявність рядка в таблиці по ключу (SELECT)
- вставити рядок в разі відсутності дублювання ключа (INSERT)
Тепер напишемо тільки один запит INSERT IGNORE без участі php