Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
PHP на примерах (2 издание). Авторы: Кузнецов М.В., Симдянов И.В. MySQL 5. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. C++. Мастер-класс в задачах и примерах. Авторы: Кузнецов М.В., Симдянов И.В. Объектно-ориентированное программирование на PHP. Авторы: Кузнецов М.В., Симдянов И.В. Программирование. Ступени успешной карьеры. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Хитрый sql запрос или как все уложить в один запрос
 
 автор: Artem S.   (30.10.2004 в 16:09)   письмо автору
 
 

Задача достаточно специфическая.
Есть 3 таблицы
users
posts
throws
Причем на одну запись из posts соответствует 1 запись из таблицы users и несколько записей из таблицы throws.
Вот когда было не несколько а одна, то было все замечательно, и я обходился таким запросом.

$sql = "SELECT u.*, p.*,  th.*
        FROM post p,  users u, throws th
        WHERE p.topic_id = $topic_id             
                        AND u.user_id = p.poster_id
                        AND th.topic_id = $topic_id
        ORDER BY p.post_time
        LIMIT $start, ".$board_config['posts_per_page'];

$topic_id - номер темы
После того, как th.topic_id и $topic_id перестало соответствовать 1:1 все полетело к чертям.
Если раньше я получал 2-х мерный массив, где кол-во столбцов было как и запрашивалось (LIMIT) то сейчас из стало больше (ровно в столько, сколько записей в throws соответствует на один topic_id)
2-x мерный массив я получал так:

if ($row = $db->sql_fetchrow($result))
{
        do
        {
                $postrow[] = $row;
        }
        while ($row = $db->sql_fetchrow($result));
        $db->sql_freeresult($result);

        $total_posts = count($postrow);
}

Как мне поступить? Я даже не могу разбить этот запрос на 2. Ведь я не знаю, сколько мне надо записей из throws (а лишь только - начальный и конечный id_topic для них)
А может быть можно было бы получить 3-х мерный массив?

   
 
 автор: cheops   (30.10.2004 в 16:25)   письмо автору
 
   для: Artem S.   (30.10.2004 в 16:09)
 

Хм... какая задача - вам нужны только пользователи (user), соотвествующие топики (post) и любой (throws)? В этом случае можно сгруппировать результат по пользователям
$sql = "SELECT u.*, p.*,  th.* 
        FROM post p,  users u, throws th 
        WHERE p.topic_id = $topic_id              
                        AND u.user_id = p.poster_id 
                        AND th.topic_id = $topic_id 
        ORDER BY p.post_time 
        GROUP BY u.user_id
        LIMIT $start, ".$board_config['posts_per_page']; 

Как и раньше будете получать 1 throws на 1 user.
Или всё-таки нужны все throws - для каждого пользователя? В чём тогда сложность - в выводе на страницу?

   
 
 автор: cheops   (30.10.2004 в 16:42)   письмо автору
 
   для: Artem S.   (30.10.2004 в 16:09)
 

Можно загнать дополнительный запрос в цикл обработки
<?php
mysql_query
(...); // первый запрос
do
{
  
mysql_query("select * from throws where user_id=".$row[0]); // второй запрос
} while (...)
?>

А полученный массив от второго запроса упаковать в строку при помощи serialize() и сунуть в качестве дополнительного параметра $postrow[].
Такой подход потребует дополнительных запросов на каждом цикле и это может быть неприемлемо, тогда можно не группировать значения, а проверять каждый результат, не имеет ли он одинаковых полей - u.user_id (сортировать следует ORDER BY p.post_time, u.user_id), если значение этого поля равно значению предыдущего цикла - продолжать формировать дополнительный массив для упаковки serialize(), если нет - создаём новый элемент массива $postrow[].

PS Так можно свернуть трёх-мерный массив в двумерный, а вообще можно обойтись и без упаковки-распаковки действительно воспользовавшись трёхмерным массивом.

http://www.softtime.ru/forum/read.php?id_forum=3&id_theme=535&page=1

   
 
 автор: Artem S.   (30.10.2004 в 17:22)   письмо автору
 
   для: cheops   (30.10.2004 в 16:42)
 

>> Или всё-таки нужны все throws - для каждого пользователя? В чём тогда сложность - в выводе на страницу?

ДА

Ну если выводить сообщения то они выводятся так.
[сообщение_1]
> Имя 1
> Сообщение 1
> Throw 1
......
[сообщение_1]
> Имя 1
> Сообщение 1
> Throw 2
......
[сообщение_2]
> Имя 2
> Сообщение 2
> Throw 3
......
Сообщения дублируются. А мне надо чтобы все throw для этого сообщения вывелись через запятую
[сообщение_1]
> Имя 1
> Сообщение 1
> Throw 1, Throw 2
$postrow[] увеличился и все превратилось хз во что. И как все распихать по массивам я не понимаю.
2-ой Вариант меня не устаивает так как будит сильно увеличена нагрузка на базу.
Может подскажете как организовать из этого запроса 3-х мерный массив.

   
 
 автор: cheops   (30.10.2004 в 18:04)   письмо автору
 
   для: Artem S.   (30.10.2004 в 17:22)
 

