tag:blogger.com,1999:blog-3110649280167018029Fri, 02 Jan 2026 20:29:56 +0000Postgresqlcheckconstraintcsv data too long value too long PostgreSQL MariaDB MySQLtransactionvacuumDave's PostgreSQL Stuffhttps://stokerpostgresql.blogspot.com/[email protected] (Dave Stokes)Blogger30125tag:blogger.com,1999:blog-3110649280167018029.post-8998196923082210128Wed, 26 Nov 2025 20:15:00 +00002025-11-26T12:15:05.260-08:00DBeaver's Visual Query Builder<p>&nbsp;Quick! You need to create a complex query, but you don't remember how to join the tables. And you need to get it done ASAP.</p><p>You are in luck if you have an enterprise edition of <a href="http://dbeaver.com" rel="nofollow" target="_blank">DBeaver</a> handy.&nbsp; You can drag and drop the tables and then adjust the clauses as needed without having to dig into the table definitions.</p><p>1. Open the database&nbsp;</p><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8nnjMb-DWYT8Xm8w7cbEQy0Kz51d-w38_mjymHHrsRZ-3aSNr2-o3JppzLGzu3huoEBFQB_UzUb5mdtO9JgtoVRyebvGRUTCS3RiCeMK2o_inj01rspbwDLTNBORVBNReMvoJeOYPSUpNaHrxzpJw5_WwflX3utCc3Jpv3tvjECWe2wbmKcz8dxisKClL/s631/Screenshot%202025-11-26%20132301.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="631" data-original-width="245" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8nnjMb-DWYT8Xm8w7cbEQy0Kz51d-w38_mjymHHrsRZ-3aSNr2-o3JppzLGzu3huoEBFQB_UzUb5mdtO9JgtoVRyebvGRUTCS3RiCeMK2o_inj01rspbwDLTNBORVBNReMvoJeOYPSUpNaHrxzpJw5_WwflX3utCc3Jpv3tvjECWe2wbmKcz8dxisKClL/w155-h400/Screenshot%202025-11-26%20132301.png" width="155" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The PostgreSQL DVD Rental database.&nbsp;</td></tr></tbody></table><p>I am using the PostgreSQL DVDRental dataset for these examples. I have opened the connection and listed the tables.</p>As you can see, the database contains numerous tables.&nbsp;<div><br /></div><div>We are going to build a query visually to get information about the customers.<br /><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p>2. Open a SQL Console&nbsp;<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgMABK1ENxnAyTBiGqcEPIBGHoJ0EJOMLn3p_ynCUvH8ppujE6-JoVagadbuD3GXMr_pSIzn8gHfr5QUjFvFYMXVVI4FzG9FPgwO-iVfyZmLtOntRoA7v5_UPs8bx3KuztDGNewXg3WbZk2thYjkEIpzowmBTB-ZjAFHT4AjrKDC8sNPK2kn9nVoGvn6wH/s532/Screenshot%202025-11-26%20132908.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="140" data-original-width="532" height="84" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgMABK1ENxnAyTBiGqcEPIBGHoJ0EJOMLn3p_ynCUvH8ppujE6-JoVagadbuD3GXMr_pSIzn8gHfr5QUjFvFYMXVVI4FzG9FPgwO-iVfyZmLtOntRoA7v5_UPs8bx3KuztDGNewXg3WbZk2thYjkEIpzowmBTB-ZjAFHT4AjrKDC8sNPK2kn9nVoGvn6wH/s320/Screenshot%202025-11-26%20132908.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Open SQL Console</td></tr></tbody></table><br /></p><p>The second step requires an SQL Console. You will find it under the SQL Editor tab.</p><br /><br /><p><br /></p><p><br /></p><p>3. Select Builder&nbsp;<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU2ix4Bl_k6T5AEP-WMkIz6RxyoZLw10FlB-iy5saB0Xa0aUN1vNbOX5CoQwUlDouoxNn2dcR_EdHVcb2c6VK5JBW76bsAto5y8B3ljT4TqMxFbZVMNKYW69Lz1Rdx6q4rkauEKe5RcYHD1ZVTjLeVpWTYOFahX55UWrH6ozM96KMBkowFFLZJFPc3nxuN/s593/Screenshot%202025-11-26%20133124.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="160" data-original-width="593" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU2ix4Bl_k6T5AEP-WMkIz6RxyoZLw10FlB-iy5saB0Xa0aUN1vNbOX5CoQwUlDouoxNn2dcR_EdHVcb2c6VK5JBW76bsAto5y8B3ljT4TqMxFbZVMNKYW69Lz1Rdx6q4rkauEKe5RcYHD1ZVTjLeVpWTYOFahX55UWrH6ozM96KMBkowFFLZJFPc3nxuN/s320/Screenshot%202025-11-26%20133124.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">On the right side, Select Builder</td></tr></tbody></table><br /></p><p>On the right side of the console pane, tilted 90 degrees, is a tab labeled Builder.&nbsp;</p><p><br /></p><p>4. You are ready to drag and drop tables</p><p>&nbsp;<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6KBwGmZfDaG2Qsmuvwhx8lMna-JgaVuKlPW4el-fYYZYn74DTrwhd0sSy2VhSXll76FL2XBd8ZCbWYVA3f9d8tzNpgoDI6-eqpyHQ8WtsEdQ5N0hCmQPlEe0fyoflfscjUWwMYDkaq9-PUhAybb37HOdb6qGtgVDRPwXZXFR_oFvrSS8ceVl4f2wgwIRE/s981/Screenshot%202025-11-26%20133354.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="565" data-original-width="981" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6KBwGmZfDaG2Qsmuvwhx8lMna-JgaVuKlPW4el-fYYZYn74DTrwhd0sSy2VhSXll76FL2XBd8ZCbWYVA3f9d8tzNpgoDI6-eqpyHQ8WtsEdQ5N0hCmQPlEe0fyoflfscjUWwMYDkaq9-PUhAybb37HOdb6qGtgVDRPwXZXFR_oFvrSS8ceVl4f2wgwIRE/s320/Screenshot%202025-11-26%20133354.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The Query Builder Is Ready to use.&nbsp;</td></tr></tbody></table>You have three new panes. The top one is where you drop the tables.&nbsp; The bottom left is where we tailor the query. The query will then appear in the bottom right pane.</p><p>Now drag the payment, rental, and customer tables from step 1 separately to the top pane, the one that says 'no active query',</p><p><br /></p><p>You can rearrange the tables to create a harmonious relationship map that aligns with your artistic aesthetic.</p><p><br /></p><p>You should have something like this:&nbsp;<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHcYx8TWB2X4QW2mBHQGpIVjY47Fo2GRaJB0gtwf9ryCN79AYNHO3UwV1he79aZghwpNOd2OX3-gmazW2jHV_cHl1n5ZJHnUhoPER5DOnfenDB0cTyEPr6Ceqon7QrR1geRhub8AzhFtsEt4ToDrO8S1MsVmRdxrHLLMtn2JgVN3iAZ8BnzFpLu4w4rEz/s559/Screenshot%202025-11-26%20133907.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="409" data-original-width="559" height="234" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbHcYx8TWB2X4QW2mBHQGpIVjY47Fo2GRaJB0gtwf9ryCN79AYNHO3UwV1he79aZghwpNOd2OX3-gmazW2jHV_cHl1n5ZJHnUhoPER5DOnfenDB0cTyEPr6Ceqon7QrR1geRhub8AzhFtsEt4ToDrO8S1MsVmRdxrHLLMtn2JgVN3iAZ8BnzFpLu4w4rEz/s320/Screenshot%202025-11-26%20133907.png" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The tables are joined and ready for querying.</td></tr></tbody></table><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p>4. The query</p><p>The lower right display will have the query.</p><div style="background-color: white; padding: 0px 0px 0px 2px;"><div style="font-family: Consolas; font-size: 10pt; white-space: pre;"><p style="margin: 0;"><span style="color: maroon; font-weight: bold;">select</span></p><p style="margin: 0;"> *</p><p style="margin: 0;"><span style="color: maroon; font-weight: bold;">from</span></p><p style="margin: 0;"> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">payment</span> <span style="color: #8e00c6; font-style: italic;">p</span></p><p style="margin: 0;"><span style="color: maroon; font-weight: bold;">inner</span> <span style="color: maroon; font-weight: bold;">join</span> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">customer</span> <span style="color: #8e00c6; font-style: italic;">c</span> <span style="color: maroon; font-weight: bold;">on</span></p><p style="margin: 0;"> <span style="color: #8e00c6; font-style: italic;">p</span>.<span style="color: #006464;">customer_id</span> = <span style="color: #8e00c6; font-style: italic;">c</span>.<span style="color: #006464;">customer_id</span></p><p style="margin: 0;"><span style="color: maroon; font-weight: bold;">inner</span> <span style="color: maroon; font-weight: bold;">join</span> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">rental</span> <span style="color: #8e00c6; font-style: italic;">r</span> <span style="color: maroon; font-weight: bold;">on</span></p><p style="margin: 0;"> <span style="color: #8e00c6; font-style: italic;">c</span>.<span style="color: #006464;">customer_id</span> = <span style="color: #8e00c6; font-style: italic;">r</span>.<span style="color: #006464;">customer_id</span></p><p style="margin: 0;"><span style="color: maroon; font-weight: bold;">inner</span> <span style="color: maroon; font-weight: bold;">join</span> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">payment</span> <span style="color: #8e00c6; font-style: italic;">p</span> <span style="color: maroon; font-weight: bold;">on</span></p><p style="margin: 0;"> <span style="color: #8e00c6; font-style: italic;">r</span>.<span style="color: #006464;">rental_id</span> = <span style="color: #8e00c6; font-style: italic;">p</span>.<span style="color: #006464;">rental_id</span></p></div></div><p>Not bad, considering you did not have to look up how to join the table.</p><p><br /></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1fFIF_jcakGfLF9-VHVCYlyBGchfR54-uZG6oniT7cB6EUcg0TbMFS6k_YNtwOw5MdTnIj1lV1PCjMTCX-QQMh7kglq4UMENQqciCsCM4gmrhYcJu7EnQUZUnUPXzA19-N4EQ-thAEi0m29KNRZh7EHpsN8007sWaiAuVocM0cStFFIXwMt7i6p9r8QBB/s732/Screenshot%202025-11-26%20134518.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto; text-align: center;"><img border="0" data-original-height="146" data-original-width="732" height="80" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1fFIF_jcakGfLF9-VHVCYlyBGchfR54-uZG6oniT7cB6EUcg0TbMFS6k_YNtwOw5MdTnIj1lV1PCjMTCX-QQMh7kglq4UMENQqciCsCM4gmrhYcJu7EnQUZUnUPXzA19-N4EQ-thAEi0m29KNRZh7EHpsN8007sWaiAuVocM0cStFFIXwMt7i6p9r8QBB/w400-h80/Screenshot%202025-11-26%20134518.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Refine here</td></tr></tbody></table><p>5. Refine the query&nbsp;</p><p>Now we get to the lower left window.&nbsp; This is where we refine.&nbsp; Select the Where tab on the left and then the icon with the plus sign on the right.&nbsp;<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2m_4Jf9CZOLOxQzL0KM1BslXgqlEAI6D6IrcwpbCwaEjw7iSIuNFCowJTFYONXCCb5YBY_DJwH-Dm83LZQB1cx2cwpfI05C6Q8dAfD3p90GH13Gc8WgtWEeCD-QH5X9tULyUbGwps9prGijpqp39436-6xg56u4JWNWJfVhiF5JKOK67KwLlETcD49weP/s43/Screenshot%202025-11-26%20134530.png" imageanchor="1" style="clear: right; display: inline !important; margin-bottom: 1em; margin-left: 1em; text-align: center;"><img border="0" data-original-height="23" data-original-width="43" height="23" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2m_4Jf9CZOLOxQzL0KM1BslXgqlEAI6D6IrcwpbCwaEjw7iSIuNFCowJTFYONXCCb5YBY_DJwH-Dm83LZQB1cx2cwpfI05C6Q8dAfD3p90GH13Gc8WgtWEeCD-QH5X9tULyUbGwps9prGijpqp39436-6xg56u4JWNWJfVhiF5JKOK67KwLlETcD49weP/s1600/Screenshot%202025-11-26%20134530.png" width="43" /></a></p><p>We want to skip the customers who have not made a payment.&nbsp;<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigqN-AmaASjLdB4W-MmhzE8e6wPC_cOhwr1jCU8nzL979XOe6OVukEBVRCowTObXOwswkb849pBMMnzYUulo9PL1VBQpxohQl60zWpH_jBEGd2g91gJwERPEP4GqYy_wovRzpz1n106x0R6dBLAVlsft61IrbaE-majNvWHOMAcjrmQK0ZVJF53dBVAlea/s989/Screenshot%202025-11-26%20134852.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="214" data-original-width="989" height="138" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigqN-AmaASjLdB4W-MmhzE8e6wPC_cOhwr1jCU8nzL979XOe6OVukEBVRCowTObXOwswkb849pBMMnzYUulo9PL1VBQpxohQl60zWpH_jBEGd2g91gJwERPEP4GqYy_wovRzpz1n106x0R6dBLAVlsft61IrbaE-majNvWHOMAcjrmQK0ZVJF53dBVAlea/w640-h138/Screenshot%202025-11-26%20134852.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Making sure that payment.amount is greater than zero.</td></tr></tbody></table><br /></p><p><br /></p><p><br /><br /></p><p><br /></p><p><br /></p><p>Note that the query is automatically adjusted to match the changes.</p><p><br /></p><p>And we will switch to the sort tab, add an expression that asks for the payments be listed in descending order.&nbsp;<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyPn_gJveWUtZtKGo3cPHjW4EqXptvypYxDpdO9luJvEA0wYOUWcXHwJK8ECD4sYdfNABSmoDvVgsXJHlYWrR92Objj06LRtVQVtLC9BkJaT1ca9R4hFht9OJzIbxElQTxK4OOKwfk3GMu19miH3Zn89fpIYRcjSz5iGU9ecJ82049IlO5nKCYoLKP1BGf/s888/Screenshot%202025-11-26%20135145.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="250" data-original-width="888" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyPn_gJveWUtZtKGo3cPHjW4EqXptvypYxDpdO9luJvEA0wYOUWcXHwJK8ECD4sYdfNABSmoDvVgsXJHlYWrR92Objj06LRtVQVtLC9BkJaT1ca9R4hFht9OJzIbxElQTxK4OOKwfk3GMu19miH3Zn89fpIYRcjSz5iGU9ecJ82049IlO5nKCYoLKP1BGf/w640-h180/Screenshot%202025-11-26%20135145.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Add a sort on the amount column, in descending order.&nbsp;</td></tr></tbody></table><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p>And again, the query is adjusted to match the sorting demand.</p><p>6. The result</p><p>The query is ready to run.</p><div style="background-color: white; padding: 0px 0px 0px 2px;"><div style="font-family: Consolas;"><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">select</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"> *</p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">from</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">payment</span> <span style="color: #8e00c6; font-style: italic;">p1</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">inner</span> <span style="color: maroon; font-weight: bold;">join</span> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">customer</span> <span style="color: #8e00c6; font-style: italic;">c</span> <span style="color: maroon; font-weight: bold;">on</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"> <span style="color: #8e00c6; font-style: italic;">p1</span>.<span style="color: #006464;">customer_id</span> = <span style="color: #8e00c6; font-style: italic;">c</span>.<span style="color: #006464;">customer_id</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">inner</span> <span style="color: maroon; font-weight: bold;">join</span> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">rental</span> <span style="color: #8e00c6; font-style: italic;">r</span> <span style="color: maroon; font-weight: bold;">on</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"> <span style="color: #8e00c6; font-style: italic;">c</span>.<span style="color: #006464;">customer_id</span> = <span style="color: #8e00c6; font-style: italic;">r</span>.<span style="color: #006464;">customer_id</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">inner</span> <span style="color: maroon; font-weight: bold;">join</span> <span style="color: #956037;">public</span>.<span style="color: #8e00c6;">payment</span> <span style="color: #8e00c6; font-style: italic;">p2</span> <span style="color: maroon; font-weight: bold;">on</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"> <span style="color: #8e00c6; font-style: italic;">r</span>.<span style="color: #006464;">rental_id</span> = <span style="color: #8e00c6; font-style: italic;">p2</span>.<span style="color: #006464;">rental_id</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">where</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"> <span style="color: #8e00c6; font-style: italic;">p1</span>.<span style="color: #006464;">amount</span> &gt; <span style="color: blue;">0</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">order</span> <span style="color: maroon; font-weight: bold;">by</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"> <span style="color: #8e00c6; font-style: italic;">p1</span>.<span style="color: #006464;">amount</span> <span style="color: maroon; font-weight: bold;">desc</span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;">And the result.<span style="font-weight: bold;"> <table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1GL9d91VfkxAyoQTai8OM1Zyy0fyVJmkB9JBe0lya8WOdYtp_H5rL-BO_jdNUxj1vaTTn8ZXDh3W7gHkzc2ll04rjxgclILm_B9dvgshG492FMm3V0Aq-g6JRi0yC2_6xqbZktc9HFJpYbg27sPnvEImWfvhyp1l4lBe-Pa1SWa3mm_a_JbDH9t8K21FY/s722/Screenshot%202025-11-26%20135641.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="199" data-original-width="722" height="110" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1GL9d91VfkxAyoQTai8OM1Zyy0fyVJmkB9JBe0lya8WOdYtp_H5rL-BO_jdNUxj1vaTTn8ZXDh3W7gHkzc2ll04rjxgclILm_B9dvgshG492FMm3V0Aq-g6JRi0yC2_6xqbZktc9HFJpYbg27sPnvEImWfvhyp1l4lBe-Pa1SWa3mm_a_JbDH9t8K21FY/w400-h110/Screenshot%202025-11-26%20135641.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The Results</td></tr></tbody></table><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /></p><p style="font-size: 10pt; margin: 0px; white-space: pre;">We can eliminate excess data by removing the SELECT * statement and specifying the desired columns.</p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /></p><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgki27ezybWLHSbULDFjgwDZuRDtil0Z6uAP4JyufrJDFYAP2NEEaoTDPm5itWGbeP6YiVnu3J7cctrfo_8XIpliGoTGcUyjNXGQttb0q6aAB2YpBCjueQDm5rYkPm_9bGww7k8Q_Ae90C7bDDx3M9OnnPQNfn70z2TnDHGXUCYwNHmm5_lC1W4PcBzJv0-/s899/Screenshot%202025-11-26%20140340.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="271" data-original-width="899" height="192" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgki27ezybWLHSbULDFjgwDZuRDtil0Z6uAP4JyufrJDFYAP2NEEaoTDPm5itWGbeP6YiVnu3J7cctrfo_8XIpliGoTGcUyjNXGQttb0q6aAB2YpBCjueQDm5rYkPm_9bGww7k8Q_Ae90C7bDDx3M9OnnPQNfn70z2TnDHGXUCYwNHmm5_lC1W4PcBzJv0-/w640-h192/Screenshot%202025-11-26%20140340.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Specifying specific columns</td></tr></tbody></table><br /><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="margin: 0px;"><br /></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><br /><br /><br /><br /><span style="color: maroon; font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;"><br /></span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;">If you find yourself fighting the syntax of SQL, the Query Builder lets you concentrate on the output and saves you from the frustration. </span></p><p style="font-size: 10pt; margin: 0px; white-space: pre;"><span style="color: maroon; font-weight: bold;"><br /></span></p></div></div><p><br /></p></div>https://stokerpostgresql.blogspot.com/2025/11/dbeavers-visual-query-builder.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-7170630012516917867Thu, 13 Nov 2025 20:19:00 +00002025-11-13T12:19:49.445-08:00csv data too long value too long PostgreSQL MariaDB MySQLCSV Loading Error Fix: Data Too Long Or Value Too Long<p>&nbsp;You are trying to load a CSV file into a database, and almost inevitably, you run into an <i>ERROR: value too long for type character varying 64</i> if you are using PostgreSQL.&nbsp; Or <i>SQL Error [1406] [22001]: Data truncation: Data too long for column 'Name' at row 1 </i>if you are using MariaDB or MySQL.</p><p>It doesn't matter if you are using the <a href="https://dbeaver.io" rel="nofollow" target="_blank">DBeaver</a> application or the native database import tools. Newbies will look at the first line of the CSV file to examine the very first line and scratch their heads, as there is no error in the first line.</p><p>The database is ingesting the data one line at a time, so each line in the file is the first line. Your line in the CSV file is hiding deep in the file. And there may be many of them in there.&nbsp;</p><p>The error message you receive is helpful, but it DOES NOT tell you which line in the CSV file is causing you this grief. You could use an 'eyeball inspection' of the data, which quickly becomes tedious. And if the CSV file has thousands of lines, your odds of finding the offending line grow longer.</p><p>Splitting up the file into smaller chunks is labor-intensive.</p><h3 style="text-align: left;">Example</h3><div>I was loading data from the <a href="https://stokerpostgresql.blogspot.com/2025/10/loading-titanic-passenger-data-into.html" rel="nofollow" target="_blank">HMS Titanic data set</a>&nbsp;when I received the following error:</div><div><br /></div><div><i>Error occurred during batch insert<br />(you can disable batch insert to skip particular rows).</i></div><p><i>Reason:</i></p><p><i>SQL Error [22001]: Batch entry 0 INSERT INTO public.titanic ("PassengerId","Survived","Pclass","Name","Sex","Age","SibSp","Parch","Ticket","Fare","Cabin","Embarked")</i></p><p><span style="white-space: normal;"><i><span style="white-space: pre;"> </span>VALUES (('308'::int8),('1'::int8),('1'::int8),('Penasco y Castellana, Mrs. Victor de Satode (Maria Josefa Perez de Soto y Vallejo)'),('female'),('17'::int8),('1'::int8),('0'::int8),('PC 17758'),('108.9'::real),('C65'),('C')) was aborted: ERROR: value too long for type character varying(64)&nbsp; Call getNextException</i> to see other errors in the batch.</span></p><h3 style="text-align: left;">A Difference Between PostgreSQL and MariaDB/MySQL</h3><p>PostgreSQL will return the offending line in the error message, as shown above. MariaDB and MySQL do not. They provide only <i>SQL Error [1406] [22001]: Data truncation: Data too long for column 'Name' at row 1</i>.</p><p>DBeaver's import tool selected a VARCHAR(64) for the name field after examining some of the records. The unfortunate&nbsp;<i>Penasco y Castellana, Mrs. Victor de Satode (Maria Josefa Perez de Soto y </i>Vallejo needs more space. And there may be other records with names as long, or longer.</p><h3 style="text-align: left;">Solution</h3><p>You can use Google Sheets to examine the CSV file before attempting to load it into the database.&nbsp;</p><p>We can use&nbsp;<b><span style="font-family: helvetica;">=max(arrayformula(len(D1:D891)))</span></b> on the columns with the names to determine the maximum length of a name. The longest name in this data is eighty-two characters long.&nbsp;&nbsp;</p><p>If you read the linked blog post, you will see that I defined the Name column as a TEXT field to solve the issue. But what if you are trying to save space? Knowing the width of that column is valuable.</p><p>So now you have a tool to determine the width of a column when you get the dreaded 'value too long'&nbsp;</p><p><span style="white-space: normal;"><br /></span></p><p><br /></p>https://stokerpostgresql.blogspot.com/2025/11/csv-loading-error-fix-data-too-long-or.html[email protected] (Dave Stokes)1tag:blogger.com,1999:blog-3110649280167018029.post-6503117238891932459Wed, 12 Nov 2025 17:09:00 +00002025-11-12T09:09:29.262-08:00Loading The Titanic Dataset Into PostgreSQL With DBeaver Part 3<p>&nbsp;In the <a href="https://stokerpostgresql.blogspot.com/2025/10/loading-titanic-passenger-data-into_22.html" rel="nofollow" target="_blank">previous</a>&nbsp;post, we asked AI to make recommendations to help clean up the data&nbsp;loaded directly from a CSV file. The initial data load for the Name failed because a VARCHAR(64) was estimated to be insufficient to hold the data. But it was not, and a TEXT field was used instead.&nbsp;</p><p>So now we want to save some disk space!&nbsp;&nbsp;</p><p>The first obvious candidate may seem to be the Name field; a TEXT field can hold up to a gigabyte of data. None of the names is that long. If we switch to VARCHAR, we do not gain space, as both have the exact underlying mechanism in PostgreSQL.&nbsp;</p><h3 style="text-align: left;">PassengerId</h3><p>There are only 891 records, so we don't have to worry about growth. This is a 'fixed' list, as no one else is going to be added to this list, so we can treat the column as having a fixed width. We can ask for the current maximum value of the columns. In the 'real' world, when creating a table, you want your primary key column to be sized to allow for growth, hopefully enormous growth. However, in cases like this, you do not have to leave room for future growth.</p><div style="background-color: white; padding: 0px 0px 0px 2px;"><div style="font-family: Consolas; font-size: 10pt; white-space: pre;"><p style="margin: 0px;"><span style="color: maroon; font-weight: bold;">SELECT</span> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: #006464;">"PassengerId"</span>) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"</span><span style="color: #006464; font-style: italic; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">Passengerid</span><span style="color: #006464; font-style: italic;">"</span>,</p><p style="margin: 0px;"> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: #006464;">"Survived"</span>) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"Survived"</span>,</p><p style="margin: 0px;"> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: #006464;">"</span><span style="color: #006464; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">Pclass</span><span style="color: #006464;">"</span>) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"</span><span style="color: #006464; font-style: italic; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">Pclass</span><span style="color: #006464; font-style: italic;">"</span>,</p><p style="margin: 0px;"> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: navy; font-weight: bold;">LENGTH</span>(<span style="color: #006464;">"Name"</span>)) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"Name"</span>,</p><p style="margin: 0px;"> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: #006464;">"Age"</span>) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"Age"</span>,</p><p style="margin: 0px;"> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: #006464;">"</span><span style="color: #006464; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">Sib</span><span style="color: #006464; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">Sp</span><span style="color: #006464;">"</span>) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"</span><span style="color: #006464; font-style: italic; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">Sib</span><span style="color: #006464; font-style: italic; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">Sp</span><span style="color: #006464; font-style: italic;">"</span>,</p><p style="margin: 0px;"> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: #006464;">"Parch"</span>) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"Parch"</span> ,</p><p style="margin: 0px;"> <span style="color: navy; font-weight: bold;">MAX</span>(<span style="color: #006464;">"Fare"</span>) <span style="color: maroon; font-weight: bold;">as</span> <span style="color: #006464; font-style: italic;">"Fare"</span></p><p style="margin: 0px;"><span style="color: maroon; font-weight: bold;">FROM</span> <span style="color: #8e00c6; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy;">titanic</span><span style="color: red;">;</span></p><p style="margin: 0px;"><span style="color: red;"><br /></span></p></div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgynrEl-7aHnroJrD4FMF4TNqMHeopXa9FmvmeWIPA1FvOafBcTX3wYhCtUKcmK2F6gb5wNXaeRvVIDy-SFCChCZcp0B777ZAS_rk-9rY2rqoDpmmZ_RIMOSKrGbZWeldxivHjPX1Yfbez_oB5XWctmtrCtPlJ6kp7tQ1VG_8HW4BBodNkiZvhA9Am0KAbn/s927/Screenshot%202025-11-10%20163811.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="111" data-original-width="927" height="76" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgynrEl-7aHnroJrD4FMF4TNqMHeopXa9FmvmeWIPA1FvOafBcTX3wYhCtUKcmK2F6gb5wNXaeRvVIDy-SFCChCZcp0B777ZAS_rk-9rY2rqoDpmmZ_RIMOSKrGbZWeldxivHjPX1Yfbez_oB5XWctmtrCtPlJ6kp7tQ1VG_8HW4BBodNkiZvhA9Am0KAbn/w640-h76/Screenshot%202025-11-10%20163811.png" width="640" /></a></div><br /><p><br /></p><p><br /></p><p>We can save space by using a SMALLINT for the PassengerId (reducing it from four bytes to two). And we can shrink other columns too.</p><div style="text-align: left;"><span style="font-size: xx-small;"><span style="font-family: courier;">ALTER TABLE public.titanic2 ALTER COLUMN "PassengerId" TYPE smallint USING "PassengerId"::smallint;<br /></span><span style="font-family: courier;">ALTER TABLE public.titanic2 ALTER COLUMN "Age" TYPE int2 USING "Age"::int2;<br />ALTER TABLE public.titanic2 ALTER COLUMN "Survived" TYPE boolean USING "Survived"::boolean;<br /></span><span style="font-family: courier;">ALTER TABLE public.titanic2 ALTER COLUMN "SibSp" TYPE smallint USING "SibSp"::smallint;<br /></span><span style="font-family: courier;">ALTER TABLE public.titanic2 ALTER COLUMN "Parch" TYPE smallint USING "Parch"::smallint;<br /></span><span style="font-family: courier;">ALTER TABLE public.titanic2 ALTER COLUMN "Embarked" TYPE varchar(1) USING "Embarked"::varchar(1);</span></span></div><div><br /></div><h3 style="text-align: left;">Using pgstattuple</h3><div><br /></div><div>We can use pgstattuple to see the results of this data size diet.</div><div><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLz6xwbWpXBmcNJ_baiSjW3etfIYk9IhFaAmRaobtun-uJkYj3C2OmXzwm6Ad3URUj9qNmslaap2E19ttjDShXQgITa7aqTHshJU7EoYG67h8JqeApmqTngXElKE8BBI57RhlJQkhIJjjpLjIBuD9wnl5HkPjvMIQRuFKzCfYriQW9wDbCfaSCgq9H576o/s1339/Screenshot%202025-11-12%20104553.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="75" data-original-width="1339" height="36" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLz6xwbWpXBmcNJ_baiSjW3etfIYk9IhFaAmRaobtun-uJkYj3C2OmXzwm6Ad3URUj9qNmslaap2E19ttjDShXQgITa7aqTHshJU7EoYG67h8JqeApmqTngXElKE8BBI57RhlJQkhIJjjpLjIBuD9wnl5HkPjvMIQRuFKzCfYriQW9wDbCfaSCgq9H576o/w640-h36/Screenshot%202025-11-12%20104553.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The original 'titanic' table</td></tr></tbody></table><div><br /></div><div><br /></div><div><br /></div><div><br /></div><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_Gs3qmK7bWiHEZSImCEO8a4Nx3TgSWm_BYUQH4kssPDFRnVTXkAcoyEzwDM8u3zWbq-aM1TwWPKYLATa-8_YVp33cYpok2j3JWyCajiJLH_hIYd0TBMGfxWejkpf_EPRm1NWGnwhRKnrqvEPz-UQXZnAri-0du_cLqkqT9Lgb79xMfwu0eqyl7xgI2Yvw/s1341/Screenshot%202025-11-12%20104617.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="95" data-original-width="1341" height="46" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_Gs3qmK7bWiHEZSImCEO8a4Nx3TgSWm_BYUQH4kssPDFRnVTXkAcoyEzwDM8u3zWbq-aM1TwWPKYLATa-8_YVp33cYpok2j3JWyCajiJLH_hIYd0TBMGfxWejkpf_EPRm1NWGnwhRKnrqvEPz-UQXZnAri-0du_cLqkqT9Lgb79xMfwu0eqyl7xgI2Yvw/w640-h46/Screenshot%202025-11-12%20104617.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The revised 'titanic2' table</td></tr></tbody></table><br /><div><br /></div><div><br /></div><div><br /></div><br /><div>The length of the live tuples has shrunk from 90,111 to 83,319 bytes. And free space and free percentage have climbed too. A smaller table is loaded into memory faster, scans more quickly, and requires less space in a backup.</div><div><br /></div>https://stokerpostgresql.blogspot.com/2025/11/loading-titanic-dataset-into-postgresql.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-8113406143485199715Sat, 01 Nov 2025 15:21:00 +00002025-11-01T08:21:43.380-07:00Migration From MySQL To PostgreSQL In Five Steps Using DBeaver<p>&nbsp;I wrote a post in my <a href="https://davesmysqlstuff.blogspot.com/2025/11/migrating-from-mysql-to-postgresql-in.html" rel="nofollow" target="_blank">MySQL blog</a> on migrating from MySQL to PostgreSQL using <a href="https://www.dbeaver.com" rel="nofollow" target="_blank">DBeaver</a>. You can pass it along to your acquaintances who want to get off the Dolphin and on the Elephant.&nbsp;</p><p>Not only will DBeaver move your tables and data, but you can compare them afterwards. In the post, I outline the process in five steps. DBeaver will let you do it in four.&nbsp;</p><p>Please share this blog with your friends to promote PostgreSQL.&nbsp;</p>https://stokerpostgresql.blogspot.com/2025/11/migration-from-mysql-to-postgresql-in.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-8155309511816530988Wed, 22 Oct 2025 12:08:00 +00002025-10-22T05:08:12.383-07:00Loading The Titanic Passenger Data Into PostgreSQL With DBeaver Part 2<p>In the last edition of this blog, the passenger list data from the HMS Titanic was loaded into a preliminary database. Now it is time to refine.</p><p>I am using <a href="https://dbeaver.com" rel="nofollow" target="_blank">DBeaver</a> Enterprise 25.2.0. PostgreSQL 18, and Github Copilot with Gpt-4.&nbsp;</p><p>Prompt: <i style="font-weight: bold;">Any recommendations on improving this table for storage efficiency? </i>This prompt was entered into the DBeaver AI Assistant.</p><div style="background-color: white; padding: 0px;"><div style="font-family: &quot;Segoe UI&quot;; white-space-collapse: preserve;"><p style="font-size: 9pt; margin: 0px;">To improve the storage efficiency of the "titanic" table, we can consider the following recommendations:</p><p style="font-size: 9pt; margin: 0px;"><br /></p><p style="font-size: 9pt; margin: 0px;">### 1. **Optimize Data Types**</p><p style="font-size: 9pt; margin: 0px;"> - **"PassengerId"**: If the number of passengers is not expected to exceed 2,147,483,647, the `int4` type is fine. However, if the range is smaller (e.g., less than 32,767), you could use `int2` (smallint) to save space.</p><p style="font-size: 9pt; margin: 0px;"> - **"Survived"**: Since this is likely a binary value (0 or 1), you can use `boolean` instead of `int4`.</p><p style="font-size: 9pt; margin: 0px;"> - **"Pclass"**: If the class is limited to 1, 2, or 3, you can use `int2` (smallint) instead of `int4`.</p><p style="font-size: 9pt; margin: 0px;"> - **"Sex"**: If this column only contains "male" and "female", consider using an `enum` type or a `char(1)` ('M' or 'F') to save space.</p><p style="font-size: 9pt; margin: 0px;"> - **"Age"**: If precision beyond one decimal place is not required, consider reducing the precision or using `numeric` with a defined scale.</p><p style="font-size: 9pt; margin: 0px;"> - **"SibSp" and "Parch"**: If these values are small integers (e.g., number of siblings/spouses or parents/children), you can use `int2` (smallint).</p><p style="font-size: 9pt; margin: 0px;"> - **"Ticket"**: If ticket numbers are of fixed length, consider using `char(n)` instead of `varchar`.</p><p style="font-size: 9pt; margin: 0px;"> - **"Fare"**: If precision beyond two decimal places is not required, consider using `numeric` with a defined scale (e.g., `numeric(10,2)`).</p><p style="font-size: 9pt; margin: 0px;"> - **"Cabin"**: If cabin identifiers are of fixed length, consider using `char(n)` instead of `varchar`.</p><p style="font-size: 9pt; margin: 0px;"> - **"Embarked"**: If this column only contains a small set of values (e.g., 'C', 'Q', 'S'), consider using an `enum` type or `char(1)`.</p><p style="font-size: 9pt; margin: 0px;"><br /></p><p style="margin: 0px;">Comment: This is what I was looking for. Saving space is always good and these are solid recommendations. </p><p style="margin: 0px;"><br /></p><p style="font-size: 9pt; margin: 0px;">### 2. **Normalize the Table**</p><p style="font-size: 9pt; margin: 0px;"> - If columns like "Sex", "Embarked", or "Pclass" have repetitive values, consider normalizing them into separate lookup tables. For example:</p><p style="font-size: 9pt; margin: 0px;"> - Create a "sex" table with "id" and "value" columns.</p><p style="font-size: 9pt; margin: 0px;"> - Create an "embarked" table with "id" and "value" columns.</p><p style="font-size: 9pt; margin: 0px;"> - Replace the original columns with foreign keys referencing these tables.</p><p style="font-size: 9pt; margin: 0px;"><br /></p><p style="margin: 0px;">Comment: Interesting idea, but will the overhead of an extra lookup to the other tables offset any gains? This suggestion is not getting a 'warm fuzzy' from me.</p><p style="margin: 0px;"><br /></p><p style="font-size: 9pt; margin: 0px;">### 3. **Remove Redundant Columns**</p><p style="font-size: 9pt; margin: 0px;"> - If any columns are not used frequently or can be derived from other columns, consider removing them. For example, if "Pclass" can be inferred from another column, it may not need to be stored.</p><p style="font-size: 9pt; margin: 0px;"><br /></p><p style="margin: 0px;">Comment: This is intriguing. Dropping redundant columns would help, but how do I make the inference to determine if and how Pclass status?</p><p style="margin: 0px;"><br /></p><p style="font-size: 9pt; margin: 0px;">### 4. **Indexing**</p><p style="font-size: 9pt; margin: 0px;"> - Add indexes to columns that are frequently queried, such as "PassengerId", "Survived", or "Embarked". However, keep in mind that indexes increase storage usage, so only index columns that are necessary for performance.</p><p style="font-size: 9pt; margin: 0px;"><br /></p><p style="margin: 0px;">Comment: This is a pretty generic suggestion. </p><p style="margin: 0px;"><br /></p><p style="font-size: 9pt; margin: 0px;">### 5. **Partitioning**</p><p style="font-size: 9pt; margin: 0px;"> - If the table grows significantly, consider partitioning it by a column like "Pclass" or "Embarked" to improve query performance and storage management.</p><p style="font-size: 9pt; margin: 0px;"><br /></p><p style="margin: 0px;">Comment: Hopefully, this dataset is closed. :-) If this was a growing set, or larger, or needed faster lookups, this would be a good consideration.</p><p style="margin: 0px;"><br /></p><p style="font-size: 9pt; margin: 0px;">### 6. **Compression**</p><p style="font-size: 9pt; margin: 0px;"> - Use PostgreSQL's `TOAST` mechanism for compressing large text fields like "Name", "Ticket", and "Cabin". This is automatically handled by PostgreSQL for large text/varchar fields, but you can ensure it is enabled.</p><p style="font-size: 9pt; margin: 0px;"><br /></p><p style="margin: 0px;">Comment: This is a reach. Nothing in a row of this data will trigger TOAST.</p><p style="margin: 0px;"><br /></p><p style="font-size: 9pt; margin: 0px;">### Example of an Optimized Table Definition</p><p style="font-size: 9pt; margin: 0px;">Here’s an example of how the table could look after applying some of these recommendations:</p><p style="font-size: 9pt; margin: 0px;"><br /></p><div style="padding: 0px 0px 0px 2px;"><div style="font-family: Consolas; text-wrap-mode: nowrap;"><p style="font-size: 10pt; margin: 0px;"><span style="color: maroon; font-weight: bold;">CREATE</span> <span style="color: maroon; font-weight: bold;">TABLE</span> <span style="color: #8e00c6;">titanic</span> (</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"PassengerId"</span> <span style="color: navy; font-weight: bold;">int4</span> <span style="color: maroon; font-weight: bold;">PRIMARY</span> <span style="color: maroon; font-weight: bold;">KEY</span>,</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Survived"</span> <span style="color: navy; font-weight: bold;">boolean</span>,</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Pclass"</span> <span style="color: navy; font-weight: bold;">int2</span>,</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Name"</span> <span style="color: navy; font-weight: bold;">text</span>,</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Sex"</span> <span style="color: navy; font-weight: bold;">char</span>(<span style="color: blue;">1</span>),</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Age"</span> <span style="color: navy; font-weight: bold;">numeric</span>(<span style="color: blue;">4</span>,<span style="color: blue;">1</span>),</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"SibSp"</span> <span style="color: navy; font-weight: bold;">int2</span>,</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Parch"</span> <span style="color: navy; font-weight: bold;">int2</span>,</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Ticket"</span> <span style="color: navy; font-weight: bold;">varchar</span>(<span style="color: blue;">20</span>),</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Fare"</span> <span style="color: navy; font-weight: bold;">numeric</span>(<span style="color: blue;">10</span>,<span style="color: blue;">2</span>),</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Cabin"</span> <span style="color: navy; font-weight: bold;">varchar</span>(<span style="color: blue;">10</span>),</p><p style="font-size: 10pt; margin: 0px;"> <span style="color: #006464;">"Embarked"</span> <span style="color: navy; font-weight: bold;">char</span>(<span style="color: blue;">1</span>)</p><p style="font-size: 10pt; margin: 0px;">)<span style="color: red;">;</span></p><p style="font-size: 10pt; margin: 0px;"><span style="color: red;"><br /></span></p><p style="margin: 0px;">Comment: At first glance, this looks good. But do I want Age as a Numeric</p><p style="margin: 0px;">field instead of an Int?</p><p style="margin: 0px;"><br /></p><div style="padding: 0px;"><div style="font-family: &quot;Segoe UI&quot;; text-wrap-mode: wrap;"><p style="font-size: 9pt; margin: 0px;">### Additional Notes</p><p style="font-size: 9pt; margin: 0px;">- If you normalize the table, you will need to create additional tables for "Sex" and "Embarked" and replace their columns with foreign keys.</p><p style="font-size: 9pt; margin: 0px;">- Always test the impact of these changes on your queries and storage before applying them in production.</p><p style="font-size: 9pt; margin: 0px;"><br /></p><h3 style="margin: 0px; text-align: left;">Conclusion</h3><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Github Copilot did what a DBA would do when looking to optimize this data. It looked for columns where data could be saved. It considered partitioning and compression.</p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">AI is a pattern-matching algorithm, and it is unsettling to realize your career was a pattern. </p><p style="margin: 0px;"><br /></p><p style="margin: 0px;">Next time, we will look at making the changes and see how it looks after the iteration.</p></div></div></div></div></div></div>https://stokerpostgresql.blogspot.com/2025/10/loading-titanic-passenger-data-into_22.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-1500723559697961716Wed, 08 Oct 2025 14:13:00 +00002025-10-08T07:13:08.915-07:00Loading The Titanic Passenger Data Into PostgreSQL With DBeaver Part 1<p>The Sinking of the RMS Titanic in 1912 has shocked and entertained for over a century.&nbsp; I will admit to being shocked to find a <a href="https://github.com/datasciencedojo/datasets/blob/master/titanic.csv" rel="nofollow" target="_blank">GitHub Repo</a> with a CSV formatted file with the passenger list. This file is an interesting glimpse into the lives of the folks who sailed on the ill fated ship so long ago. And interesting datasets are always good for examples. I had been looking for a project to show off what I call 'Lifting and Shifting' but others call ETL.&nbsp;</p><p>At the PG NYC 2025 Conference, <a href="https://postgresql.us/events/pgconfus2025/sessions/session/2127-transforming-data-with-the-power-of-postgresql-and-sql/" rel="nofollow" target="_blank">Ryan Booz</a> had a session title&nbsp;Transforming Data with the Power of PostgreSQL and SQL. For many years, ETL was considered as the way to move data from one source to another&nbsp; Ryan argued that it should be ELT, and rather convincingly.&nbsp;</p><h4 style="text-align: left;">ETL?</h4><p>ETL is a three-step data integration process (Extract, Transform, Load) used to collect raw data from various sources, process it into a consistent, usable format, and then load it into a central data warehouse or database for analytics and business intelligence.</p><p>Spreadsheet conversions are a fairly common task and <a href="https:://dbeaver.com" rel="nofollow" target="_blank">DBeaver</a> is a slick way to make the change.&nbsp;</p><h4 style="text-align: left;">ELT?</h4><p>The 'T' is often the tricky part. Databases have a lot of tools to manipulate data. So why put off using the power of the database until the end? What if we loaded the data into a database as an intermediate step and used the power of the database to transform the data into a data final form? This seems like an obviously better practice. So how do we do it?</p><h4 style="text-align: left;">Peek At The Data</h4><p>Using the above link, I down loaded the CSV file of the passenger list. Using the&nbsp;<a href="https://dbeaver.com" rel="nofollow" target="_blank">DBeaver</a> Community Edition, I used<span style="font-family: courier;"> File -&gt; Open</span> to access the titanic.csv file. This was done to investigate the file only and does not actually do anything to import the data.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJTrV3oRykcqxCytOx4pBOSzC3coB7nNGcX_5K3BF9CuRQ6OyE-rvwh5DSOIZ1ZsohdYtASMqNjOITeN_Q9__Nq8RTUy8B5X9V8XCZA3_nONez5lhwJ3WpkY748SB7Z8z03tJgB1f7ji1KSuORD06isYPSZpWEa-ICSv2gqYcea2Ey-RjDWEY6PTzGpZIM/s1059/Screenshot%202025-10-07%20161222.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="581" data-original-width="1059" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJTrV3oRykcqxCytOx4pBOSzC3coB7nNGcX_5K3BF9CuRQ6OyE-rvwh5DSOIZ1ZsohdYtASMqNjOITeN_Q9__Nq8RTUy8B5X9V8XCZA3_nONez5lhwJ3WpkY748SB7Z8z03tJgB1f7ji1KSuORD06isYPSZpWEa-ICSv2gqYcea2Ey-RjDWEY6PTzGpZIM/w640-h352/Screenshot%202025-10-07%20161222.png" width="640" /></a></div><br /><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p>The data can be viewed at this point. A quick review shows nothing out of the ordinary. Foreshadowing - there was something wrong.&nbsp; I like to perform this step just to make sure there is nothing obviously wrong with the data - bad encoding, weird line feeds, sudden changes in character sets, and the like. There maybe formatting issues (JSON, XML, SGML) or other anomalies. I would rather find them at this step than later discovering the data was wrong from the start.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJWodweNGbtMHpL0CrxpKp_zcwKi2G-JikE7ZCWjCJw1fqVyV4l7TIE_yiahyEnKUwVWJLZOluEsDE4-FRG_eM5nVFG3YrgVKKNlWxK_bZCQObReDuY4gL07o7gw_EZtc6x8rBAs7ixx9ogRKA3KOCrwGsLPzZagLlZBCJW6Ycq7O71IpAS-O5Wh7SffLG/s1139/Screenshot%202025-10-07%20161410.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="318" data-original-width="1139" height="178" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJWodweNGbtMHpL0CrxpKp_zcwKi2G-JikE7ZCWjCJw1fqVyV4l7TIE_yiahyEnKUwVWJLZOluEsDE4-FRG_eM5nVFG3YrgVKKNlWxK_bZCQObReDuY4gL07o7gw_EZtc6x8rBAs7ixx9ogRKA3KOCrwGsLPzZagLlZBCJW6Ycq7O71IpAS-O5Wh7SffLG/w640-h178/Screenshot%202025-10-07%20161410.png" width="640" /></a></div><br /><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><h4 style="text-align: left;">The E</h4><p>The next step is to import the data to a new PostgreSQL 18 instance. I connected to the instance. Next,&nbsp; Right click on the tables and select Import Data.</p><p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU_hd-JIpm7OFiovQy6O1R0bn78vGl3PPM4jyJ5XspjkepW3uPvg0djgMQ_rj6w31KlkzhGcH-3lF0wDj_0uGmcFuKE5vMkFeQB84ImLc_md3C7i5rMjARQHC8qzKPvmV58o6Ie62y2TQV2U9f2zdx0jci3vTI4LNUD9IeNr8Z5zya5e4_Epyi3PiZzp18/s782/Screenshot%202025-10-07%20162726.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="782" data-original-width="645" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU_hd-JIpm7OFiovQy6O1R0bn78vGl3PPM4jyJ5XspjkepW3uPvg0djgMQ_rj6w31KlkzhGcH-3lF0wDj_0uGmcFuKE5vMkFeQB84ImLc_md3C7i5rMjARQHC8qzKPvmV58o6Ie62y2TQV2U9f2zdx0jci3vTI4LNUD9IeNr8Z5zya5e4_Epyi3PiZzp18/w330-h400/Screenshot%202025-10-07%20162726.png" width="330" /></a></p><p>And then we need to specify the Import source as a CSV file. DBeaver can use a database table, XML, XLXS file. And yes you can move from Database X to Database Y too,&nbsp;</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAfL7Ushm2P3aZ07U2xlVFLb64-wu8nrf_TzInmP61gVW-QOcaCplCeFxVu7riSMym4eNXKmE3QaJdkQBTadF2YKP5zQQcZ3HdMJWnWifwXPGpSMl_ecG2V3ebJjjTjJa9KAyHQwd1FeiKqZ6oZAGrc7-ljVrbto9PkOiUj-nSUB85Iw2LddyR8ROL0Ic3/s1428/Screenshot%202025-10-07%20162759.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="928" data-original-width="1428" height="416" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAfL7Ushm2P3aZ07U2xlVFLb64-wu8nrf_TzInmP61gVW-QOcaCplCeFxVu7riSMym4eNXKmE3QaJdkQBTadF2YKP5zQQcZ3HdMJWnWifwXPGpSMl_ecG2V3ebJjjTjJa9KAyHQwd1FeiKqZ6oZAGrc7-ljVrbto9PkOiUj-nSUB85Iw2LddyR8ROL0Ic3/w640-h416/Screenshot%202025-10-07%20162759.png" width="640" /></a></div><br /><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p>Now select<span style="font-family: courier;">&nbsp;Input File</span>.</p><p>We browse for the titanic.csv file. At this point we can change the importer settings, but the default values were perfectly fine in this case. Often, you have have to change delimiters from single to double quotes, or end of line characters. Life is simpler if you select the proper character set so you do not lose data.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMSBHklWtErQRkQ4deumhv0wcX7eocltl5H1wzgDM-GsCyXV88eBrJBXG0jAX6o3oNbiERbfb3PPfF73CO40WbZg18gs9cf08BkdeYIUctYjb_o9vWJq3lgJzkJNDcQil8BRcYgGH0PKWgElCaVxIrmwrvZ_8-caitoM77xc3oDRmcpY8GS7zFZGaAUB44/s1431/Screenshot%202025-10-07%20162851.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="922" data-original-width="1431" height="412" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMSBHklWtErQRkQ4deumhv0wcX7eocltl5H1wzgDM-GsCyXV88eBrJBXG0jAX6o3oNbiERbfb3PPfF73CO40WbZg18gs9cf08BkdeYIUctYjb_o9vWJq3lgJzkJNDcQil8BRcYgGH0PKWgElCaVxIrmwrvZ_8-caitoM77xc3oDRmcpY8GS7zFZGaAUB44/w640-h412/Screenshot%202025-10-07%20162851.png" width="640" /></a></div><br /><p><br /></p><p>Select Next and then Preview Data.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI9pfXUqk9ikD9n51x9x5W91a9N2jOIkP2oXiysoehw9P4V6sIdRywVBe5cMhnhYlD4NyC5sZN5EGEncFKSY3GWE7kesloKHIoNSFhf-z_RQwcR8P_919h-sCVNbwo_jdB2Y04PSx5ZaUEBQxTDW-ZnfVjFto5xMfpzjFV650B112qaq-yI9kLsd8OAVAu/s1451/Screenshot%202025-10-07%20162949.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="938" data-original-width="1451" height="414" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI9pfXUqk9ikD9n51x9x5W91a9N2jOIkP2oXiysoehw9P4V6sIdRywVBe5cMhnhYlD4NyC5sZN5EGEncFKSY3GWE7kesloKHIoNSFhf-z_RQwcR8P_919h-sCVNbwo_jdB2Y04PSx5ZaUEBQxTDW-ZnfVjFto5xMfpzjFV650B112qaq-yI9kLsd8OAVAu/w640-h414/Screenshot%202025-10-07%20162949.png" width="640" /></a></div><br /><p>We can customize the input settings to change the data types or change the column names. again, the defaults are always a good first choice.</p><p>&nbsp;The preview looks good, at least at this point.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHHmp-YblsUbSfMbISCHwSKhJ5bfnIvtAtqxWUkhRzfXrPhGtFnL_F46D5BGcj36aUZUPd10hqo1qfPWGRIsfnDWDTElw4rbPlqEnzRQVbEDQvfOKlHXW649vbI3C4sZmWDxUA60chTq95wj-safJuzorXSQjNtoGnRQ6byPkUE5RpujzJUITOovZyxlsW/s984/Screenshot%202025-10-07%20163002.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="851" data-original-width="984" height="554" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHHmp-YblsUbSfMbISCHwSKhJ5bfnIvtAtqxWUkhRzfXrPhGtFnL_F46D5BGcj36aUZUPd10hqo1qfPWGRIsfnDWDTElw4rbPlqEnzRQVbEDQvfOKlHXW649vbI3C4sZmWDxUA60chTq95wj-safJuzorXSQjNtoGnRQ6byPkUE5RpujzJUITOovZyxlsW/w640-h554/Screenshot%202025-10-07%20163002.png" width="640" /></a></div><br /><p>The settings for loading that data into the database can now be set. I went with the defaults but you may want to bulk load the data, log queries, or even truncate a table from a previous attempt.</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkXEVn7RoATZU6lVCu7TwgoerltuKx0ERlOj0VEhz40Zan7JhjGHlQHLwic7joyOtpGUon7DrR2zPuutk06-nSQhJX0cPLgg3iYTryutJjhOAJstiICq8yvWJu86xCUVM9X4kNIhXZKRZM5ekmEFl3mS9NGy3ixTTF9yQMsJGsSgwsOe-UnnKqQEJqg7uo/s1451/Screenshot%202025-10-07%20163105.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="938" data-original-width="1451" height="414" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkXEVn7RoATZU6lVCu7TwgoerltuKx0ERlOj0VEhz40Zan7JhjGHlQHLwic7joyOtpGUon7DrR2zPuutk06-nSQhJX0cPLgg3iYTryutJjhOAJstiICq8yvWJu86xCUVM9X4kNIhXZKRZM5ekmEFl3mS9NGy3ixTTF9yQMsJGsSgwsOe-UnnKqQEJqg7uo/w640-h414/Screenshot%202025-10-07%20163105.png" width="640" /></a></div><br /><p>And we 'Proceed'. At least until we hit an error.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbzV89ksh9D6Br48mh_M1NX2o4oZhFj8wsVIpWpWIBq4D8J96eh2sZ2oAmy1V6Zks5kKN7PLEOiuamw5G_VEju28jnNU1EZ3NjoqR4Rh_AY11wjW6WEwGCdQnfxwupD6B-3DWf8y1NH-p2cqFcKew1OaFRle8T7dFsv8D7BIHl883I61rjWuxg0nr1zEhN/s1920/Screenshot%202025-10-07%20163239.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1920" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbzV89ksh9D6Br48mh_M1NX2o4oZhFj8wsVIpWpWIBq4D8J96eh2sZ2oAmy1V6Zks5kKN7PLEOiuamw5G_VEju28jnNU1EZ3NjoqR4Rh_AY11wjW6WEwGCdQnfxwupD6B-3DWf8y1NH-p2cqFcKew1OaFRle8T7dFsv8D7BIHl883I61rjWuxg0nr1zEhN/w640-h400/Screenshot%202025-10-07%20163239.png" width="640" /></a></div><br /><p>Okay, so it is time to realize we have an issue. This is fairly common in importing data that one (or more than one) records does not comply with what we thought was the layout. We have a name that can not be used with our varchar(64) specification. At this point, I want ALL the data. So I backed out of the conversion and restarted.</p><p>This time I changed the length of the Name column. The heuristics thought that a varchar(64) would suffice but I changed it to a text.&nbsp; PostgreSQL has an amazingly rich set of data types, but your database of choice may not - and you need to check. This is a simple case with one row that does not comply.&nbsp; But often the case is that you have a CSV with many rows that are non-complaint. And the non-complaint rows need to be corrected or the target rows need to be adjusted.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKPVoMFl8WHaiIsXJDD-F3SNwIQZXLMgWN0cYLqjQsgqs5WZxXLyzm0o8bBtaPavIxJxHN9rhbwcYhwUXlI4OTIEBybq3pkjEbAATZEmimqQCYXeMW3VFJrNBga4PIhO_qASfm2NcbMlnaR2qIL-LFK5LNK2jMPUS1IA7oRdyRH8FbZHApD0nLHg9gdmod/s1146/Screenshot%202025-10-07%20163432.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="887" data-original-width="1146" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKPVoMFl8WHaiIsXJDD-F3SNwIQZXLMgWN0cYLqjQsgqs5WZxXLyzm0o8bBtaPavIxJxHN9rhbwcYhwUXlI4OTIEBybq3pkjEbAATZEmimqQCYXeMW3VFJrNBga4PIhO_qASfm2NcbMlnaR2qIL-LFK5LNK2jMPUS1IA7oRdyRH8FbZHApD0nLHg9gdmod/w640-h496/Screenshot%202025-10-07%20163432.png" width="640" /></a></div><br /><p>&nbsp;Is using TEXT wasteful? Maybe if I had counted the maximum length of the name column used that number for a varchar things would have been better. The trade off is trying to be perfect on the first conversion versus adjust as needed. Remember this is an intermediate stage and we can trim later, if needed.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGNtJ5n7JIcTt54u6gDcADzdR2dtucg51gf1eIqPN9hCd3R7ztzqk661le5T-NWRExCJE45SAWwKaElbXioFiTCb56IbX3e9fjc8o_r0wXB1Kg8afss_EjTjUgYlROvQb8H3DaLIFdmSZ3XKusfJqegmyhPwltAwHNWCKrqCnv2bZ8U4Ma6SoGuR9D1KCy/s1146/Screenshot%202025-10-07%20163509.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="887" data-original-width="1146" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGNtJ5n7JIcTt54u6gDcADzdR2dtucg51gf1eIqPN9hCd3R7ztzqk661le5T-NWRExCJE45SAWwKaElbXioFiTCb56IbX3e9fjc8o_r0wXB1Kg8afss_EjTjUgYlROvQb8H3DaLIFdmSZ3XKusfJqegmyhPwltAwHNWCKrqCnv2bZ8U4Ma6SoGuR9D1KCy/w640-h496/Screenshot%202025-10-07%20163509.png" width="640" /></a></div><br /><p>Rerun the dialogs and now we have all of our data in a table.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCw6KGSO_OQ5DC-oTS-2cgk3azb5dvJZ1cWPo1Lrd97GenkQGRvHk_6epTASKxnONiJA9eCCB9E53aYVCyG3HXadlbYZNasxH9XJG6VatiRZdOX-TM046NMKhqTbRwy75-qbXwWHBFPJ82twQnd1gz0jqm7NDt8g9vzNVPJ_oj4fabRyePno9zeRBRL-b1/s1278/Screenshot%202025-10-07%20163603.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="757" data-original-width="1278" height="380" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCw6KGSO_OQ5DC-oTS-2cgk3azb5dvJZ1cWPo1Lrd97GenkQGRvHk_6epTASKxnONiJA9eCCB9E53aYVCyG3HXadlbYZNasxH9XJG6VatiRZdOX-TM046NMKhqTbRwy75-qbXwWHBFPJ82twQnd1gz0jqm7NDt8g9vzNVPJ_oj4fabRyePno9zeRBRL-b1/w640-h380/Screenshot%202025-10-07%20163603.png" width="640" /></a></div><br /><p><br /></p><p>This is the end of part one. We now have the data from a CSV file loaded into a temporary database. Next comes a serious look at the data to see what we can do to save space, add value to the, or improve the usability.&nbsp;</p><p>Do we want to keep 'female' and 'male' or using a binary representation. Would it help to have separate first and last name columns?&nbsp; Do we expand the Embarked data. Do we know what all the columns represent? All that will be in Part 2.&nbsp;</p><br /><br />https://stokerpostgresql.blogspot.com/2025/10/loading-titanic-passenger-data-into.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-4662841134335147584Tue, 30 Sep 2025 12:25:00 +00002025-09-30T05:25:09.502-07:00PostgreSQL 18 Old & New<p>&nbsp;Learning Structured Query Language can be frustrating when double-checking that what you wanted to have done is actually&nbsp;what was done. PostgreSQL 18 has 'OLD and NEW support for RETURNING clauses in INSERT, UPDATE, DELETE, and MERGE commands'. Now you can get instant feedback.&nbsp;</p><p>The addition of the RETURNING clause in the previous version made MERGE much easier to use. Now it makes other commands easier.</p><p>To demonstrate, let's create a table with one column that is designated as a unique, primary key integer and insert a value.</p><div style="background-color: white; padding: 0px 0px 0px 2px;"><div style="font-family: Consolas; font-size: 10pt; white-space: pre;"><p style="margin: 0;"><span style="color: maroon; font-weight: bold;">create</span> <span style="color: maroon; font-weight: bold;">table</span> <span style="color: #8e00c6;">foo</span> (<span style="color: #006464;">a</span> <span style="color: navy; font-weight: bold;">int</span> <span style="color: maroon; font-weight: bold;">unique</span> <span style="color: maroon; font-weight: bold;">primary</span> <span style="color: maroon; font-weight: bold;">key</span>)<span style="color: red;">;</span></p><p style="margin: 0;"><br /></p><p style="margin: 0;"><span style="color: maroon; font-weight: bold;">insert</span> <span style="color: maroon; font-weight: bold;">into</span> <span style="color: #8e00c6;">foo</span> (<span style="color: #006464;">a</span>) <span style="color: maroon; font-weight: bold;">values</span> (<span style="color: blue;">1</span>)<span style="color: red;">;</span></p></div></div><p>Now is the point where many of us would run a <span style="font-family: courier;"><b>SELECT a FROM foo</b></span>, just to double check that indeed there is a 1 in column a.</p><p>There is now an option in PG 18 to use RETURNING, and it provides the previous 'old' value of a column along with the 'new' value.</p><p><span style="background-color: white; padding: 0px 0px 0px 2px;"><span style="font-family: Consolas; font-size: 10pt; white-space: pre;"><span style="color: maroon; font-weight: bold;">insert</span> <span style="color: maroon; font-weight: bold;">into</span> <span style="color: #8e00c6;">foo</span> (<span style="color: #006464;">a</span>) <span style="color: maroon; font-weight: bold;">values</span> (<span style="color: blue;">2</span>) <span style="color: maroon; font-weight: bold;">returning</span> <span style="color: maroon; font-weight: bold;">old</span>.a, <span style="color: maroon; font-weight: bold;">new</span>.a<span style="color: red;">;</span></span></span></p><div style="text-align: left;"><span style="font-family: courier;"><b><span style="white-space: pre;">a|a|<br /></span><span style="white-space: pre;">-+-+<br /></span><span style="white-space: pre;">&nbsp;|2|</span></b></span></div><div style="text-align: left;"><span style="font-family: courier;"><span style="color: red;"><span style="white-space: pre;"><br /></span></span></span></div><div style="text-align: left;"><span style="white-space: pre;"><span style="font-family: inherit;">The 'old' value was a NULL, and the new value is 2. </span></span></div><div style="text-align: left;"><span style="white-space: pre;"><span style="font-family: inherit;"><br /></span></span></div><div style="text-align: left;"><span style="white-space: pre;">I suggest adding an alias to make things more<span style="font-family: inherit;"> explicit.</span></span></div><div style="text-align: left;"><span style="font-size: 13.3333px; white-space: pre;"><span style="font-family: inherit;"><br /></span></span></div><div style="text-align: left;"><span style="background-color: white; padding: 0px 0px 0px 2px;"><span style="font-family: Consolas; font-size: 10pt; white-space: pre;"><span style="color: maroon; font-weight: bold;">insert</span> <span style="color: maroon; font-weight: bold;">into</span> <span style="color: #8e00c6;">foo</span> (<span style="color: #006464;">a</span>) <span style="color: maroon; font-weight: bold;">values</span> (<span style="color: blue;">3</span>) <span style="color: maroon; font-weight: bold;">returning</span> <span style="color: maroon; font-weight: bold;">old</span>.a <span style="color: maroon; font-weight: bold;">as</span> <span style="color: navy; font-weight: bold;">"Old"</span>, <span style="color: maroon; font-weight: bold;">new</span>.a <span style="color: maroon; font-weight: bold;">as</span> <span style="color: navy; font-weight: bold;">"New"</span><span style="color: red;">;</span></span></span></div><div style="text-align: left;"><span style="background-color: white; padding: 0px 0px 0px 2px;"><span style="font-family: Consolas; font-size: 10pt; white-space: pre;"><div>Old|New|</div><div>---+---+</div><div>&nbsp; &nbsp;|&nbsp; 3|</div><div style="color: red;"><br /></div><h4 style="text-align: left;">Upserts</h4><div>As I mentioned earlier, sometimes doubt creeps into your mind when you enter or update data.</div><div>RETURNING also works with UPSERTS (Thanks to the brilliant Magnus Hagander, who pointed this out out in</div><div>his presentation at the PG NYC 2025 show and his slides are available <a href="https://postgresql.us/events/pgconfus2025/schedule/session/2095-a-look-at-the-elephants-trunk-postgresql-18/" rel="nofollow" target="_blank">here</a>). </div><div><br /></div><div>In the following example, we have a conflict because there is already a '2' in the table, so the</div><div>ON CONFLICT path is taken. Using RETURNING in this case takes away any doubt of what happened when</div><div>this query was executed.</div><div><br /></div></span></span></div><div style="background-color: white; padding: 0px 0px 0px 2px;"><div style="font-family: Consolas; font-size: 10pt; white-space: pre;"><p style="margin: 0px;"><span style="color: maroon; font-weight: bold;">insert</span> <span style="color: maroon; font-weight: bold;">into</span> <span style="color: #8e00c6;">foo</span>(<span style="color: #006464;">a</span>) <span style="color: maroon; font-weight: bold;">values</span> (<span style="color: blue;">2</span>)</p><p style="margin: 0px;"><span style="color: maroon; font-weight: bold;">on</span> conflict(a) do <span style="color: maroon; font-weight: bold;">update</span> </p><p style="margin: 0px;"> <span style="color: maroon; font-weight: bold;">set</span> a = <span style="color: blue;">99</span></p><p style="margin: 0px;">returning <span style="color: maroon; font-weight: bold;">old</span>.a <span style="color: maroon; font-weight: bold;">as</span> <span style="color: maroon; font-weight: bold;">old</span>, <span style="color: maroon; font-weight: bold;">new</span>.a <span style="color: maroon; font-weight: bold;">as</span> <span style="color: maroon; font-weight: bold;">update</span></p></div></div><br /><div><div>old|update|</div><div>---+------+</div><div>&nbsp; 2|&nbsp; &nbsp; 99|</div></div><div><br /></div>https://stokerpostgresql.blogspot.com/2025/09/postgresql-18-old-new.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-777256401655979260Sun, 28 Sep 2025 12:58:00 +00002025-09-28T05:58:58.962-07:00PostgreSQL 18 Release Notes<p>&nbsp;The <a href="https://www.postgresql.org/docs/current/release-18.html" rel="nofollow" target="_blank">PostgreSQL 18 Release Notes</a> are like a great noir novel, full of surprises and intrigue. But we know who did it - the amazing PostgreSQL community.</p><p>If you have not perused the Notes, I advise you to do so now. They contain many of what I call 'Wow!' level items, such as the redesigned I/O subsystem, skip scan lookups, virtual generated columns, and more.&nbsp; The new Uuidv7 function and temporal constraints would be a significant leap forward. The OLD and NEW support for RETURNING will help lessen the learning curve for many who are new to SQL.</p><p>But go down to the seemingly more mundane items. Removing redundant self-joins, merge-joins can now use incremental sorts, and internally reordering the keys of SELECT DISTINCT to avoid sorting are the types of improvements that are not as attention-getting as the items in the preceding paragraph, but will make life much nicer for us.&nbsp;</p><p>Each release of PostgreSQL is faster than the previous one, and early reports report that this has happened again. This is an astounding delivery for those of us with backgrounds in other databases. All too often, new features came with a trade-off in performance that relegated upgrades to the back burner.</p><p>Please read the Acknowledgments. This is a list of the good folks who have worked very hard on our behalf to deliver an improved version of PostgreSQL.&nbsp;And they are already working on 19! If you run into any of them, please give them a big 'thank you'.&nbsp;&nbsp;</p>https://stokerpostgresql.blogspot.com/2025/09/postgresql-18-release-notes.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-3446252185718993986Wed, 06 Aug 2025 19:43:00 +00002025-08-06T12:43:22.588-07:00Book Review - Mastering PostgreSQL 17<p>&nbsp;<span>&nbsp; &nbsp; Authors find it challenging to write a comprehensive book about PostgreSQL for several reasons. The first is that a new version of PostgreSQL is released every year with changes, new features, and tweaks.&nbsp; I am lucky to have been able to review several new database titles each year, and I would like to let you know that Hans-Jurgen Schonig's <a href="https://www.amazon.com/Mastering-PostgreSQL-deployment-optimization-strategies/dp/183620597X" rel="nofollow" target="_blank">Mastering PostgreSQL</a> is a well-written reference.&nbsp;</span></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIN0VoaHw7scKuEuFtrkM02awY510a5ar-zk28pZxjhTAmeM1x2tgaYCGV60saylEqaqG6TcFjn4CbFtOMLDTlbvCbIhvWYzPNOtRf8ETMyyTzGstg55pHRVMDQZbv-8F9bDdfs7EZiItpGosBd2INZxQB1na9WlQCkLMP9CD1Cowt45pOxXxzcmMyoxPZ/s804/Screenshot%202025-08-06%20140658.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="804" data-original-width="632" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIN0VoaHw7scKuEuFtrkM02awY510a5ar-zk28pZxjhTAmeM1x2tgaYCGV60saylEqaqG6TcFjn4CbFtOMLDTlbvCbIhvWYzPNOtRf8ETMyyTzGstg55pHRVMDQZbv-8F9bDdfs7EZiItpGosBd2INZxQB1na9WlQCkLMP9CD1Cowt45pOxXxzcmMyoxPZ/s320/Screenshot%202025-08-06%20140658.png" width="252" /></a></div><p></p><p>The first chapter of this book covers changes in 17. The explanations and examples are clear, concise, and easy to comprehend.&nbsp;For instance, the CPY command is convenient, and in 17, it can handle errors. The book's example of this new ability quickly shows how it works—short and sweet!&nbsp;</p><p>From there, it covers transactions and locking, indexing, advanced SQL log files, system statistics, optimizing queries (very well done here), how to write stored procedures, security, backups, replication (also very well done), extensions, and troubleshooting.&nbsp; I will have a longer review later this month, covering the outstanding parts of the book that you will need to pay strict attention to..</p><p>I highly recommend this to those new to administering PostgreSQL systems or looking to enhance their skills. For instance, the authors start the chapter on replication with a summary of the Write Ahead Log. Understanding the WAL is not only key to transactions but also essential to proper replication. The material on Point In Time Recovery and handling conflicts needs to be studied closely before you have to try it for the first time in real life.&nbsp;&nbsp;</p><p><br /></p>https://stokerpostgresql.blogspot.com/2025/08/book-review-mastering-postgresql-17.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-9125393573535842028Fri, 01 Aug 2025 14:45:00 +00002025-08-01T08:04:19.825-07:00Data Security and AI - Sharing Your PostgreSQL Database With Ollama AI<div>You probably saw the <span style="color: #0000ee;"><u>story about your public ChatGPT queries being indexed by Google and other search engines</u></span>. Yikes!&nbsp;</div><div><br /></div><div>You may want AI with your data but do not wish your prompts to end up in a search engine, especially if you are working with sensitive data.<span>&nbsp;&nbsp;</span></div><div><br /></div><div><a href="https://stokerpostgresql.blogspot.com/2025/07/postgresql-ollama-and-dbeaver-ai.html" rel="nofollow" target="_blank">Previously</a>, we covered using some of DBeaver's Artificial Intelligence Assistant features with <a href="https://ollama.com" rel="nofollow" target="_blank">Ollama</a>. Ollama is popular because it runs on your system, keeping your data from travelling over networks to an AI and back. What happens only on your system stays on your system, to paraphrase the Las Vegas motto. Paranoia in defense of your data is a good thing.</div><div><br /></div><h3 style="text-align: left;">Secure By Default</h3><div><br /></div><div>We will start with an example of preference settings for DBeaver Enterprise Edition for Ollama. DBeaver has other security options I want to bring to your attention when working with an AI. Ollama differs from most other AIs because you do not have to obtain a&nbsp;key to work with it.</div><div><br /></div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwvVNC3z7w-SEE99i4flOal3e9wWGLS1zt2RJg8bUcXv9DFnxnzGrUvryx3Jq11HftJ4xxRZ9WRgrHbZ7aGHZfZdoqN_HMqQDYMr06GwP1-ypFY7y3_sMGyATZROsgyR_ogx9fxnYnAv_pQvb1EgownyuIAgiHT6MnqHWEyJqfcSwLRx6ew0VhJWHlGrSA/s696/Screenshot%202025-07-31%20083141.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="540" data-original-width="696" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwvVNC3z7w-SEE99i4flOal3e9wWGLS1zt2RJg8bUcXv9DFnxnzGrUvryx3Jq11HftJ4xxRZ9WRgrHbZ7aGHZfZdoqN_HMqQDYMr06GwP1-ypFY7y3_sMGyATZROsgyR_ogx9fxnYnAv_pQvb1EgownyuIAgiHT6MnqHWEyJqfcSwLRx6ew0VhJWHlGrSA/w640-h496/Screenshot%202025-07-31%20083141.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">AI Configuration - DBeaver Enterprise Edition</td></tr></tbody></table><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The ability to tailor what you send to the AI should be your next step. You can also configure what to share with the AI.&nbsp;The default is to share only the metadata—tables, column names, and Views—but not the data. Optionally, sharing information about keys and object descriptions can improve the AI's job, at the expense of extra token consumption.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTWwe8ZcUAbRs_KTRkoFWSu5oBx81LPJ2kLIWnkvjjUhDIvBdTthmNl_cZyZ4yE8uMT6KeZW4xXbiEnxq0kSASyirBFOKDSUnNTcghnuqZ4V8hqPNuhf4frvlZabdI8LY3xk6J4_Otzgi_zAvZVMhtAiBWS0HqsZOUoSR1qw5C5ao1A_Rz5753Gh7Yrayo/s709/Screenshot%202025-07-31%20083159.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="539" data-original-width="709" height="486" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTWwe8ZcUAbRs_KTRkoFWSu5oBx81LPJ2kLIWnkvjjUhDIvBdTthmNl_cZyZ4yE8uMT6KeZW4xXbiEnxq0kSASyirBFOKDSUnNTcghnuqZ4V8hqPNuhf4frvlZabdI8LY3xk6J4_Otzgi_zAvZVMhtAiBWS0HqsZOUoSR1qw5C5ao1A_Rz5753Gh7Yrayo/w640-h486/Screenshot%202025-07-31%20083159.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Send database structure options in this example, including foreign, unique, and primary keys.</td></tr></tbody></table><div class="separator" style="clear: both; text-align: center;"><br /><br /><div style="text-align: left;">Note that you can send table data to the AI to help it develop a better solution. This is the only option, and you can make the choice. If you do not want to share, you do not have to. The query will also run only on the local machine.&nbsp;</div></div><h3 style="clear: both; text-align: left;">Setting The Scope</h3><div class="separator" style="clear: both; text-align: center;"><br /><table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfy9oo5NPTxWepuedv-nyE-ODATodz-MWjcX3IdyPElyZ8uX8I061qHRvRz8GzoOZ05NxSTHvXTi2Ygi-SMToK3nZ0iPxRzzU0jRAHdt2q6d49VOknfOQdaNDfe1VafTcX4VI-hrkW0HrSIgM5QnUvhZUt67-2fwCsChk7f_s25UI3up2lJRRFnMsxzqFc/s528/Screenshot%202025-07-31%20083230.png" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><span style="color: black;"><img border="0" data-original-height="181" data-original-width="528" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfy9oo5NPTxWepuedv-nyE-ODATodz-MWjcX3IdyPElyZ8uX8I061qHRvRz8GzoOZ05NxSTHvXTi2Ygi-SMToK3nZ0iPxRzzU0jRAHdt2q6d49VOknfOQdaNDfe1VafTcX4VI-hrkW0HrSIgM5QnUvhZUt67-2fwCsChk7f_s25UI3up2lJRRFnMsxzqFc/w640-h221/Screenshot%202025-07-31%20083230.png" width="640" /></span></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The AI Assistant can direct the scope of the data to be used.</td></tr></tbody></table><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">You can define the scope of database objects that the AI should consider for generating SQL queries and providing assistance. This scope enables the AI to focus on relevant parts of the database, thereby improving accuracy and efficiency, particularly in large schemas. This is particularly handy when you have two similar schemas or databases, and only one of them is intended for the work.&nbsp;</div><div class="separator" style="clear: both; text-align: center;"><br /></div><h3 style="clear: both; text-align: left;">Memory</h3><div class="separator" style="clear: both; text-align: left;">Ollama will not impress you with its performance until you have 32GB of memory. My original attempts with 16GB were slow and disappointing.&nbsp;Remember that you are running Ollama and DBeaver at a minimum&nbsp;and maybe a local instance, all of which use memory. I suggest you not attempt using Ollama until you have at least 32GB.&nbsp;</div><div class="separator" style="clear: both; text-align: left;"><br /></div><h3 style="clear: both; text-align: left;">Example</h3><div class="separator" style="clear: both; text-align: left;">We ask Ollama to create a table on a local PostgreSQL instance</div><div class="separator" style="clear: both; text-align: center;"><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwWVlR0tdH8QmBbzrmByF7oQZ3Jx5E1CjYH-VYNpO2qVFnymXfQi6IiFghXakUDg_mWgRrXJF7mASUM7U7FMz4sjq8Fve4rjMpDEUdIKEN19myEkAo5vBsBBMaJ6Z24uebGo-dt3E93ntULymfdaNd2gkEyZ2GCo2KWnosbYTtOLWnicprEiN01dTVkLcf/s508/Screenshot%202025-08-01%20093244.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="205" data-original-width="508" height="258" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwWVlR0tdH8QmBbzrmByF7oQZ3Jx5E1CjYH-VYNpO2qVFnymXfQi6IiFghXakUDg_mWgRrXJF7mASUM7U7FMz4sjq8Fve4rjMpDEUdIKEN19myEkAo5vBsBBMaJ6Z24uebGo-dt3E93ntULymfdaNd2gkEyZ2GCo2KWnosbYTtOLWnicprEiN01dTVkLcf/w640-h258/Screenshot%202025-08-01%20093244.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">We ask Ollama to create a contact list table</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMAujWcW_b3o1UsmQP7rxjSslsx5kWxiWpkAh_g0vlKexammpNV7npX6I-UW2IQiJIsH82aZon-MGtcqA7CG-tmlocfYyiEfcqC3DR0tvbFjBbO9tRRIEM3jQ6VPBIDX3lBYgRyECfb8sY2CAfJ1IwvunWyV87XhpbvKyUErQSh95Q1MeQZb4R0gq5jkUa/s490/Screenshot%202025-08-01%20093307.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="440" data-original-width="490" height="574" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMAujWcW_b3o1UsmQP7rxjSslsx5kWxiWpkAh_g0vlKexammpNV7npX6I-UW2IQiJIsH82aZon-MGtcqA7CG-tmlocfYyiEfcqC3DR0tvbFjBbO9tRRIEM3jQ6VPBIDX3lBYgRyECfb8sY2CAfJ1IwvunWyV87XhpbvKyUErQSh95Q1MeQZb4R0gq5jkUa/w640-h574/Screenshot%202025-08-01%20093307.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Ollama responds with the DDL for the requested table</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5mR_c1PxnqUDoHpv0ti7Bff6cQ3W-XRnBwsuRf4NUO9lVc8_8yBEZ6gaLOfG41-lLMq7YyHNQRhitnUL7xirVepgt_vQ1p7pPWxiWduX8cOeuVXX9vLLGBaG7TcETI0KrlG5z9dfWJoxbtUdf5xkgGlqVO-OXMa6wbgyFuFSuaSHzAujWcbRqS0TMHqar/s483/Screenshot%202025-08-01%20093527.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="329" data-original-width="483" height="436" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5mR_c1PxnqUDoHpv0ti7Bff6cQ3W-XRnBwsuRf4NUO9lVc8_8yBEZ6gaLOfG41-lLMq7YyHNQRhitnUL7xirVepgt_vQ1p7pPWxiWduX8cOeuVXX9vLLGBaG7TcETI0KrlG5z9dfWJoxbtUdf5xkgGlqVO-OXMa6wbgyFuFSuaSHzAujWcbRqS0TMHqar/w640-h436/Screenshot%202025-08-01%20093527.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Now we request some test data</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwZ6BilZvB49ygUYpASrB60kBwME5RHp5lpHTCd2-A3_HAlCkc6VPcF5YL5ulv47VVofFK_eji_DI7prfxDltKnx1d3W4Wu9A4Qapw0o_Auo3czJYsZsYp3IT6gw1svNccgOi9GwwiR-4Vpw08wgs1i3SyXrD7bdopUoaoUtHdXKhNVHeN3AmmUCnKUaEv/s719/Screenshot%202025-08-01%20093631.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="260" data-original-width="719" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwZ6BilZvB49ygUYpASrB60kBwME5RHp5lpHTCd2-A3_HAlCkc6VPcF5YL5ulv47VVofFK_eji_DI7prfxDltKnx1d3W4Wu9A4Qapw0o_Auo3czJYsZsYp3IT6gw1svNccgOi9GwwiR-4Vpw08wgs1i3SyXrD7bdopUoaoUtHdXKhNVHeN3AmmUCnKUaEv/w640-h232/Screenshot%202025-08-01%20093631.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">The table and data from Ollama<br /><br /><br /><h3 style="text-align: left;">Conclusion</h3><div><br /></div><div style="text-align: left;">DBeaver allows you to use an AI to work without worrying that your prompts will end up on a search engine.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div></td></tr></tbody></table><br /><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><br /><div><br /></div>https://stokerpostgresql.blogspot.com/2025/08/data-security-and-ai-sharing-your.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-5176300312504932865Mon, 28 Jul 2025 15:01:00 +00002025-07-28T08:01:14.918-07:00PostgreSQL, Ollama, and the DBeaver AI Assistant <p><a href="https://ollama.com" rel="nofollow" target="_blank">Ollama</a> is an open-source project that simplifies running large language models (LLMs). It runs locally on your machine, and you can choose from multiple LLMs. Keeping all the data on your machine should provide a security bonus.&nbsp; &nbsp;</p><p><a href="https://dbeaver.com" rel="nofollow" target="_blank">DBeaver</a> is a universal database tool with an AI Assistant. This assistant provides an extra layer of security by allowing you to lock down what is shared, with the default being&nbsp;metadata only, no data.</p><h3 style="text-align: left;">Webinar</h3><p>I recently presented a <a href="https://www.youtube.com/watch?v=9t2RozoJgvQ" rel="nofollow" target="_blank">webinar</a>&nbsp;on using the DBeaver AI Assistant. Several questions were raised about using Ollama, one of the many AI Engines available. But I had not tried it.</p><p>The first step is downloading Ollama for your platform (Linux, Windows, or Mac) and installing.&nbsp; Once you have Ollama installed, you will need to pick a LLM.&nbsp; I chose the Gemma3 and installed it using the command line client.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTmhvVL7DGMWnK47udO5g7qLiGhMPAjTJnbPVzv1pB-GSL-N6sGsoLYiduT6r2oruFlM9t9qH6ohWFOk_-MuF-gDKAQJhOr2MI1O-1mvrs8kAUsIbj7lLEPcwn1Xi1SB023oKyuBgyRC_BxHxvYzlPKL69_UDIsUnNmnf6c6K78VWIiT8yRxrRiDl5f3Pl/s909/Screenshot%202025-07-28%20085419.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="439" data-original-width="909" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTmhvVL7DGMWnK47udO5g7qLiGhMPAjTJnbPVzv1pB-GSL-N6sGsoLYiduT6r2oruFlM9t9qH6ohWFOk_-MuF-gDKAQJhOr2MI1O-1mvrs8kAUsIbj7lLEPcwn1Xi1SB023oKyuBgyRC_BxHxvYzlPKL69_UDIsUnNmnf6c6K78VWIiT8yRxrRiDl5f3Pl/w640-h310/Screenshot%202025-07-28%20085419.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Adding the Gemma3 LLM to Ollama</td></tr></tbody></table><p><br /></p><h3 style="text-align: left;">DBeaver Configuration</h3><p>Ollama listens on port 11434. I was able to load the Gemma3 model using the 'Load Model' button.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOqH9EgQH7ByNtOLn6ownYcIjgxKfeBW9ow-1kt2DlG37L4yvDpLSrs8zFJ3dhlsn5XY15j7KBYQikB_PWo7l9-JWK5Kxu4WeIr6Gzq1cOeOUmMzukefL0T6ZHuebw05QdnZWteae5ArDwKNTqzGaKWAMDcIwXLz9dliz_1LFSep_ebDHaUp8RBbGaBoo8/s508/Screenshot%202025-07-28%20085716.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="312" data-original-width="508" height="394" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOqH9EgQH7ByNtOLn6ownYcIjgxKfeBW9ow-1kt2DlG37L4yvDpLSrs8zFJ3dhlsn5XY15j7KBYQikB_PWo7l9-JWK5Kxu4WeIr6Gzq1cOeOUmMzukefL0T6ZHuebw05QdnZWteae5ArDwKNTqzGaKWAMDcIwXLz9dliz_1LFSep_ebDHaUp8RBbGaBoo8/w640-h394/Screenshot%202025-07-28%20085716.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Sample Ollama Settings for Dbeaver EE</td></tr></tbody></table><br /><p><br /></p><h3 style="text-align: left;">Example 1</h3><p>I started with a simple address book type schema and asked about one last name -&nbsp;<span style="background-color: white; color: purple; font-family: Consolas; font-size: 10pt; font-weight: bold; white-space: pre;">@ai</span><span style="background-color: white; font-family: Consolas; font-size: 10pt; white-space: pre;"> who in the address_book has a name last name of </span><span style="background-color: white; font-family: Consolas; font-size: 10pt; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy; white-space: pre;">Jones?</span></p><p><span style="background-color: white; font-family: Consolas; font-size: 10pt; text-decoration-color: rgb(160, 160, 160); text-decoration-line: underline; text-decoration-style: wavy; white-space: pre;"><br /></span></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2DOiCLiu1M5VlnFNwdt8VC3SbzNqMdM94lmvfDauivSIS6RJovlr5WEn3LkfCNlv40XhTPMIy1al607RLZXlhcD-Pz8gfqS6pIbssbdXy4MWnw6JoVt-cl3lmJxKA2VWzXdUSx-QSvupBiAbPwf_xvG7CcBGwdMssaj7GgvNJZP4so09lQ0yZwv-MoN21/s872/Screenshot%202025-07-28%20093619.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="252" data-original-width="872" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2DOiCLiu1M5VlnFNwdt8VC3SbzNqMdM94lmvfDauivSIS6RJovlr5WEn3LkfCNlv40XhTPMIy1al607RLZXlhcD-Pz8gfqS6pIbssbdXy4MWnw6JoVt-cl3lmJxKA2VWzXdUSx-QSvupBiAbPwf_xvG7CcBGwdMssaj7GgvNJZP4so09lQ0yZwv-MoN21/w640-h184/Screenshot%202025-07-28%20093619.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Finding the Jones</td></tr></tbody></table><br /><h3 style="text-align: left;">Example 2</h3><p><br /></p><p>The nest test was more of a conversation.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh30IxJvm187bU-FyOlRjOFuhbJt_NV6RtfH1A4anHPkTNl0lJGlPpH3ax8KtAalG2J2Ea5_ZF30P25eqZ9bEYXfS7oUdubY_kfcE6_3LF3EupRy_euuhIYt5gj5PQ-bRehRwSIHgx1U14n8rTYof1QWlNyTq8bisqiUoW-vPv1ONBlyqptVmTSzx4JkfUM/s531/Screenshot%202025-07-28%20095555.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="494" data-original-width="531" height="596" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh30IxJvm187bU-FyOlRjOFuhbJt_NV6RtfH1A4anHPkTNl0lJGlPpH3ax8KtAalG2J2Ea5_ZF30P25eqZ9bEYXfS7oUdubY_kfcE6_3LF3EupRy_euuhIYt5gj5PQ-bRehRwSIHgx1U14n8rTYof1QWlNyTq8bisqiUoW-vPv1ONBlyqptVmTSzx4JkfUM/w640-h596/Screenshot%202025-07-28%20095555.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Using the AI Assistant in DBeaver to converse with Ollama</td></tr></tbody></table><br /><p>And the results:</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh70JXhYzTp9vGfd01jQ0LXFbPQ0TJTtbDDfcXeFzQOD86IeORxs2xAbHTrKa8adZPj269KOHda7C1yGTztlGpCuiQoasB9larWVDZMIN8FtQzF4VNTUyVoZqGjpf16gxvAxB9zYH9XyN4HUboghLrNynySvbaA6gIt02uDgoxlm-puBOQKOHAXbZq9EBj/s316/Screenshot%202025-07-28%20095535.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="316" data-original-width="249" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh70JXhYzTp9vGfd01jQ0LXFbPQ0TJTtbDDfcXeFzQOD86IeORxs2xAbHTrKa8adZPj269KOHda7C1yGTztlGpCuiQoasB9larWVDZMIN8FtQzF4VNTUyVoZqGjpf16gxvAxB9zYH9XyN4HUboghLrNynySvbaA6gIt02uDgoxlm-puBOQKOHAXbZq9EBj/w504-h640/Screenshot%202025-07-28%20095535.png" width="504" /></a></div><br /><h3 style="text-align: left;">Summary</h3><p><br /></p><p>DBeaver works well with Ollama.&nbsp; To me, Ollama is many times slower than the paid AI engines. Now my laptop is not a superpower GPU-laden machine.&nbsp; And maybe the Gemma3 engine is not the optimum for database work.&nbsp; But it does work and is a good place for starting the exploration of your data with an AI.</p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p><p><br /></p>https://stokerpostgresql.blogspot.com/2025/07/postgresql-ollama-and-dbeaver-ai.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-5177123153507105112Thu, 24 Jul 2025 13:00:00 +00002025-07-24T06:00:45.260-07:00Writing Queries In English to Create An Address Book On A PostgreSQL Instance<p>&nbsp;Structured Query Language, or SQL, is my favorite programming language, but its quirky syntax can sometimes be frustrating. Many have professed a need to bypass SQL and query databases in English (another quirky language. Is it possible to have an Artificial Intelligence take a request in English and translate it into SQL on my behalf?</p><p>I am using the AI Chat Feature of <a href="https://dbeaver.com/dbeaver-enterprise/" rel="nofollow" target="_blank">DBeaver Enterprise</a> to test.&nbsp;</p><h4 style="text-align: left;">The Prompt&nbsp;</h4><p>My prompt was&nbsp;<i>Create a table in this postgresql database to hold address book information and then add ten example records from random data</i> to OpenAI.&nbsp;</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibza9Z7ercdMga39_W4tfnjT_UPEawNhKX2Et5_s8Ej9ltUu_IBcMAe_AsFpb2_05raH8XYwusuqCIeNyLDXxPsf-p4WGnYSs7NwRV9NSn6wQZlc2mTGaPZ1J79rJCLDYrsc5W0shF8XawTpi_XlFslU-f7_9YWPSQMQxRTi4fNsaNAiJ1p9O98YSJJW0s/s617/Screenshot%202025-07-24%20072524.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="AI Dialog" border="0" data-original-height="617" data-original-width="545" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibza9Z7ercdMga39_W4tfnjT_UPEawNhKX2Et5_s8Ej9ltUu_IBcMAe_AsFpb2_05raH8XYwusuqCIeNyLDXxPsf-p4WGnYSs7NwRV9NSn6wQZlc2mTGaPZ1J79rJCLDYrsc5W0shF8XawTpi_XlFslU-f7_9YWPSQMQxRTi4fNsaNAiJ1p9O98YSJJW0s/w354-h400/Screenshot%202025-07-24%20072524.png" title="Asking an AI to create an address book table and populate it with test data." width="354" /></a></div><br /><p></p><h4 style="text-align: left;">The Response</h4><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Sure, let's create a table named `address_book` with the following fields: `id`, `first_name`, `last_name`, `email`, `phone_number`, and `address`.</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><br /></span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">CREATE</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">TABLE</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: #8e00c6; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">address_book</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&nbsp;&nbsp;&nbsp;</span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">id</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> serial </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">PRIMARY</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">KEY</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">,</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&nbsp;&nbsp;&nbsp;</span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">first_name</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">varchar</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: blue; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">50</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&nbsp;&nbsp;&nbsp;</span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">last_name</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">varchar</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: blue; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">50</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&nbsp;&nbsp;&nbsp;</span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">email</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">varchar</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: blue; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">100</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&nbsp;&nbsp;&nbsp;</span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">phone_number</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">varchar</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: blue; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">15</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">&nbsp;&nbsp;&nbsp;</span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">address</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> text</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="background-color: white; color: red; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">;</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Now, let's insert ten example records into the `address_book` table. Please note that the data is randomly generated and does not represent real individuals. </span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">INSERT</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">INTO</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> </span><span style="background-color: white; color: #8e00c6; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">address_book</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"> (</span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">first_name</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">last_name</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">email</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">phone_number</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: #006464; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">address</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">) </span><span style="background-color: white; color: maroon; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 700; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">VALUES</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'John'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Doe'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'123-456-7890'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'123 Main St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jane'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Doe'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'234-567-8901'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'456 Maple St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jim'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Smith'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'345-678-9012'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'789 Oak St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jill'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Smith'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'456-789-0123'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'012 Pine St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Joe'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Johnson'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'567-890-1234'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'345 Birch St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jenny'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Johnson'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'678-901-2345'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'678 Cedar St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jack'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Williams'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'789-012-3456'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'901 Elm St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jill'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Williams'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'890-123-4567'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'234 Spruce St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jerry'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Brown'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'901-234-5678'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'567 Redwood St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">),</span></p><p dir="ltr" style="background-color: white; line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">(</span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Jasmine'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'Brown'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'[email protected]'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'012-345-6789'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">, </span><span style="background-color: white; color: green; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">'890 Willow St, Anytown, USA'</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">)</span><span style="background-color: white; color: red; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">;</span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Please replace the example data with your actual data when you use this in a production environment.</span></p><p><b style="font-weight: normal;"><br /></b></p><h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt; text-align: left;"><span style="background-color: transparent; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-size: small;">The Result</span></span></h3><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">So, I ran a quick </span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><b>SELECT * FROM address_book</b> </span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">1</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">John</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Doe</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">123-456-7890</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">123 Main St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">2</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jane</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Doe</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">234-567-8901</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">456 Maple St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">3</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jim</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Smith</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">345-678-9012</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">789 Oak St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">4</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jill</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Smith</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">456-789-0123</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">012 Pine St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">5</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Joe</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Johnson</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">567-890-1234</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">345 Birch St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">6</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jenny</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Johnson</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">678-901-2345</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">678 Cedar St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">7</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jack</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Williams</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">789-012-3456</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">901 Elm St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">8</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jill</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Williams</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">890-123-4567</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">234 Spruce St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">9</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jerry</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Brown</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">901-234-5678</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">567 Redwood St, Anytown, USA</span></span></p><p dir="ltr" style="line-height: 1.38; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-size: x-small;"><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">10</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Jasmine</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">Brown</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">[email protected]</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">012-345-6789</span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span class="Apple-tab-span" style="white-space: pre;"> </span></span><span style="background-color: white; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; font-weight: 400; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;">890 Willow St, Anytown, USA</span></span></p><p><b style="font-weight: normal;">Yup, I got exactly what I asked for. The ONLY SQL I used was to query the data after the AI did the 'heavy lifting'.</b></p><h3 style="line-height: 1.38; margin-bottom: 6pt; margin-top: 18pt; text-align: left;"><span style="background-color: transparent; color: black; font-family: Arial, sans-serif; font-style: normal; font-variant: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap;"><span style="font-size: small;">Analysis</span></span></h3><p>I could quibble that the table structure was not efficient, with the <i>address</i> column defined as TEXT. This would restrict searches by postal code, city, or state. That could easily be fixed by asking the AI in a second round to break out those fields.&nbsp;</p><p><br /></p><p>Overall, this was a very quick way to create a table and fill it with test data. So, it is possible to have an Artificial Intelligence take a request in English and translate it into SQL on my behalf.</p>https://stokerpostgresql.blogspot.com/2025/07/writing-queries-in-english-to-create.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-6489303020844086693Wed, 23 Jul 2025 13:53:00 +00002025-07-25T05:03:39.743-07:00Write PostgreSQL Queries in English? <p>&nbsp;If you've ever wanted to skip SQL and its quirks and write queries in English, I am happy to report that you can do it with an AI of your choice and DBeaver. I had a <a href="https://www.youtube.com/watch?v=9t2RozoJgvQ" rel="nofollow" target="_blank">webinar</a> on July 24th that shows you how to do just that.&nbsp;&nbsp;</p><p>I will show you how to set up <a href="https://DBeaver.com" target="_blank">DBeaver</a>&nbsp;using a wide variety of commercial AIs to let you work with an AI. We will also go through the security features to ensure your data is safe. By default, only the schema metadata is shared with the AI. You can lock down access to just one or more tables being available to the AI.<br /><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCxRWBwRloli7g2p4a6Yi29BzJkn2gRuZrAIOiveQFe14gmxAnq2_OVUhaNqshTZ3xnxC8-jyjISxZ8EKZgymQtK6aB3HOP_UQZbw8jwxz7TNcYzoKxjLRO_dH95bUT_aIszx_LKpEhn0ecmxAnsG2jiDczrKYLmoIWkekRK7_PL3uHRCAxhlSo6LhabFw/s433/Screenshot%202025-07-23%20084548.png" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="241" data-original-width="433" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCxRWBwRloli7g2p4a6Yi29BzJkn2gRuZrAIOiveQFe14gmxAnq2_OVUhaNqshTZ3xnxC8-jyjISxZ8EKZgymQtK6aB3HOP_UQZbw8jwxz7TNcYzoKxjLRO_dH95bUT_aIszx_LKpEhn0ecmxAnsG2jiDczrKYLmoIWkekRK7_PL3uHRCAxhlSo6LhabFw/w400-h223/Screenshot%202025-07-23%20084548.png" width="400" /></a></div>Imagine not having to translate a data request from management speak to SQL! Here we simply ask an AI to provide the five most popular films by revenue and get an answer.&nbsp;<br /><p><br /></p><p>I will also show you how an AI can help by explaining and fixing query problems. You can even identify how that 'thrown over a wall' query works.</p><p>Have you ever been handed a strange database and have no idea of its content? Then, use an AI to identify its uses.&nbsp;</p><p>If you have been looking for a tool to help you with your database, then you need to try DBeaver's AI Assistant.&nbsp;</p><p><a href="https://www.youtube.com/watch?v=9t2RozoJgvQ" rel="nofollow" target="_blank">Watch here</a></p><br />https://stokerpostgresql.blogspot.com/2025/07/write-postgresql-queries-in-english.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-1504249572009171688Wed, 25 Jun 2025 13:26:00 +00002025-06-25T06:48:14.275-07:00Entity Relationship Maps<p><span>&nbsp;&nbsp; &nbsp;</span>Even the most experienced database professionals are known to feel a little anxious when peering into an unfamiliar database. Hopefully, they inspect to see how the data is normalized and how the various tables are combined to answer complex queries.&nbsp; Entity Relationship Maps (ERM) provide a visual overview of how tables are related and can document the structure of the data.</p><p><span>&nbsp; &nbsp; The Community Edition of <a href="https://dbeaver.com" rel="nofollow" target="_blank">DBeaver</a> can provide an ERM easily.&nbsp; First, connect to the database. Right-click on the 'Tables' and then select 'View Diagram'.</span><img height="499" src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdjvOcdKV9SE5MvW0njnxvdbh2G6H592-ytZJmIvHboI7VfAJbQVCf3UrvI918zkyPjYwQTRRJOYoqBLrlkIj30RNkKUrvAndvwmAFmz4E0iqj_SelNmahVs80lCKilntI-iTtG6g=w640-h499?key=KSs6knRH_W8A12Akb5zAaA" style="font-family: Arial, sans-serif; font-size: 11pt; margin-left: 0px; margin-top: 0px; white-space-collapse: preserve;" width="640" /></p><p><span><br /></span></p><p>The ERM is then displayed.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOqY-6jFJaJ3Bgkv3KfWdZxfIp35CMclwvlCtDrZK1sjbPsTdixjMHZUMRUt1YJxUiQ81w3IzSIZIe8PW5kLh8sQXF1x9X4dDQPtgKm2H7rF-3UFTCHvjLe7ijSWAI6Ku5KYTiGrpJYtaleMj7Q1_7vbMhM17n5crJsGJkBgZxmA0ZBYYMzX8vOrlUpCCT/s1511/dvdrental%20-%20public.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1511" data-original-width="1266" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOqY-6jFJaJ3Bgkv3KfWdZxfIp35CMclwvlCtDrZK1sjbPsTdixjMHZUMRUt1YJxUiQ81w3IzSIZIe8PW5kLh8sQXF1x9X4dDQPtgKm2H7rF-3UFTCHvjLe7ijSWAI6Ku5KYTiGrpJYtaleMj7Q1_7vbMhM17n5crJsGJkBgZxmA0ZBYYMzX8vOrlUpCCT/w536-h640/dvdrental%20-%20public.png" width="536" /></a></div><br /><p><br /></p><p>And it gets better! If you are not sure how two tables are joined? Click on the link between the tables, and the names of the columns you will want to use in your JOIN statement are highlighted.</p><p><br /></p><p><span id="docs-internal-guid-622f2f11-7fff-449a-8db6-724874984369"><span face="Arial, sans-serif" style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"><span style="border: none; display: inline-block; height: 367px; overflow: hidden; width: 475px;"><img height="367" src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeunUeRhzIn7WMgZX55dz8n90vGPjFpLyHfYmD8xvyoxs5asaQEGqGAoLhRWFV3iW2SFUB-A7jm9HjYDy09FRrxMQQXRcNhSTczry8ko_wT-BlI3lZWN60bZvIz8NNx2pMBJwYF?key=KSs6knRH_W8A12Akb5zAaA" style="margin-left: 0px; margin-top: 0px;" width="475" /></span></span></span></p><p><br /></p><h3 style="text-align: left;">Conclusion</h3><p><span>&nbsp; &nbsp; Exploring an unfamiliar database can be daunting. But Entity Relationship Maps provide a way to navigate the territory. And <a href="https://dbeaver.com" rel="nofollow" target="_blank">DBeaver </a>is a fantastic tool for working with databases.</span><br /></p><p><br /></p>https://stokerpostgresql.blogspot.com/2025/06/entity-relationship-maps.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-6911859659274258310Wed, 30 Apr 2025 15:22:00 +00002025-04-30T08:22:27.549-07:00Queries on Vacuum<p>&nbsp;I am (slowly) adding handy PostgreSQL queries to my <a href="https://github.com/davestokes/HandySQL" rel="nofollow" target="_blank">GitHub</a>, and Vacuum is the newest category.&nbsp; The end goal is to have a compilation of queries for those of us who need to keep an instance healthy.</p><p>Over the years, I have collected hundreds of various queries and hate hunting them down in my code snippet library. Finally, they will be in one place and easy to search.&nbsp;</p><p>Please contribute if you have similar or better queries (hint, hint!).</p>https://stokerpostgresql.blogspot.com/2025/04/queries-on-vacuum.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-103820534472192272Sat, 26 Apr 2025 15:26:00 +00002025-04-26T08:26:01.824-07:00Incremental Backups in PostgreSQL 17<span>&nbsp;The old adage that a DBA or SRE is only as good as their last backup is true.&nbsp; PostgreSQL 17 added the ability to combine multiple incremental backups with a full backup to provide a complete data dictionary to recover a failed system. It is very easy to use.</span><div><br /></div><div>This is a quick example of using incrementals. I recommend watching this <a href="https://www.youtube.com/watch?v=F_uqARRaqNw" rel="nofollow" target="_blank">video</a> by Robert Haas for more details and some discussion of backup strategies.<br /><div><span><br /></span></div><h3 style="text-align: left;"><span>Step 1 - Enable WALL Summerization</span></h3><div><span><br /></span></div><div><span>The incremental backups require Write Ahead Log Summarization. It is likely not on your instance by default. But it is easy to enable.</span></div><div><span><br /></span></div><div><span><div><span style="font-family: courier;">demo=# <b>alter system set summerize_wal = 'on';</b></span></div><div><span style="font-family: courier;">ALTER SYSTEM</span></div><div><span style="font-family: courier;">demo=# <b>select pg_reload_conf();</b></span></div><div><span style="font-family: courier;">pg_reload_conf&nbsp;</span></div><div><span style="font-family: courier;">----------------</span></div><div><span style="font-family: courier;">&nbsp;t</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><br /></div><h3 style="text-align: left;">Step 2 - Full Backup</h3><div>The examples below are much simpler than you will run into in real life.&nbsp; The scenario is that we make a full backup on Sunday and do incrementals the rest of the week. Wednesday through Saturday are omitted below.&nbsp;</div><div><br /></div><div><div><span style="font-family: courier;">stoker@ThinkPad:~$ <b>mkdir full monday tuesday</b></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">stoker@ThinkPad:~$ <b>pg_basebackup -D full --verbose</b></span></div><div><span style="font-family: courier;">Password:&nbsp;</span></div><div><span style="font-family: courier;">pg_basebackup: initiating base backup, waiting for checkpoint to complete</span></div><div><span style="font-family: courier;">pg_basebackup: checkpoint completed</span></div><div><span style="font-family: courier;">pg_basebackup: write-ahead log start point: 6/12000028 on timeline 1</span></div><div><span style="font-family: courier;">pg_basebackup: starting background WAL receiver</span></div><div><span style="font-family: courier;">pg_basebackup: created temporary replication slot "pg_basebackup_21051"</span></div><div><span style="font-family: courier;">pg_basebackup: write-ahead log end point: 6/12000158</span></div><div><span style="font-family: courier;">pg_basebackup: waiting for background process to finish streaming ...</span></div><div><span style="font-family: courier;">pg_basebackup: syncing data to disk ...</span></div><div><span style="font-family: courier;">pg_basebackup: renaming backup_manifest.tmp to backup_manifest</span></div><div><span style="font-family: courier;">pg_basebackup: base backup completed</span></div><div><span style="font-family: courier;">stoker@ThinkPad:~$&nbsp;</span></div></div><div><br /></div><div><br /></div><h4 style="text-align: left;">Step 3 - First Incremental (Monday)</h4><div>On Monday, the first incremental backup is created. The manifest, with all the WAL information on what has changed, needed to be referenced.</div><div><br /></div><div><div><span style="font-family: courier;">stoker@ThinkPad:~$ <b>pg_basebackup -D monday -i ./full/backup_manifest --verbose</b></span></div><div><span style="font-family: courier;">Password:&nbsp;</span></div><div><span style="font-family: courier;">pg_basebackup: initiating base backup, waiting for checkpoint to complete</span></div><div><span style="font-family: courier;">pg_basebackup: checkpoint completed</span></div><div><span style="font-family: courier;">pg_basebackup: write-ahead log start point: 6/14000028 on timeline 1</span></div><div><span style="font-family: courier;">pg_basebackup: starting background WAL receiver</span></div><div><span style="font-family: courier;">pg_basebackup: created temporary replication slot "pg_basebackup_22703"</span></div><div><span style="font-family: courier;">pg_basebackup: write-ahead log end point: 6/14000120</span></div><div><span style="font-family: courier;">pg_basebackup: waiting for background process to finish streaming ...</span></div><div><span style="font-family: courier;">pg_basebackup: syncing data to disk ...</span></div><div><span style="font-family: courier;">pg_basebackup: renaming backup_manifest.tmp to backup_manifest</span></div><div><span style="font-family: courier;">pg_basebackup: base backup completed</span></div></div><div><br /></div><div><br /></div><div>Step 4 - Second Incremental (Tuesday)</div><div><br /></div><div>On Tuesday, another incremental backup is created.&nbsp;&nbsp;</div><div><br /></div><div>We could have just recorded the changes since the last incremental backup by referring to the backup manifest in the Monday directory. In that case, to completely restore the data, we need to combine the full backup and both the Monday and Tuesday incremental backups. That is a very valid way of operating. If the data does not churn a great deal, this can save time making incrementals.&nbsp;</div><div><br /></div><div>My personal preference is to base incrementals on full backups.&nbsp; This means only the full and Tuesday backups must be used for complete restoration. This reduction in logical steps may seem trivial, but tempting a Murphy's Law Ticket is something I avoid after too many restorations that are too early in the morning after catastrophic system failures.&nbsp;</div><div><br /></div><div><div><span style="font-family: courier;">stoker@ThinkPad:~$ <b>pg_basebackup -D tuesday -i ./full/backup_manifest --verbose</b></span></div><div><span style="font-family: courier;">Password:&nbsp;</span></div><div><span style="font-family: courier;">pg_basebackup: initiating base backup, waiting for checkpoint to complete</span></div><div><span style="font-family: courier;">pg_basebackup: checkpoint completed</span></div><div><span style="font-family: courier;">pg_basebackup: write-ahead log start point: 6/16000028 on timeline 1</span></div><div><span style="font-family: courier;">pg_basebackup: starting background WAL receiver</span></div><div><span style="font-family: courier;">pg_basebackup: created temporary replication slot "pg_basebackup_22870"</span></div><div><span style="font-family: courier;">pg_basebackup: write-ahead log end point: 6/16000120</span></div><div><span style="font-family: courier;">pg_basebackup: waiting for background process to finish streaming ...</span></div><div><span style="font-family: courier;">pg_basebackup: syncing data to disk ...</span></div><div><span style="font-family: courier;">pg_basebackup: renaming backup_manifest.tmp to backup_manifest</span></div><div><span style="font-family: courier;">pg_basebackup: base backup completed</span></div></div><div><br /></div><h3 style="text-align: left;">Step 5 - Combine Full, Monday, and Tuesday</h3><div>Sometime after the Tuesday incremental backup, something horrible happens to your instance (probably someone with too many instance privileges made a DELETE query without a WHERE clause (AGAIN!?!?)). It is simple to create a new home for the data and combine the Full and Tuesday backup data into that new home.</div><div><br /></div><div><div><span style="font-family: courier;">stoker@ThinkPad:~$ <b>mkdir newdatadir</b></span></div><div><span style="font-family: courier;">stoker@ThinkPad:~$ <b>/usr/lib/postgresql/17/bin/pg_combinebackup full tuesday -o newdatadir</b></span></div><div><span style="font-family: courier;">stoker@ThinkPad:~$&nbsp;</span></div></div><div><br /></div><h3 style="text-align: left;">Step 6 - Restart</h3><div><br /></div><div>Make sure you 'chmod 700' your new datadir and that your <i>postgres</i> account is the owner. Stop the running instance. You're now ready to use the restored data directory. Point pg_ctl at the new datadir, and you are done.</div><div><br /></div><h4 style="text-align: left;">Summary</h4><div><br /></div><div>Incremental backups will make your life easier, and this feature is a big reason for those not on PostgreSQL 17 to upgrade.&nbsp;</div><div><br /></div><div><br /></div></span></div></div>https://stokerpostgresql.blogspot.com/2025/04/incremental-backups-in-postgresql-17.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-7054030134248646496Fri, 04 Apr 2025 18:58:00 +00002025-04-04T11:58:38.061-07:00A Repository Of Handy Database Administration Queries<p>&nbsp;I have started a repository of handy SQL Queries for Database Administration, which I have collected over the years. Finding unused or duplicate indexes or queries without indexes and other information can speed up response times. Knowing statistics like cache hit rates can aid in planning upgrades.&nbsp;</p><p>I am currently populating this repo as I run across the queries as part of my 'spring cleaning. ' I welcome your participation if you have a handy bit of SQL for an open-source database you'd like to share. The repo is at <a href="https://github.com/davestokes/HandySQL">https://github.com/davestokes/HandySQL</a>, and I will be adding to it over the next few months.</p><p>If you have a question that you would like to address, please don't hesitate to let me know. I will try to accommodate you.&nbsp;</p>https://stokerpostgresql.blogspot.com/2025/04/a-repository-of-handy-database.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-4142079483539065368Thu, 27 Mar 2025 20:39:00 +00002025-03-27T13:40:57.139-07:00Saving The Output From psql<div>&nbsp;<br />Occasionally, you will need to capture data when working with a database. You can cut-n-paste or use a shell command like <a href="https://man7.org/linux/man-pages/man1/script.1.html" rel="nofollow" target="_blank">script</a>. In typical PostgreSQL fashion, <i>psql</i>&nbsp;has two ways to save your output.<br /></div><h3 style="text-align: left;">The First Way</h3><div>You must invoke <i>psql</i> with the <i>--log-file-&lt;filename&gt;</i> or <i>-L &lt;filename&gt;</i> option. The entire session will be recorded.<br /><br /><span style="font-family: courier;">stoker@ThinkPad:~$ <b>psql --log-file=/tmp/psqllog demo</b><br />Password for user stoker:<br />psql (17.4 (Ubuntu 17.4-1.pgdg24.04+2))<br />Type "help" for help.<br />demo=# <b>\d ledger</b><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Table "public.ledger"<br />&nbsp; &nbsp; &nbsp;Column&nbsp; &nbsp; &nbsp;|&nbsp; Type&nbsp; &nbsp;| Collation | Nullable | Default<br />----------------+---------+-----------+----------+---------<br />&nbsp;id&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| integer |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />&nbsp;tx_id&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | integer |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />&nbsp;total_register | money&nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />&nbsp;misc&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| integer |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />&nbsp;status&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| text&nbsp; &nbsp; |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />demo=# <b>\q</b><br />stoker@ThinkPad:~$ <b>cat /tmp/psqllog</b><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Table "public.ledger"<br />&nbsp; &nbsp; &nbsp;Column&nbsp; &nbsp; &nbsp;|&nbsp; Type&nbsp; &nbsp;| Collation | Nullable | Default<br />----------------+---------+-----------+----------+---------<br />&nbsp;id&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| integer |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />&nbsp;tx_id&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | integer |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br />&nbsp;total_register | money&nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |<br /></span></div><h4 style="text-align: left;">The Second Way</h4><p>The other choice is to use <i>-o &lt;file&gt;</i> or <i>--output=&lt;filename&gt;</i> when you start <i>psql</i>. The differences are that the SQL command is not recorded</p><div style="text-align: left;">and the output does not show on your screen.<br /><br /><span style="font-family: courier;">stoker@ThinkPad:~$ <b>psql -o /tmp/out demo</b><br />Password for user stoker:<br />psql (17.4 (Ubuntu 17.4-1.pgdg24.04+2))<br />Type "help" for help.<br />demo=# <b>\dt&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: red;"># note the lack of output compared to the above</span></b><br />demo=# <b>\q</b><br />stoker@ThinkPad:~$ cat /tmp/out<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; List of relations<br />&nbsp;Schema |&nbsp; &nbsp;Name&nbsp; &nbsp; | Type&nbsp; | Owner<br />--------+-----------+-------+--------<br />&nbsp;public | customers | table | stoker<br />&nbsp;public | ex01&nbsp; &nbsp; &nbsp; | table | stoker<br />&nbsp;public | ex02&nbsp; &nbsp; &nbsp; | table | stoker<br />&nbsp;public | ex03&nbsp; &nbsp; &nbsp; | table | stoker<br />&nbsp;public | foo&nbsp; &nbsp; &nbsp; &nbsp;| table | stoker<br />&nbsp;public | ledger&nbsp; &nbsp; | table | stoker<br />&nbsp;public | register&nbsp; | table | stoker<br />&nbsp;public | two&nbsp; &nbsp; &nbsp; &nbsp;| table | stoker<br />&nbsp;public | v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| table | stoker<br />&nbsp;public | z&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;| table | stoker<br />&nbsp;public | zz&nbsp; &nbsp; &nbsp; &nbsp; | table | stoker<br />(11 rows)</span></div><h4 style="text-align: left;">Summary</h4><div style="text-align: left;">These two little options can save you a lot of grief when you need to save the output from <i>psql</i>.&nbsp; You will find them very handy every so often.</div><div style="text-align: left;"><br /></div>https://stokerpostgresql.blogspot.com/2025/03/saving-ourput-from-psql.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-4798423148464801164Wed, 19 Mar 2025 13:59:00 +00002025-03-19T06:59:39.393-07:00PostgreSQL's COPY and \COPY<div style="text-align: left;">PostgreSQL is equivalent to a Swiss Army Knife in the database world. There are things in PostgreSQL that are very simple to use, while in another database, they take many more steps to accomplish. But sometimes, the knife has too many blades, which can cause confusion. This is one of those cases.</div><div style="text-align: left;"><h3 style="text-align: left;">COPY and \COPY</h3></div><div style="text-align: left;">I needed a quick dump of a table as part of an ETL (Extract, Transform, and Load - official definition, Exorcism, Trauma, and Lost-sleep - unofficially) to port some data. The <b>COPY</b> command is a quick way to output and write the data to a file.&nbsp; The two copy commands below illustrate adding a delimiter and then writing the output to a table.</div><div style="text-align: left;"><span style="font-family: courier;"><br />demo=# <b>select * from z;</b><br />&nbsp;a | b&nbsp; |&nbsp; c&nbsp;&nbsp;<br />---+----+-----<br />&nbsp;1 | 10 | 100<br />&nbsp;2 | 20 | 200<br />&nbsp;3 | 30 | 300<br />&nbsp;4 | 40 | 400<br />(4 rows)<br />demo=# <b>copy z to stdout (DELIMITER ',');</b><br />1,10,100<br />2,20,200<br />3,30,300<br />4,40,400<br />demo=# <b>copy z to '/tmp/z' (DELIMITER ',');</b><br />COPY 4<br />demo=#&nbsp;</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">The trick is that&nbsp;<span style="box-sizing: border-box; margin: 0px; padding: 0px;"><i>you</i> must&nbsp;<em>REMEMBER</em>&nbsp;that the PostgreSQL server is writing the file and&nbsp;</span>will need permission to write in the desired directory. If you try to write your current directory, you will probably see this:</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="font-family: courier;">demo=# <b>copy z to 'z' (DELIMITER ',');</b></span></div><div><span style="font-family: courier;">ERROR:&nbsp; relative path not allowed for COPY to file</span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div><h3 style="text-align: left;">ETL</h3><div>The table I needed to 'fix' as part of the ETL process was edited and was now ready to load. I created a new table.</div><div><br /></div></div><div style="text-align: left;"><span style="font-family: courier;">demo=# <b>create table zz (a1 int, b1 int, c1 int);</b><br />CREATE TABLE</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">The tricky part is that you can not use <b>COPY</b> to load the data. You need to use psql's <b>\COPY</b>! Consider <b>\COPY</b> a wrapper on the client side around the server's <b>COPY</b> command.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-family: courier;">demo-# <b>\copy zz from '/tmp/z/' (delimiter ',');</b><br />COPY 4<br />demo-#&nbsp;</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">I find the <b>COPY</b> - <b>/COPY </b>pairing very handy.</div><div style="text-align: left;"><br /></div><h4 style="text-align: left;">More?</h4><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="box-sizing: border-box; margin: 0px; padding: 0px;">I highly recommend Wager Bianchi's&nbsp;<a href="" target="_blank">COPY vs. \copy in PostgreSQL and their Security Implications for DBAs</a>&nbsp;for those seeking more information on this subject and its healthy warning about security issues.</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div>https://stokerpostgresql.blogspot.com/2025/03/postgresqls-copy-and-copy.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-5036238337643296225Sat, 15 Mar 2025 18:08:00 +00002025-03-15T11:08:35.472-07:00Can Artificial Intelligence Created Better Tables Than You?<p style="text-align: left;">&nbsp;Artificial Intelligence is one of those conundrums where the ability to have some tasks handled for you contrasts with a mix of ego and pride that it may be better than you at those tasks.&nbsp; I recently wrote a blog using another database about an <a href="https://davesmysqlstuff.blogspot.com/2025/02/does-artificial-intelligence-query.html" rel="nofollow" target="_blank">AI-generated SQL that was quite sophisticated</a>. But what about asking an AI to create a table with a specification like a DBA/SRE/Analyst might receive?</p><p style="text-align: left;">I used <a href="https://grok.com/?referrer=website" rel="nofollow" target="_blank">Grok</a> and entered the following prompt:</p><p style="text-align: left;"><i>Write the SQL to create a table on a PostgreSQL version 17 server to record data on customers. The data will include a first name, a last name, an address, a birthdate, and a unique UUID primary key. Partition the data by the year of birth. And produce some test data of at least ten records.</i></p><p style="text-align: left;">I am sure many of us have started large projects given less instruction.&nbsp; Notice: I did not denote the format for the address (US Post Office's format, or UK, or other). Nor did I set a name length. I wanted to see what assumptions were made by the AI.</p><h4 style="text-align: left;">The Answer</h4><p style="text-align: left;">The response came very quickly, much more quickly that I could interactively input the CREATE TABLE statement.</p><p style="text-align: left;"><span style="font-family: times;">-- First, create the parent table with partitioning</span></p><div style="text-align: left;"><span style="font-family: courier;">CREATE TABLE customers (<br />&nbsp; &nbsp; customer_id UUID PRIMARY KEY,<br />&nbsp; &nbsp; first_name VARCHAR(50) NOT NULL,<br />&nbsp; &nbsp; last_name VARCHAR(50) NOT NULL,<br />&nbsp; &nbsp; address TEXT NOT NULL,<br />&nbsp; &nbsp; birthdate DATE NOT NULL<br />) PARTITION BY RANGE (EXTRACT(YEAR FROM birthdate));</span></div><p style="text-align: left;"><span style="font-family: courier;"><br /></span></p><div style="text-align: left;"><span style="font-family: courier;">-- Create partitions for specific year ranges<br />CREATE TABLE customers_y1960_1980 PARTITION OF customers<br />&nbsp; &nbsp; FOR VALUES FROM (1960) TO (1980);<br />CREATE TABLE customers_y1980_2000 PARTITION OF customers<br />&nbsp; &nbsp; FOR VALUES FROM (1980) TO (2000);<br />CREATE TABLE customers_y2000_2020 PARTITION OF customers<br />&nbsp; &nbsp; FOR VALUES FROM (2000) TO (2020);<br />CREATE TABLE customers_y2020_2040 PARTITION OF customers<br />&nbsp; &nbsp; FOR VALUES FROM (2020) TO (2040);<br />-- Insert test data (10 records)<br /><span style="font-size: x-small;">INSERT INTO customers (customer_id, first_name, last_name, address, birthdate) VALUES<br />&nbsp; &nbsp; -- 1960-1980 partition<br />&nbsp; &nbsp; (gen_random_uuid(), 'John', 'Smith', '123 Maple St, Boston, MA', '1975-03-15'),<br />&nbsp; &nbsp; (gen_random_uuid(), 'Mary', 'Johnson', '456 Oak Ave, Chicago, IL', '1968-07-22'),<br />&nbsp; &nbsp;&nbsp;<br />&nbsp; &nbsp; -- 1980-2000 partition<br />&nbsp; &nbsp; (gen_random_uuid(), 'Robert', 'Williams', '789 Pine Rd, Seattle, WA', '1985-11-30'),<br />&nbsp; &nbsp; (gen_random_uuid(), 'Sarah', 'Brown', '321 Elm St, Austin, TX', '1992-04-17'),<br />&nbsp; &nbsp; (gen_random_uuid(), 'Michael', 'Davis', '654 Birch Ln, Denver, CO', '1998-09-05'),<br />&nbsp; &nbsp;&nbsp;<br />&nbsp; &nbsp; -- 2000-2020 partition<br />&nbsp; &nbsp; (gen_random_uuid(), 'Emily', 'Taylor', '987 Cedar Dr, Miami, FL', '2001-12-25'),<br />&nbsp; &nbsp; (gen_random_uuid(), 'James', 'Wilson', '147 Spruce Way, Portland, OR', '2005-06-10'),<br />&nbsp; &nbsp; (gen_random_uuid(), 'Lisa', 'Anderson', '258 Willow Ct, Phoenix, AZ', '2010-02-14'),<br />&nbsp; &nbsp;&nbsp;<br />&nbsp; &nbsp; -- 2020-2040 partition<br />&nbsp; &nbsp; (gen_random_uuid(), 'Ethan', 'Martinez', '369 Aspen Pl, San Diego, CA', '2021-08-19'),<br />&nbsp; &nbsp; (gen_random_uuid(), 'Olivia', 'Garcia', '741 Poplar St, New York, NY', '2023-01-03');</span><br />-- Verify the data distribution across partitions<br />SELECT tableoid::regclass, COUNT(*)&nbsp;<br />FROM customers&nbsp;<br />GROUP BY tableoid;</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Note the last part about selecting the tableoid, which I was not expecting. Maybe a COUNT * on the Primary Key, but not that. Hmmm.&nbsp;</div><h3 style="text-align: left;">Wow</h3><div style="text-align: left;">The response was very quick. A quick scan of the table definition shows some good but probably most efficient choices for the column data types.&nbsp; I specified unique PKs and was unsure if the AI would pop in a UNIQUE constraint or stick with the UUID types ratre conflict strategy. The test data looks good, too, at a cursory glance.</div><div style="text-align: left;"><br /></div><h4 style="text-align: left;">But Does It Work?</h4><div style="text-align: left;">The real test is if the supplied SQL works. I copied the CREATE TABLE section and tried.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="font-family: courier;">demo=# CREATE TABLE customers (</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; customer_id UUID PRIMARY KEY,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; first_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; last_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; address TEXT NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birthdate DATE NOT NULL</span></div><div><span style="font-family: courier;">) PARTITION BY RANGE (EXTRACT(YEAR FROM birthdate));</span></div><div><span style="font-family: courier;"><b>ERROR:&nbsp; unsupported PRIMARY KEY constraint with partition key definition</b></span></div><div><span style="font-family: courier;"><b>DETAIL:&nbsp; PRIMARY KEY constraints cannot be used when partition keys include expressions.</b></span></div><div><span style="font-family: courier;">demo=#</span>&nbsp;</div><div><br /></div><div>Dang! Well, everyone runs into syntax and function issues from time to time.&nbsp;</div><h3 style="text-align: left;"><br />Second Pass</h3><div><br /></div><div>So, maybe it is time to be a little more specific about the birthday data. I modified the prompt to break out the birthday fields into unique columns.</div></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><i>Write the SQL to create a table on a PostgreSQL version 17 server to record data on customers. The data will include a first name, a last name, an address, a birthdate, and a unique UUID primary key. Have separate columns for the birthday month, birthday day, and birthday year. Partition the data by the year of birth. And produce some test data from at least ten records.</i></div><div style="text-align: left;"><i><br /></i></div><div style="text-align: left;">Once again, the response came quickly.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="font-family: courier;">-- Create the parent table with partitioning</span></div><div><span style="font-family: courier;">CREATE TABLE customers (</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; customer_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; first_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; last_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; address TEXT NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_date DATE NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_month SMALLINT GENERATED ALWAYS AS (EXTRACT(MONTH FROM birth_date)) STORED,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_day SMALLINT GENERATED ALWAYS AS (EXTRACT(DAY FROM birth_date)) STORED,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_year SMALLINT GENERATED ALWAYS AS (EXTRACT(YEAR FROM birth_date)) STORED</span></div><div><span style="font-family: courier;">) PARTITION BY RANGE (birth_year);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">-- Create partitions for specific year ranges</span></div><div><span style="font-family: courier;">CREATE TABLE customers_y1970s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (1970) TO (1980);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y1980s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (1980) TO (1990);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y1990s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (1990) TO (2000);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y2000s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (2000) TO (2010);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y2010s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (2010) TO (2020);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">-- Insert test data</span></div><div><span style="font-family: courier;">INSERT INTO customers (first_name, last_name, address, birth_date) VALUES</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('John', 'Smith', '123 Main St, Boston MA', '1985-03-15'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Sarah', 'Johnson', '456 Oak Ave, Chicago IL', '1992-07-22'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Michael', 'Brown', '789 Pine Rd, Seattle WA', '1978-11-30'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Emily', 'Davis', '321 Elm St, Austin TX', '2001-02-14'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('David', 'Wilson', '654 Cedar Ln, Miami FL', '1988-09-05'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Lisa', 'Anderson', '987 Birch Dr, Denver CO', '1995-12-25'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('James', 'Taylor', '147 Maple St, Portland OR', '1975-06-18'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Rachel', 'Martinez', '258 Willow Way, Phoenix AZ', '2004-04-03'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Thomas', 'Lee', '369 Spruce Ct, Atlanta GA', '1982-08-27'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Kelly', 'White', '741 Ash Pl, San Diego CA', '1998-10-11'),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; ('Robert', 'Garcia', '852 Poplar Ave, Houston TX', '1972-01-09');</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">-- Verify the data</span></div><div><span style="font-family: courier;">SELECT * FROM customers ORDER BY birth_year, birth_month, birth_day;</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: inherit;">The choice of generated columns for breaking out the birthdate information looked like a good choice. I was surprised to see the last query changed from counting the rows to selecting all the data. What triggered that?</span></div><div><span style="font-family: inherit;"><br /></span></div><div><span style="font-family: inherit;">Now, does that new CREATE TABLE work?</span></div></div><div style="text-align: left;"><br /></div><h4 style="text-align: left;">Close, Lacking Cigar</h4><div style="text-align: left;">I had high hopes that the second revision would work. The date was split up into individual columns.</div><div style="text-align: left;"><div><br /></div><div><span style="font-family: courier;">demo=# CREATE TABLE customers (</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; customer_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; first_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; last_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; address TEXT NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_date DATE NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_month SMALLINT GENERATED ALWAYS AS (EXTRACT(MONTH FROM birth_date)) STORED,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_day SMALLINT GENERATED ALWAYS AS (EXTRACT(DAY FROM birth_date)) STORED,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_year SMALLINT GENERATED ALWAYS AS (EXTRACT(YEAR FROM birth_date)) STORED</span></div><div><span style="font-family: courier;">) PARTITION BY RANGE (birth_year);</span></div><div><span style="font-family: courier;"><b>ERROR:&nbsp; cannot use generated column in partition key</b></span></div><div><span style="font-family: courier;"><b>LINE 10: ) PARTITION BY RANGE (birth_year);</b></span></div><div><span style="font-family: courier;"><b>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;^</b></span></div><div><span style="font-family: courier;"><b>DETAIL:&nbsp; Column "birth_year" is a generated column.</b></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div><div><br /></div><div>Dang! I did not know that using a GENERATED COLUMN as an argument for partition would be an issue. (Time for me and the AI to RTFM, right?)</div><div><br /></div><h3 style="text-align: left;">One More Into The Breech</h3><div>So this time, I added 'do not use generated columns'.</div><div><br /></div><div><i>Write the SQL to create a table on a PostgreSQL version 17 server to record data on customers. The data will include a first name, a last name, an address, a birthdate, and a unique UUID primary key. Have separate columns, do not use generated columns,&nbsp; for the birthday month, birthday day, and birthday year. Partition the data by the year of birth. And produce some test data from at least ten records.</i></div><div><br /></div><div><br /></div><div><div><span style="font-family: courier;">-- Create the parent table with partitioning</span></div><div><span style="font-family: courier;">CREATE TABLE customers (</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; customer_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; first_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; last_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; address TEXT NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_date DATE NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_month SMALLINT NOT NULL CHECK (birth_month &gt;= 1 AND birth_month &lt;= 12),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_day SMALLINT NOT NULL CHECK (birth_day &gt;= 1 AND birth_day &lt;= 31),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_year SMALLINT NOT NULL</span></div><div><span style="font-family: courier;">) PARTITION BY RANGE (birth_year);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">-- Create partitions for specific year ranges</span></div><div><span style="font-family: courier;">CREATE TABLE customers_y1970s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (1970) TO (1980);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y1980s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (1980) TO (1990);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y1990s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (1990) TO (2000);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y2000s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (2000) TO (2010);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">CREATE TABLE customers_y2010s PARTITION OF customers</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; FOR VALUES FROM (2010) TO (2020);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">-- Insert test data</span></div><div><span style="font-family: courier; font-size: x-small;">INSERT INTO customers (first_name, last_name, address, birth_date, birth_month, birth_day, birth_year) VALUES</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('John', 'Smith', '123 Main St, Boston MA', '1985-03-15', 3, 15, 1985),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Sarah', 'Johnson', '456 Oak Ave, Chicago IL', '1992-07-22', 7, 22, 1992),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Michael', 'Brown', '789 Pine Rd, Seattle WA', '1978-11-30', 11, 30, 1978),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Emily', 'Davis', '321 Elm St, Austin TX', '2001-02-14', 2, 14, 2001),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('David', 'Wilson', '654 Cedar Ln, Miami FL', '1988-09-05', 9, 5, 1988),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Lisa', 'Anderson', '987 Birch Dr, Denver CO', '1995-12-25', 12, 25, 1995),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('James', 'Taylor', '147 Maple St, Portland OR', '1975-06-18', 6, 18, 1975),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Rachel', 'Martinez', '258 Willow Way, Phoenix AZ', '2004-04-03', 4, 3, 2004),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Thomas', 'Lee', '369 Spruce Ct, Atlanta GA', '1982-08-27', 8, 27, 1982),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Kelly', 'White', '741 Ash Pl, San Diego CA', '1998-10-11', 10, 11, 1998),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Robert', 'Garcia', '852 Poplar Ave, Houston TX', '1972-01-09', 1, 9, 1972);</span></div><div><span style="font-family: courier; font-size: x-small;"><br /></span></div><div><span style="font-family: courier;">-- Verify the data</span></div><div><span style="font-family: courier;">SELECT * FROM customers ORDER BY birth_year, birth_month, birth_day;</span></div></div><div><br /></div><div>No generated columns and some constrict checks added gratis. Now, the final query has been augmented with an ORDER BY.&nbsp;</div><div><br /></div><div>But, once again, does IT work?</div><div><br /></div><h3 style="text-align: left;">You Make Take A Prize From The Losest Shelf</h3><div><br /></div><div><div><span style="font-family: courier;">demo=# CREATE TABLE customers (</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; customer_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; first_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; last_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; address TEXT NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_date DATE NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_month SMALLINT NOT NULL CHECK (birth_month &gt;= 1 AND birth_month &lt;= 12),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_day SMALLINT NOT NULL CHECK (birth_day &gt;= 1 AND birth_day &lt;= 31),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_year SMALLINT NOT NULL</span></div><div><span style="font-family: courier;">) PARTITION BY RANGE (birth_year);</span></div><div><span style="font-family: courier;"><b>ERROR:&nbsp; unique constraint on partitioned table must include all partitioning columns</b></span></div><div><span style="font-family: courier;"><b>DETAIL:&nbsp; PRIMARY KEY constraint on table "customers" lacks column "birth_year" which is part of the partition key.</b></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><div>Okay, I need to read up on the partitioning by range manual page.&nbsp;&nbsp;</div><div><br /></div><h4 style="text-align: left;">Conclusions</h4><div>The DDL definition looks good at first glance. And it is almost right, which may catch the attention of many of us who write SQL for a living. Replace the AI with a Junior DBA in your mind, and you would consider the results pretty good. Now, to get the AI to RTFM about partitioning and generated columns, the results will be stupifying. I am sure there are some clever folks working to include the source and manuals for PostgreSQL into a model so the issues I had above will disappear.&nbsp;</div><div><br /></div><div>IDEs have become essential tools for many developers, with features like syntax completion. Could the future include a similar collection of features where we supply the DDL and/or DML spec and the AI returns what was requested?&nbsp;</div><div><br /></div><div>One of my first programming classes featured a section on unexpressed latent desires in specifications. Earlier, I mentioned not specifying a format for addresses. I have been on a project where the multinationalization of addresses became a headache, and maybe the AI could anticipate that issue before the developers notice it with the proper design to accommodate the situation.&nbsp;</div><div><br /></div><div>I am impressed how well Grok did with my requirements. I am sure it will only get better as time goes on. Do not automatically reject the idea that an AI can produce good SQL. Just like we trust compilers to take our thoughts and turn them into machine code, AI will soon turn our database ideas and create the required DML and DDL.</div><h3 style="text-align: left;">Update</h3><div>After finishing this blog's first pass, I was drinking a coffee when the brain asked, 'What if I drop the partitioning requirement to the original query and try again?' The results:</div><div><br /></div><div><div><span style="font-family: courier;">-- Create the customers table</span></div><div><span style="font-family: courier;">CREATE TABLE customers (</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; customer_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; first_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; last_name VARCHAR(50) NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; address TEXT NOT NULL,</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; birth_date DATE NOT NULL</span></div><div><span style="font-family: courier;">);</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">-- Insert test data</span></div><div><span style="font-family: courier; font-size: x-small;">INSERT INTO customers (first_name, last_name, address, birth_date) VALUES</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('John', 'Smith', '123 Main St, Boston, MA 02108', '1985-06-15'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Sarah', 'Johnson', '456 Oak Ave, Chicago, IL 60601', '1990-03-22'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Michael', 'Brown', '789 Pine Rd, Austin, TX 73301', '1978-11-30'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Emily', 'Davis', '321 Elm St, Seattle, WA 98101', '1995-09-12'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('David', 'Wilson', '654 Cedar Ln, Miami, FL 33101', '1982-04-18'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Lisa', 'Anderson', '987 Birch Dr, Denver, CO 80201', '1988-12-05'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('James', 'Taylor', '147 Maple Way, Portland, OR 97201', '1975-07-25'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Rachel', 'Martinez', '258 Spruce Ct, Phoenix, AZ 85001', '1993-01-09'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Thomas', 'Lee', '369 Willow Pl, Atlanta, GA 30301', '1980-10-14'),</span></div><div><span style="font-family: courier; font-size: x-small;">&nbsp; &nbsp; ('Kelly', 'White', '741 Aspen St, San Diego, CA 92101', '1987-08-03');</span></div></div><div><br /></div><div>I ran a quick query:</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>select first_name, last_name, birth_date&nbsp;</b></span></div><div><span style="font-family: courier;"><b>&nbsp; &nbsp; &nbsp; &nbsp;from customers order by last_name, first_name;</b></span></div><div><span style="font-family: courier;">&nbsp;first_name | last_name | birth_date&nbsp;</span></div><div><span style="font-family: courier;">------------+-----------+------------</span></div><div><span style="font-family: courier;">&nbsp;Lisa&nbsp; &nbsp; &nbsp; &nbsp;| Anderson&nbsp; | 1988-12-05</span></div><div><span style="font-family: courier;">&nbsp;Michael&nbsp; &nbsp; | Brown&nbsp; &nbsp; &nbsp;| 1978-11-30</span></div><div><span style="font-family: courier;">&nbsp;Emily&nbsp; &nbsp; &nbsp; | Davis&nbsp; &nbsp; &nbsp;| 1995-09-12</span></div><div><span style="font-family: courier;">&nbsp;Sarah&nbsp; &nbsp; &nbsp; | Johnson&nbsp; &nbsp;| 1990-03-22</span></div><div><span style="font-family: courier;">&nbsp;Thomas&nbsp; &nbsp; &nbsp;| Lee&nbsp; &nbsp; &nbsp; &nbsp;| 1980-10-14</span></div><div><span style="font-family: courier;">&nbsp;Rachel&nbsp; &nbsp; &nbsp;| Martinez&nbsp; | 1993-01-09</span></div><div><span style="font-family: courier;">&nbsp;John&nbsp; &nbsp; &nbsp; &nbsp;| Smith&nbsp; &nbsp; &nbsp;| 1985-06-15</span></div><div><span style="font-family: courier;">&nbsp;James&nbsp; &nbsp; &nbsp; | Taylor&nbsp; &nbsp; | 1975-07-25</span></div><div><span style="font-family: courier;">&nbsp;Kelly&nbsp; &nbsp; &nbsp; | White&nbsp; &nbsp; &nbsp;| 1987-08-03</span></div><div><span style="font-family: courier;">&nbsp;David&nbsp; &nbsp; &nbsp; | Wilson&nbsp; &nbsp; | 1982-04-18</span></div><div><span style="font-family: courier;">(10 rows)</span></div></div><div><br /></div><div><br /></div><div>Grok added a note at the end of the code that I had not concentrated on before this iteration,</div><div><br /></div><div><div><i>This SQL code:</i></div><div><i><br /></i></div><div><ol style="text-align: left;"><li><i>Creates a table named "customers" with:</i></li><ul><li><i>customer_id: A UUID primary key that auto-generates using PostgreSQL 17's gen_random_uuid() function</i></li><li><i>first_name: A required variable-length string (up to 50 characters)</i></li><li><i>last_name: A required variable-length string (up to 50 characters)</i></li><li><i>address: A required text field for full addresses</i></li><li><i>birth_date: A required date field</i></li></ul><li><i>Inserts 10 test records with realistic sample data:</i></li></ol><ul style="text-align: left;"><ul><li><i>Varied names</i></li><li><i>Addresses in different US cities with plausible ZIP codes</i></li><li><i>Birth dates spanning different years from 1975 to 1995</i></li></ul></ul><i>The UUID values will be automatically generated when you run the INSERT statement. Each record will get a unique UUID in the format like '550e8400-e29b-41d4-a716-446655440000'.</i></div><div><i><br /></i></div><div><i>You can run this SQL directly in a PostgreSQL 17 database to create the table and populate it with the test data.</i></div></div><div><br /></div><div><br /></div></div><div style="text-align: left;">So, is this a good thing or a bad thing?&nbsp; Grok was very close to making my socks fly off. I submit that the partitioning issue is a minor issue. Maybe some extra specification on my part could guide it past the rocky shoals of the partitioning definition waters.&nbsp;</div>https://stokerpostgresql.blogspot.com/2025/03/can-artificial-intelligence-created.html[email protected] (Dave Stokes)1tag:blogger.com,1999:blog-3110649280167018029.post-7692611623553039735Tue, 25 Feb 2025 15:29:00 +00002025-02-25T07:29:33.272-08:00Use PASSING with JSON_TABLE() To Make Calculations<p>I ran across this trick while preparing a talk for the <a href="https://www.socallinuxexpo.org/scale/22x" rel="nofollow" target="_blank">Southern California Linux Expo </a>&nbsp;(SCaLE) on using JSON_TABLE(). This is a 'reserve' talk, which means it will presented IF another speaker drops out. But I thought this was interesting enough to share here. I will make sure the slides are posted online when finished.</p><h3 style="text-align: left;">Passing Interest</h3><p>JSON_TABLE() temporarily converts JSON data into a relational table so that the temporary table can be processed by Structured Query Langauge (SQL) Commands. A feature in the PostgreSQL implementation is the passing clause.&nbsp; In the example below, you will see <span style="color: red;">PASSING 1500 AS price_limit</span> in red. This declares <b>price_limit</b> as a variable and assigns a value of <b>1500</b> to it.&nbsp;</p><p>Later, in the body of the JSON_TABLE() call, you will see in green&nbsp;<span style="color: #04ff00;">is_too_expensive BOOLEAN EXISTS PATH '$.price ? (@ &gt; $price_limit)'</span>.&nbsp; Here a variable is created named <b>is_too_expensive</b> of type BOOLEAN, which means we get a true or false answer from the rest of the declaration.&nbsp;<span style="box-sizing: border-box; margin: 0px; padding: 0px;">If the&nbsp;<strong>price</strong>&nbsp;column has a value, it is compared with the&nbsp;<strong>price_limit</strong>&nbsp;value to see if that value is greater or not than the limit.</span>&nbsp;</p><div style="text-align: left;"><span style="font-family: courier;"><span style="font-size: x-small;">demo=# <b>SELECT *<br />FROM JSON_TABLE(<br />&nbsp; &nbsp; '{"guitars":[{"item": "Fender Telecaster", "price": 1000},&nbsp;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{"item": "Gibson Les Paul", "price": 2000},<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{"item": "PRS semi-hollowbody", "price": 1500}]}',<br />&nbsp; &nbsp; '$.guitars[*]' <span style="color: red;">PASSING 1500 AS price_limit</span><br />&nbsp; &nbsp; COLUMNS (<br />&nbsp; &nbsp; &nbsp;row_num for ordinality,<br />&nbsp; &nbsp; &nbsp; &nbsp; Guitar TEXT PATH '$.item',<br />&nbsp; &nbsp; &nbsp; &nbsp; Price INT PATH '$.price',<br />&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #04ff00;">is_too_expensive BOOLEAN EXISTS PATH '$.price ? (@ &gt; $price_limit)'</span><br />&nbsp; &nbsp; )<br />) AS my_example;</b></span><br />&nbsp;row_num |&nbsp; &nbsp; &nbsp; &nbsp;guitar&nbsp; &nbsp; &nbsp; &nbsp; | price | is_too_expensive&nbsp;<br />---------+---------------------+-------+------------------<br />&nbsp; &nbsp; &nbsp; &nbsp;1 | Fender Telecaster&nbsp; &nbsp;|&nbsp; 1000 | f<br />&nbsp; &nbsp; &nbsp; &nbsp;2 | Gibson Les Paul&nbsp; &nbsp; &nbsp;|&nbsp; 2000 | t<br />&nbsp; &nbsp; &nbsp; &nbsp;3 | PRS semi-hollowbody |&nbsp; 1500 | f<br />(3 rows)<br />demo=#&nbsp;</span></div><div style="text-align: left;"><br /></div><h3 style="text-align: left;">How Does This Help You?</h3><div style="text-align: left;">JSON data is tough on relational databases as it can not be processed with the regular operators found in SQL without special calls. Those calls can slow down a query tremendously. We could have pulled the data from JSON and then used another query to make our quantitative determination. But that is another query on top of the JSON_TABLE() call, and two queries are going to be slower than one. Or we could have used some sort of case statement. But this is such a simple calculation that it seems tragic to separate it from the rest of the data.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">This is a quick and 'not dirty' to handle the data.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">And if you are at SCaLE, please day 'hello!'</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><br /></div>https://stokerpostgresql.blogspot.com/2025/02/use-passing-with-jsontable-to-make.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-4446366627145703997Sat, 22 Feb 2025 15:41:00 +00002025-02-22T07:41:26.483-08:00How PostgreSQL's Aggregate FILTER Will Spoil You<p>Using PostgreSQL will definitely spoil you. I recently had to use another database and found, to my chagrin, that aggregate filters were not included in their implementation of Structured Query Language. Specifically, there was no FILTER clause available.</p><h3 style="text-align: left;">FILTER?</h3><p>To illustrate my dependency, let me start with some simple data.</p><div style="text-align: left;"><span style="font-family: courier;">demo=# <b>create table z (a int, b int, c int);</b><br />CREATE TABLE<br />demo=# <b>insert into z values (1,10,100),(2,20,200),(3,30,300),(4,40,400);</b><br />INSERT 0 4<br />demo=# select a,b,c from z;<br />&nbsp;a | b&nbsp; |&nbsp; c&nbsp;&nbsp;<br />---+----+-----<br />&nbsp;1 | 10 | 100<br />&nbsp;2 | 20 | 200<br />&nbsp;3 | 30 | 300<br />&nbsp;4 | 40 | 400<br />(4 rows)<br />demo=#&nbsp;</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">If I wanted the overall number of rows, I could use a simple COUNT(*). If I wanted an overall number of rows where one of the columns was of a certain value, I could add the logic for getting those rows to the WHERE clause.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">What if I wanted both?&nbsp; Do I need two queries to do the work separately? Maybe a materialized view or a CTE (Common Table Expression)? Will I need a Window Function?&nbsp;</div><div style="text-align: left;"><br /></div><h3 style="text-align: left;">FILTER!</h3><div style="text-align: left;"><br /></div><div style="text-align: left;">PostgreSQL provides the ability to do what is needed with one SELECT statement.&nbsp;</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="font-family: courier;">demo=# <b>select count(*) as all_rows,</b></span></div><div><span style="font-family: courier;">demo-#&nbsp; &nbsp; &nbsp; &nbsp; <b>count(*) FILTER (where b &gt; 11) as b_gt_11</b></span></div><div><span style="font-family: courier;">demo-# <b>from z;</b></span></div><div><span style="font-family: courier;">&nbsp;all_rows | b_gt_11&nbsp;</span></div><div><span style="font-family: courier;">----------+---------</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; &nbsp; &nbsp; 4 |&nbsp; &nbsp; &nbsp; &nbsp;3</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=#</span>&nbsp;</div><div><br /></div><div>The above SELECT statement asks for two columns of data. The first asks for a count of all rows.&nbsp;</div><div><br /></div><div><span style="box-sizing: border-box; margin: 0px; padding: 0px;">The second requests a count of all rows where the<i> b</i> column<i>'s </i>value&nbsp;exceeds 11.</span>&nbsp; The FILTER defines the rows of interest.&nbsp;</div><div><br /></div><div>Long-time SQL writers will immediately spot how handy the ability to filter rows this way is.&nbsp;</div><div><br /></div><div>Sadly, the database I had to use did not offer the ability to do this.&nbsp; What should have been a simple query became a detour into the bumpy backroads of finding another way to get the answer.&nbsp;</div></div>https://stokerpostgresql.blogspot.com/2025/02/how-postgresqls-aggregate-filter-will.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-4525118584445149591Wed, 19 Feb 2025 17:37:00 +00002025-02-19T09:37:41.314-08:00checkconstraintPostgresqlConstraint Checks And Dirty Data<p>&nbsp;It is much easier to keep bad data out of your database than it is to have to correct it later. Those costs are measured in time, money, and sanity. One of the easier ways of protecting your data is with a <a href="https://www.postgresql.org/docs/current/ddl-constraints.html" rel="nofollow" target="_blank">constraint check</a>.&nbsp;</p><p>I was recently approached by someone who urgently needed a constraint check and could afford to 'fix' the bad data after the constraint was in place. They had to quickly keep new 'bad' data out and could afford to correct the old 'bad' data at leisure.&nbsp;</p><h3 style="text-align: left;">The Overly Simplified Example</h3><p>To illustrate how to do just that, here is an example of a table and some simple data.</p><div style="text-align: left;"><span style="font-family: courier;">demo=# <b>create table x1 (a int);</b><br />CREATE TABLE<br />demo=# <b>insert into x1 (a) values (10),(20),(30),(-5),(1);</b><br />INSERT 0 5</span></div><div style="text-align: left;"><span style="font-family: courier;">demo=#</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Sometime later, it is discovered that any value of <i>a</i> needs to be greater than five. If we add the constraint, the system will balk and refuse the request.</div><div style="text-align: left;"><br /><span style="font-family: courier;">demo=# <b>alter table x1 add constraint x_id_gt_5 check (a &gt; 5);</b><br />ERROR:&nbsp; check constraint "x_id_gt_5" of relation "x1" is violated by some row</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">The trick is to tell the ALTER TABLE command to not validate the constraint check. The constraint is added, but the system does not ensure the data conforms to that check. This is done by adding NOT VALID to the ALTER TABLE command.</div><div style="text-align: left;"><br /><span style="font-family: courier;">demo=# alter table x1 add constraint a_gt_5 check (a &gt; 5) not valid;<br />ALTER TABLE<br /></span></div><div><br /></div><div>The data is in the same state as before the constraint was added. The old 'bad' data is still there and can be corrected or deleted later.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>select a from x1;</b></span></div><div><span style="font-family: courier;">&nbsp;a&nbsp;&nbsp;</span></div><div><span style="font-family: courier;">----</span></div><div><span style="font-family: courier;">&nbsp;10</span></div><div><span style="font-family: courier;">&nbsp;20</span></div><div><span style="font-family: courier;">&nbsp;30</span></div><div><span style="font-family: courier;">&nbsp;-5</span></div><div><span style="font-family: courier;">&nbsp; 1</span></div><div><span style="font-family: courier;">(5 rows)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><div>Any new attempts to add new 'bad' data will fail.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>insert into x1 (a) values (3);</b></span></div><div><span style="font-family: courier;">ERROR:&nbsp; new row for relation "x1" violates check constraint "a_gt_5"</span></div><div><span style="font-family: courier;">DETAIL:&nbsp; Failing row contains (3).</span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">Now, it is time to fix the rows that do not conform to the check. When that is done, the check can be validated.</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><div>demo=# <b>alter table x1 validate constraint a_gt_5;</b></div><div>ALTER TABLE</div><div>demo=#&nbsp;</div><div><br /></div></span></div><div><span style="font-family: courier;"><br /></span></div><div><br /></div><h3 style="text-align: left;">Constraint Hints</h3><div><br /></div><div>1. Name your constraints. The name 'a_gt_5' describes the constraint and explains why the data was rejected. If you do not name the constraint, the system will assign a name that will list the table name, the column name, the word 'check,' and no clue what went wrong.&nbsp;</div><div><br /></div><div>Look at the following definitions. Seeing <b>a_gt_t </b>error informs you what is wrong, But <b>x1_a_check</b> is nebulous, at best. Remember that you may be the poor person trying to figure out why the inserts are failing at three in the morning.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>\d x1</b></span></div><div><span style="font-family: courier;">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Table "public.x1"</span></div><div><span style="font-family: courier;">&nbsp;Column |&nbsp; Type&nbsp; &nbsp;| Collation | Nullable | Default&nbsp;</span></div><div><span style="font-family: courier;">--------+---------+-----------+----------+---------</span></div><div><span style="font-family: courier;">&nbsp;a&nbsp; &nbsp; &nbsp; | integer |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |&nbsp;</span></div><div><span style="font-family: courier;">Check constraints:</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; "a_gt_5" CHECK (a &gt; 5)&nbsp;</span></div><div><span style="font-family: courier;">&nbsp; &nbsp; "x1_a_check" CHECK (a &gt; 6)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><div>2. Double-check the constraints.&nbsp; In the above, having a check on <i>a </i>being greater than five and another greater than six makes no sense.&nbsp; This type of problem happens as the tables age and multiple people make changes without making sure there no conflict or similar action already established.&nbsp;</div><div><br /></div><div><br /></div>https://stokerpostgresql.blogspot.com/2025/02/constraint-checks-and-dirty-data.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-1981313673275158120Mon, 17 Feb 2025 14:01:00 +00002025-02-17T06:01:16.074-08:00PostgreSQL MERGE To Reconcile Cash Register Sales Data Part 2<p>Part one of this series covered a simple use of MERGE and can be found&nbsp;<a href="https://stokerpostgresql.blogspot.com/2025/02/postgresql-merge-to-reconcile-cash.html" rel="nofollow" target="_blank">here</a>.&nbsp; In Part Two, we get more complicated logically. Previously, we only had a WHEN NOT MATCHED logical leg in the code. Now, we add a WHEN MATCHED logic.</p><h3 style="text-align: left;">WHEN MATCHED and WHEN NOT MATCHED</h3><p>The query now has two logical legs - when there are <span style="color: #ffa400;">matching rows</span> and when there are <span style="color: #04ff00;">no matching rows.</span>&nbsp;I have color-coded the query to illustrate the separate paths.</p><div style="text-align: left;"><span style="font-family: courier;">demo=# <b>merge into ledger l<br />using register r<br />on l.id = r.id<br /><span style="color: #04ff00;">WHEN NOT MATCHED THEN<br />&nbsp;insert (id, tx_id, total_register, misc)<br />&nbsp;values (r.id, r.tx_id, r.amount, 0)</span><br /><span style="color: #ffa400;">WHEN MATCHED THEN<br />&nbsp;update set misc = misc + 1</span><br />RETURNING merge_action(), l.*;</b><br />~&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<br />&nbsp;merge_action | id | tx_id | total_register | misc&nbsp;<br />--------------+----+-------+----------------+------<br />&nbsp;UPDATE&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 1<br />(1 row)<br />MERGE 1<br />demo-#&nbsp;</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;">The&nbsp;<em>ledger</em>&nbsp;table already has a match in the above example, so we increment the <i>misc</i> column when the query runs. If we rerun the query, the same column gets incremented again.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><div><span style="font-family: courier;">demo=# <b>merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;insert (id, tx_id, total_register, misc)</b></span></div><div><span style="font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0)</b></span></div><div><span style="font-family: courier;"><b>WHEN MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;update set misc = misc + 1</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">~&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------</span></div><div><span style="font-family: courier;">&nbsp;UPDATE&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 2</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MERGE 1</span></div><div><span style="font-family: courier;">demo-#&nbsp;</span></div><div><br /></div><h3 style="text-align: left;">Delete</h3><div>Another option with Merge is to DELETE the row. The NOT MATCHED logic does not change but there is now a <span style="color: #ffa400;">WHEN MATCHED </span>logical leg.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;insert (id, tx_id, total_register, misc)</b></span></div><div><span style="font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0)</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>WHEN MATCHED THEN</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>&nbsp;DELETE</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------</span></div><div><span style="font-family: courier;">&nbsp;DELETE&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 2</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MERGE 1</span></div><div><span style="font-family: courier;">demo=# <b>select * from ledger;</b></span></div><div><span style="font-family: courier;">&nbsp;id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">----+-------+----------------+------</span></div><div><span style="font-family: courier;">(0 rows)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><h3 style="text-align: left;">Doing Nothing</h3><div><br /></div><div>There is also an option to DO NOTHING.&nbsp; Yes, you can get paid to write code that does nothing.</div><div><br /></div><div>Remember that the ledger table is empty after the last example. We have to run the code one to insert a row into this table. Then, we run it again to test the WHEN MATCHED logic leg with the <span style="color: #ffa400;">DO NOTHING</span> option.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;insert (id, tx_id, total_register, misc)</b></span></div><div><span style="font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0)</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>WHEN MATCHED THEN</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>&nbsp;DO NOTHING</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------</span></div><div><span style="font-family: courier;">&nbsp;INSERT&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 0</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MERGE 1</span></div><div><span style="font-family: courier;">demo=#<b> merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;insert (id, tx_id, total_register, misc)</b></span></div><div><span style="font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0)</b></span></div><div><span style="font-family: courier;"><b>WHEN MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;DO NOTHING</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------</span></div><div><span style="font-family: courier;">(0 rows)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MERGE 0</span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><div>Note that the merge_action() function does not say 'Nothing'.&nbsp;&nbsp;</div><div><br /></div><div>Checking the ledger table shows us one row.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>select * from ledger;</b></span></div><div><span style="font-family: courier;">&nbsp;id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">----+-------+----------------+------</span></div><div><span style="font-family: courier;">&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 0</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><h3 style="text-align: left;">More Complicated Logic</h3><div><br /></div><div>We can add complications to the logic to make the query more useful. We will add a status column to the ledger table to clarify the illustration.&nbsp;</div><div><br /></div><div><div><span style="font-family: courier;">demo=#<b> alter table ledger add column status text;</b></span></div><div><span style="font-family: courier;">ALTER TABLE</span></div><div><span style="font-family: courier;">demo=#<b> truncate ledger;</b></span></div><div><span style="font-family: courier;">TRUNCATE TABLE</span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><div>The new logic for the query is just a little more complicated.</div><div><ul style="text-align: left;"><li><span style="color: #ffa400;">No Match</span></li><ul><li>Insert data</li></ul><li><span style="color: #04ff00;">There is a match, but it is the first time we match</span></li><ul><li>Update the status field</li></ul><li><span style="color: #ff00fe;">There is a match, and it is the second time we match</span></li><ul><li>Delete the row</li></ul></ul></div><div>The first run puts 'inserted' into the<i> status</i> column.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><div><span style="color: #ffa400; font-family: courier;"><b>insert (id, tx_id, total_register, misc, status)</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0, 'inserted')</b></span></div><div><span style="color: #04ff00; font-family: courier;"><b>WHEN MATCHED and l.status = 'inserted' THEN</b></span></div><div><span style="color: #04ff00; font-family: courier;"><b>&nbsp;update set misc = misc + 1, status = 'already seen'</b></span></div><div><span style="color: #ff00fe; font-family: courier;"><b>WHEN MATCHED and l.status = 'already seen' THEN</b></span></div></div><div><span style="color: #ff00fe; font-family: courier;"><b>&nbsp;DELETE</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">~&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc |&nbsp; status&nbsp;&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------+----------</span></div><div><span style="font-family: courier;">&nbsp;INSERT&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 0 | inserted</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MERGE 1</span></div><div><span style="font-family: courier;">demo-#&nbsp;</span></div></div><div><br /></div><div>The second pass updates the status column to 'already seen'.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;insert (id, tx_id, total_register, misc, status)</b></span></div><div><span style="font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0, 'inserted')</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>WHEN MATCHED and l.status = 'inserted' THEN</b></span></div><div><span style="color: #ffa400; font-family: courier;"><b>&nbsp;update set misc = misc + 1, status = 'already seen'</b></span></div><div><span style="font-family: courier;"><b>WHEN MATCHED and l.status = 'already seen' THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;DELETE</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc |&nbsp; &nbsp; status&nbsp; &nbsp;&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------+--------------</span></div><div><span style="font-family: courier;">&nbsp;UPDATE&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 1 | already seen</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><br /></div><div>MERGE 1</div><div><br /></div><div>The third pass of the query where status equals 'already seen' will DELETE the row.</div><div><br /></div><div><span style="font-family: courier;">demo=# <b>merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;insert (id, tx_id, total_register, misc, status)</b></span></div><div><span style="font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0, 'inserted')</b></span></div><div><span style="font-family: courier;"><b>WHEN MATCHED and l.status = 'inserted' THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;update set misc = misc + 1, status = 'already seen'</b></span></div><div><span style="color: #ff00fe; font-family: courier;"><b>WHEN MATCHED and l.status = 'already seen' THEN</b></span></div><div><span style="color: #ff00fe; font-family: courier;"><b>&nbsp;DELETE</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc |&nbsp; &nbsp; status&nbsp; &nbsp;&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------+--------------</span></div><div><span style="font-family: courier;">&nbsp;DELETE&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 1 | already seen</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MERGE 1</span></div><div><span style="font-family: courier;">demo=# <b>select * from ledger;</b></span></div><div><span style="font-family: courier;">&nbsp;id | tx_id | total_register | misc | status&nbsp;</span></div><div><span style="font-family: courier;">----+-------+----------------+------+--------</span></div><div><span style="font-family: courier;">(0 rows)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><div><br /></div><h3 style="text-align: left;">Summary</h3><div><br /></div><div>Merge is very handy for tasks like cash register sales reconciliation. You can make the WHEn MATCHED or WHEN NOT MATCHED logic as complicated as needed. Please peruse the <a href="https://www.postgresql.org/docs/current/sql-merge.html" rel="nofollow" target="_blank">manual page</a> as it provides more details on its use.</div><div><br /></div><div>In 'real life,' you probably want a primary key that is complex enough to be unique, like a store number, register number, and transaction number to ensure uniqueness. Make sure you use techniques like constraint checks on the data, as it is easier and cheaper to out than try to go back later to correct it.&nbsp;</div><div><br /></div><div>If you have an interesting use for Merger and can share it, please let me know.</div><div><br /></div><div><br /></div><div><br /></div></div>https://stokerpostgresql.blogspot.com/2025/02/postgresql-merge-to-reconcile-cash_17.html[email protected] (Dave Stokes)0tag:blogger.com,1999:blog-3110649280167018029.post-5228830072101424352Tue, 11 Feb 2025 16:14:00 +00002025-02-13T08:14:56.866-08:00PostgreSQL MERGE To Reconcile Cash Register Sales Data Part 1<p>&nbsp;I hoped to present the following at the <a href="https://postgresconf.org/conferences/postgresconf_global_2025" rel="nofollow" target="_blank">PostgreSQL Conference 2025</a> in March. But I can not make it to Orlando for this wonderful event (Please attend if you can! It is a fantastic event). Cash Register information is vital to retail businesses and it is often processed one line item at a time (pronounced 'S-L-O-W'). MERGE was added a few years ago to PostgreSQL's arsenal. It is powerful and easy to use.</p><h3 style="text-align: left;">The Cash Register</h3><p>The next time you are handed a receipt from a cash register, take the time to examine all the information on it. Time, date, location, total, line items, and possibly other information essential to the vendor's operations are on that slip of paper. There may be information not on the sales receipt collected for processing. Now imagine you are a major fast-food restaurant with tens of thousands of cash registers or more. All that data needs to be collected, transmitted, and processed.&nbsp; It is easy to see how efficiency is important.&nbsp;</p><h3 style="text-align: left;">MERGE</h3><p>The <a href="https://www.postgresql.org/docs/current/sql-merge.html" rel="nofollow" target="_blank">manual page </a>describes the function of MERGE distinctly as <i>MERGE performs actions that modify rows in the target table identified as target_table_name, using the data_source. MERGE provides a single SQL statement that can conditionally INSERT, UPDATE or DELETE rows, a task that would otherwise require multiple procedural language statements</i>. In the examples below the data source is the data from the cash register and the target table is the sales table.&nbsp;</p><p>Our simple example uses the following table for cash register data. We will assume that each cash register in our imaginary fast-food chain has a unique identification number.</p><div style="text-align: left;"><span style="font-family: courier;">demo=# <b>create table register (id int, tx_id int, amount money);</b><br />CREATE TABLE<br />demo=# <b>insert into register values (1,101,19.99);</b><br />INSERT 0 1<br />demo=#&nbsp;</span></div><div><br /></div><div>A table to record all the transactions is created.&nbsp; Not that this ledger table is empty and our first sale is ready to be processed.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>create table ledger (id int, tx_id int, total_register money, misc int);</b></span></div><div><span style="font-family: courier;">CREATE TABLE</span></div><div><span style="font-family: courier;">demo=# <b>select * from register;</b></span></div><div><span style="font-family: courier;">&nbsp;id | tx_id | amount&nbsp;</span></div><div><span style="font-family: courier;">----+-------+--------</span></div><div><span style="font-family: courier;">&nbsp; 1 |&nbsp; &nbsp;101 | $19.99</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=# <b>select * from ledger;</b></span></div><div><span style="font-family: courier;">&nbsp;id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">----+-------+----------------+------</span></div><div><span style="font-family: courier;">(0 rows)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><h3 style="text-align: left;">First Merge</h3><div><br /></div><div>The first time we merge, the code is simple. We compare the entries in the register table to see if there are matches in the ledger table.&nbsp; There are no entries in the leger table. A clause in the code moves the data from the register table into the ledger table if there are NO MATCHES.&nbsp; &nbsp; We end with the RETURNING line to report what has been performed (the INSERT) and the new values in the ledger table.</div><div><br /></div><div><div><span style="font-family: courier;">demo=# <b>merge into ledger l</b></span></div><div><span style="font-family: courier;"><b>using register r</b></span></div><div><span style="font-family: courier;"><b>on l.id = r.id</b></span></div><div><span style="font-family: courier;"><b>WHEN NOT MATCHED THEN</b></span></div><div><span style="font-family: courier;"><b>&nbsp;insert (id, tx_id, total_register, misc)</b></span></div><div><span style="font-family: courier;"><b>&nbsp;values (r.id, r.tx_id, r.amount, 0)</b></span></div><div><span style="font-family: courier;"><b>RETURNING merge_action(), l.*;</b></span></div><div><span style="font-family: courier;">&nbsp;merge_action | id | tx_id | total_register | misc&nbsp;</span></div><div><span style="font-family: courier;">--------------+----+-------+----------------+------</span></div><div><span style="font-family: courier;">&nbsp;INSERT&nbsp; &nbsp; &nbsp; &nbsp;|&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 0</span></div><div><span style="font-family: courier;">(1 row)</span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MERGE 1</span></div><div><span style="font-family: courier;">demo=#&nbsp;</span></div></div><div><br /></div><h3 style="text-align: left;">Second Merge</h3><p>If we run the same SQL code again, the results look a little different even if the data in the ledger tables does not change. This time there IS a match between the two tables. Later you will see how to right WHEN MATCH clauses.</p><div style="text-align: left;"><span style="font-family: courier;">demo=# <b>merge into ledger l<br />using register r<br />on l.id = r.id<br />WHEN NOT MATCHED THEN<br />&nbsp;insert (id, tx_id, total_register, misc)<br />&nbsp;values (r.id, r.tx_id, r.amount, 0)<br />RETURNING merge_action(), l.*;</b><br />&nbsp;merge_action | id | tx_id | total_register | misc&nbsp;<br />--------------+----+-------+----------------+------<br />(0 rows)<br />MERGE 0<br />demo=# <b>select * from ledger;</b><br />&nbsp;id | tx_id | total_register | misc&nbsp;<br />----+-------+----------------+------<br />&nbsp; 1 |&nbsp; &nbsp;101 |&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$19.99 |&nbsp; &nbsp; 0<br />(1 row)<br />demo=#&nbsp;</span></div><p style="text-align: left;">Sometimes the same data is applied twice which is a disaster in many cases. But in this case, there was no action taken.</p><p style="text-align: left;">What is needed is to compare the transaction IDs along with the register IDs to make sure we do not record twice the same line of data.&nbsp; heck, we probably in real life want a unique key made with the store number and the register id.&nbsp;</p><h3 style="text-align: left;">Next Time</h3><p style="text-align: left;">I am trying to keep these blogs short, if not sweet. And this one went on a bit longer than I thought.&nbsp; Next time we will look at more MERGE() functionality and explore the logic of making matches to optimize the performance of the query.</p><p style="text-align: left;"><br /></p>https://stokerpostgresql.blogspot.com/2025/02/postgresql-merge-to-reconcile-cash.html[email protected] (Dave Stokes)0