Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Выбор первого свободного значения из двух таблиц  (Прочитано 12092 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
Shredder
Гость
« : 28-06-2010 12:23 » 

Здравствуйте.
Есть следующий код:

Код:
$sql = mysql_query("SELECT h1.forum_name + 1
FROM hs_forums h1
LEFT JOIN hs_forums h2 ON h1.forum_name = h2.forum_name-1
WHERE h2.forum_name IS NULL
LIMIT 1");
$res = mysql_result($sql, 0);
$item['value'] = $res;

Этот код выводит первое свободное число (значение forum_name) из таблицы hs_forums и присваивает его значение переменной $item['value']. Смысл в том, что это число в дальнейшем становится именем нового регистрируемого форума, т.к. все форумы должны идти сплошным "массивом". Поэтому в случае, например, дальнейшего удаления некоторых форумов, новые регистрации будут автоматически заполнять "пробелы".

Но как выполнить проверку на наличие числа ещё и в другой таблице - hs_registrations (в поле с таким же названием - forum_name), чтобы, при наличии там этого числа, переменная $item['value'] не смогла принять его значение, а смогла бы принять только то первое свободное значение, которое свободно в обоих таблицах?
Море вариантов перепробовал - ничего не помогает...
Записан
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #1 : 28-06-2010 14:16 » 

Тоже попробовал, но ничего хорошего не получается с помощью одного только SQL. Проблема в константе 1 - искомое число может находиться не в h1.forum_name + 1, а в h1.forum_name+2 или +3, если используется еще одна таблица. Более того, такого числа вообще может не быть в таблице - в случае если все записи идут "сплошным массивом" до конца.
Правильнее всего будет написать и вызывать хранимую процедуру, которая уже будет реализовывать цикл по строкам.
Менее правильное, но более простое решение (для тех, кто не силен в процедурах для SQL) - выбирать ВСЕ строки из обеих таблиц:
select distinct * from (select forum_name from hs_forums union select forum_name from hs_registrations) t
и затем на том же php реализовывать цикл со сравнением.
В обоих случаях не забудьте проверять факт отсутствия записи в таблице.
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Shredder
Гость
« Ответ #2 : 28-06-2010 16:26 » new

Уже пробовал вот так вот (привёл код в конце, чтобы не разбивать слова) - не помогает...
Эту вторую таблицу hs_registrations он как будто бы вообще не видит. Даже echo mysql_result($res, $i-1, 0); внутри цикла на экран для проверки пробовал выводить - результаты только из первой таблицы, а из второй, опять же, ничего нету...

Код:
$res = mysql_query("SELECT MAX(forum_name) FROM hs_forums");
$max1 = mysql_result($res, 0, 0);
$hr = mysql_query("SELECT MAX(forum_name) FROM hs_registrations");
$max2 = mysql_result($hr, 0, 0);
$max = MAX($max1, $max2);

$res = mysql_query("SELECT DISTINCT * from (SELECT forum_name FROM hs_forums WHERE forum_name IS NOT NULL
UNION
SELECT forum_name FROM hs_registrations WHERE forum_name IS NOT NULL) t");

for ($i = 1; $i <= $max; $i++)
{
if ($i != mysql_result($res, $i-1, 0))
{
$item['value'] = $i;
break;
}
}
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 28-06-2010 16:31 » 

Код: (Text)
SELECT TOP 1 f1.forum_name + 1 AS free_name
FROM
  hs_forums AS f1
    LEFT OUTER JOIN hs_forums AS f2 ON f1.forum_name + 1 = f2.forum_name
    LEFT OUTER JOIN hs_registrations AS r ON f1.forum_name = r.forum_name
WHERE f2.forum_name IS NULL AND r.forum_name IS NULL
Из таблицы hs_forums берутся все числа forum_name (псевдоним f1), например 1, 2, 3, 6, 7. В этой же таблице для каждого значения forum_name ищется значение forum_name + 1 - если находится, то к результатирующему кортежу добавляется найденное значение, иначе NULL: (1, 2), (2, 3), (3, NULL), (6, 7), (7, NULL). Затем аналогичным образом добавляются значения из hs_registrations - тоже равные искомому forum_name + 1. Пусть в hs_registrations хранится 2, 3, 4, 6, 7. Получается (1, 2, 2), (2, 3, 3), (3, NULL, 4), (6, 7, 7), (7, NULL, NULL).

Под условия выборки попадает значение 7, поэтому ответ 8. Проблема заключается в пропуске значения 5. Как эта проблема образуется? Если у нас в hs_forums есть большая "дырка" в последовательности, например, 1, 10 - "дырка" 2...9, мы получаем первое подходящее значение 3. Допустим, что в hs_registrations находится только 5 - тогда 3 полностью подходит в качестве ответа, и этот случай не вызывает проблему. Проблему вызывает случай, когда в hs_registrations находится 3, тогда подходящим ответом будет 4. А если hs_registrations содержит 3, 4, то подходящим ответом будет 5.

Всегда ответом является число, на 1 больше любого из чисел в полном соединении двух таблиц. Отсюда решение.
Код: (Text)
SELECT TOP 1 n.forum_name + 1 AS free_name
FROM
  (SELECT COALESCE(f.forum_name, r.forum_name) AS forum_name
   FROM hs_forums AS f FULL OUTER JOIN hs_registrations AS r ON f.forum_name = r.forum_name
   WHERE f.forum_name IS NOT NULL OR r.forum_name IS NOT NULL) n
    LEFT OUTER JOIN hs_forums AS f1 ON n.forum_name + 1 = f1.forum_name
    LEFT OUTER JOIN hs_registrations AS r ON n.forum_name = r.forum_name
WHERE f1.forum_name IS NULL AND r.forum_name IS NULL
Не проверял, чистые рассуждения.

P.S. Не знаю, есть ли в MySQL FULL JOIN (это LEFT и RIGHT одновременно), но его несложно сконструировать, если таковой отсутствует. TOP 1 в T-SQL эквивалентен LIMIT 1 в MySQL. Функция COALESCE - стандартная из SQL-92, поэтому скорее всего в MySQL тоже имеется (возвращает первое не-NULL значение из своих аргументов).
« Последнее редактирование: 28-06-2010 16:32 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Shredder
Гость
« Ответ #4 : 28-06-2010 16:49 » 

Dimka
Пробовал твой запрос, пишет:

PHP Notice: in file includes/functions_register.php on line 549: mysql_result(): supplied argument is not a valid MySQL result resource
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 28-06-2010 17:39 » 

Shredder, а мой запрос не MySQL, мой запрос T-SQL. Его нужно преобразовать в MySQL, и я даже написал, что именно нужно изменить.
« Последнее редактирование: 28-06-2010 17:41 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Shredder
Гость
« Ответ #6 : 28-06-2010 20:01 » 

Сделал вот так:
Вроде всё, как надо. Но результат, по сравнению с моим первым кодом, не изменился...

Код:
SELECT n.forum_name + 1 AS free_name
FROM
(SELECT COALESCE(f.forum_name, r.forum_name) AS forum_name
FROM hs_forums AS f
LEFT OUTER JOIN hs_registrations AS r ON f.forum_name = r.forum_name
UNION ALL
SELECT COALESCE(f.forum_name, r.forum_name) AS forum_name
FROM hs_forums AS f
RIGHT OUTER JOIN hs_registrations AS r ON f.forum_name = r.forum_name
WHERE f.forum_name IS NOT NULL OR r.forum_name IS NOT NULL) n
LEFT OUTER JOIN hs_forums AS f1 ON ( n.forum_name + 1 = f1.forum_name )
LEFT OUTER JOIN hs_registrations AS r ON ( n.forum_name = r.forum_name )
WHERE f1.forum_name IS NULL AND r.forum_name IS NULL
LIMIT 1
Записан
HandKot
Молодой специалист

ru
Offline Offline

« Ответ #7 : 29-06-2010 05:21 » 

Shredder , Ваш запрос не совпадает с предложенным Dimka

со своей стороны,  предлагаю такой вариант

Код:
$sql = mysql_query("
SELECT h1.forum_name + 1
FROM hs_forums h1
LEFT JOIN (SELECT forum_name FROM hs_forums UNION SELECT forum_name FROM hs_registrations) h2 ON h1.forum_name = h2.forum_name-1
WHERE h2.forum_name IS NULL
LIMIT 1");
$res = mysql_result($sql, 0);
$item['value'] = $res;

в словах так, выбираем все значения из таблиц hs_registrations и hs_forums (UNION ) и соединив с таблой (h2.forum_name-1)
, определяем пропущенное число (h2.forum_name IS NULL)

если я конечно правильно понял условие задачи

ЗЫЖ коли есть LIMIT 1. то вероятно нужна и сортировка, иначе не понятно какая строка вернется (т.е не факт что это будет наименьшее число)
Записан

I Have Nine Lives You Have One Only
THINK!
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #8 : 29-06-2010 06:11 » 

Shredder, вот такое должно работать:
Код: (SQL)
SELECT n.n1+1 RESULT FROM (
  SELECT forum_name n1 FROM hs_forums f1
  UNION
  SELECT forum_name n1 FROM hs_registrations r1
  ORDER BY 1) n
LEFT JOIN hs_forums f2 ON f2.forum_name-1=n.n1
LEFT JOIN hs_registrations r2 ON r2.forum_name-1=n.n1
WHERE f2.forum_name IS NULL AND r2.forum_name IS NULL
LIMIT 1

Что-то похожее предлагал и Dimka
« Последнее редактирование: 29-06-2010 06:13 от baldr » Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #9 : 29-06-2010 06:30 » 

Аналогичная тема уже была - рекомендую почитать: https://forum.shelek.ru/index.php/topic,15241.0.html
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
baldr
Команда клуба

cy
Offline Offline
Пол: Мужской
Дорогие россияне


WWW
« Ответ #10 : 29-06-2010 07:17 » 

RXL, вот за это тебя и ценим Улыбаюсь
Записан

Приличный компьютер всегда будет стоить дороже 1000 долларов, потому что 500 долларов - это не вполне прилично
Shredder
Гость
« Ответ #11 : 29-06-2010 14:26 » 

Кажется, решение найдено. Вариант baldr работает.

Всем спасибо!
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 29-06-2010 16:30 » 

Цитата: HandKot
ЗЫЖ коли есть LIMIT 1. то вероятно нужна и сортировка, иначе не понятно какая строка вернется (т.е не факт что это будет наименьшее число)
Согласен.

Я вижу, есть трудности с реализацией FULL OUTER JOIN. Технически это будет:
Код:
SELECT * FROM A LEFT JOIN B ON A.AID = B.AID
UNION
SELECT * FROM A RIGHT JOIN B ON A.AID = B.AID

В данном случае, поскольку не интересует связь межу таблицами, достаточно того, что предложил baldr.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines