<?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: Jitendra Suryavanshi</title>
    <description>The latest articles on DEV Community by Jitendra Suryavanshi (@jitendrasuryavanshi).</description>
    <link>https://dev.to/jitendrasuryavanshi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4161225%2F5fcc536b-b220-4cab-a82b-09935790f0e3.gif</url>
      <title>DEV Community: Jitendra Suryavanshi</title>
      <link>https://dev.to/jitendrasuryavanshi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jitendrasuryavanshi"/>
    <language>en</language>
    <item>
      <title>SQL Pattern Matching Without the Pain: LIKE, Wildcards, and the Gotchas Nobody Warns You About</title>
      <dc:creator>Jitendra Suryavanshi</dc:creator>
      <pubDate>Fri, 30 Oct 2026 04:08:57 +0000</pubDate>
      <link>https://dev.to/jitendrasuryavanshi/sql-pattern-matching-without-the-pain-like-wildcards-and-the-gotchas-nobody-warns-you-about-5gda</link>
      <guid>https://dev.to/jitendrasuryavanshi/sql-pattern-matching-without-the-pain-like-wildcards-and-the-gotchas-nobody-warns-you-about-5gda</guid>
      <description>&lt;p&gt;A practical guide to searching text in SQL using LIKE, wildcards, SUBSTRING and LOWER, with real examples and the mistakes that cost you hours.&lt;/p&gt;

&lt;p&gt;Every app I've worked on eventually hits the same moment: someone types a few letters into a search box and expects the right row to show up.&lt;/p&gt;

&lt;p&gt;Behind that tiny feature sits one of the oldest tools in SQL: &lt;strong&gt;pattern matching&lt;/strong&gt;. It looks simple, but it has quirks that silently return wrong results, or make a fast query crawl.&lt;/p&gt;

&lt;p&gt;In this post I'll walk through pattern matching using a small &lt;code&gt;Employees&lt;/code&gt; table, then cover the gotchas I wish someone had told me earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&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;Employees&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;EmpID&lt;/span&gt; &lt;span class="nb"&gt;INT&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;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;Department&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;Email&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;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EmpID&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;Department&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&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;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Alice Johnson'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'HR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="s1"&gt;'alice.johnson@example.com'&lt;/span&gt;&lt;span class="p"&gt;),&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="s1"&gt;'Bob Smith'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="s1"&gt;'IT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="s1"&gt;'bob.smith@example.com'&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="s1"&gt;'Charlie Brown'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Finance'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'charlie.brown@example.com'&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="s1"&gt;'Diana Prince'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s1"&gt;'HR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="s1"&gt;'diana.prince@example.com'&lt;/span&gt;&lt;span class="p"&gt;),&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="s1"&gt;'Eve Adams'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="s1"&gt;'IT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;      &lt;span class="s1"&gt;'eve.adams@example.org'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five rows is enough. The goal is to understand the patterns, not to stress the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two wildcards you need
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;LIKE&lt;/code&gt; understands exactly two special characters:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Wildcard&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;any number of characters (including zero)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;exactly one character&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Everything else is matched literally. Once that clicks, most of the rest is just where you place them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Starts with, ends with, contains
&lt;/h2&gt;

&lt;p&gt;The position of &lt;code&gt;%&lt;/code&gt; tells SQL what you mean.&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="c1"&gt;-- Starts with 'A'&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'A%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Alice Johnson&lt;/span&gt;

&lt;span class="c1"&gt;-- Ends with 'son'&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%son'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Alice Johnson&lt;/span&gt;

&lt;span class="c1"&gt;-- Contains 'IT'&lt;/span&gt;
&lt;span class="k"&gt;SELECT&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;Department&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Department&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%IT%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Bob Smith (IT), Eve Adams (IT)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick way to remember it: the &lt;code&gt;%&lt;/code&gt; goes on the side where you don't care what comes next. &lt;code&gt;'A%'&lt;/code&gt; means "I care about the start, anything can follow."&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Filtering by domain
&lt;/h2&gt;

&lt;p&gt;Pattern matching is great for light data cleaning, like finding emails from one domain:&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;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%@example.com'&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 Alice, Bob, Charlie and Diana. Eve is left out because her address ends in &lt;code&gt;.org&lt;/code&gt;. Including the &lt;code&gt;@&lt;/code&gt; in the pattern matters. Without it, &lt;code&gt;'%example.com'&lt;/code&gt; would also match something like &lt;code&gt;bob@notexample.com&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. "Second letter is v": two ways
&lt;/h2&gt;

&lt;p&gt;This one is a nice puzzle because there are two valid solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using a function:&lt;/strong&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;Name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;SUBSTRING&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="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="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Using the single-character wildcard:&lt;/strong&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;Name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'_v%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both return &lt;code&gt;Eve Adams&lt;/code&gt;. Read the second one as: "one any character, then &lt;code&gt;v&lt;/code&gt;, then anything."&lt;/p&gt;

&lt;p&gt;I prefer the &lt;code&gt;LIKE&lt;/code&gt; version. It's shorter, and it reads like the pattern it describes. &lt;code&gt;SUBSTRING&lt;/code&gt; is better when you need to compare the extracted piece against something more complex.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Case-insensitive matching
&lt;/h2&gt;

