<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Thomas Lowry</title>
    <description>The latest articles on Forem by Thomas Lowry (@tommydreamer57).</description>
    <link>https://forem.com/tommydreamer57</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F143775%2F09e44367-1103-45a3-80f0-4a219f65ce1d.jpeg</url>
      <title>Forem: Thomas Lowry</title>
      <link>https://forem.com/tommydreamer57</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tommydreamer57"/>
    <language>en</language>
    <item>
      <title>How Functions and Composite Types Changed the Way I Use PostgreSQL</title>
      <dc:creator>Thomas Lowry</dc:creator>
      <pubDate>Mon, 27 May 2019 20:56:56 +0000</pubDate>
      <link>https://forem.com/tommydreamer57/how-functions-and-composite-types-changed-the-way-i-use-postgresql-7a2</link>
      <guid>https://forem.com/tommydreamer57/how-functions-and-composite-types-changed-the-way-i-use-postgresql-7a2</guid>
      <description>&lt;p&gt;Having learned learned just enough SQL to create some tables and perform the most simple CRUD operations during my time at a coding bootcamp, I was blown away when I learned about composite types in Postgres.&lt;/p&gt;

&lt;p&gt;I originally learned that an SQL query always returns a list, so if you want to return a JSON-like structure from your database, you either have to perform multiple SQL queries and stitch the results together in Node (or whatever other language you use), or you have to use something like GraphQL, that automatically generates the SQL you need and structures it in the format you specify.&lt;/p&gt;

&lt;p&gt;However, using composite types (a feature that only exists in Postgres, if I am not mistaken), you can not only return a JSON-like structure from a query, but you can also accept JSON-like data as an input to an operation.&lt;/p&gt;

&lt;p&gt;Please note that I'm not talking about STORING data in a composite type (which has its own much more limited use-case), I'm rather talking about using composite types as INPUTS and OUTPUTS of a query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Schema
&lt;/h2&gt;

&lt;p&gt;My go-to example-schema is one that represents classes in a school.&lt;/p&gt;

&lt;p&gt;Here's a simple schema to capture classes, students, teachers, and enrollments:&lt;br&gt;
(Note how students and teachers are both stored in the table "people")&lt;/p&gt;
&lt;h3&gt;
  
  
  Tables
&lt;/h3&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;teacher_id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;student_class_enrollments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;student_id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Sample Data
&lt;/h3&gt;

&lt;p&gt;Let's create a class just so we have something to query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- create a teacher and some students&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Tommy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Lowry'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- id 1&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Student'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'One'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- id 2&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Student'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Two'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;-- id 3&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Student'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Three'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- id 4&lt;/span&gt;

&lt;span class="c1"&gt;-- create a class&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;teacher_id&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Postgres Composite Types'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- enroll students in the class&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;student_class_enrollments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;student_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cid&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Querying: The Old Way
&lt;/h2&gt;

&lt;p&gt;Now if I wanted to retreive all the information about a class, I formerly would have written a query something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;teacher_id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;student_class_enrollments&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This would give me data something like:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;class id&lt;/th&gt;
&lt;th&gt;subject&lt;/th&gt;
&lt;th&gt;teacher id&lt;/th&gt;
&lt;th&gt;teacher first name&lt;/th&gt;
&lt;th&gt;teacher last name&lt;/th&gt;
&lt;th&gt;student id&lt;/th&gt;
&lt;th&gt;student first name&lt;/th&gt;
&lt;th&gt;student last name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Post...&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Thomas&lt;/td&gt;
&lt;td&gt;Lowry&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Student&lt;/td&gt;
&lt;td&gt;One&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Post...&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Thomas&lt;/td&gt;
&lt;td&gt;Lowry&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Student&lt;/td&gt;
&lt;td&gt;Two&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Post...&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Thomas&lt;/td&gt;
&lt;td&gt;Lowry&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Student&lt;/td&gt;
&lt;td&gt;Three&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;See how we have redundant, unstructured data? Not only are the class and teacher represented once for each student, but our list lacks any representation of our data's depth - class, teacher, and student are all lumped together in each node of the list instead of being organized into a heirarchy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composite Types
&lt;/h2&gt;

