M
M
Maxim Timofeev2017-05-27 16:38:50
PHP
Maxim Timofeev, 2017-05-27 16:38:50

How to select unique records, make groupBy work after union?

There is a table (chat) id | from | to | text | datetime
And related user
Tried through union, but duplicates are removed only before union:

the code
public function getSended(){
        return $this->hasMany(Chat::className(),['from'=>'id']);
    }

    public function getReceived(){
        return $this->hasMany(Chat::className(),['to'=>'id']);
    }

    public function getDialogs(){
        return $this->getSended()->select('id,to,to as userid')->groupBy('userid')->union($this->getReceived()->select('id,from,from as userid')->groupBy('userid'))->with('toUser');
    }

a request is received

SELECT COUNT(*) FROM ((SELECT `id`, `to`, `to` AS `userid` FROM `chat` WHERE `from`=1 GROUP BY `userid`) UNION ( SELECT `id`, `from`, `from` AS `userid` FROM `chat` WHERE `to`=1 GROUP BY `userid` )) `c`


Roughly speaking, 2,3,4 are combined with 2,4,5 and get 2,3,4,2,4,5 instead of the desired, 2,3,4,5. groupBy after union does not plow. Jammed me tightly, need a help.
So far I got out of the situation like this:
hellish idiocy
public function getDialogs(){
        $ids = $this->getSended()->select('to as userid')->groupBy('userid')->union($this->getReceived()->select('from as userid')->groupBy('userid'))->asArray()->all();
        return self::find()->andWhere(['id'=>ArrayHelper::getColumn($ids,'userid')])->groupBy('id');
    }

but it's a coma

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
Maxim Fedorov, 2017-05-30
@qonand

That is, in fact, you have a table of messages between users and you need to select the last message within each dialog.
I see three solutions here:
Option 1.
If we assume that the last message is a message with a large identifier, then all the last messages in the dialog can be obtained like this

SELECT
  *
FROM
  chat
LEFT JOIN (
  SELECT
    max(id) AS message_id,
    IF (`to` = 1, `from`, `to`) AS `collocutor_id`
  FROM
    chat
  GROUP BY
    `collocutor_id`
) AS m ON chat.id = m.message_id
WHERE
  m.message_id IS NOT NULL

or so
SELECT
  *
FROM
  chat
WHERE
  id IN (
    SELECT
      max(id) AS message_id
    FROM
      chat
    GROUP BY
    IF (`to` = 1, `from`, `to`)
  )

Option 2.
If we assume that the last message is a message with a greater date, then all the latest messages in the dialogue can be obtained like this
SELECT
  chat.*
FROM
  chat
LEFT JOIN (
  SELECT
    max(datetime) AS datetime,

  IF (`to` = 1, `from`, `to`) AS `collocutor_id`
  FROM
    chat
  GROUP BY
    `collocutor_id`
) AS m ON (
  m.`collocutor_id` = chat.`to`
  OR m.`collocutor_id` = chat.`from`
)
AND chat.datetime < m.datetime
WHERE
  m.datetime IS NULL

But here it is worth understanding that if the date and time coincide within the framework of one dialogue, there will be a bug. Although I think the probability of this is extremely small
. Option 3.
Redesign the database, namely, create a separate table containing information about the dialogs and work directly with it. This would avoid unnecessary queries to the database

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question