SQL сложный запрос “один ко многим”

287
21 декабря 2016, 01:52

Ситуация. Имеем 3 таблицы. Пользователь, доп. емейлы пользователя и некоторые данные, привязанные к каждому емейлу.

  1. table_users

UserID | username | email

  1. table_emails

UserID | additional_email

  1. table_data

email | date_added | email_data

ЗАДАЧА: username | email_data

Для каждого пользователя нужно вывести одни данные (email_data), первые по времени добавления (date_added). Причем данные могут быть привязаны к любому из емейлов пользователя, как к основному из таблицы table_users, так и к дополнительному емейлу из table_emails, которых может быть бесконечно много (реально, конечно 2-3 штуки). Данных email_data для каждого email может быть несколько, а может и совсем не найтись, так что как-то так: LEFT JOIN ..ORDER BY date_added LIMIT 1. Все это в связке с рнр, так что думаю как-то динамически запрос строить, в первый шаг собрать в массив все емейлы, а потом типа:

<?php
foreach( $emails as $email ) {
    $i++;
    $sql .= " LEFT JOIN table_data as td$i ON td$i.email = $email";
}
?> 

Но как-то все равно не придумывается итоговый запрос... Помогите плз.

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

Answer 1

Думаю как то так:

select *
  from table_users U
  left join table_data D
    on (D.email, D.date_added)=
       (select email, date_added from table_data D1
         where D1.email in(select additional_email from table_emails E
                            where E.UserID=U.UserID
                           union all
                           select U.email
                          )
         order by date_added desc
         limit 1
       )

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

А вообще я бы предложил убрать поле email из таблицы пользователей и хранить все email в таблице emails. Если нужен "главный email" то помечать его специальным флагом в этой таблице.

Answer 2

для конкретного usera что то типо этого

select u.userid, d.email, d.date_added, d.email_data from table_users u left join table_emails e on u.userid= e.userid left join table_data d on d.email = e.additional_email or u.email = d.email where u.userid = 1 order by d.date_added desc limit 1 http://sqlfiddle.com/#!9/0b53b40/1

Answer 3

Проблемы с сортировкой если информаци о основном email не вставляется в таблицу table_emails:

select
    u.username,
    COALESCE(td1.email_data, td2.email_data) as email_data
from users u
left join table_emails te on te.user_id = u.user_id
left join table_data td1 on td1.email = u.email
left join table_data td2 on td2.email = te.email
order by ???

Проблемы, потому, что время нужно выбирать то с "одной" таблицы, то с "другой" (td1 и td2). НО!!! Если вы в таблицу table_emails вставляете данные о основном email, тогда все в порядке:

select
    u.username,
    td.email_data
from users u
left join table_emails te on te.user_id = u.user_id
left join table_data td on td.email = te.email
order by
    td.date_add
READ ALSO
Запрос в MySQL и выбор из нескольких таблиц

Запрос в MySQL и выбор из нескольких таблиц

Есть несколько одинаковых таблиц в базеКак сделать запрос из всех? Вот запрос на выбор из одной:

303
Обработка ошибок MySQL в PHP

Обработка ошибок MySQL в PHP

ЗдравствуйтеВ разных частях моего php-скрипта я выполняю запросы к БД MySQL

264
Подсчитать кол-во записей в каждой категории

Подсчитать кол-во записей в каждой категории

ЗдравствуйтеТребуется на сайте произвести подсчет, сколько записей находится в каждой категории, а так же подсчитать общее кол-во всех записей

251