Хм... ну примерно, нужно двигаться в следующем направлении
<?php
  $id_user 
= -1// Инициализируем значением, которого нет в таблицах
  
$i = -1// Счётчик
  
while ($row $db->sql_fetchrow($result)); 
  { 
    if(
$id_user == $row['user_id'])
    {
       
// Если user_id равен значению предыдущего цикла
       // продолжаем формировать третье измерение массива
        
$postrow[$i]['throw'][] = $row['throw_id'];
    }
    else
    {
      ++
$i
      
$postrow[$i] = $row;
      
$postrow[$i]['throw'][] = $row['throw_id'];
    }
  } 
  
$db->sql_freeresult($result); 
  
$total_posts count($postrow); 
?>

   
 
 автор: elenaki   (05.11.2004 в 13:48)   письмо автору
 
   для: cheops   (30.10.2004 в 18:04)
 

у меня тоже хитрый запрос. надо выбрать из таблицы четыре последних баннера с признаком top. но расположить их не в порядке выборки, а в порядке, указанном в поле banner_ord. я делаю запрос
$query = "SELECT * FROM banner_main WHERE pos = 'top' ORDER BY id DESC LIMIT 0,3";

где мне еще раз отсортировать выбранные баннеры по полю banner_ord, ведь у меня уже есть в запросе ORDER BY id?

   
 
 автор: cheops   (05.11.2004 в 14:57)   письмо автору
 
   для: elenaki   (05.11.2004 в 13:48)
 

А по id тоже нужно сортировать? Если нет, то можно воспользоваться запросом:
$query = "SELECT * FROM banner_main WHERE pos = 'top' ORDER BY banner_ord DESC LIMIT 0,3";

   
 
 автор: elenaki   (05.11.2004 в 15:04)   письмо автору
 
   для: cheops   (05.11.2004 в 14:57)
 

по id я сортирую, потому что мне нужно 4 послeдних баннера. т.к. id=autoincrement, то по DESC id мне выберутся 4 последних. если можно как-то по-другому выбирать четыре баннера с наибольшими id, то потом, конечно, можно сортировать по banner_ord....

   
 
 автор: glsv (Дизайнер)   (05.11.2004 в 15:08)   письмо автору
 
   для: cheops   (05.11.2004 в 14:57)
 

Так а id (я так понял) используется для сортировки чтобы вывести 4 последних значения.
О как можно! Придумал.

Ставьте не LIMIT 0,3 , а LIMIT [количество строк], [количество строк -4]
Тогда Ваша сортировка ORDER BY будет работать именно для сортировки. Т.е. ORDER BY banner_ord

   
 
 автор: glsv (Дизайнер)   (05.11.2004 в 15:10)   письмо автору
 
   для: glsv (Дизайнер)   (05.11.2004 в 15:08)
 

Точнее:

LIMIT [количество строк-4], [количество строк]

   
 
 автор: elenaki   (05.11.2004 в 15:30)   письмо автору
 
   для: glsv (Дизайнер)   (05.11.2004 в 15:10)
 

а можно узнать количество строк в базе, не делая дополнительный запрос?

   
 
 автор: cheops   (05.11.2004 в 15:36)   письмо автору
 
   для: elenaki   (05.11.2004 в 15:30)
 

Функция count() должна в любой части запроса работать
$query = "SELECT * FROM banner_main WHERE pos = 'top' ORDER BY banner_ord DESC LIMIT COUNT(*)-4,3";

   
 
 автор: elenaki   (05.11.2004 в 15:48)   письмо автору
 
   для: cheops   (05.11.2004 в 15:36)
 

SELECT * FROM banner_main WHERE pos = 'top' ORDER BY banner_ord DESC LIMIT COUNT(*)-4,3 failed!
You have an error in your SQL syntax near 'COUNT(*)-4,3' at line 1

   
 
 автор: glsv (Дизайнер)   (05.11.2004 в 16:09)   письмо автору
 
   для: elenaki   (05.11.2004 в 15:48)
 

Да... вложенная таким образом агрегатная функция может и не работать.

   
 
 автор: cheops   (05.11.2004 в 21:14)   письмо автору
 
   для: glsv (Дизайнер)   (05.11.2004 в 16:09)
 

Ну в любом случае, MySQL не сканирует таблицы, а хранит число записей в таблице, поэтому дополнительный запрос будет выполняться очень быстро.

   
 
 автор: elenaki   (05.11.2004 в 22:08)   письмо автору
 
   для: cheops   (05.11.2004 в 21:14)
 

хорошо, я сделаю дополнительный запрос, узнаю, сколько у меня записей в таблице и возьму 4 с конца (поначалу там может быть и меньше 4-х записей). спасибочки. :)

   
 
 автор: glsv (Дизайнер)   (05.11.2004 в 15:03)   письмо автору
 
   для: elenaki   (05.11.2004 в 13:48)
 

Да, хитрый запрос.
Вам фактически нужно 2 раза сортировать. Первый раз для выборки и второй только для, собственно, сортировки.
Учитывая, что в третьей версии MySQL подзапросов еще нет, то... наверное одним запросом это не сделать. А сортировать уже выборку. Хорошо что она хоть не большая - всего 4 позиции.

   
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования