<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Jools' blog of things]]></title><description><![CDATA[Jools' blog of things]]></description><link>https://blog.juliendephix.fr</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 02:43:39 GMT</lastBuildDate><atom:link href="https://blog.juliendephix.fr/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Calling all beginners: simple SQL challenge]]></title><description><![CDATA[Hi!
Here's a little challenge for beginners.
We have a users table and we need to know the number of users in 3 age ranges.

if age < 18 then age range is minor.
if age < 65 then age range is adult.
if age >= 65 then age range is senior.

Table struc...]]></description><link>https://blog.juliendephix.fr/calling-all-beginners-simple-sql-challenge</link><guid isPermaLink="true">https://blog.juliendephix.fr/calling-all-beginners-simple-sql-challenge</guid><category><![CDATA[Beginner Developers]]></category><category><![CDATA[SQL]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Julien Dephix]]></dc:creator><pubDate>Mon, 11 Dec 2023 08:48:33 GMT</pubDate><content:encoded><![CDATA[<p>Hi!</p>
<p>Here's a little challenge for beginners.</p>
<p>We have a <code>users</code> table and we need to know <em>the number of users in 3 age ranges</em>.</p>
<ul>
<li>if age &lt; 18 then age range is <code>minor</code>.</li>
<li>if age &lt; 65 then age range is <code>adult</code>.</li>
<li>if age &gt;= 65 then age range is <code>senior</code>.</li>
</ul>
<h2 id="heading-table-structure">Table structure</h2>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> (
  <span class="hljs-keyword">id</span> <span class="hljs-built_in">int</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> AUTO_INCREMENT,
  age <span class="hljs-built_in">SMALLINT</span>,
  PRIMARY <span class="hljs-keyword">KEY</span> (<span class="hljs-string">`id`</span>)
) <span class="hljs-keyword">ENGINE</span>=<span class="hljs-keyword">InnoDB</span>;
</code></pre>
<h2 id="heading-sample-data">Sample data</h2>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">users</span>(age)
<span class="hljs-keyword">VALUES</span>
(<span class="hljs-number">2</span>), (<span class="hljs-number">4</span>), (<span class="hljs-number">6</span>),
(<span class="hljs-number">12</span>), (<span class="hljs-number">14</span>), (<span class="hljs-number">16</span>), (<span class="hljs-number">21</span>), (<span class="hljs-number">25</span>),
(<span class="hljs-number">27</span>), (<span class="hljs-number">31</span>), (<span class="hljs-number">33</span>), (<span class="hljs-number">37</span>), (<span class="hljs-number">39</span>),
(<span class="hljs-number">57</span>), (<span class="hljs-number">57</span>), (<span class="hljs-number">59</span>), (<span class="hljs-number">61</span>), (<span class="hljs-number">63</span>), (<span class="hljs-number">65</span>)
;
</code></pre>
<h2 id="heading-expected-results">Expected results</h2>
<pre><code class="lang-plaintext">| ageRange | nb     |
| -------- | ------ |
| 6        | minor  |
| 12       | adult  |
| 1        | senior |
</code></pre>
<p>Go ahead and give it a try!</p>
<p>You might want to read about the following statements: <code>IF</code>, <code>CASE/WHEN</code> and <code>GROUP BY</code>.</p>
<h2 id="heading-a-possible-solution">A possible solution</h2>
<p>Note that I'm using MySQL but since it is standard SQL you should be able to port it to another DB engine.</p>
<p>First we need to determine age ranges and for that we're going to add a <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/case.html">CASE statement</a>.</p>
<pre><code class="lang-sql">CASE
    WHEN age &lt; 18 THEN 'minor'
    WHEN age &lt; 65 THEN 'adult'
    ELSE 'senior'
