Skip to content

Commit 025d15d

Browse files
expand Relationships
1 parent def2afb commit 025d15d

File tree

1 file changed

+158
-35
lines changed

1 file changed

+158
-35
lines changed

book/30-schema-design/050-relationships.ipynb

Lines changed: 158 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"cell_type": "markdown",
4242
"metadata": {},
4343
"source": [
44-
"# 1:N Relationships\n",
44+
"# One-to-Many Relationships\n",
4545
"\n",
4646
"In the first example, let the rule be that customers are independent entities and accounts have exactly one owner but customers can have any number of accounts.\n",
4747
"This is an example of an 1:N relationship between customers and their accounts.\n",
@@ -816,7 +816,9 @@
816816
"cell_type": "markdown",
817817
"metadata": {},
818818
"source": [
819-
"Looking at the diagrams alone, only one is obviously a one-on-one relationship (`schema5`), but all others may have additional constraints modifying the relationship. In Jupyter, you may hover over the diagram to see its definition, including secondary uniqueness constraints."
819+
"By examining the diagrams alone, only one (`schema5`) clearly shows a one-to-one relationship. For the others, additional constraints may modify the relationships, though these may not be immediately visible. In Jupyter, you can hover over a diagram element to view its full definition, including any secondary uniqueness constraints.\n",
820+
"\n",
821+
"In practice, DataJoint users generally avoid secondary unique constraints when the primary key can enforce uniqueness. This approach has the added benefit of making relationships more transparent in the diagrams, providing a clearer representation of the schema’s structure."
820822
]
821823
},
822824
{
@@ -849,60 +851,60 @@
849851
},
850852
{
851853
"cell_type": "code",
852-
"execution_count": 42,
854+
"execution_count": 49,
853855
"metadata": {},
854856
"outputs": [
855857
{
856858
"data": {
857859
"image/svg+xml": [
858-
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"166pt\" height=\"113pt\" viewBox=\"0.00 0.00 165.62 113.12\">\n",
860+
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"180pt\" height=\"113pt\" viewBox=\"0.00 0.00 180.12 113.12\">\n",
859861
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 109.12)\">\n",
860-
"<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-109.12 161.62,-109.12 161.62,4 -4,4\"/>\n",
861-
"<!-- CustomerAccount -->\n",
862+
"<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-109.12 176.12,-109.12 176.12,4 -4,4\"/>\n",
863+
"<!-- Customer7 -->\n",
862864
"<g id=\"node1\" class=\"node\">\n",
863-
"<title>CustomerAccount</title>\n",
864-
"<g id=\"a_node1\"><a xlink:title=\"→ Customer\r→ Account\r\">\n",
865-
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"142.88,-34.56 19.62,-34.56 19.62,0 142.88,0 142.88,-34.56\"/>\n",
866-
"<text text-anchor=\"middle\" x=\"81.25\" y=\"-13.01\" font-family=\"arial\" font-size=\"12.00\" fill=\"darkgreen\">CustomerAccount</text>\n",
865+
"<title>Customer7</title>\n",
866+
"<g id=\"a_node1\"><a xlink:title=\"customer_id          \r------------------------------\rfull_name            \r\">\n",
867+
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"82,-105.12 0,-105.12 0,-70.56 82,-70.56 82,-105.12\"/>\n",
868+
"<text text-anchor=\"start\" x=\"8\" y=\"-84.56\" font-family=\"arial\" text-decoration=\"underline\" font-size=\"12.00\" fill=\"darkgreen\">Customer7</text>\n",
867869
"</a>\n",
868870
"</g>\n",
869871
"</g>\n",
870-
"<!-- Customer -->\n",
871-
"<g id=\"node2\" class=\"node\">\n",
872-
"<title>Customer</title>\n",
873-
"<g id=\"a_node2\"><a xlink:title=\"customer_id          \r------------------------------\rfull_name            \r\">\n",
874-
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"74.5,-105.12 0,-105.12 0,-70.56 74.5,-70.56 74.5,-105.12\"/>\n",
875-
"<text text-anchor=\"start\" x=\"8\" y=\"-84.56\" font-family=\"arial\" text-decoration=\"underline\" font-size=\"12.00\" fill=\"darkgreen\">Customer</text>\n",
872+
"<!-- CustomerAccount7 -->\n",
873+
"<g id=\"node3\" class=\"node\">\n",
874+
"<title>CustomerAccount7</title>\n",
875+
"<g id=\"a_node3\"><a xlink:title=\"→ Customer7\r→ Account7\r\">\n",
876+
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"153.38,-34.56 22.62,-34.56 22.62,0 153.38,0 153.38,-34.56\"/>\n",
877+
"<text text-anchor=\"middle\" x=\"88\" y=\"-13.01\" font-family=\"arial\" font-size=\"12.00\" fill=\"darkgreen\">CustomerAccount7</text>\n",
876878
"</a>\n",
877879
"</g>\n",
878880
"</g>\n",
879-
"<!-- Customer&#45;&gt;CustomerAccount -->\n",
881+
"<!-- Customer7&#45;&gt;CustomerAccount7 -->\n",
880882
"<g id=\"edge1\" class=\"edge\">\n",
881-
"<title>Customer-&gt;CustomerAccount</title>\n",
882-
"<path fill=\"none\" stroke=\"#000000\" stroke-width=\"0.75\" stroke-opacity=\"0.250980\" d=\"M47.9,-70.24C54.82,-59.46 63.8,-45.47 70.7,-34.72\"/>\n",
883+
"<title>Customer7-&gt;CustomerAccount7</title>\n",
884+
"<path fill=\"none\" stroke=\"#000000\" stroke-width=\"0.75\" stroke-opacity=\"0.250980\" d=\"M52.38,-70.24C59.77,-59.46 69.36,-45.47 76.73,-34.72\"/>\n",
883885
"</g>\n",
884-
"<!-- Account -->\n",
885-
"<g id=\"node3\" class=\"node\">\n",
886-
"<title>Account</title>\n",
887-
"<g id=\"a_node3\"><a xlink:title=\"account_id           \r------------------------------\ropen_date            \r\">\n",
888-
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"157.62,-105.12 92.88,-105.12 92.88,-70.56 157.62,-70.56 157.62,-105.12\"/>\n",
889-
"<text text-anchor=\"start\" x=\"100.88\" y=\"-84.56\" font-family=\"arial\" text-decoration=\"underline\" font-size=\"12.00\" fill=\"darkgreen\">Account</text>\n",
886+
"<!-- Account7 -->\n",
887+
"<g id=\"node2\" class=\"node\">\n",
888+
"<title>Account7</title>\n",
889+
"<g id=\"a_node2\"><a xlink:title=\"account_id           \r------------------------------\ropen_date            \r\">\n",
890+
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"172.12,-105.12 99.88,-105.12 99.88,-70.56 172.12,-70.56 172.12,-105.12\"/>\n",
891+
"<text text-anchor=\"start\" x=\"107.88\" y=\"-84.56\" font-family=\"arial\" text-decoration=\"underline\" font-size=\"12.00\" fill=\"darkgreen\">Account7</text>\n",
890892
"</a>\n",
891893
"</g>\n",
892894
"</g>\n",
893-
"<!-- Account&#45;&gt;CustomerAccount -->\n",
895+
"<!-- Account7&#45;&gt;CustomerAccount7 -->\n",
894896
"<g id=\"edge2\" class=\"edge\">\n",
895-
"<title>Account-&gt;CustomerAccount</title>\n",
896-
"<path fill=\"none\" stroke=\"#000000\" stroke-width=\"0.75\" stroke-opacity=\"0.250980\" d=\"M114.6,-70.24C107.68,-59.46 98.7,-45.47 91.8,-34.72\"/>\n",
897+
"<title>Account7-&gt;CustomerAccount7</title>\n",
898+
"<path fill=\"none\" stroke=\"#000000\" stroke-width=\"0.75\" stroke-opacity=\"0.250980\" d=\"M124.38,-70.24C116.83,-59.46 107.04,-45.47 99.51,-34.72\"/>\n",
897899
"</g>\n",
898900
"</g>\n",
899901
"</svg>"
900902
],
901903
"text/plain": [
902-
"<datajoint.diagram.Diagram at 0x7fc003e5da50>"
904+
"<datajoint.diagram.Diagram at 0x7fc003b88510>"
903905
]
904906
},
905-
"execution_count": 42,
907+
"execution_count": 49,
906908
"metadata": {},
907909
"output_type": "execute_result"
908910
}
@@ -911,26 +913,26 @@
911913
"schema7 = dj.Schema('bank7')\n",
912914
"\n",
913915
"@schema7\n",
914-
"class Customer(dj.Manual):\n",
916+
"class Customer7(dj.Manual):\n",
915917
" definition = \"\"\"\n",
916918
" customer_id : int unsigned\n",
917919
" ---\n",
918920
" full_name : varchar(30)\n",
919921
" \"\"\"\n",
920922
"\n",
921923
"@schema7\n",
922-
"class Account(dj.Manual):\n",
924+
"class Account7(dj.Manual):\n",
923925
" definition = \"\"\"\n",
924926
" account_id : int unsigned\n",
925927
" ---\n",
926928
" open_date : date\n",
927929
" \"\"\"\n",
928930
"\n",
929931
"@schema7\n",
930-
"class CustomerAccount(dj.Manual):\n",
932+
"class CustomerAccount7(dj.Manual):\n",
931933
" definition = \"\"\"\n",
932-
" -> Customer\n",
933-
" -> Account\n",
934+
" -> Customer7\n",
935+
" -> Account7\n",
934936
" \"\"\"\n",
935937
"\n",
936938
"dj.Diagram(schema7)"
@@ -953,6 +955,127 @@
953955
{
954956
"cell_type": "markdown",
955957
"metadata": {},
958+
"source": [
959+
"Association tables are primarily used to establish many-to-many relationships, but they also offer the flexibility to model one-to-many and even one-to-one relationships by applying additional uniqueness constraints. By controlling the uniqueness on the foreign keys within the association table, you can fine-tune the type of relationship between entities.\n",
960+
"\n",
961+
"### Example, enforcing One-to-Many with Shared Accounts \n",
962+
"\n",
963+
"In the following example, we model a scenario where each customer can have only one account, but each account may be shared among multiple customers. This structure enforces a one-to-many relationship between Customer8 and Account8 via the CustomerAccount8 association table."
964+
]
965+
},
966+
{
967+
"cell_type": "code",
968+
"execution_count": 54,
969+
"metadata": {},
970+
"outputs": [
971+
{
972+
"data": {
973+
"image/svg+xml": [
974+
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"180pt\" height=\"113pt\" viewBox=\"0.00 0.00 180.12 113.12\">\n",
975+
"<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 109.12)\">\n",
976+
"<polygon fill=\"white\" stroke=\"none\" points=\"-4,4 -4,-109.12 176.12,-109.12 176.12,4 -4,4\"/>\n",
977+
"<!-- Customer8 -->\n",
978+
"<g id=\"node1\" class=\"node\">\n",
979+
"<title>Customer8</title>\n",
980+
"<g id=\"a_node1\"><a xlink:title=\"customer_id          \r------------------------------\rfull_name            \r\">\n",
981+
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"82,-105.12 0,-105.12 0,-70.56 82,-70.56 82,-105.12\"/>\n",
982+
"<text text-anchor=\"start\" x=\"8\" y=\"-84.56\" font-family=\"arial\" text-decoration=\"underline\" font-size=\"12.00\" fill=\"darkgreen\">Customer8</text>\n",
983+
"</a>\n",
984+
"</g>\n",
985+
"</g>\n",
986+
"<!-- CustomerAccount8 -->\n",
987+
"<g id=\"node3\" class=\"node\">\n",
988+
"<title>CustomerAccount8</title>\n",
989+
"<g id=\"a_node3\"><a xlink:title=\"→ Customer8\r------------------------------\r→ Account8\r\">\n",
990+
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"153.38,-34.56 22.62,-34.56 22.62,0 153.38,0 153.38,-34.56\"/>\n",
991+
"<text text-anchor=\"middle\" x=\"88\" y=\"-13.01\" font-family=\"arial\" font-size=\"12.00\" fill=\"darkgreen\">CustomerAccount8</text>\n",
992+
"</a>\n",
993+
"</g>\n",
994+
"</g>\n",
995+
"<!-- Customer8&#45;&gt;CustomerAccount8 -->\n",
996+
"<g id=\"edge1\" class=\"edge\">\n",
997+
"<title>Customer8-&gt;CustomerAccount8</title>\n",
998+
"<path fill=\"none\" stroke=\"#000000\" stroke-width=\"2\" stroke-opacity=\"0.250980\" d=\"M52.38,-70.24C59.77,-59.46 69.36,-45.47 76.73,-34.72\"/>\n",
999+
"</g>\n",
1000+
"<!-- Account8 -->\n",
1001+
"<g id=\"node2\" class=\"node\">\n",
1002+
"<title>Account8</title>\n",
1003+
"<g id=\"a_node2\"><a xlink:title=\"account_id           \r------------------------------\ropen_date            \r\">\n",
1004+
"<polygon fill=\"#00ff00\" fill-opacity=\"0.188235\" stroke=\"#00ff00\" stroke-opacity=\"0.188235\" points=\"172.12,-105.12 99.88,-105.12 99.88,-70.56 172.12,-70.56 172.12,-105.12\"/>\n",
1005+
"<text text-anchor=\"start\" x=\"107.88\" y=\"-84.56\" font-family=\"arial\" text-decoration=\"underline\" font-size=\"12.00\" fill=\"darkgreen\">Account8</text>\n",
1006+
"</a>\n",
1007+
"</g>\n",
1008+
"</g>\n",
1009+
"<!-- Account8&#45;&gt;CustomerAccount8 -->\n",
1010+
"<g id=\"edge2\" class=\"edge\">\n",
1011+
"<title>Account8-&gt;CustomerAccount8</title>\n",
1012+
"<path fill=\"none\" stroke=\"#000000\" stroke-width=\"0.75\" stroke-dasharray=\"5,2\" stroke-opacity=\"0.250980\" d=\"M124.38,-70.24C116.83,-59.46 107.04,-45.47 99.51,-34.72\"/>\n",
1013+
"</g>\n",
1014+
"</g>\n",
1015+
"</svg>"
1016+
],
1017+
"text/plain": [
1018+
"<datajoint.diagram.Diagram at 0x7fc003a3e9d0>"
1019+
]
1020+
},
1021+
"execution_count": 54,
1022+
"metadata": {},
1023+
"output_type": "execute_result"
1024+
}
1025+
],
1026+
"source": [
1027+
"schema8 = dj.Schema('bank8')\n",
1028+
"\n",
1029+
"@schema8\n",
1030+
"class Customer8(dj.Manual):\n",
1031+
" definition = \"\"\"\n",
1032+
" customer_id : int unsigned\n",
1033+
" ---\n",
1034+
" full_name : varchar(30)\n",
1035+
" \"\"\"\n",
1036+
"\n",
1037+
"@schema8\n",
1038+
"class Account8(dj.Manual):\n",
1039+
" definition = \"\"\"\n",
1040+
" account_id : int unsigned\n",
1041+
" ---\n",
1042+
" open_date : date\n",
1043+
" \"\"\"\n",
1044+
"\n",
1045+
"@schema8\n",
1046+
"class CustomerAccount8(dj.Manual):\n",
1047+
" definition = \"\"\"\n",
1048+
" -> Customer8\n",
1049+
" ---\n",
1050+
" -> Account8\n",
1051+
" \"\"\"\n",
1052+
"\n",
1053+
"dj.Diagram(schema8)"
1054+
]
1055+
},
1056+
{
1057+
"cell_type": "markdown",
1058+
"metadata": {},
1059+
"source": [
1060+
"### Explanation of the Design\n",
1061+
"* **Association Table (`CustomerAccount8`)**: The CustomerAccount8 table links Customer8 and Account8 through foreign keys. Although it resembles a many-to-many structure, additional constraints can transform this into a one-to-many relationship.\n",
1062+
"* **One-to-Many Relationship**: By enforcing uniqueness on the foreign key pointing to Customer8, we ensure that each customer is associated with only one account. However, we leave the Account8 foreign key unconstrained, allowing multiple customers to link to the same account, which enables account sharing.\n",
1063+
"\n",
1064+
"## Adding Constraints for Relationship Flexibility\n",
1065+
"To achieve various relationship types using association tables:\n",
1066+
"\n",
1067+
"* One-to-Many: Apply a unique constraint on one foreign key within the association table, while leaving the other unconstrained.\n",
1068+
"* One-to-One: Apply a unique constraint on both foreign keys in the association table, ensuring each pair of entities is uniquely linked.\n",
1069+
"* Many-to-Many: Leave both foreign keys unconstrained, allowing each entity to associate freely with multiple instances of the other.\n",
1070+
"\n",
1071+
"This approach makes association tables a powerful tool for defining relationships of varying cardinality, adding flexibility and adaptability to DataJoint schemas. By managing uniqueness constraints directly in the association table, you can model complex relationships while keeping the primary entities’ structures simple and intuitive."
1072+
]
1073+
},
1074+
{
1075+
"cell_type": "code",
1076+
"execution_count": null,
1077+
"metadata": {},
1078+
"outputs": [],
9561079
"source": []
9571080
}
9581081
],

0 commit comments

Comments
 (0)