Mysql. Заказывайте места и присоединяйтесь к спискам случайным образом

У меня есть две таблицы: местоположения и списки.

местоположения
id title address latitude longitude

объявления
id location info status

SELECT locations.title, 
       locations.address, 
       ( 3959 * acos( cos( radians('".$center_lat."') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians('".$center_lng."') ) + sin( radians('".$center_lat."') ) * sin( radians( latitude ) ) ) ) AS distance      
  FROM locations 
ORDER BY distance

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

  1. Перечислите по одному «списку» для каждого местоположения, и пусть местоположения остаются в порядке.
  2. Если в месте есть более одного «списка», он должен быть полностью случайным.

Было бы лучше сделать все это в одном SQL-запросе? Или заполнить все местоположения, в которых есть хотя бы одно «список», а затем использовать другой запрос, чтобы выбрать случайные «списки» для этого местоположения?

ОБНОВЛЕНИЕ

При условии создания таблицы:

CREATE TABLE `listings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `token` varchar(4) DEFAULT NULL,
  `location` varchar(45) DEFAULT NULL,
  `info` varchar(45) DEFAULT NULL,
  `status` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=24 DEFAULT CHARSET=utf8;

CREATE TABLE `locations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(45) DEFAULT NULL,
  `address_street` varchar(45) DEFAULT NULL,
  `addrees_city` varchar(45) DEFAULT NULL,
  `address_state` varchar(45) DEFAULT NULL,
  `address_zip` varchar(45) DEFAULT NULL,
  `latitude` decimal(10,6) DEFAULT NULL,
  `longitude` decimal(10,6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

person floatleft    schedule 25.09.2010    source источник


Ответы (1)


Чтобы вывести location.title, если с местоположением связана хотя бы 1 строка в таблице «listings», используйте:

SELECT loc.title
  FROM LOCATIONS loc
 WHERE EXISTS(SELECT NULL
                FROM LISTING li
               WHERE li.location = loc.id)

Использовать:

  SELECT x.title, 
         x.address,
         x.distance,
         x.info,
         x.status
    FROM (SELECT loc.title, 
                 loc.address, 
                 ( 3959 * acos( cos( radians('".$center_lat."') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians('".$center_lng."') ) + sin( radians('".$center_lat."') ) * sin( radians( latitude ) ) ) ) AS distance,
                 li.*,
                 CASE 
                   WHEN @location = loc.id THEN @rownum := @rownum + 1
                   ELSE @rownum := 1
                 END AS rank,
                 @location := loc.id
            FROM LOCATIONS loc
       LEFT JOIN LISTINGS li ON li.location = loc.id
            JOIN (SELECT @rownum := 0, @location := -1) r
        ORDER BY loc.id, RAND()) x
   WHERE x.rank = 1
ORDER BY x.distance

Используя сообщество MySQL 5.1.49, я успешно получил желаемые результаты с помощью приведенного выше запроса.

Я не могу воспроизвести дублированную строку OP, используя:

СОЗДАТЬ ТАБЛИЦЫ

CREATE TABLE `locations` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(45) DEFAULT NULL,
  `address_street` varchar(45) DEFAULT NULL,
  `address_city` varchar(45) DEFAULT NULL,
  `address_state` varchar(45) DEFAULT NULL,
  `address_zip` varchar(45) DEFAULT NULL,
  `latitude` decimal(10,6) DEFAULT NULL,
  `longitude` decimal(10,6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1$$

CREATE TABLE `listings` (
  `id` int(11) NOT NULL,
  `token` varchar(4) DEFAULT NULL,
  `location` varchar(45) DEFAULT NULL,
  `info` varchar(45) DEFAULT NULL,
  `status` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$

ВСТАВИТЬ операторы:

INSERT INTO `locations` 
VALUES (1,'John\'s Ice Cream','1701 S Martin Luther King Jr Blvd','Lansing','MI','48910','42.714672','-84.567139'),
       (2,'7 Eleven','3500 Okemos Rd','Okemos','MI','48864','42.683331','-84.431709'),
       (3,'Kurt\'s Pizza','213 Ann St.','East Lansing','MI','48823','42.736053','-84.481636'),
       (4,'Walmart','16275 National Pkwy','Lansing','MI','48906','42.780350','-84.637238'),
       (5,'Alex\'s Hot dog Shop','8505 Delta Market Dr','Lansing','MI','48917','42.739830','-84.677330');

INSERT INTO `listings` 
VALUES (19,'39c4','1','5 gallons for $8','active'),
       (21,'89dF','4','2 mens shirts for $2','active'),
       (22,'67oP','1','Ice cream cones for $1','active'),
       (23,'5tG8','2','Large soft drinks only $0.99!','active');
person OMG Ponies    schedule 25.09.2010
comment
Выглядит отлично, но я получаю синтаксическую ошибку рядом с '@location := loc.id FROM LOCATIONS loc LEFT JOIN LISTINGS li' Не могу понять. - person floatleft; 25.09.2010
comment
@Chad Whitaker: мне не хватило запятой после AS rank, исправлено. - person OMG Ponies; 25.09.2010
comment
Сейчас это работает, но... 1. Он отображает местоположения, в которых нет списков. 2. Элемент не выглядит случайным, если для местоположения имеется более одного. - person floatleft; 25.09.2010
comment
@Chad Whitaker: Ваш первоначальный заказ не включал, если был связан listing, поэтому LEFT JOIN дал бы идентичные результаты. Измените LEFT JOIN LISTINGS на JOIN LISTINGS, если вы не хотите видеть LOCATION записей без хотя бы одной LISTING записи. - person OMG Ponies; 26.09.2010
comment
@Chad Whitaker: ORDER BY loc.id, RAND() гарантирует, что помимо порядка LOCATION.id строки будут настолько случайными, насколько это позволит MySQL. Имейте в виду, что случайный порядок двух записей LISTING не будет таким уж случайным;) - person OMG Ponies; 26.09.2010
comment
@Chad Whitaker: Да, внутренний запрос должен будет обрабатываться, когда есть более одной записи LISTING, связанной с LOCATION, но внешний запрос — тот, который фактически возвращает данные — будет возвращать только строки на основе значения rank (которое на запись LOCATION будет только один. Спасибо за предложение PayPal, но голосование за мой ответ было бы хорошо. - person OMG Ponies; 26.09.2010
comment
Извини! но когда я удаляю LEFT из LEFT JOIN LISTINGS, он будет отображать местоположение несколько раз для каждого списка, который у него есть, я хочу отображать местоположение только один раз с его заголовком (locations.title) и случайным списком (listings.info) рядом с Это. Спасибо за помощь. - person floatleft; 26.09.2010
comment
@Chad Whitaker: Ваш обновленный комментарий имеет больше смысла, но мой ответ объясняет, почему нет проблем с фильтрацией внешнего запроса. У меня нет привычки принимать $ за ответы на вопросы по SO. Я польщен, но у вас достаточно репутации (более 15), чтобы проголосовать сейчас. - person OMG Ponies; 26.09.2010
comment
С ЛЕВЫМ СОЕДИНЕНИЕМ: cl.ly/2YQv (отображает местоположения, которые не имеют списков, т.е. Пицца Курта, Хот-доги Алекса ; Только с JOIN: cl.ly/2YbK Отображает мороженое Джона дважды, а не один раз, и показывает listings.info случайным образом между двумя (5 галлонов за 8 долларов и конусы мороженого за 1 доллар) - person floatleft; 26.09.2010
comment
@Chad Whitaker: Спасибо, я понимаю, что вы говорите, но мне нужны ваши данные, чтобы посмотреть, смогу ли я воспроизвести проблему. Не имеет смысла, как может дублироваться значение ранга для каждого местоположения, если только у вас нет дубликатов LOCATION.title с разными значениями идентификатора. - person OMG Ponies; 26.09.2010
comment
таблица местоположений: cl.ly/2YeB ; Таблица списков: cl.ly/2YC5 Дайте мне знать, если вам нужны какие-либо разъяснения. - person floatleft; 26.09.2010
comment
@Chad Whitaker: с благодарностью, но настоящие операторы CREATE TABLE и INSERT были бы лучше. Основываясь на данных на снимках экрана, я создал две таблицы и не могу воспроизвести дубликат, который вы видите. Я не включил вычисление distance — оно не должно было что-то изменить, и я был прав, когда добавлял столбцы и логику в свой пример. - person OMG Ponies; 26.09.2010
comment
Я переделал свою базу данных и получил те же результаты, что и раньше. Можете ли вы опубликовать свой выбор sql? Даже без расчета distance? - person floatleft; 26.09.2010
comment
@OMG Ponies - Если бы я составил простую карту того, что мне нужно сделать, и результат, могу ли я нанять вас? Я знаю, что это займет у вас менее 1-2 часов, чтобы создать. Вы, похоже, очень опытны. Спасибо за все что ты сделал для меня! - person floatleft; 26.09.2010
comment
@Чад Уитакер: Я лучше научу тебя делать это сам. Вам необходимо предоставить операторы CREATE TABLE для обеих задействованных таблиц. - person OMG Ponies; 26.09.2010
comment
@OMG Ponies - я обновил сообщение операторами CREATE TABLE. Еще раз большое спасибо за вашу помощь. - person floatleft; 26.09.2010
comment
@Chad Whitaker: я использовал ваши операторы таблицы и воспроизвел ваш вывод. Единственное отличие, которое я вижу, это использование MyISAM при тестировании с использованием InnoDB. Есть ли у вас особая потребность в использовании MyISAM? - person OMG Ponies; 26.09.2010
comment
Нет, я не понимаю использование двигателя. Как я мог изменить это? - person floatleft; 26.09.2010
comment
@Chad Whitaker: MySQL имеет разные механизмы для разных функций. MyISAM не поддерживает транзакции или ссылочную целостность, но единственный поддерживает полнотекстовое индексирование и поиск. Вы можете изменить движок на InnoDB, используя: ALTER TABLE your_table_name ENGINE = InnoDB; - person OMG Ponies; 26.09.2010
comment
Возможно, это было проблемой. Но вернемся к проблеме, когда список не является случайным. Например, у John's Ice Cream есть 2 объявления, но отображается только первое (5 галлонов за 8 долларов). Я выполнил запрос более 50 раз с тем же результатом. - person floatleft; 26.09.2010
comment
@Chad Whitaker: попробуйте большую выборку. Я видел случайную работу во внутреннем запросе, но удивляюсь, что кеш запросов отвечает за то, что окончательный набор результатов не сильно меняется. Но я думаю, что сделал достаточно, чтобы хотя бы заслужить голосование. - person OMG Ponies; 26.09.2010
comment
Да, я обязательно проголосую за вас сейчас. Я надеюсь, что вы все еще можете помочь с этой проблемой. Этот проект просрочен, и я застрял на этом этапе ..... Я добавил несколько строк из местоположения 1 (Мороженое Джона), и ничего случайного. Я действительно обнаружил, что он захватывает списки с самым низким идентификатором. Может быть, вы можете отправить мне свой запрос, который вы используете, чтобы я мог видеть результаты. Благодарю вас! - person floatleft; 27.09.2010
comment
@Chad Whitaker: я выполнял только внутренний запрос - материал внутри предложения FROM. Я начал думать, что вам лучше использовать PostgreSQL — 8.4+ имеет аналитические функции, и это то, что этот запрос использовал бы, если бы MySQL имел функциональность. - person OMG Ponies; 27.09.2010
comment
@Chad Whitaker: Может быть, но это был бы более уродливый хак, чем то, что я предоставил. - person OMG Ponies; 27.09.2010
comment
Как насчет этого? Можете ли вы предоставить запрос, который будет искать и перечислять только местоположения с заголовком местоположения, где у местоположения есть хотя бы 1 список? - person floatleft; 27.09.2010
comment
@Chad Whitaker: Да, вам нужно видеть информацию из таблицы списков также в наборе результатов? Я мог получить один столбец из LISTING; два или более столбца из LISTING не могут быть гарантированно взяты из одной и той же записи LISTING, если с записью LOCATION связано более одного столбца. - person OMG Ponies; 27.09.2010
comment
Нет, только для того, чтобы иметь возможность выводить location.title, если с местоположением связана хотя бы 1 строка в таблице списков. - person floatleft; 27.09.2010
comment
Спасибо. Это то, что я сделал. Я назвал местоположения одним запросом, затем я вытащил списки наугад с помощью внутреннего запроса, используя php. pastebin.com/FcS3YbWs Как вы думаете, это нормально или это будет очень сложно в базе данных? Каков ваш вклад? - person floatleft; 27.09.2010
comment
@Chad Whitaker: мой запрос делает одно обращение к базе данных для получения всей информации; ваш сделает один для списка местоположений и поездку для каждого возвращаемого места. Чем больше местоположений возвращено, тем больше нагрузка на базу данных. - person OMG Ponies; 27.09.2010
comment
Я понимаю. Итак, почему я не могу смешать SELECT loc.id, loc.title, ( 3959 * acos( cos( радианы('42.7159912')) * cos( радианы( широта )) * cos( радианы(долгота ) - радианы('- 84.561563')) + sin( радианы('42.7159912') ) * sin( радианы( широта ) )) ) ) КАК расстояние ОТ местоположений loc WHERE EXISTS(SELECT NULL FROM listings li WHERE li.location = loc.id) AND SELECT listings. токен, listings.info ОТ списков, ГДЕ listings.location = '.$locations['id'].' ORDER BY RAND() в один запрос? - person floatleft; 27.09.2010
comment
@Chad Whitaker: 1) для этого вы должны использовать JOIN, а не EXISTS 2) объединение не возвращает ни одной записи LISTING для каждой записи LOCATION. Вы можете выбрать из LISTING в подвыборке, но вы можете получить только один столбец из подвыборки. На самом деле то, что вы хотите, выходит за рамки возможностей MySQL предоставить средство, которое масштабируется для обработки любого количества записей LOCATION. - person OMG Ponies; 27.09.2010
comment
Я понимаю. Ну, я пока использую то, что показал вам в pastebin. И если мы вырастем намного больше, я, надеюсь, найму программиста. Большое спасибо за вашу помощь OMG Ponies. Фантастическая помощь! - person floatleft; 27.09.2010
comment
@Chad Whitaker: Рад помочь, это раздвинуло границы того, что я использовал в псевдоранжировании. - person OMG Ponies; 27.09.2010