Как получить только вторую дублированную запись в laravel 5.5? - PullRequest
2 голосов
/ 08 мая 2020

Допустим, у меня есть такая таблица пользователей:

+----+-----------+----------------------+------+
| ID | Name      | Email                | Age  |
+----+-----------+----------------------+------+
| 1  | John      | john.doe1@mail.com   | 24   |
| 2  | Josh      | josh99@mail.com      | 29   |
| 3  | Joseph    | joseph410@mail.com   | 21   |
| 4  | George    | gge.48@mail.com      | 28   |
| 5  | Joseph    | jh.city89@mail.com   | 24   |
| 6  | Kim       | kimsd@mail.com       | 32   |
| 7  | Bob       | bob.s@mail.com       | 38   |
| 8  | Joseph    | psa.jos@mail.com     | 34   |
| 9  | Joseph    | joseph.la@mail.com   | 28   |
| 10 | Jonathan  | jonhan@mail.com      | 22   |
+----+-----------+---------+------------+------+

Фактически, база данных состоит из большего количества данных, и некоторые из них дублируются, с более чем двумя записями. Но дело в том, что я хочу получить только первую и вторую строки дублированных строк, которые содержат имя «Джозеф». Как я могу этого добиться? Мой код на данный момент ...

User::withTrashed()->groupBy('name')->havingRaw('count("name") >= 1')->get();

С этим кодом будет получен результат:

+----+-----------+----------------------+------+
| ID | Name      | Email                | Age  |
+----+-----------+----------------------+------+
| 1  | John      | john.doe1@mail.com   | 24   |
| 2  | Josh      | josh99@mail.com      | 29   |
| 3  | Joseph    | joseph410@mail.com   | 21   |
| 4  | George    | gge.48@mail.com      | 28   |
| 6  | Kim       | kimsd@mail.com       | 32   |
| 7  | Bob       | bob.s@mail.com       | 38   |
| 10 | Jonathan  | jonhan@mail.com      | 22   |
+----+-----------+---------+------------+------+

И я использую этот код, чтобы попытаться получить вторую дублированную строку:

User::withTrashed()->groupBy('name')->havingRaw('count("name") >= 2')->get();

Результат такой же, как указано выше:

+----+-----------+----------------------+------+
| ID | Name      | Email                | Age  |
+----+-----------+----------------------+------+
| 1  | John      | john.doe1@mail.com   | 24   |
| 2  | Josh      | josh99@mail.com      | 29   |
| 3  | Joseph    | joseph410@mail.com   | 21   |
| 4  | George    | gge.48@mail.com      | 28   |
| 6  | Kim       | kimsd@mail.com       | 32   |
| 7  | Bob       | bob.s@mail.com       | 38   |
| 10 | Jonathan  | jonhan@mail.com      | 22   |
+----+-----------+---------+------------+------+

Я хочу, чтобы в результате была получена запись с идентификатором «5» и именем «Джозеф», например:

    +----+-----------+----------------------+------+
    | ID | Name      | Email                | Age  |
    +----+-----------+----------------------+------+
    | 1  | John      | john.doe1@mail.com   | 24   |
    | 2  | Josh      | josh99@mail.com      | 29   |
    | 4  | George    | gge.48@mail.com      | 28   |
    | 5  | Joseph    | jh.city89@mail.com   | 24   |
    | 6  | Kim       | kimsd@mail.com       | 32   |
    | 7  | Bob       | bob.s@mail.com       | 38   |
    | 10 | Jonathan  | jonhan@mail.com      | 22   |
    +----+-----------+---------+------------+------+

Но кажется, что извлекается только первая повторяющаяся строка, и я не могу получить вторую повторяющуюся строку, может ли кто-нибудь дать мне предложение?

1 Ответ

3 голосов
/ 09 мая 2020

Начнем с вашего запроса

User::withTrashed()->groupBy('name')->havingRaw('count("name") >= 1')->get();

Это покажет все группы строк, количество которых равно 1 или больше. и это описание DISTINCT.

Если вы хотите получить только повторяющиеся записи, вы должны получить группы, количество которых БОЛЬШЕ, чем 1.

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

+--------+-------------+-------+
| Name   | Count(Name) | Email |
+--------+-------------+-------+
| Joseph | 4           | X     |
+--------+-------------+-------+

чего вы ожидаете вместо X? какое из 4 писем? на самом деле, в SQLServer запрещено выбирать неагрегированный столбец, а другие базы данных просто выдадут вам случайный из подсчитываемых 3. см. этот ответ для более подробной информации, он очень хорошо объяснен: Все ли столбцы в списке SELECT имеют чтобы появиться в предложении GROUP BY

Итак, мы будем использовать having count(name) > 1 и выбрать только агрегированный столбец name

DB::from('users')->select('name')->groupBy('name')->havingRaw('count("name") > 1')->get();

Это должно дать вам (didn ' t проверьте это) это:

+--------+-------------+
| name   | Count(name) |
+--------+-------------+
| Joseph | 4           |
+--------+-------------+

Это даст вам все имена, у которых есть 2 или более экземпляров. вы можете определить количество дубликатов в предложении имеющего. например, having count(name) = 3 даст вам все имена, которые имеют ровно 3 дубликата.

Итак, как получить второй дубликат? У меня вопрос:

Какой первый (оригинальный) дубликат? это тот, у которого самый старый created_at или самый старый updated_at? а может какое-то другое условие ?. по этой причине вам следует сделать еще один запрос с предложением order by, чтобы получить дубликаты в наиболее удобном для вас порядке. например:

select * from `users` where `name` in  (select `name` from users group by `name` having count(`name`) > 1) order by `id` asc

, что даст:

+----+-----------+----------------------+------+
| ID | Name      | Email                | Age  |
+----+-----------+----------------------+------+
| 3  | Joseph    | joseph410@mail.com   | 21   |
| 5  | Joseph    | jh.city89@mail.com   | 24   |
| 8  | Joseph    | psa.jos@mail.com     | 34   |
| 9  | Joseph    | joseph.la@mail.com   | 28   |
+----+-----------+---------+------------+------+
...