|
12 | 12 | "---" |
13 | 13 | ] |
14 | 14 | }, |
| 15 | + { |
| 16 | + "cell_type": "markdown", |
| 17 | + "metadata": {}, |
| 18 | + "source": [ |
| 19 | + "\n", |
| 20 | + "This textbook comes with a Devcontainer that runs a MySQL database server with the following credentials:\n", |
| 21 | + "- database server address: `db`\n", |
| 22 | + "- username: `dev`\n", |
| 23 | + "- password: `devpass`" |
| 24 | + ] |
| 25 | + }, |
| 26 | + { |
| 27 | + "cell_type": "markdown", |
| 28 | + "metadata": {}, |
| 29 | + "source": [ |
| 30 | + "# Connect with SQL \"Magic\" for Jupyter Notebooks\n", |
| 31 | + "\n", |
| 32 | + "You can execute SQL statements directly from Jupyter with the help of SQL \"Jupyter magic\" implemented by the [`sqljupy`](https://ploomber.io/blog/jupysql/) library.\n", |
| 33 | + "\n", |
| 34 | + "\n", |
| 35 | + "The following cell sets up the connection to the database for the Jupyter SQL Magic." |
| 36 | + ] |
| 37 | + }, |
| 38 | + { |
| 39 | + "cell_type": "markdown", |
| 40 | + "metadata": {}, |
| 41 | + "source": [] |
| 42 | + }, |
| 43 | + { |
| 44 | + "cell_type": "code", |
| 45 | + "execution_count": null, |
| 46 | + "metadata": {}, |
| 47 | + "outputs": [ |
| 48 | + { |
| 49 | + "name": "stdout", |
| 50 | + "output_type": "stream", |
| 51 | + "text": [ |
| 52 | + "The sql extension is already loaded. To reload it, use:\n", |
| 53 | + " %reload_ext sql\n" |
| 54 | + ] |
| 55 | + }, |
| 56 | + { |
| 57 | + "data": { |
| 58 | + "text/html": [ |
| 59 | + "<span style=\"None\">Connecting to 'mysql+pymysql://dev:***@db'</span>" |
| 60 | + ], |
| 61 | + "text/plain": [ |
| 62 | + "Connecting to 'mysql+pymysql://dev:***@db'" |
| 63 | + ] |
| 64 | + }, |
| 65 | + "metadata": {}, |
| 66 | + "output_type": "display_data" |
| 67 | + } |
| 68 | + ], |
| 69 | + "source": [ |
| 70 | + "%load_ext sql\n", |
| 71 | + "%sql mysql+pymysql://dev:devpass@db" |
| 72 | + ] |
| 73 | + }, |
15 | 74 | { |
16 | 75 | "cell_type": "markdown", |
17 | 76 | "metadata": {}, |
|
20 | 79 | "\n", |
21 | 80 | "The Devcontainer that comes with this textbook contains a running MySQL server.\n", |
22 | 81 | "\n", |
23 | | - "The root credentials are set in environment variables `DJ_HOST`, `DJ_USER`, and `DJ_PASS`.\n", |
| 82 | + "The user credentials are set in environment variables `DJ_HOST`, `DJ_USER`, and `DJ_PASS`.\n", |
24 | 83 | "These credentials are not secret since this database is not exposed to the external world.\n", |
25 | 84 | "\n", |
26 | 85 | "The DataJoint client library uses these environment variables to connect to the database.\n", |
|
31 | 90 | }, |
32 | 91 | { |
33 | 92 | "cell_type": "code", |
34 | | - "execution_count": 5, |
| 93 | + "execution_count": 1, |
35 | 94 | "metadata": {}, |
36 | 95 | "outputs": [ |
| 96 | + { |
| 97 | + "name": "stderr", |
| 98 | + "output_type": "stream", |
| 99 | + "text": [ |
| 100 | + "[2025-09-16 01:37:46,977][INFO]: DataJoint 0.14.6 connected to dev@db:3306\n" |
| 101 | + ] |
| 102 | + }, |
37 | 103 | { |
38 | 104 | "data": { |
39 | 105 | "text/plain": [ |
40 | | - "DataJoint connection (connected) root@localhost:3306" |
| 106 | + "DataJoint connection (connected) dev@db:3306" |
41 | 107 | ] |
42 | 108 | }, |
43 | | - "execution_count": 5, |
| 109 | + "execution_count": 1, |
44 | 110 | "metadata": {}, |
45 | 111 | "output_type": "execute_result" |
46 | 112 | } |
|
66 | 132 | "If you are reading this text to also learn SQL, you can use two ways to issue queries: with IPython magic commands or a client library." |
67 | 133 | ] |
68 | 134 | }, |
69 | | - { |
70 | | - "cell_type": "markdown", |
71 | | - "metadata": {}, |
72 | | - "source": [ |
73 | | - "If you are only learning DataJoint, you are done.\n", |
74 | | - "If you are reading this text to also learn SQL, you can use two ways to issue queries: with IPython magic commands or a client library." |
75 | | - ] |
76 | | - }, |
77 | | - { |
78 | | - "cell_type": "markdown", |
79 | | - "metadata": {}, |
80 | | - "source": [ |
81 | | - "# Connect with IPython \"Magic\"\n", |
82 | | - "\n", |
83 | | - "You can execute SQL statements directly from Jupyter with the help of [\"magic commdands\"](https://towardsdatascience.com/jupyter-magics-with-sql-921370099589). \n", |
84 | | - "\n", |
85 | | - "The following cell sets up the connection to the database for the Jupyter SQL Magic." |
86 | | - ] |
87 | | - }, |
88 | | - { |
89 | | - "cell_type": "code", |
90 | | - "execution_count": null, |
91 | | - "metadata": {}, |
92 | | - "outputs": [], |
93 | | - "source": [ |
94 | | - "import pymysql\n", |
95 | | - "import os\n", |
96 | | - "pymysql.install_as_MySQLdb()\n", |
97 | | - "\n", |
98 | | - "connection_string = \"mysql://{user}:{password}@{host}\".format(\n", |
99 | | - " user=os.environ['DJ_USER'],\n", |
100 | | - " host=os.environ['DJ_HOST'],\n", |
101 | | - " password=os.environ['DJ_PASS']\n", |
102 | | - ")\n", |
103 | | - "\n", |
104 | | - "%load_ext sql\n", |
105 | | - "%sql $connection_string\n", |
106 | | - "\n", |
107 | | - "%config SqlMagic.style = '_DEPRECATED_DEFAULT' # addresses a bug in the SQL magic extension\n", |
108 | | - "\n" |
109 | | - ] |
110 | | - }, |
111 | 135 | { |
112 | 136 | "cell_type": "markdown", |
113 | 137 | "metadata": {}, |
|
118 | 142 | }, |
119 | 143 | { |
120 | 144 | "cell_type": "code", |
121 | | - "execution_count": 4, |
| 145 | + "execution_count": 3, |
122 | 146 | "metadata": { |
123 | 147 | "vscode": { |
124 | 148 | "languageId": "sql" |
125 | 149 | } |
126 | 150 | }, |
127 | 151 | "outputs": [ |
128 | 152 | { |
129 | | - "name": "stdout", |
130 | | - "output_type": "stream", |
131 | | - "text": [ |
132 | | - " * mysql://root:***@localhost\n", |
133 | | - "5 rows affected.\n" |
134 | | - ] |
| 153 | + "data": { |
| 154 | + "text/html": [ |
| 155 | + "<span style=\"None\">Running query in 'mysql+pymysql://dev:***@db'</span>" |
| 156 | + ], |
| 157 | + "text/plain": [ |
| 158 | + "Running query in 'mysql+pymysql://dev:***@db'" |
| 159 | + ] |
| 160 | + }, |
| 161 | + "metadata": {}, |
| 162 | + "output_type": "display_data" |
135 | 163 | }, |
136 | 164 | { |
137 | | - "ename": "KeyError", |
138 | | - "evalue": "'DEFAULT'", |
139 | | - "output_type": "error", |
140 | | - "traceback": [ |
141 | | - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", |
142 | | - "\u001b[31mKeyError\u001b[39m Traceback (most recent call last)", |
143 | | - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[4]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mget_ipython\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun_cell_magic\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43msql\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m'\u001b[39;49m\u001b[33;43m-- show all users\u001b[39;49m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[33;43mSELECT User FROM mysql.user\u001b[39;49m\u001b[38;5;130;43;01m\\n\u001b[39;49;00m\u001b[33;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n", |
144 | | - "\u001b[36mFile \u001b[39m\u001b[32m/opt/conda/lib/python3.11/site-packages/IPython/core/interactiveshell.py:2565\u001b[39m, in \u001b[36mInteractiveShell.run_cell_magic\u001b[39m\u001b[34m(self, magic_name, line, cell)\u001b[39m\n\u001b[32m 2563\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m.builtin_trap:\n\u001b[32m 2564\u001b[39m args = (magic_arg_s, cell)\n\u001b[32m-> \u001b[39m\u001b[32m2565\u001b[39m result = \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2567\u001b[39m \u001b[38;5;66;03m# The code below prevents the output from being displayed\u001b[39;00m\n\u001b[32m 2568\u001b[39m \u001b[38;5;66;03m# when using magics with decorator @output_can_be_silenced\u001b[39;00m\n\u001b[32m 2569\u001b[39m \u001b[38;5;66;03m# when the last Python token in the expression is a ';'.\u001b[39;00m\n\u001b[32m 2570\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(fn, magic.MAGIC_OUTPUT_CAN_BE_SILENCED, \u001b[38;5;28;01mFalse\u001b[39;00m):\n", |
145 | | - "\u001b[36mFile \u001b[39m\u001b[32m/opt/conda/lib/python3.11/site-packages/sql/magic.py:219\u001b[39m, in \u001b[36mSqlMagic.execute\u001b[39m\u001b[34m(self, line, cell, local_ns)\u001b[39m\n\u001b[32m 216\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m\n\u001b[32m 218\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m219\u001b[39m result = \u001b[43msql\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mconn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparsed\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43msql\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muser_ns\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 221\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[32m 222\u001b[39m result \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 223\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(result, \u001b[38;5;28mstr\u001b[39m)\n\u001b[32m (...)\u001b[39m\u001b[32m 226\u001b[39m \u001b[38;5;66;03m# Instead of returning values, set variables directly in the\u001b[39;00m\n\u001b[32m 227\u001b[39m \u001b[38;5;66;03m# user's namespace. Variable names given by column names\u001b[39;00m\n\u001b[32m 229\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.autopandas:\n", |
146 | | - "\u001b[36mFile \u001b[39m\u001b[32m/opt/conda/lib/python3.11/site-packages/sql/run.py:374\u001b[39m, in \u001b[36mrun\u001b[39m\u001b[34m(conn, sql, config, user_namespace)\u001b[39m\n\u001b[32m 372\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m result \u001b[38;5;129;01mand\u001b[39;00m config.feedback:\n\u001b[32m 373\u001b[39m \u001b[38;5;28mprint\u001b[39m(interpret_rowcount(result.rowcount))\n\u001b[32m--> \u001b[39m\u001b[32m374\u001b[39m resultset = \u001b[43mResultSet\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresult\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 375\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m config.autopandas:\n\u001b[32m 376\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m resultset.DataFrame()\n", |
147 | | - "\u001b[36mFile \u001b[39m\u001b[32m/opt/conda/lib/python3.11/site-packages/sql/run.py:116\u001b[39m, in \u001b[36mResultSet.__init__\u001b[39m\u001b[34m(self, sqlaproxy, config)\u001b[39m\n\u001b[32m 114\u001b[39m \u001b[38;5;28mlist\u001b[39m.\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, sqlaproxy.fetchall())\n\u001b[32m 115\u001b[39m \u001b[38;5;28mself\u001b[39m.field_names = unduplicate_field_names(\u001b[38;5;28mself\u001b[39m.keys)\n\u001b[32m--> \u001b[39m\u001b[32m116\u001b[39m \u001b[38;5;28mself\u001b[39m.pretty = PrettyTable(\u001b[38;5;28mself\u001b[39m.field_names, style=\u001b[43mprettytable\u001b[49m\u001b[43m.\u001b[49m\u001b[34;43m__dict__\u001b[39;49m\u001b[43m[\u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstyle\u001b[49m\u001b[43m.\u001b[49m\u001b[43mupper\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m)\n\u001b[32m 117\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 118\u001b[39m \u001b[38;5;28mlist\u001b[39m.\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, [])\n", |
148 | | - "\u001b[31mKeyError\u001b[39m: 'DEFAULT'" |
149 | | - ] |
| 165 | + "data": { |
| 166 | + "text/html": [ |
| 167 | + "<span style=\"color: green\">6 rows affected.</span>" |
| 168 | + ], |
| 169 | + "text/plain": [ |
| 170 | + "6 rows affected." |
| 171 | + ] |
| 172 | + }, |
| 173 | + "metadata": {}, |
| 174 | + "output_type": "display_data" |
| 175 | + }, |
| 176 | + { |
| 177 | + "data": { |
| 178 | + "text/html": [ |
| 179 | + "<table>\n", |
| 180 | + " <thead>\n", |
| 181 | + " <tr>\n", |
| 182 | + " <th>User</th>\n", |
| 183 | + " </tr>\n", |
| 184 | + " </thead>\n", |
| 185 | + " <tbody>\n", |
| 186 | + " <tr>\n", |
| 187 | + " <td>dev</td>\n", |
| 188 | + " </tr>\n", |
| 189 | + " <tr>\n", |
| 190 | + " <td>root</td>\n", |
| 191 | + " </tr>\n", |
| 192 | + " <tr>\n", |
| 193 | + " <td>mysql.infoschema</td>\n", |
| 194 | + " </tr>\n", |
| 195 | + " <tr>\n", |
| 196 | + " <td>mysql.session</td>\n", |
| 197 | + " </tr>\n", |
| 198 | + " <tr>\n", |
| 199 | + " <td>mysql.sys</td>\n", |
| 200 | + " </tr>\n", |
| 201 | + " <tr>\n", |
| 202 | + " <td>root</td>\n", |
| 203 | + " </tr>\n", |
| 204 | + " </tbody>\n", |
| 205 | + "</table>" |
| 206 | + ], |
| 207 | + "text/plain": [ |
| 208 | + "+------------------+\n", |
| 209 | + "| User |\n", |
| 210 | + "+------------------+\n", |
| 211 | + "| dev |\n", |
| 212 | + "| root |\n", |
| 213 | + "| mysql.infoschema |\n", |
| 214 | + "| mysql.session |\n", |
| 215 | + "| mysql.sys |\n", |
| 216 | + "| root |\n", |
| 217 | + "+------------------+" |
| 218 | + ] |
| 219 | + }, |
| 220 | + "execution_count": 3, |
| 221 | + "metadata": {}, |
| 222 | + "output_type": "execute_result" |
150 | 223 | } |
151 | 224 | ], |
152 | 225 | "source": [ |
|
236 | 309 | "name": "python", |
237 | 310 | "nbconvert_exporter": "python", |
238 | 311 | "pygments_lexer": "ipython3", |
239 | | - "version": "3.11.13" |
| 312 | + "version": "3.13.2" |
240 | 313 | } |
241 | 314 | }, |
242 | 315 | "nbformat": 4, |
|
0 commit comments