<?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>DEV Community: Ziga Petek</title>
    <description>The latest articles on DEV Community by Ziga Petek (@ziga_petek_c2bfdf4d05a5cb).</description>
    <link>https://dev.to/ziga_petek_c2bfdf4d05a5cb</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%2F2554112%2F64b73be8-a158-455d-b3ef-beea3357ce72.png</url>
      <title>DEV Community: Ziga Petek</title>
      <link>https://dev.to/ziga_petek_c2bfdf4d05a5cb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ziga_petek_c2bfdf4d05a5cb"/>
    <language>en</language>
    <item>
      <title>The Power of Postgres Arrays in Your Supabase Projects</title>
      <dc:creator>Ziga Petek</dc:creator>
      <pubDate>Fri, 26 Sep 2025 05:43:56 +0000</pubDate>
      <link>https://dev.to/ziga_petek_c2bfdf4d05a5cb/the-power-of-postgres-arrays-in-your-supabase-projects-2iml</link>
      <guid>https://dev.to/ziga_petek_c2bfdf4d05a5cb/the-power-of-postgres-arrays-in-your-supabase-projects-2iml</guid>
      <description>&lt;p&gt;Relational databases are great at organizing structured data, but I've always found it a messy way to store multiple values as lists—like tags for blog posts or values from multiselect fields. The traditional approach means creating join tables, which basically requires intermediate tables linking records in many-to-many relationships. &lt;/p&gt;

&lt;p&gt;Using Postgres &lt;a href="https://supabase.com/docs/guides/database/arrays" rel="noopener noreferrer"&gt;arrays&lt;/a&gt;, you can store a list of values like &lt;code&gt;['news', 'tech', 'ai']&lt;/code&gt; directly in a column. This keeps your schema cleaner and lets you write much simpler queries.&lt;/p&gt;

&lt;p&gt;In this guide, I'll show you how arrays can simplify your database design, how to query them with real SQL examples I've used, and when they actually improve performance in everyday projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use Postgres Arrays in the First Place
&lt;/h2&gt;

&lt;p&gt;Postgres arrays allow you to store multiple values in a single column, which is why they can be a powerful alternative to relational modeling. They are ideal for rapid prototyping and small-scale features like tags, checklists, or multiselect form inputs. In traditional database modeling, you have to create and manage multiple lookup and join tables. For example, to support tagging, you'd typically have a &lt;code&gt;products&lt;/code&gt; table, a &lt;code&gt;tags&lt;/code&gt; table, and a &lt;code&gt;product_tags&lt;/code&gt; join table to associate multiple tags with each product. With Postgres arrays, you can represent related values directly in one field. This makes arrays particularly useful for quick prototyping and for simple lists that don't require separate records with extra details.&lt;/p&gt;

&lt;p&gt;Using arrays instead of normalized tables can lead to performance improvements. You avoid the overhead of join operations, extra indexes, and multitable queries. This is particularly useful in read-heavy applications that fetch entire records along with their associated attributes. Arrays also help keep your schema simple and easier to maintain. Not only is this useful for performance, but it also makes onboarding new developers faster.&lt;/p&gt;

&lt;p&gt;Postgres arrays shine in practical scenarios where simplicity and speed matter. You can use them for product tagging, storing selected features or colors in a product catalog, or saving survey responses. In all these cases, arrays offer a straightforward and performant alternative to traditional many-to-many relationships. In the next sections, I'll show you how arrays can simplify common scenarios like product tagging, helping you reduce complexity in both your database design and your queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Array Columns in Postgres
&lt;/h2&gt;

&lt;p&gt;When you use Postgres arrays, you work with actual data types, not just comma-separated strings. This gives you access to proper validation and powerful array operations that aren't possible with plain text.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;text[]&lt;/code&gt; stores an array of strings, while &lt;code&gt;integer[]&lt;/code&gt;, &lt;code&gt;boolean[]&lt;/code&gt;, &lt;code&gt;date[]&lt;/code&gt;, and &lt;code&gt;numeric[]&lt;/code&gt; restrict values to their respective types. This ensures consistency and prevents invalid data. You can even store structured content using &lt;code&gt;jsonb[]&lt;/code&gt;, which allows you to keep arrays of JSON objects in a single column.&lt;/p&gt;

&lt;p&gt;You can define and populate array columns in Postgres using either the Supabase Table Editor or the SQL Editor. Using Supabase is the easier option: you can explore Postgres features right in the browser without installing or configuring anything locally.&lt;/p&gt;

&lt;p&gt;Let's walk through a real-life example. First, I'll explain how to create a &lt;code&gt;products&lt;/code&gt; table with a &lt;code&gt;tags&lt;/code&gt; field using a Postgres text array. Based on this table, I'll then show you how to insert rows with multiple tags and perform common operations like filtering, searching, and manipulating tag values using array functions.&lt;/p&gt;

&lt;p&gt;First, create a project in &lt;a href="https://supabase.com/dashboard/new" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp01x7l6kxja271tmlpe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmp01x7l6kxja271tmlpe.png" alt="Create a new project in Supabase" width="800" height="306"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open it, and choose between the Table Editor and SQL Editor to create and manage your tables:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flowk4p1bsyinmsu7z2os.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flowk4p1bsyinmsu7z2os.png" alt="Select SQL Editor in Supabase dashboard" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, use the SQL Editor to define a &lt;code&gt;products&lt;/code&gt; table. Each product can have multiple tags, such as "organic", "eco-friendly", or "sale", and multiple options, like color and size, which is stored in a &lt;code&gt;JSONB[]&lt;/code&gt; array. Your table definition will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&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;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&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;price&lt;/span&gt; &lt;span class="nb"&gt;NUMERIC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&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;-- Array of tags for product categorization&lt;/span&gt;
    &lt;span class="k"&gt;options&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;                 &lt;span class="c1"&gt;-- Array of product options, like color and size&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this setup, you only need one table to store all relevant product data. You avoid joins and additional tables, keeping your schema simple and efficient.&lt;/p&gt;

&lt;p&gt;Run the above query in the SQL editor to create your &lt;code&gt;products&lt;/code&gt; table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fousxdsghn2pe7ktpzuij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fousxdsghn2pe7ktpzuij.png" alt="Products table" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inserting and Updating Array Data in Postgres
&lt;/h2&gt;

&lt;p&gt;You can insert and update array data in Postgres using the &lt;code&gt;ARRAY&lt;/code&gt; keyword. For example, if you want to add a T-shirt product to your &lt;code&gt;products&lt;/code&gt; table with multiple tags, run the following in the Supabase SQL Editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&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;'T-Shirt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Soft cotton T-shirt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'cotton'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'summer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'men'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After executing this, open the Table Editor to view your newly added row:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdn2ammd32tyqycbos2zi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdn2ammd32tyqycbos2zi.png" alt="New row added" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't specify a value for the array column, Postgres sets it to &lt;code&gt;NULL&lt;/code&gt; by default. If you want to insert an empty array instead of a null value, you can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&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;'Mug'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Ceramic coffee mug'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[]::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This inserts a new row into your table:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1nxp3cpizfpw2punv42.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1nxp3cpizfpw2punv42.png" alt="Inserting an empty array" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Postgres correctly distinguishes between &lt;code&gt;NULL&lt;/code&gt; and an empty array. If you try to query for &lt;code&gt;tags = NULL&lt;/code&gt;, you'll get no results because comparisons with &lt;code&gt;NULL&lt;/code&gt; don't behave the way they do with regular values.&lt;/p&gt;

&lt;p&gt;You can also insert JSONB data into an array, just like other types. This is useful when storing flexible, structured items, such as per-user settings, third-party credentials, or service configurations. In your case, you can save store product options in a &lt;code&gt;jsonb[]&lt;/code&gt; column. Just keep in mind that Postgres doesn't enforce the structure of JSON values. So if you're using this in a real-world application, you'll need to handle validation at the application level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;options&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;'Hat'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'Wide brim hat'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'men'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'winter'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'{"color":"white", "size":"M", "images":["http://www.image1.img","http://www.image2.img","http://www.image3.img"]}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{"color":"red", "size":"S", "images":[]}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jsonb&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;With just a single &lt;code&gt;INSERT&lt;/code&gt;, you save structured, multivalued data in one place. You did not need any extra joins or tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Efficiently Querying Arrays
&lt;/h2&gt;

&lt;p&gt;You can query Postgres arrays in many ways using built-in operators and functions. For example, to find rows where the &lt;code&gt;tags&lt;/code&gt; array exactly matches a specific sequence, you can run this query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'cotton'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'summer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'men'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns only one result: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fwqekzhbhfayutgn6wx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fwqekzhbhfayutgn6wx.png" alt="Querying tags" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That is because the array contents and order have to match exactly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Contains" Operator
&lt;/h3&gt;

