Uma para muitas tabela de link causando linhas duplicadas devolvidas
-
18-09-2019 - |
Pergunta
Tudo o que vi até agora é remover as entradas duplicadas em um banco de dados automaticamente. Eu quero enfatizar no início disso que Não há dados duplicados no banco de dados. Também começarei com o fato de ainda estar aprendendo muito sobre o design, normalização, relacionamentos, e, acima de tudo, SQL!
Eu tenho uma tabela de clientes, com um clientID (pk) e um client_name. Eu tenho uma tabela de funções, com um Roleid (PK) e um Role_name. Qualquer cliente pode ter várias funções associadas a ele. Por isso, criei uma tabela client_role_link, com ClientID e Roleid como os dois campos. Então eu corro isto:
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')
Então, digamos que eu tenha um cliente que tenha duas funções associadas a ele (funções '1' e '2'). Esta consulta retorna duas linhas, uma para cada função. Quando eu recuperar esses resultados, estou usando um while
loop para percorrer os resultados e produzi -los em um <select>
Lista. Então está causando dois <option>
S com o mesmo cliente listado.
Eu entendo por que minha consulta está retornando duas linhas, faz sentido. Então, aqui vem a pergunta duas vezes:
- Existe um design de banco de dados/tabela melhor que eu deveria estar usando ou uma consulta mais otimizada?
- Ou isso é algo que eu devo lidar no PHP? Em caso afirmativo, existe uma solução mais elegante que adicionar todos os resultados em uma matriz e depois voltar pela matriz e remover duplicatas?
Pensamentos?
Solução
Se você quiser mostrar as duas funções, sua consulta é OK
.
MySQL
não suporta tipos de dados da matriz, então você deve preencher uma matriz associativa no PHP
lado usando o ResultSet com os nomes de clientes duplicados.
Se você só precisar mostrar aos clientes que têm uma das funções, use esta consulta:
SELECT c.*
FROM client c
WHERE c.clientid IN
(
SELECT roleid
FROM client_role_link crl
WHERE crl.roleid IN (1, 2)
)
Isso retornará um registro por cliente, mas não mostrará nenhum papel.
A terceira maneira implodiria os nomes de função no lado do servidor:
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
e explodi -los PHP
lado, mas verifique se os nomes de função não se misturam com o separador.
Outras dicas
Você poderia usar mysql_fetch_assoc () Para recuperá -los em forma de matriz. Então você poderia ter algo como código não testado, mas pode funcionar:
$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
}