database-design - reglas - principios importantes del diseño de bases de datos?
Diseño de la base de datos: ¿la mejor estructura de tabla para capturar la relación usuario/amigo? (13)
Estoy tratando de diseñar un modelo de datos que denote que un usuario es amigo de otro usuario. Esto es lo que he propuesto hasta ahora, pero parece torpe, ¿hay una mejor solución?
User
=====
Id
Name
etc...
UserFriend
===========
UserId
FriendId
IsMutual
IsBlocked
¿De verdad necesitas una mesa física para descubrir si hay amigos en común? ¿Por qué no hacer una consulta SQL como:
SELECT U.ID, U.NAME FROM USER U
INNER JOIN USERFRIEND UF
ON U.ID = UF.FRIENDID
WHERE U.ID = (SELECT USER.ID FROM USER
WHERE USER.ID = friend_id AND USER.ID != your_id);
Los resultados de la consulta deben devolver todos los amigos comunes.
¿Qué piensas de mi solución?
Tienes 3 tablas
1 **the user** (id,.etc)
2 **friend_request**(id,flaggerID,flaggedID)
3 **friend**(id,frienderID,friendedID)
Usted inicia sesión, verifique si estoy en la mesa de amigos, si es así haga una lista de amigos (estoy en la lista de amigos) (estoy en la lista de amigos> listfriender)
¿Tengo una solicitud? (¿Estoy en flaggedID?) ¿Lo conozco? Si no, elimine el registro; en caso afirmativo, cree un nuevo registro en la solicitud, ya que me pongan en el marcador y el marcador se marque. Ahora tenemos mutualidad entre 2 registros en la tabla de solicitud, por lo que los eliminamos y los ponemos en la tabla de amigos. Req / friends fáciles de separar.
Actualmente estoy construyendo un sitio de redes sociales para un cliente y expresé las cosas de esta manera
CREATE TABLE [dbo].[PersonFriend] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Timestamp] DATETIME NOT NULL,
[ChangeUser] NVARCHAR (200) NOT NULL,
[FriendStatusId] TINYINT NOT NULL,
[Person1Id] INT NOT NULL,
[Person2Id] INT NOT NULL,
[Person1RequestTimestamp] DATETIME NOT NULL,
[Person2AcknowledgeTimestamp] DATETIME NULL
);
Cada persona está almacenada en la tabla Person (imagine eso). Los campos Person1Id y Person2Id son FK a la tabla de personas. Guardo una lista de estado en la tabla FriendStatus para cubrir si algo ha sido solicitado, aceptado, denegado, ignorado, etc. El campo Timestamp es estándar en mi diseño para indicar la creación de registros (es un patrón que se usa en la clase base de persistencia) ) y se duplica en esta tabla, ya que Person1RequestTimestamp contiene los mismos datos. También capturo cuando Person2 vio la solicitud y realizó una acción (que se indica en FriendStatusId) y la almacena en Person2AcknowledgeTimestamp).
Se puede afirmar que una de las suposiciones centrales de este diseño es que Person1 solicitó la amistad de Person2; si esa amistad es aceptada, la amistad se considera mutua.
Bueno, llegué tarde a la fiesta, pero esta es mi oferta.
Primero las tablas:
User
-id
Type
-id
Relationship
-user_id_1
-user_id_2
-type_id
Ahora, quiero mantener mi tabla de tipos simple. Entonces, los tipos que agrego solo representan la relación de un solo usuario con otro. Nunca representan la relación bidireccional. Por ejemplo:
friend
ignored
Esto hace que agregar nuevos tipos sea fácil. No tengo que pensar o crear todas las combinaciones posibles de todos mis tipos. Solo agrego el nuevo tipo.
Para establecer una amistad, necesitaría 2 entradas en la tabla de relaciones. Si ambos usuarios aceptan que son amigos, son amigos. Si solo uno dice que es amigo del otro y que el otro lo tiene bloqueado, no son amigos.
Hacer consultas es muy simple. Así es como obtienes todos los amigos en MySQL:
SELECT u.* FROM user u
LEFT JOIN relationship r1 ON u.id = r1.user_id_2
LEFT JOIN relationship r2 ON u.id = r2.user_id_1
WHERE r1.user_id_1 = <my_user_id> # my relationship with them
AND r1.type_id = <friend_type_id> # i say i''m a friend
AND r2.user_id_2 = <my_user_id> # their relationship with me
AND r2.type_id = <friend_type_id> # they say they''re friends
También creo que este enfoque es más "seguro para las transacciones". Imagine que envía una solicitud de amistad a alguien y luego la bloquea. Si esa persona luego acepta la solicitud de amistad, no importa. No cambia el estado de la relación, porque son completamente independientes.
Si tuviera un solo tipo que representara la relación bidireccional, se vería forzado a hacer algún tipo de evaluación en su código, de lo que debería ser exactamente el nuevo estado de la relación, una vez que el amigo haya aceptado la solicitud de amistad. Si no lo hace, podría terminar desbloqueando a un usuario y haciendo que esa persona sea amiga del usuario que él o ella bloqueó.
Preferiría manejar esto a nivel de base de datos. Si tiene un grupo de programadores trabajando en la aplicación, no pasará mucho tiempo antes de que alguien olvide este problema en alguna parte y se cree un error difícil de encontrar.
Creo que deberías crear dos tablas:
1. usuario
u_id int
u_username string
balahhh ............
2. amistad
fs_id int
relation_id int
related_id int
Desde mi entender, la amistad es el resultado de una relación entre dos usuarios (Usuario1 y Usuario2) y porque un usuario1 puede tener 0 o muchos usuarios como amigos y viceversa, y por lo tanto una mesa de enlace "Amigo" commes en el medio para representar esa relación de esta manera:
User1 id (int) nombre de usuario (cadena)
User2 id (int) nombre de usuario (cadena)
Amigo ---- id (int) usuario1_Id (int) usuario2_Id (int) activo (bool)
User1_Id y user2_Id son FK en Frind Table
Espero tener razón
Haría algo similar a lo que tienes, pero eliminaría la bandera "IsMutual". Simplemente agregue una segunda fila con valores inversos cuando sea mutua. Agrega filas, pero se siente mucho más limpio.
Las amistades son menos claras que los escenarios clásicos de empleador / jefe y usuario / cónyuge. ¿Es la amistad una relación o una actividad? He recibido una buena cantidad de críticas por descuidar este último. De cualquier manera, es probable que necesite más de una tabla, sin importar cuán genérico sea su modelo de datos.
Lo hice así:
TABLE usuario
id name
-----------
1 foo
2 roo
3 shoo
4 mooo
TABLE relación de amigos
id u_id f_id
--------------
1 1 2
2 2 1
3 3 1
4 1 3
Cada vez que se acepta la solicitud de un amigo, inserte la manera inversa 1 2 & 2 1
y la consulta simple:
$ufq=mysql_query("SELECT t1.f_id,t2.id,t2.name FROM friends AS t1, user AS t2 WHERE t1.u_id=''$u_id'' AND t2.id=t1.f_id ORDER BY t2.name ")or die(mysql_error());
while($fn=mysql_fetch_array($ufq)){
echo $fn["name"].''<br>'';
}
Para ir un registro por cada dos usuarios y evitar consumir memoria extra que sugieren los métodos propuestos (el doble de lo necesario, ya que hay dos registros para cada usuario), puede hacer lo siguiente:
Estructura de la tabla:
USER_RELATIONSHIP { user_first_id, user_second_id, type primary key(user_first_id, user_second_id) }
Asegúrese de:
user_first_id < user_second_id
La parte más interesante:
type
: para todos los estados posibles de una relación, creas los valores correspondientes. Por ejemplo:pending_first_second pending_second_first friends block_first_second block_second_first block_both
Que tienes:
-
user_first_id < user_second_id
asegura que solo hay un registro de una relación entre dos usuarios dados, ya que esto y las restricciones de clave primaria no permitirán colocarlo de otra manera. - Al cambiar correctamente entre los estados de relación, usted determina cómo cada uno de los dos usuarios se relacionan entre sí. Si no hay relación entre dos usuarios, no hay registro.
Para averiguar la relación entre dos usuarios (así como también la actualización), simplemente acceda a una sola consulta:
select * from USER_RELATIONSHIP where user_first_id = user1_id and user_second_id = user2_id;
sin una declaración
or
que verifique estas columnas al revés, que es más rápido.
Escenario de ejemplo :
no record
: no están en una relaciónpending_first_second
: el primero hizo una solicitud de amistad al segundofriends
: el segundo aprobó la solicitud de amistadno record
: uno de los usuarios quitó el otro de sus amigos
Esta solución es eficiente en términos de memoria y velocidad, ya que crea, almacena y actualiza solo un registro.
Probablemente en la parte superior, pero uno puede usar la web semántica para modelar esto. Se puede usar el formato FOAF (amigo de FOAF).
Tal vez agregue una tabla de relaciones, coloque las propiedades de relación allí y haga referencia a ella desde UserFriend.
UserRelationship
====
RelatingUserID
RelatedUserID
Type[friend, block, etc]
Acepta que la mutualidad no pertenece a una columna; rompe la normalización.