&lt;p&gt;You can avoid such strict conditions if you use the "contains" operator (&lt;code&gt;@&amp;gt;&lt;/code&gt;) to check if an array contains certain elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;@&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'men'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'summer'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query returns all rows where both "men" and "summer" exist within the &lt;code&gt;tags&lt;/code&gt; array, regardless of order:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0tc9j3nlt087bv0fda5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0tc9j3nlt087bv0fda5.png" alt="Using the @&amp;gt; operator" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Is Contained By" Operator
&lt;/h3&gt;

&lt;p&gt;You can also reverse the logic using the "is contained by" operator (&lt;code&gt;&amp;lt;@&lt;/code&gt;) to find rows where the entire &lt;code&gt;tags&lt;/code&gt; array is a subset of another array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;@&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'cotton'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'men'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'summer'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since an empty array is considered a subset of any array, using the &lt;code&gt;&amp;lt;@&lt;/code&gt; operator will always include rows where the compared array is empty. That's why the above query returns two rows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0tc9j3nlt087bv0fda5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0tc9j3nlt087bv0fda5.png" alt="Using the &amp;lt;@ operator" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is especially useful when searching for simpler or narrower tag combinations. For example, you could search &lt;code&gt;['winter', 'clothes']&lt;/code&gt; to find all clothes suited for the colder months of the year.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the "Overlaps" Operator
&lt;/h3&gt;

&lt;p&gt;If you only care whether at least one matching tag exists, such as searching for clothes suited for either winter or summer, use the "overlaps" (&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;) operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'men'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return any row that contains "clothing", "men", or both:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ezcmlvs6xtnfr7btsk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ezcmlvs6xtnfr7btsk4.png" alt="Using the &amp;amp;&amp;amp; operator" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Cardinality Function
&lt;/h3&gt;

&lt;p&gt;To identify empty arrays, use the &lt;code&gt;cardinality&lt;/code&gt; function. This function returns the number of elements in an array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM products WHERE CARDINALITY(tags) = 0;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps you find products without any tags:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpa1asvktauclw91237i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flpa1asvktauclw91237i.png" alt="Using the cardinality function" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since arrays in Postgres are ordered and 1-indexed, you can also query specific positions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tags&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="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'cotton'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns rows where the second element in the &lt;code&gt;tags&lt;/code&gt; array is exactly "cotton":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzjjofbg5ssndlqa36yr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzjjofbg5ssndlqa36yr.png" alt="Querying specific positions" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use arrays as search parameters with the &lt;code&gt;ANY&lt;/code&gt; and &lt;code&gt;ALL&lt;/code&gt; keywords. Use &lt;code&gt;ANY&lt;/code&gt; to check if at least one value satisfies a condition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fetches products priced at either 9.99 or 14.99:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk18jh0qq1pz93qd14tjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk18jh0qq1pz93qd14tjc.png" alt="Using the ANY keyword" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;ALL&lt;/code&gt; to apply a condition across all values in the array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns products priced higher than every value in the array:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sm6pt61adn5v1o10hs5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sm6pt61adn5v1o10hs5.png" alt="Using the ALL keyword" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those are just some of the array operators and keywords you can use to unlock the full potential of arrays in your Postgres queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modifying Arrays in Postgres
&lt;/h2&gt;

&lt;p&gt;After learning how to use arrays in search queries, the next step is understanding how to update the array values themselves. For example, you might want to add or remove a tag from a specific product over time. With Postgres, you can modify arrays with simple and expressive SQL functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using array_append, array_prepend, and array_remove
&lt;/h3&gt;

&lt;p&gt;If you want to add a new value to an existing array—for example, a new tag to one of your products—you can use &lt;code&gt;array_append&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fabric'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&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;p&gt;This adds &lt;code&gt;fabric&lt;/code&gt; to the end of the &lt;code&gt;tags&lt;/code&gt; array:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzb6htd4zvy4uyy9ojgdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzb6htd4zvy4uyy9ojgdr.png" alt="Using array_append" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add a value to the beginning instead, use &lt;code&gt;array_prepend&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_prepend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fabric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&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;p&gt;This query adds a tag to the beginning of the array:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wvumysn8b26df5p9k8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wvumysn8b26df5p9k8n.png" alt="Using array_prepend" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To remove a specific element from the array, you can call &lt;code&gt;array_remove&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fabric'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&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;p&gt;This SQL removed all &lt;code&gt;fabric&lt;/code&gt; tags in the array:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta15k33z5cn9hqkzt2u4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fta15k33z5cn9hqkzt2u4.png" alt="Using array_remove" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;array_append&lt;/code&gt;, &lt;code&gt;array_prepend&lt;/code&gt;, and &lt;code&gt;array_remove&lt;/code&gt; return a new array, which is why you had to assign the result to the &lt;code&gt;tags&lt;/code&gt; column.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Concatenation Operator
&lt;/h3&gt;

&lt;p&gt;When working with tags, you will rarely want to  add just one at a time. If you need to add multiple tags at once, use the array concatenation operator &lt;code&gt;||&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'fabric'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'colored'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&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;p&gt;This appends all values from the new array to the existing one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F183ennwgml0570kvb57x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F183ennwgml0570kvb57x.png" alt="Using the || operator" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using array_position
&lt;/h3&gt;

&lt;p&gt;To find an item in a Postgres array, use &lt;code&gt;array_position&lt;/code&gt;. It returns the index of the first match starting at 1 since Postgres arrays are 1-based:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;array_position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'summer'&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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&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;p&gt;Executing the above query gets you the index of the tag "summer":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzlmvt5kfmj3oiulgr00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzlmvt5kfmj3oiulgr00.png" alt="Using array_position" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the tag you search for does not exist in the array, you get &lt;code&gt;NULL&lt;/code&gt; as a result.&lt;/p&gt;

&lt;h3&gt;
  
  
  The array_replace Function
&lt;/h3&gt;

&lt;p&gt;Sometimes you may need to replace a value in a Postgres array. For example, if you want to change a product's tag from "summer" to "winter" to recategorize it, you can use the function &lt;code&gt;array_replace&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array_replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'summer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'winter'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&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;p&gt;With this change, your product is now tagged for winter instead of summer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1171je07yh1ldmvwcdi2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1171je07yh1ldmvwcdi2.png" alt="Using array_replace" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Combine these tools so you can efficiently manage and transform array data in Postgres with clear and readable SQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Array Operations: Filtering, Sorting, and Aggregating
&lt;/h2&gt;

&lt;p&gt;Sometimes it's not enough to store or update arrays. You might also need to work with the values inside them. Postgres provides functions to filter, sort, or analyze array elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  The unnest Function
&lt;/h3&gt;

&lt;p&gt;For simple analytics, having one row per array item can be very helpful. To break arrays into individual elements, use the &lt;code&gt;unnest()&lt;/code&gt; function. For example, you can run the following query to list all tags for a product with id = 1 :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;UNNEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&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;products&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&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;p&gt;This query returns one row per tag, showing each tag separately for that product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farjfuvfq08ruvobyh91k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farjfuvfq08ruvobyh91k.png" alt="Using unnest()" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is especially useful for analytics. Suppose you want to count how many products use each tag. You can unnest all tags, group them, and count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&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;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;sub&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query gives you a list of tags along with the number of products that include each one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68eu8tc8civqza4tdry4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68eu8tc8civqza4tdry4.png" alt="Counting tag occurrences" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This helps you quickly identify the most popular tags.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using array_agg for Aggregation
&lt;/h3&gt;

&lt;p&gt;To combine values into a single array, use the &lt;code&gt;array_agg()&lt;/code&gt; function. To explain this function, let's take another real-life example that can show it better than our products table could. Suppose we have a &lt;code&gt;students&lt;/code&gt; table where each student has chosen a subject. The table would consist of two columns—&lt;code&gt;name&lt;/code&gt; and &lt;code&gt;subject&lt;/code&gt;. If you want to aggregate the names of all students who have the subject "math" associated with them, you can use the function &lt;code&gt;array_agg()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&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="n"&gt;name&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;students&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'math'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns a single row containing an array of all students who have chosen "math":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfm0lpeyu6sdpvfy1t6c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfm0lpeyu6sdpvfy1t6c.png" alt="Using array_agg()" width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can combine &lt;code&gt;array_agg()&lt;/code&gt; with &lt;code&gt;GROUP BY&lt;/code&gt; to rebuild arrays from grouped rows, allowing you to pivot, reformat, or aggregate your data as needed. &lt;/p&gt;

&lt;h3&gt;
  
  
  Using jsonb_array_elements_text
&lt;/h3&gt;