&lt;p&gt;Say you want everyone in HR, but your data might contain &lt;code&gt;HR&lt;/code&gt;, &lt;code&gt;hr&lt;/code&gt; or &lt;code&gt;Hr&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;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Department&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Employees&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;LOWER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Department&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%hr%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lowercasing the column and writing the pattern in lowercase makes the match independent of how the data was typed. You get Alice Johnson and Diana Prince.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gotchas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gotcha 1: &lt;code&gt;LIKE&lt;/code&gt; isn't case-sensitive everywhere (or case-insensitive everywhere)
&lt;/h3&gt;

&lt;p&gt;This is the biggest source of "works on my machine" bugs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;PostgreSQL&lt;/strong&gt;, &lt;code&gt;LIKE&lt;/code&gt; is case-sensitive. It also has &lt;code&gt;ILIKE&lt;/code&gt; for case-insensitive matching.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;MySQL&lt;/strong&gt; and &lt;strong&gt;SQL Server&lt;/strong&gt;, case sensitivity depends on the column's &lt;em&gt;collation&lt;/em&gt;, and the common defaults are case-insensitive.&lt;/li&gt;
&lt;li&gt;In &lt;strong&gt;SQLite&lt;/strong&gt;, &lt;code&gt;LIKE&lt;/code&gt; is case-insensitive for plain ASCII letters by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That affects the &lt;code&gt;'%IT%'&lt;/code&gt; query above. On a case-insensitive setup, it would also match a department named &lt;code&gt;Facilities&lt;/code&gt;, because "Facilities" contains "it". If exact case matters, don't rely on the default. Be explicit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotcha 2: Searching for a literal &lt;code&gt;%&lt;/code&gt; or &lt;code&gt;_&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;What if you need to find a value like &lt;code&gt;50%_off&lt;/code&gt;? The wildcards get in your way. Use &lt;code&gt;ESCAPE&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Promotions&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Code&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'50&lt;/span&gt;&lt;span class="se"&gt;\%\_&lt;/span&gt;&lt;span class="s1"&gt;off'&lt;/span&gt; &lt;span class="k"&gt;ESCAPE&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backslash tells SQL to treat the next character literally. Any time user input goes into a &lt;code&gt;LIKE&lt;/code&gt; pattern, remember to escape these characters, or users can accidentally (or deliberately) match far more than they intended.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotcha 3: A leading &lt;code&gt;%&lt;/code&gt; can make queries slow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'Ali%'&lt;/span&gt;   &lt;span class="c1"&gt;-- can use a normal index&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%ice'&lt;/span&gt;   &lt;span class="c1"&gt;-- usually cannot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a pattern that starts with a fixed prefix, the database can jump into a regular index. With a leading wildcard, it generally has to scan every row. On five rows you'll never notice. On five million, you will.&lt;/p&gt;

&lt;p&gt;If you need fast "contains" search on large tables, look at full-text search, or trigram indexes in PostgreSQL, instead of leaning on &lt;code&gt;LIKE '%...%'&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotcha 4: &lt;code&gt;NULL&lt;/code&gt; never matches
&lt;/h3&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;Employees&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="k"&gt;LIKE&lt;/span&gt; &lt;span class="s1"&gt;'%@example.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A row where &lt;code&gt;Email&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt; is not returned, and it won't be returned by &lt;code&gt;NOT LIKE&lt;/code&gt; either. Comparisons with &lt;code&gt;NULL&lt;/code&gt; evaluate to unknown, not true. If you need those rows, handle them with &lt;code&gt;IS NULL&lt;/code&gt; or &lt;code&gt;COALESCE&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which tool should I use?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You want to...&lt;/th&gt;
&lt;th&gt;Reach for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Match a simple shape (starts, ends, contains)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;LIKE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Match by position&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_&lt;/code&gt; or &lt;code&gt;SUBSTRING&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ignore case reliably&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LOWER()&lt;/code&gt; / &lt;code&gt;UPPER()&lt;/code&gt; (or &lt;code&gt;ILIKE&lt;/code&gt; in PostgreSQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Match complex patterns&lt;/td&gt;
&lt;td&gt;Regex (&lt;code&gt;REGEXP&lt;/code&gt; in MySQL, &lt;code&gt;~&lt;/code&gt; in PostgreSQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search large text quickly&lt;/td&gt;
&lt;td&gt;Full-text search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Pattern matching is one of those topics that feels too basic to study, until a search feature returns the wrong rows in production. The core ideas are small:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;%&lt;/code&gt; means "anything", &lt;code&gt;_&lt;/code&gt; means "exactly one character".&lt;/li&gt;
&lt;li&gt;Where you place the wildcard defines the meaning.&lt;/li&gt;
&lt;li&gt;Case sensitivity depends on your database, so check it.&lt;/li&gt;
&lt;li&gt;Leading wildcards are fine for small data and costly for big data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Try the queries above on your own data and break them on purpose. Adding a lowercase &lt;code&gt;hr&lt;/code&gt; row or a &lt;code&gt;NULL&lt;/code&gt; email will teach you more than reading about it.&lt;/p&gt;

&lt;p&gt;If this helped, drop a comment with the weirdest &lt;code&gt;LIKE&lt;/code&gt; bug you've run into. I'd love to hear it.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
