Таблица ссылок «один ко многим», вызывающая повторяющиеся строки - PullRequest
1 голос
/ 04 февраля 2010

Все, что я видел до сих пор, - это автоматическое удаление повторяющихся записей в базе данных. В начале хочу подчеркнуть, что в базе данных нет повторяющихся данных . Я также начну с того, что я еще очень много изучаю в области проектирования СУБД, нормализации, отношений и, прежде всего, SQL!

У меня есть таблица клиентов с клиентами (PK) и именем клиента. У меня есть таблица ролей, с ролью (PK) и ролью_имя. Любой клиент может иметь несколько ролей, связанных с ним. Поэтому я создал таблицу client_role_link, в которой в качестве двух полей используются clientid и roleid. Затем я запускаю это:

SELECT client.client_name, role.role_name FROM client 
  LEFT JOIN client_role_link ON client_role_link.clientid=client.clientid 
  LEFT JOIN role ON client_role_link.roleid=role.roleid 
  WHERE (role.roleid='1' OR role.roleid='2')

Итак, допустим, у меня есть клиент, с которым связаны две роли (роли «1» и «2»). Этот запрос возвращает две строки, по одной для каждой роли. Когда я получаю эти результаты обратно, я использую цикл while для циклического просмотра результатов и вывода их в список <select>. Затем он вызывает два <option> с тем же клиентом в списке.

Я понимаю, почему мой запрос возвращает две строки, это имеет смысл. Итак, возникает два вопроса:

  1. Есть ли лучший дизайн базы данных / таблицы, который мне следует использовать, или более оптимизированный запрос?
  2. Или это то, что я должен обрабатывать в PHP? Если это так, есть ли более элегантное решение, которое добавляет все результаты в массив, а затем возвращает их обратно в массив и удаляет дубликаты?

Мысли

Ответы [ 2 ]

3 голосов
/ 04 февраля 2010

Если вы хотите показать обе роли, тогда ваш запрос OK.

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

Если вам просто нужно показать клиентов, имеющих одну из ролей, используйте этот запрос:

SELECT  c.*
FROM    client c
WHERE   c.clientid IN
        (
        SELECT  roleid
        FROM    client_role_link crl
        WHERE   crl.roleid IN (1, 2)
        )

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

Третий способ - использовать имена ролей на стороне сервера:

SELECT  c.*, GROUP_CONCAT(role_name SEPARATOR ';') AS roles
FROM    client c
LEFT JOIN
        client_role_link crl
ON      crl.clientid = c.clientid
        AND crl.roleid IN (1, 2)
LEFT JOIN
        role r
ON      r.roleid = crl.roleid
GROUP BY
        c.id

и взорвите их на стороне PHP, но убедитесь, что имена ролей не будут смешиваться с разделителем.

0 голосов
/ 04 февраля 2010

Вы можете использовать mysql_fetch_assoc () , чтобы вернуть их обратно в виде массива. Тогда у вас может быть что-то вроде кода, не проверенного, но может работать :

$sql = "SELECT client.id, client.client_name, role.role_name FROM client LEFT JOIN client_role_link ON client_role_link.clientid=client.clientid LEFT JOIN role ON client_role_link.roleid=role.roleid WHERE (role.roleid='1' OR role.roleid='2')";
$result = mysql_query($sql);
$res = array();
while ($row = mysql_fetch_assoc($result)) {
    $res[$row['id']]['roles'][] = $row['role_name'];
    $res[$row['id']]['client_name'] = $row['client_name']; //you'll be overwriting each iteration probably a better way
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...