&lt;p&gt;If you're working with a &lt;code&gt;jsonb&lt;/code&gt; array instead of a native Postgres array, you can extract each value using &lt;code&gt;jsonb_array_elements_text()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;jsonb_array_elements_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'["fruit", "organic", "local"]'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns one row per JSON array element, which is perfect for hybrid JSON-array structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Using Postgres Arrays
&lt;/h2&gt;

&lt;p&gt;While Postgres arrays work well for simple lists like tags or flags, you should avoid them when dealing with many-to-many relationships that include metadata.&lt;/p&gt;

&lt;p&gt;For example, if you want to assign categories to products and each category may carry additional information or must be unique, you should use a separate &lt;code&gt;categories&lt;/code&gt; table and a join table to connect products with their categories. Arrays can't reference foreign keys, so you lose referential integrity and the ability to enforce constraints.&lt;/p&gt;

&lt;h3&gt;
  
  
  When Not to Use Postgres Arrays
&lt;/h3&gt;

&lt;p&gt;Though it may seem counterintuitive, one of the best practices you can adopt is knowing when &lt;em&gt;not&lt;/em&gt; to use arrays. You might feel tempted to use arrays for many-to-many links, but doing so breaks database normalization. While arrays can be indexed with the Generalized Inverted Index (GIN) for fast searches, they don't support constraints, can't store extra info like timestamps or statuses for each item, and aren't as flexible as proper join tables.&lt;/p&gt;

&lt;p&gt;For instance, if you store user roles in an array within a &lt;code&gt;users&lt;/code&gt; table, it becomes difficult to associate each role with important contextual data.&lt;/p&gt;

&lt;p&gt;In these cases, just stick with proper relational tables and joins. They give you flexibility, integrity, and maintainability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Indexing Arrays in Postgres with GIN Indexes
&lt;/h3&gt;

&lt;p&gt;When you store arrays in a Postgres column, like you did in the above examples with &lt;code&gt;tags TEXT[]&lt;/code&gt;, you need to think about how you'll efficiently query them. Searching through large data sets can get slow. Luckily, Postgres also has a solution for that use case. You can speed things up by adding a GIN index.&lt;/p&gt;

&lt;p&gt;GIN indexes work well with composite types like arrays, JSONB, or full-text search vectors by indexing each array element individually rather than the entire row. For example, if you create a GIN index, you'll see a significant speedup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_tags_gin&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;GIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows Postgres to quickly locate rows where the array contains a specific value, like in the following query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM products WHERE 'clothing' = ANY(tags);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind that you'll only see the real benefits of GIN indexes with larger data sets. If your table has just a few thousand rows, you won't notice much of a performance difference compared to a nonindexed search. Postgres can scan the entire table so quickly that using an index wouldn't save time. You'll start to see measurable improvements when working with tens or hundreds of thousands of rows.&lt;/p&gt;

&lt;p&gt;This setup allows you to scan arrays much more efficiently, especially when filtering by individual values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validation Considerations
&lt;/h3&gt;

&lt;p&gt;Besides performance, you also need to enforce data integrity. Postgres gives you tools to validate arrays directly in your schema. For example, if you want to allow only up to five tags per product, you can define a &lt;code&gt;CHECK&lt;/code&gt; constraint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&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="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postgres already enforces the array's data type, but you can restrict the allowed values even further using containment checks. Below is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;
&lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;@&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'clothing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'winter'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fabric'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes sure that only predefined tags are accepted.&lt;/p&gt;

&lt;p&gt;If you combine GIN indexing for performance and table constraints for validation, you keep your array data both fast and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, you've seen how Postgres arrays can simplify your database design and make your queries more efficient. Arrays let you store related values in a single column—like tags, flags, or options—without the overhead of extra join tables. This can help you get simpler schemas, fewer joins, and often, faster reads. This is especially true when combined with GIN indexing. You also learned how to manipulate arrays with functions like &lt;code&gt;array_append&lt;/code&gt;, &lt;code&gt;array_remove&lt;/code&gt;, and &lt;code&gt;array_replace&lt;/code&gt;; how to flatten them with &lt;code&gt;unnest()&lt;/code&gt; for aggregation; and how to enforce validation to maintain clean, consistent data.&lt;/p&gt;

&lt;p&gt;That said, arrays aren't a silver bullet. Use them when you need simple lists that don't require additional metadata or relational integrity. But if your data involves many-to-many relationships with extra attributes, like timestamps or roles with permissions, you'll still want to reach for traditional relational modeling.&lt;/p&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>postgres</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