&lt;p&gt;Let's see how composite types can help us to format this data into a more reasonable structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; If you're not familiar with composite types, a composite type is a type that is 'composed' of other types. It allows you to have a single item that may contain other items - essentially it allows a nested structure. Composite types can be composed of built-in SQL types as well as other composite types.&lt;/p&gt;

&lt;p&gt;You can read more about PostgreSQL composite types in the &lt;a href="https://www.postgresql.org/docs/10/rowtypes.html"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To show you how this works, let's create some composite types that mirror our data structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- A `person` is composed of an `integer` and two `varchar`s titled 'id', 'first_name', and 'last_name', respectively.&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Likewise, a `class` is composed of an `integer`, a `varchar`, a `person`, and a `person[]` array, titled 'id', 'subject', 'teacher', and 'students', respectively.&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;teacher&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Notice how this structure somewhat resembles our tables, with a few key differences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In place of a one-to-many relationship (a single foreign key), we simply nest a teacher within a class.&lt;/li&gt;
&lt;li&gt;In place of a many-to-many relationship (a join table), we simply nest an array of students within a class.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Querying: The New Way
&lt;/h2&gt;

&lt;p&gt;Now we're ready to create a function that will output an entire class in the structure that we've defined using our composite types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Function Signature
&lt;/h2&gt;

&lt;p&gt;There are a few steps to this, so let's start with a function signature that takes in a class id and returns a class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;read_entire_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Variables
&lt;/h2&gt;