<span class="hljs-keyword">END</span> <span class="hljs-keyword">as</span> ageRange
</code></pre>
<p>Pretty straightforward, right? We could have used two <code>IF</code> statements but I opted for <code>CASE/WHEN</code> as I find it elegant. :)</p>
<p>Add that to a <code>SELECT</code> statement and you get users' age along with their age group.</p>
<p>Next we need to count how many users belong to each group. It's as simple as adding a <code>GROUP BY ageRange</code> clause.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span>
    <span class="hljs-keyword">count</span>(*),
    <span class="hljs-keyword">CASE</span>
        <span class="hljs-keyword">WHEN</span> age &lt; <span class="hljs-number">18</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">'minor'</span>
        <span class="hljs-keyword">WHEN</span> age &lt; <span class="hljs-number">65</span> <span class="hljs-keyword">THEN</span> <span class="hljs-string">'adult'</span>
        <span class="hljs-keyword">ELSE</span> <span class="hljs-string">'senior'</span>
    <span class="hljs-keyword">END</span> <span class="hljs-keyword">as</span> ageRange
<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> ageRange
</code></pre>
<p>And we're done!</p>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Let's have fun with MySQL JSON functions!]]></title><description><![CDATA[Hello, coders! 💻
Intro
I'd like to talk about SQL and how we can have fun with the many functions it has to offer. Today we'll have a look at how we can leverage JSON functions to help us build queries.
What we want to retrieve from our database
Let...]]></description><link>https://blog.juliendephix.fr/lets-have-fun-with-mysql-json-functions</link><guid isPermaLink="true">https://blog.juliendephix.fr/lets-have-fun-with-mysql-json-functions</guid><category><![CDATA[Tutorial]]></category><category><![CDATA[SQL]]></category><category><![CDATA[MySQL]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Julien Dephix]]></dc:creator><pubDate>Fri, 08 Dec 2023 16:00:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1702051038308/761af2e4-442c-4174-b543-691b283c9724.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello, coders! 💻</p>
<h2 id="heading-intro">Intro</h2>
<p>I'd like to talk about SQL and how we can have fun with the many functions it has to offer. Today we'll have a look at how we can leverage JSON functions to help us build queries.</p>
<h2 id="heading-what-we-want-to-retrieve-from-our-database">What we want to retrieve from our database</h2>
<p>Let's say we log actions our users perform on some app. Actions can be <code>start</code> or <code>stop</code> for simplicity's sake.</p>
<p>You're tasked to craft a query to retrieve ID of users whose latest actions is <code>start</code>.</p>
<h2 id="heading-database-structure-and-data">Database structure and data</h2>
<p>I'm using MySQL for this tutorial and the table we'll be working with is as follows:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> actions (
  action_name <span class="hljs-built_in">varchar</span>(<span class="hljs-number">20</span>) <span class="hljs-built_in">CHARACTER</span> <span class="hljs-keyword">SET</span> utf8mb4 <span class="hljs-keyword">COLLATE</span> utf8mb4_unicode_ci <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  user_id <span class="hljs-built_in">tinyint</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  created_at datetime <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>
) <span class="hljs-keyword">ENGINE</span>=<span class="hljs-keyword">InnoDB</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">CHARSET</span>=utf8mb4 <span class="hljs-keyword">COLLATE</span>=utf8mb4_unicode_ci;
</code></pre>
<p>And some data to play with:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> actions (action_name, user_id, created_at) <span class="hljs-keyword">VALUES</span>
(<span class="hljs-string">'start'</span>, <span class="hljs-string">'1'</span>, <span class="hljs-string">'2023-11-24 10:48:00'</span>),
(<span class="hljs-string">'stop'</span>, <span class="hljs-string">'1'</span>, <span class="hljs-string">'2023-11-24 10:49:00'</span>),
(<span class="hljs-string">'stop'</span>, <span class="hljs-string">'1'</span>, <span class="hljs-string">'2023-11-24 10:49:00'</span>),
(<span class="hljs-string">'start'</span>, <span class="hljs-string">'1'</span>, <span class="hljs-string">'2023-11-24 10:50:00'</span>),
(<span class="hljs-string">'stop'</span>, <span class="hljs-string">'1'</span>, <span class="hljs-string">'2023-11-24 10:51:00'</span>),

