помочь оптимизировать запрос (показывает силу двусторонних отношений между контактами) - PullRequest
1 голос
/ 30 июня 2010

У меня есть таблица contact_relationship, в которой хранится заявленная сила отношений между одним контактом и другим в данный момент времени.

mysql> desc contact_relationship;
+------------------+-----------+------+-----+-------------------+-----------------------------+
| Field            | Type      | Null | Key | Default           | Extra                       |
+------------------+-----------+------+-----+-------------------+-----------------------------+
| relationship_id  | int(11)   | YES  |     | NULL              |                             |
| contact_id       | int(11)   | YES  | MUL | NULL              |                             |
| other_contact_id | int(11)   | YES  |     | NULL              |                             |
| strength         | int(11)   | YES  |     | NULL              |                             |
| recorded         | timestamp | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------------+-----------+------+-----+-------------------+-----------------------------+

Теперь я хочу получить список двусторонних отношений междуконтакты (это означает, что имеется два ряда, один с контактом, задающим силу взаимосвязи с контактом b, а другой с контактом b, задающим прочность для контакта a - сила двустороннего отношения является меньшей из этих двух значений прочности).

это запрос, который я придумал, но он довольно медленный:

select 
    mrcr1.contact_id, 
    mrcr1.other_contact_id, 
    case when (mrcr1.strength < mrcr2.strength) then 
        mrcr1.strength 
    else 
        mrcr2.strength 
    end strength 
from ( 
    select 
        cr1.* 
    from ( 
        select 
            contact_id,
            other_contact_id,
            max(recorded) as max_recorded 
        from 
            contact_relationship 
        group by 
            contact_id,
            other_contact_id 
    ) as cr2 
    inner join contact_relationship cr1 on 
        cr1.contact_id = cr2.contact_id 
        and cr1.other_contact_id = cr2.other_contact_id 
        and cr1.recorded = cr2.max_recorded 
) as mrcr1, 
( 
    select 
        cr3.* 
    from ( 
        select 
            contact_id,
            other_contact_id,
            max(recorded) as max_recorded 
        from 
            contact_relationship 
        group by 
            contact_id,
            other_contact_id 
    ) as cr4 
    inner join contact_relationship cr3 on 
        cr3.contact_id = cr4.contact_id 
        and cr3.other_contact_id = cr4.other_contact_id 
        and cr3.recorded = cr4.max_recorded 
) as mrcr2 
where 
    mrcr1.contact_id = mrcr2.other_contact_id 
    and mrcr1.other_contact_id = mrcr2.contact_id 
    and mrcr1.contact_id != mrcr1.other_contact_id 
    and mrcr2.contact_id != mrcr2.other_contact_id 
    and mrcr1.contact_id <= mrcr1.other_contact_id; 

у кого-нибудь есть какие-либо рекомендации по его ускорению?

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

update: вот результат объяснения запроса..

+----+-------------+----------------------+-------+----------------------------------------------------------------------------------------+------------------------------+---------+-------------------------------------+-------+--------------------------------+
| id | select_type | table                | type  | possible_keys                                                                          | key                          | key_len | ref                                 | rows  | Extra                          |
+----+-------------+----------------------+-------+----------------------------------------------------------------------------------------+------------------------------+---------+-------------------------------------+-------+--------------------------------+
|  1 | PRIMARY     | <derived2>           | ALL   | NULL                                                                                   | NULL                         | NULL    | NULL                                | 36029 | Using where                    |
|  1 | PRIMARY     | <derived4>           | ALL   | NULL                                                                                   | NULL                         | NULL    | NULL                                | 36029 | Using where; Using join buffer |
|  4 | DERIVED     | <derived5>           | ALL   | NULL                                                                                   | NULL                         | NULL    | NULL                                | 36021 |                                |
|  4 | DERIVED     | cr3                  | ref   | contact_relationship_index_1,contact_relationship_index_2,contact_relationship_index_3 | contact_relationship_index_2 | 10      | cr4.contact_id,cr4.other_contact_id |     1 | Using where                    |
|  5 | DERIVED     | contact_relationship | index | NULL                                                                                   | contact_relationship_index_3 | 14      | NULL                                | 37973 | Using index                    |
|  2 | DERIVED     | <derived3>           | ALL   | NULL                                                                                   | NULL                         | NULL    | NULL                                | 36021 |                                |
|  2 | DERIVED     | cr1                  | ref   | contact_relationship_index_1,contact_relationship_index_2,contact_relationship_index_3 | contact_relationship_index_2 | 10      | cr2.contact_id,cr2.other_contact_id |     1 | Using where                    |
|  3 | DERIVED     | contact_relationship | index | NULL                                                                                   | contact_relationship_index_3 | 14      | NULL                                | 37973 | Using index                    |
+----+-------------+----------------------+-------+----------------------------------------------------------------------------------------+------------------------------+---------+-------------------------------------+-------+--------------------------------+

Ответы [ 2 ]

0 голосов
/ 30 июня 2010

Ответ Scorpi0 заставил меня задуматься, может быть, я мог бы использовать временную таблицу ...

create temporary table mrcr1 (
    contact_id int, 
    other_contact_id int, 
    strength int, 
    index mrcr1_index_1 (
        contact_id, 
        other_contact_id
    )
) replace as 
    select 
        cr1.contact_id, 
        cr1.other_contact_id, 
        cr1.strength from ( 
            select 
                contact_id, 
                other_contact_id, 
                max(recorded) as max_recorded 
            from 
                contact_relationship 
            group by 
                contact_id, other_contact_id 
        ) as cr2 
        inner join 
            contact_relationship cr1 on 
                cr1.contact_id = cr2.contact_id 
                and cr1.other_contact_id = cr2.other_contact_id 
                and cr1.recorded = cr2.max_recorded;

, что я должен был сделать дважды (во второй раз во временную таблицу с именем mrcr2), потому что у mysql есть ограничение, когда нельзя использовать псевдоним дважды для одной временной таблицы в одном запросе.

с двумя созданными временными таблицами мой запрос становится:

select 
    mrcr1.contact_id, 
    mrcr1.other_contact_id, 
    case when (mrcr1.strength < mrcr2.strength) then 
        mrcr1.strength 
    else 
        mrcr2.strength 
    end strength 
from 
    mrcr1,
    mrcr2 
where 
    mrcr1.contact_id = mrcr2.other_contact_id 
    and mrcr1.other_contact_id = mrcr2.contact_id 
    and mrcr1.contact_id != mrcr1.other_contact_id 
    and mrcr2.contact_id != mrcr2.other_contact_id 
    and mrcr1.contact_id <= mrcr1.other_contact_id; 
0 голосов
/ 30 июня 2010

Вы теряете очень много времени, выбирая самую последнюю запись.2 варианта:

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

2 - использовать аналитический запрос для выборасамая последняя запись, если ваша СУБД позволяет вам это сделать.Что-то вроде

Select first_value(strength) over(partition by contact_id, other_contact_id order by recorded desc)
from contact_relationship

Как только вы получите хорошую строку записи, я думаю, ваш запрос будет выполняться намного быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...