&lt;p&gt;Now we'll need to define the variables we will use in order to retreive each key/node to build our composite class:&lt;br&gt;
(This'll make more sense once we actually use them)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;read_entire_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;teacher&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Constructing a Composite Value / Explicit Type Casting
&lt;/h2&gt;

&lt;p&gt;Next, before we get into the difficult stuff, let's add our return statement. Here we'll use the &lt;code&gt;ROW()&lt;/code&gt; function, along with explicit type casting (&lt;code&gt;::class&lt;/code&gt;) to put our class together and return it. This might seem rather abstract at first, but under the hood, that's all a composite type is: a single row of field names and corresponding data types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;read_entire_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;teacher&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;

&lt;span class="c1"&gt;----------&lt;/span&gt;
    &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;     &lt;span class="c1"&gt;-- ROW() function to create composite&lt;/span&gt;
        &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;         &lt;span class="c1"&gt;-- these must be in correct order&lt;/span&gt;
        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;teacher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;students&lt;/span&gt;
    &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;-- explicit type casting&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that the order of the values used is important and must always be the same as in the declaration of the type. If you put them in the wrong order and the types don't match up, it'll throw an error when you run the function. Worse, if you put them in the wrong order and the types are compatible, you'll be silently mismatching keys in your query until you recognize that something's off.&lt;/p&gt;

&lt;p&gt;According to the docs, you actually can leave the &lt;code&gt;ROW&lt;/code&gt; keyword out, as long as the composite type has more than one field. To do this, you remove &lt;code&gt;ROW&lt;/code&gt; and leave the parentheses. You may also be able to leave out the explicit type-cast, since SQL will automatically cast the RETURN into the function's declared return type.&lt;/p&gt;

&lt;p&gt;Now you should be able to create the function and run it - however you'll get an empty class every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filling the Composite Value With Query Results
&lt;/h2&gt;

&lt;p&gt;All that's left now is to fill the data structure with query results from the database.&lt;/p&gt;

&lt;p&gt;We'll take this one step at a time, assuming minimal knowledge of the language.&lt;/p&gt;

&lt;h3&gt;
  
  
  Subject
&lt;/h3&gt;

&lt;p&gt;First, let's get the subject:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;read_entire_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;teacher&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;

&lt;span class="c1"&gt;----------&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;   &lt;span class="c1"&gt;-- selects `subject` from classes&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;                      &lt;span class="c1"&gt;-- assigns query result to variable `subject`&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;

    &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;teacher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;students&lt;/span&gt;
    &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SELECT ... INTO&lt;/code&gt; will assign the result of the query to whatever variable is specified, so that we can reference the result later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Teacher
&lt;/h3&gt;

&lt;p&gt;Now, let's get the teacher:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;read_entire_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;
    &lt;span class="n"&gt;tid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- add teacher id variable&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;
    &lt;span class="n"&gt;teacher&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;

    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;----------&lt;/span&gt;
    &lt;span class="c1"&gt;-- EITHER --------&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;teacher&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;                             &lt;span class="c1"&gt;-- subquery to select correct teacher&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;teacher_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;-- OR --------&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;teacher_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;          &lt;span class="c1"&gt;-- extra query to get `teacher_id` instead of using subquery&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;teacher&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;-- END OR ----&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;

    &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;teacher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;students&lt;/span&gt;
    &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There's not much new here, just a potential subquery to get the teacher of the class. I'll keep the subquery since it's more concise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Students
&lt;/h3&gt;

&lt;p&gt;Now we just need to get the students. This one is a bit more difficult, since we're dealing with a list of students. Here's how you do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;read_entire_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cid&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
    &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;teacher&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="n"&gt;PERSON&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;

    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;teacher&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;teacher_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;classes&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;----------&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ARRAY_AGG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;people&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;student_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;student_class_enrollments&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;
        &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cid&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;----------&lt;/span&gt;

    &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;teacher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;students&lt;/span&gt;
    &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ARRAY_AGG()&lt;/code&gt; takes the results of the query and converts them into an array, so they are compatible with the array type of the students variable. &lt;code&gt;ROW(...)::person&lt;/code&gt; converts each student into a composite person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Done!
&lt;/h2&gt;

&lt;p&gt;Now we have a function that requires nothing more than a class id to bundle up a class with its teacher and students into a nice, nested, JSON-like structure.&lt;/p&gt;

&lt;p&gt;In order to invoke the function, run &lt;code&gt;SELECT * FROM read_entire_class(1)&lt;/code&gt; and look at the beautifully nested results!&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
      <category>database</category>
      <category>learning</category>
    </item>
    <item>
      <title>Things I've Learned as a Web Development Contractor</title>
      <dc:creator>Thomas Lowry</dc:creator>
      <pubDate>Sat, 18 May 2019 21:23:54 +0000</pubDate>
      <link>https://forem.com/tommydreamer57/things-i-ve-learned-as-a-web-development-contractor-8eb</link>
      <guid>https://forem.com/tommydreamer57/things-i-ve-learned-as-a-web-development-contractor-8eb</guid>
      <description>&lt;h3&gt;
  
  
  A New Experience
&lt;/h3&gt;

&lt;p&gt;Several months ago I started working as a software development contractor. I began working for a single client on a part-time basis while continuing to work part-time as a mentor at DevMountain.&lt;/p&gt;

&lt;p&gt;In recent months, and since leaving my job at DevMountain, my working hours have evolved from a regular, enforced schedule, working mostly in person, into an almost entirely remote, autonomous agenda. This shift has presented me with some incredibly difficult challenges that have taught me some valuable lessons and altered my mindset about work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges of an Autonomous Schedule
&lt;/h3&gt;

&lt;p&gt;One of the most difficult challenges that has come with this change is that I have had to constantly reevaluate my expectations of myself and of my work. For example, I went into this year expecting to clock 40 hours a week, but very frequently found myself too stressed and anxiety-ridden to make even 30. To make matters worse, I became stressed that I wasn't meeting my expectations, and thus my stress and anxiety compounded themselves against me.&lt;/p&gt;

&lt;p&gt;When I stopped to really think about why I wasn't meeting my expectations for hourly work, I recognized quite a few things that I had failed to incorporate into my perspective.&lt;/p&gt;




&lt;p&gt;First, I recognized that my expectation of clocking 40 hours a week failed to account for hours that I work on things that are not billable to my one client. Most prominently, until March this year I was still working 11 hours a week at DevMountain, and these were inconvenient hours on Tuesday/Thursday nights and Saturday mornings.&lt;/p&gt;

&lt;p&gt;Additionally, I was still mostly working in Salt Lake, which provided only 6-1/2 working hours at the cost of a 1-1/2 hour commute two-ways. This, combined with a change in church hours on Sundays meant that I had 0 mornings a week to myself - something that is very important to my mental health. Also, since quitting DevMountain, I've begun working on some other endeavors which are not accounted for on the clock, such as starting a meetup here in Provo.&lt;/p&gt;

&lt;p&gt;Based on all of these factors, I reevaluated my expectation and accepted that I'm likely not going to reach 40 hours a week, and even if I did, it would come at the cost of things that are much more important to me. I have a new goal to clock 30 hours a week, understanding that I am putting plenty of time into non-billable work, and that this is my intentional ideal. I am also considering increasing my hourly billing rate to account for this recognition - something that I am already discussing with my client.&lt;/p&gt;




&lt;p&gt;Secondly, I recognized that my contract work involves several aspects that I hadn't incorporated into my work expectations. I realized that I was expecting myself to either be "programming" or collaborating anytime I'm on the clock, but that I also need to allow myself time for project management, planning, and research, among other things. I needed to expel the expectation that I can just sit down and write the application and recognize that this job doesn't work like all the other jobs I've had where I just sat down (or stood up) and "worked".&lt;/p&gt;

&lt;p&gt;Now that I am doing creative work, I have to allow myself time for creativity and not stress out if I spend multiple hours at a time simply contemplating problems, researching technologies, and designing solutions. Also, since I am one of only two people working on the project, I need to allow adequate time for project management, so that I remain on track with my work. Until recently, I never really understood the role all these things play in a software development job.&lt;/p&gt;

&lt;p&gt;This incomplete perspective caused me to expect much quicker results from my work than were realistic, which caused a great deal of stress and anxiety around my work, which led both to less effective time on the clock and less time on the clock altogether.&lt;/p&gt;




&lt;p&gt;Reevaluating my expectations of what my work looks like has helped me to take it one step at a time and feel confident that I am providing the best value to my client even during the hours that I'm not hacking at my keyboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I've Learned
&lt;/h3&gt;

&lt;p&gt;I've learned from this that focusing solely on the completion of my goals is often not nearly as productive as focusing on discovering and removing the obstacles that are preventing me from accomplishing my goals.&lt;/p&gt;

&lt;p&gt;The truth is, when I have something that I truly want to accomplish, I don't have to force myself to put time and effort toward that goal. In fact, when I have a goal that I'm truly passionate about, I more often than not I have to force myself NOT to work on that thing too much - I have to force myself to stop for a minute here and there so I can eat, sleep, shower, be social, and take care of myself.&lt;/p&gt;

&lt;p&gt;So when I find myself in a situation where I am not putting as much time and effort into a goal as I would like to, then there is almost always one of two conditions at play: either I don't really care about the goal as much as I thought I did, or else there is something unseen holding me back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-Evaluation
&lt;/h3&gt;

&lt;p&gt;Before I stop to think about it, I tend to feel frustrated with myself and stressed as to why I am not progressing toward my goal. I tend to see myself as lazy, apathetic, and unproductive. In this situation it's important for me to recognize that my struggle comes not from a lack of virtue (i.e. diligence, work ethic, perseverance), but from a lack of perspective, and that I need to stop for a minute to engage in some introspection.&lt;/p&gt;

&lt;p&gt;Three questions I need to ask myself are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is my goal, along with its associated expectations, in line with my values and capabilities?&lt;/li&gt;
&lt;li&gt;What unseen challenges are holding me back from pursuing this goal and meeting my expectations?&lt;/li&gt;
&lt;li&gt;What changes do I need to make to my goals and expectations in order to more effortlessly progress toward my goal?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These questions help me to evaluate whether I am expecting myself to work toward something that I truly value and whether I have accounted for all the steps necessary to accomplish the goal, and then to adjust my goals and expectations accordingly.&lt;/p&gt;

&lt;p&gt;My anxiety is usually caused by a lack of clarity regarding steps that I need to take to move forward, and going through this process of reevalutating expectations helps me to ground myself once more in reality so that I can move forward one step at a time.&lt;/p&gt;




&lt;p&gt;I hope my story and the things I've shared can help you to be more congruent in aligning your goals and expectations with your actions and your reality. Let me know if you relate to this story in any way and what you have learned from your experiences!&lt;/p&gt;

</description>
      <category>goals</category>
      <category>expectations</category>
      <category>mentalhealth</category>
      <category>planning</category>
    </item>
    <item>
      <title>Does Brooks' Law Apply to a Project with Only a Single Developer?</title>
      <dc:creator>Thomas Lowry</dc:creator>
      <pubDate>Tue, 16 Apr 2019 16:53:17 +0000</pubDate>
      <link>https://forem.com/tommydreamer57/does-brooks-law-apply-to-a-project-with-only-a-single-developer-7o7</link>
      <guid>https://forem.com/tommydreamer57/does-brooks-law-apply-to-a-project-with-only-a-single-developer-7o7</guid>
      <description>

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ej6kBrUo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.leadingagile.com/wp-content/uploads/2018/02/Applying-Brooks-Law.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ej6kBrUo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.leadingagile.com/wp-content/uploads/2018/02/Applying-Brooks-Law.jpg" alt="brooks' law"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So here's the deal:&lt;/p&gt;

&lt;p&gt;I'm working on this project that is pretty large, and extremely nuanced (it's a solution for automatically drafting glass in a building after the building has been architected, but before construction). I'm working on a team with just one other person. He is a designer who has been in the architecture industry for several years and has partnered with the business owner to create this software. So, I'm the one who understands how software works and he's the one who understands how the architecture works.&lt;/p&gt;