(<span class="hljs-string">'start'</span>, <span class="hljs-string">'2'</span>, <span class="hljs-string">'2023-11-24 10:52:00'</span>),
(<span class="hljs-string">'stop'</span>, <span class="hljs-string">'2'</span>, <span class="hljs-string">'2023-11-24 10:53:00'</span>),
(<span class="hljs-string">'start'</span>, <span class="hljs-string">'2'</span>, <span class="hljs-string">'2023-11-24 10:54:00'</span>),
(<span class="hljs-string">'stop'</span>, <span class="hljs-string">'2'</span>, <span class="hljs-string">'2023-11-24 10:55:00'</span>),
(<span class="hljs-string">'start'</span>, <span class="hljs-string">'2'</span>, <span class="hljs-string">'2023-11-24 10:56:00'</span>),

(<span class="hljs-string">'start'</span>, <span class="hljs-string">'3'</span>, <span class="hljs-string">'2023-11-24 10:48:00'</span>),
(<span class="hljs-string">'stop'</span>, <span class="hljs-string">'3'</span>, <span class="hljs-string">'2023-11-24 10:49:00'</span>),

(<span class="hljs-string">'start'</span>, <span class="hljs-string">'4'</span>, <span class="hljs-string">'2023-11-24 10:50:00'</span>)
;
</code></pre>
<h2 id="heading-expectations">Expectations</h2>
<p>Based on the above data users with ID 2 and 4 are the ones we're looking for.</p>
<h2 id="heading-challenge-rule">Challenge / rule</h2>
<p>I tried to do that in a single query, no joins or sub queries. Why? Ya know, for fun and as learning experience!</p>
<h2 id="heading-query-build-up">Query build up</h2>
<p><a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat">GROUP_CONCAT</a> immediately came to mind. It allows us to get a list of actions, grouped by user, ordered by created date in descending order so the latest action is the first from left to right.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> user_id, <span class="hljs-keyword">GROUP_CONCAT</span>(action_name <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> created_at <span class="hljs-keyword">DESC</span>) <span class="hljs-keyword">as</span> list_of_actions
<span class="hljs-keyword">FROM</span> actions
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> user_id;
</code></pre>
<p>Which results in:</p>
<pre><code class="lang-plaintext">| user_id | list_of_actions             |
| ------- | --------------------------- |
| 1       | stop,start,stop,stop,start  |
| 2       | start,stop,start,stop,start |
| 3       | stop,start                  |
| 4       | start                       |
</code></pre>
<p>We can easily see that users with ID 2 and 4 are the ones we want but how can we check that the leftmost value is <code>start</code>?</p>
<p>How about we convert <code>list_of_actions</code> into an array, grab the first element and make sure it is <code>start</code>?</p>
<p>Sounds fun, let's do this.</p>
<h3 id="heading-what-does-a-json-array-look-like">What does a JSON array look like?</h3>
<p>It's a list of comma separated values, each surrounded by double quotes. Example: <code>["first_value","second_value"]</code></p>
<h3 id="heading-values-double-quotes-and-commas">Values, double quotes and commas</h3>
<p>Let's start by surrounding each value with double quotes.</p>
<p><code>GROUP_CONCAT</code>'s default separator is <code>,</code> but we can pass any string we want. What would we get if we joined with <code>","</code> instead?</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> user_id, <span class="hljs-keyword">GROUP_CONCAT</span>(action_name <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> created_at <span class="hljs-keyword">DESC</span> SEPARATOR <span class="hljs-string">'","'</span>) <span class="hljs-keyword">as</span> list_of_actions
<span class="hljs-keyword">FROM</span> actions
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> user_id;
</code></pre>
<p>which gives us:</p>
<pre><code class="lang-plaintext">| user_id | list_of_actions                     |
| ------- | ----------------------------------- |
| 1       | stop","start","stop","stop","start  |
| 2       | start","stop","start","stop","start |
| 3       | stop","start                        |
| 4       | start                               |
</code></pre>
<p>Almost! We're just missing a <code>"</code> both at the start and end of <code>list_of_actions</code>. Let's fix this and, while we're at it, let's also add <code>[</code> at the start and <code>]</code> at the end using <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_concat">CONCAT</a>!</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> user_id, <span class="hljs-keyword">CONCAT</span>(<span class="hljs-string">'["'</span>, <span class="hljs-keyword">GROUP_CONCAT</span>(action_name <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> created_at <span class="hljs-keyword">DESC</span> SEPARATOR <span class="hljs-string">'","'</span>), <span class="hljs-string">'"]'</span>) <span class="hljs-keyword">as</span> list_of_actions
<span class="hljs-keyword">FROM</span> actions
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> user_id;
</code></pre>
<p>which results in:</p>
<pre><code class="lang-plaintext">| user_id | list_of_actions                         |
| ------- | --------------------------------------- |
| 1       | ["stop","start","stop","stop","start"]  |
| 2       | ["start","stop","start","stop","start"] |
| 3       | ["stop","start"]                        |
| 4       | ["start"]                               |
</code></pre>
<p>Looking good! <code>list_of_actions</code> is now a valid JSON string.</p>
<h3 id="heading-getting-the-first-element-of-each-array">Getting the first element of each array</h3>
<p>Now we just need to tell MySQL to consider <code>list_of_actions</code> as JSON and then take the first element. Converting is done with <code>CAST(value) AS JSON</code>.</p>
<p>And to extract the first value of an array we use <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/json-search-functions.html#function_json-extract">JSON_EXTRACT</a>.</p>
<p>Our query is now:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> user_id, JSON_EXTRACT( <span class="hljs-keyword">CAST</span>( <span class="hljs-keyword">CONCAT</span>( <span class="hljs-string">'["'</span>, <span class="hljs-keyword">GROUP_CONCAT</span>( action_name <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> created_at <span class="hljs-keyword">DESC</span> SEPARATOR <span class="hljs-string">'","'</span> ), <span class="hljs-string">'"]'</span> ) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">JSON</span> ), <span class="hljs-string">'$[0]'</span> ) <span class="hljs-keyword">as</span> latest_action
<span class="hljs-keyword">FROM</span> actions
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> user_id;
</code></pre>
<p>which results in:</p>
<pre><code class="lang-plaintext">| user_id | latest_action |
| ------- | ------------- |
| 1       | "stop"        |
| 2       | "start"       |
| 3       | "stop"        |
| 4       | "start"       |
</code></pre>
<p>Excellent!</p>
<h2 id="heading-final-query">Final query</h2>
<p>Since we said we just wanted users whose latest action is <code>start</code> then we can leave <code>user_id</code> in the <code>SELECT</code> clause and move our JSON manipulating functions to an <code>HAVING</code> clause:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> user_id
<span class="hljs-keyword">FROM</span> actions
<span class="hljs-keyword">GROUP</span> <span class="hljs-keyword">BY</span> user_id
<span class="hljs-keyword">HAVING</span> JSON_EXTRACT( <span class="hljs-keyword">CAST</span>( <span class="hljs-keyword">CONCAT</span>( <span class="hljs-string">'["'</span>, <span class="hljs-keyword">GROUP_CONCAT</span>( action_name <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> created_at <span class="hljs-keyword">DESC</span> SEPARATOR <span class="hljs-string">'","'</span> ), <span class="hljs-string">'"]'</span> ) <span class="hljs-keyword">AS</span> <span class="hljs-keyword">JSON</span> ), <span class="hljs-string">'$[0]'</span> ) = <span class="hljs-string">'start'</span>;
</code></pre>
<p>and boom:</p>
<pre><code class="lang-plaintext">| user_id |
| ------- |
| 2       |
| 4       |
</code></pre>
<p>Thank you very much indeed.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you've learned a thing or two while using functions such as <code>GROUP_CONCAT</code>, <code>CONCAT</code>, <code>CAST</code> or <code>JSON_EXTRACT</code>.</p>
<p>You probably have better alternatives so please don't hesitate to share your methods! That's how we get better.</p>
<p>Would you be interested in other articles where we experiment more with JSON (or other) functions? Let me know in the comments.</p>
<p>Till next time.</p>
<p>Happy coding! ⌨️</p>
]]></content:encoded></item></channel></rss>