&lt;p&gt;Anyways, I am the only developer and have therefore taken on a ton of responsibilities, including architecting the entire solution, designing a database, organizing the workflow into three data-entry applications and one end-user application, choosing which technologies to use, building the entire front-end of those applications, and so on.&lt;/p&gt;

&lt;p&gt;There are some things I love about being a solo developer - the codebase is all mine; I know it inside and out; and so on.&lt;/p&gt;

&lt;p&gt;But I often wish I had another developer to take on some of the tasks and to collaborate with, and I feel like adding another developer to the team could greatly increase our productivity if we do it right.&lt;/p&gt;

&lt;p&gt;However, I keep running into the idea of Brooks' Law that states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Adding human resources to a late software project makes it later.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My question is:&lt;/p&gt;

&lt;p&gt;Does this law apply to small teams like mine?&lt;/p&gt;

&lt;p&gt;Everything I read on the subject has to do with teams at least several times as large as mine, with project managers and senior developers and such, so it's hard to relate to those examples. Articles consistently refer to the fact that a newcomer to a project ALWAYS has less experience in the project and therefore can NEVER contribute as quickly or as well to a project as those already working on it. However, in my situation adding one person is equivalent to doubling the number of software developers on the project, so there is bound to be a point where the increased productivity of having two developers exceeds the initial cost of training the new developer.&lt;/p&gt;

&lt;p&gt;What tips or experiences do you guys have that could give me insight into how to know when and how to add another developer to the project?&lt;/p&gt;

&lt;p&gt;Thanks in advance!&lt;/p&gt;


</description>
      <category>help</category>
      <category>brookslaw</category>
      <category>teams</category>
      <category>efficiency</category>
    </item>
  </channel>
</rss>
