Wicked Smart Data
LearnArticlesAbout
Sign InSign Up
LearnArticlesAboutContact
Sign InSign Up
Wicked Smart Data

The go-to platform for professionals who want to master data, automation, and AI — from Excel fundamentals to cutting-edge machine learning.

Platform

  • Learning Paths
  • Articles
  • About
  • Contact

Connect

  • Contact Us
  • RSS Feed

© 2026 Wicked Smart Data. All rights reserved.

Privacy PolicyTerms of Service
All Articles
SQL Filtering and Sorting Mastery: WHERE, AND, OR, IN, BETWEEN, ORDER BY

SQL Filtering and Sorting Mastery: WHERE, AND, OR, IN, BETWEEN, ORDER BY

SQL⚡ Practitioner13 min readApr 28, 2026Updated Apr 28, 2026
Table of Contents
  • Prerequisites
  • The Foundation: WHERE Clause Essentials
  • Building Complex Logic with AND and OR
  • The AND Operator: All Conditions Must Be True
  • The OR Operator: Any Condition Can Be True
  • Combining AND and OR: The Power of Parentheses
  • The IN Operator: Elegant List Matching
  • NOT IN: The Exclusion Tool
  • The BETWEEN Operator: Range Queries Made Simple
  • Date Ranges with BETWEEN
  • NOT BETWEEN for Exclusions
  • ORDER BY: Sorting for Meaningful Results

Picture this: you're analyzing customer transaction data for an e-commerce company, and your manager asks for "all premium customers who made purchases between $500 and $2000 last quarter, sorted by purchase date." Your heart sinks as you realize you have 2.3 million rows of data. Without proper filtering and sorting techniques, you'd be scrolling through spreadsheets until next Tuesday.

This is where SQL's filtering and sorting capabilities become your superpower. These aren't just basic database operations—they're the foundation of every meaningful data analysis you'll ever do. Whether you're building reports, cleaning datasets, or feeding machine learning models, you'll use these techniques daily.

Filtering and sorting data efficiently isn't just about getting results; it's about getting the right results quickly. A poorly constructed WHERE clause can turn a lightning-fast query into a database-grinding nightmare, while smart use of compound conditions can slice through millions of records in milliseconds.

What you'll learn:

  • Build complex filtering logic using WHERE with AND, OR, and parentheses for precise data selection
  • Use IN and BETWEEN operators to handle ranges and lists more elegantly than multiple OR conditions
  • Implement multi-column sorting strategies with ORDER BY that reflect real business requirements
  • Optimize filter performance by understanding how databases process different condition types
  • Troubleshoot common filtering mistakes that return unexpected results or cause performance issues

Prerequisites

You should be comfortable with basic SQL SELECT statements and understand fundamental database concepts like tables, columns, and data types. We'll reference JOINs briefly, but won't teach them here—if you need a refresher, review those concepts first.

The Foundation: WHERE Clause Essentials

The WHERE clause is your precision instrument for data selection. Unlike a blunt filter that shows or hides entire categories, WHERE lets you specify exactly which rows meet your criteria using logical conditions.

-- Basic WHERE structure
SELECT column1, column2, column3
FROM table_name
WHERE condition;

Let's work with a realistic dataset. Imagine you're analyzing an e-commerce platform's customer orders:

-- Sample data structure
CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    total_amount DECIMAL(10,2),
    status VARCHAR(20),
    shipping_country VARCHAR(50),
    customer_tier VARCHAR(20)
);

Here's a foundational WHERE example:

SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE total_amount > 1000;

This returns only orders above $1,000. Simple, but notice what's happening: the database engine scans each row, evaluates the condition total_amount > 1000, and includes only rows where this evaluates to TRUE.

Building Complex Logic with AND and OR

Real business questions rarely involve single conditions. You need to combine multiple criteria to answer questions like "Which high-value customers from specific regions made recent purchases?"

The AND Operator: All Conditions Must Be True

AND requires every condition to be true for a row to be included:

SELECT order_id, customer_id, total_amount, shipping_country
FROM orders
WHERE total_amount > 500
  AND shipping_country = 'United States'
  AND customer_tier = 'Premium';

This query finds premium US customers who spent more than $500. All three conditions must be satisfied. If any condition fails, the row is excluded.

The OR Operator: Any Condition Can Be True

OR includes a row if any condition is true:

SELECT order_id, customer_id, total_amount, status
FROM orders
WHERE status = 'shipped'
   OR status = 'delivered'
   OR status = 'processing';

This finds orders in any of these three statuses. Only one condition needs to be true for inclusion.

Combining AND and OR: The Power of Parentheses

Here's where it gets interesting—and where most people make mistakes. Consider this business requirement: "Find all high-value orders (over $1000) OR any orders from premium customers, but only if they're from the US or Canada."

Your first instinct might be:

-- WRONG - This doesn't do what you think
SELECT *
FROM orders
WHERE total_amount > 1000
   OR customer_tier = 'Premium'
  AND shipping_country IN ('United States', 'Canada');

This query has a logical flaw. Due to operator precedence, it's actually interpreted as:

-- How the database actually reads the above query
SELECT *
FROM orders
WHERE total_amount > 1000
   OR (customer_tier = 'Premium' AND shipping_country IN ('United States', 'Canada'));

This returns ALL orders over $1000 regardless of country, plus premium customers from US/Canada. That's not what we wanted.

The correct approach uses parentheses to group conditions explicitly:

-- CORRECT - Parentheses make the logic clear
SELECT *
FROM orders
WHERE (total_amount > 1000 OR customer_tier = 'Premium')
  AND shipping_country IN ('United States', 'Canada');

Now we get high-value orders OR premium customers, but only from US/Canada.

Pro tip: Always use parentheses when combining AND and OR, even when operator precedence would give you the right result. Your future self (and your teammates) will thank you for the clarity.

The IN Operator: Elegant List Matching

When you need to match against multiple specific values, IN is more readable and often faster than multiple OR conditions.

Instead of this verbose approach:

-- Verbose and error-prone
SELECT *
FROM orders
WHERE shipping_country = 'United States'
   OR shipping_country = 'Canada'
   OR shipping_country = 'Mexico'
   OR shipping_country = 'United Kingdom'
   OR shipping_country = 'Germany';

Use IN for cleaner code:

-- Clean and efficient
SELECT *
FROM orders
WHERE shipping_country IN ('United States', 'Canada', 'Mexico', 'United Kingdom', 'Germany');

IN works with any data type:

-- Numbers
WHERE customer_id IN (12345, 67890, 24681, 13579);

-- Dates
WHERE order_date IN ('2024-01-15', '2024-02-14', '2024-03-15');

-- Multiple columns (less common but powerful)
WHERE (customer_tier, shipping_country) IN (('Premium', 'United States'), ('VIP', 'Canada'));

NOT IN: The Exclusion Tool

NOT IN excludes rows that match any value in the list:

-- Exclude specific problem customers
SELECT *
FROM orders
WHERE customer_id NOT IN (12345, 67890, 99999);

Warning: NOT IN with NULL values can produce unexpected results. If the list contains NULL, NOT IN returns no rows at all. Use WHERE column NOT IN (values) OR column IS NULL if you need to handle NULLs explicitly.

The BETWEEN Operator: Range Queries Made Simple

BETWEEN handles range conditions more elegantly than separate greater-than and less-than conditions.

Traditional approach:

-- Verbose range condition
SELECT *
FROM orders
WHERE total_amount >= 500
  AND total_amount <= 2000;

BETWEEN approach:

-- Cleaner range condition
SELECT *
FROM orders
WHERE total_amount BETWEEN 500 AND 2000;

BETWEEN is inclusive on both ends—it includes 500 and 2000 in the results.

Date Ranges with BETWEEN

BETWEEN excels with date ranges:

-- Orders from Q1 2024
SELECT *
FROM orders
WHERE order_date BETWEEN '2024-01-01' AND '2024-03-31';

-- Last 30 days (using date functions)
SELECT *
FROM orders
WHERE order_date BETWEEN CURRENT_DATE - INTERVAL '30 days' AND CURRENT_DATE;

NOT BETWEEN for Exclusions

-- Exclude mid-range orders, keep very low and very high
SELECT *
FROM orders
WHERE total_amount NOT BETWEEN 100 AND 500;

This returns orders under $100 or over $500.

ORDER BY: Sorting for Meaningful Results

Data without order is just noise. ORDER BY transforms raw results into actionable insights by arranging rows according to business logic.

Single Column Sorting

-- Highest value orders first
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE total_amount > 1000
ORDER BY total_amount DESC;

-- Most recent orders first
SELECT order_id, customer_id, total_amount, order_date
FROM orders
WHERE order_date >= '2024-01-01'
ORDER BY order_date DESC;

ASC (ascending) is the default, but always specify DESC (descending) when you want reverse order.

Multi-Column Sorting: The Power of Hierarchy

Real business requirements often need multiple sort criteria. ORDER BY processes columns left-to-right, using subsequent columns to break ties:

-- Sort by customer tier (most valuable first), then by order date (newest first)
SELECT customer_id, customer_tier, order_date, total_amount
FROM orders
WHERE order_date >= '2024-01-01'
ORDER BY 
    CASE customer_tier 
        WHEN 'VIP' THEN 1 
        WHEN 'Premium' THEN 2 
        WHEN 'Standard' THEN 3 
        ELSE 4 
    END,
    order_date DESC;

This creates a hierarchy: VIP customers appear first, then Premium, then Standard. Within each tier, newest orders come first.

A simpler multi-column example:

-- Group by country, then by order value (highest first)
SELECT shipping_country, customer_id, total_amount, order_date
FROM orders
ORDER BY shipping_country ASC, total_amount DESC;

Sorting by Calculated Fields

You can sort by expressions and functions:

-- Sort by profit margin (calculated field)
SELECT 
    order_id,
    total_amount,
    cost_amount,
    (total_amount - cost_amount) / total_amount * 100 AS profit_margin
FROM orders
WHERE total_amount > 0
ORDER BY profit_margin DESC;

-- Sort by customer's total order count (requires aggregation)
SELECT 
    customer_id,
    COUNT(*) as order_count,
    SUM(total_amount) as total_spent
FROM orders
GROUP BY customer_id
ORDER BY order_count DESC, total_spent DESC;

Advanced Filtering Techniques

Combining All Operators for Complex Business Logic

Let's solve a realistic business problem: "Find all orders from our top-tier customers (Premium or VIP) who either made large purchases (over $1500) or frequent small purchases (between $50-$300) in Q4 2023, from our key markets, sorted by customer value then recency."

SELECT 
    o.order_id,
    o.customer_id,
    o.customer_tier,
    o.total_amount,
    o.order_date,
    o.shipping_country
FROM orders o
WHERE 
    -- Customer tier criteria
    customer_tier IN ('Premium', 'VIP')
    
    -- Amount criteria: large purchases OR small frequent purchases
    AND (
        total_amount > 1500
        OR total_amount BETWEEN 50 AND 300
    )
    
    -- Date range: Q4 2023
    AND order_date BETWEEN '2023-10-01' AND '2023-12-31'
    
    -- Geographic criteria
    AND shipping_country IN ('United States', 'Canada', 'United Kingdom', 'Germany', 'France')
    
    -- Exclude cancelled orders
    AND status NOT IN ('cancelled', 'refunded')

ORDER BY 
    -- Sort by tier importance first
    CASE customer_tier 
        WHEN 'VIP' THEN 1 
        WHEN 'Premium' THEN 2 
        ELSE 3 
    END,
    -- Then by order value (highest first)
    total_amount DESC,
    -- Finally by recency
    order_date DESC;

Pattern Matching with LIKE

When IN isn't flexible enough, LIKE handles pattern matching:

-- Find orders shipped to cities starting with "San"
SELECT *
FROM orders
WHERE shipping_city LIKE 'San%';

-- Find orders with promotional codes containing "HOLIDAY"
WHERE promo_code LIKE '%HOLIDAY%';

-- Find customer emails from company domains (ending in organization identifiers)
WHERE customer_email LIKE '%.edu' OR customer_email LIKE '%.gov' OR customer_email LIKE '%.org';

NULL Handling in Filters

NULL values require special handling because they don't equal anything—not even other NULLs:

-- Find orders with missing shipping information
SELECT *
FROM orders
WHERE shipping_address IS NULL;

-- Find orders with complete shipping information
WHERE shipping_address IS NOT NULL
  AND shipping_city IS NOT NULL
  AND shipping_postal_code IS NOT NULL;

-- Handle NULLs in calculations
WHERE COALESCE(discount_amount, 0) > 50;  -- Treats NULL as 0

Hands-On Exercise: E-commerce Sales Analysis

Let's apply these concepts to solve a real business challenge. You're analyzing sales data for an online retailer to identify opportunities for a targeted marketing campaign.

Scenario: The marketing team wants to create a "Customer Win-Back" campaign targeting valuable customers who haven't ordered recently. They need a list of customers who:

  1. Have made at least one purchase over $200 OR are Premium/VIP customers
  2. Last ordered between 60-180 days ago (not too recent, not too old)
  3. Are from major English-speaking markets
  4. Have never had a refunded order
  5. Results sorted by customer value (total lifetime spending) descending

Here's the solution, built step by step:

-- Step 1: Create a CTE to calculate customer metrics
WITH customer_metrics AS (
    SELECT 
        customer_id,
        customer_tier,
        shipping_country,
        MAX(order_date) as last_order_date,
        SUM(total_amount) as lifetime_value,
        COUNT(*) as total_orders,
        MAX(CASE WHEN total_amount > 200 THEN 1 ELSE 0 END) as has_high_value_order,
        MAX(CASE WHEN status = 'refunded' THEN 1 ELSE 0 END) as has_refund
    FROM orders
    GROUP BY customer_id, customer_tier, shipping_country
)

-- Step 2: Apply the filtering criteria
SELECT 
    cm.customer_id,
    cm.customer_tier,
    cm.shipping_country,
    cm.last_order_date,
    cm.lifetime_value,
    cm.total_orders,
    CURRENT_DATE - cm.last_order_date as days_since_last_order
FROM customer_metrics cm
WHERE 
    -- Criteria 1: High-value purchase OR premium tier
    (cm.has_high_value_order = 1 OR cm.customer_tier IN ('Premium', 'VIP'))
    
    -- Criteria 2: Last ordered 60-180 days ago
    AND cm.last_order_date BETWEEN CURRENT_DATE - INTERVAL '180 days' 
                                AND CURRENT_DATE - INTERVAL '60 days'
    
    -- Criteria 3: Major English-speaking markets
    AND cm.shipping_country IN ('United States', 'Canada', 'United Kingdom', 'Australia')
    
    -- Criteria 4: No refunded orders
    AND cm.has_refund = 0
    
    -- Additional filter: Must have multiple orders (more engaged customers)
    AND cm.total_orders >= 2

-- Criteria 5: Sort by customer value
ORDER BY cm.lifetime_value DESC, cm.total_orders DESC;

This query demonstrates several advanced concepts:

  • Using a CTE to pre-calculate customer metrics
  • Combining multiple filtering strategies (ranges, lists, exclusions)
  • Handling date calculations for recency analysis
  • Multi-level sorting for business prioritization

Common Mistakes & Troubleshooting

Mistake 1: Operator Precedence Confusion

Problem: Mixing AND and OR without parentheses leads to unexpected results.

-- WRONG: Gets more results than intended
WHERE status = 'shipped' OR status = 'delivered' AND total_amount > 1000;

-- RIGHT: Use parentheses to clarify intent
WHERE (status = 'shipped' OR status = 'delivered') AND total_amount > 1000;

Debug tip: When results look wrong, add parentheses to make your logic explicit, then remove them if the query works correctly.

Mistake 2: NULL Value Surprises

Problem: NOT IN with NULLs returns zero rows.

-- This returns no rows if the list contains any NULL
WHERE customer_id NOT IN (SELECT customer_id FROM problematic_customers);

-- FIX: Filter out NULLs explicitly
WHERE customer_id NOT IN (
    SELECT customer_id 
    FROM problematic_customers 
    WHERE customer_id IS NOT NULL
);

Mistake 3: Date Range Edge Cases

Problem: BETWEEN with timestamps includes partial days.

-- WRONG: Misses orders on 2024-03-31 after midnight
WHERE order_date BETWEEN '2024-01-01' AND '2024-03-31';

-- RIGHT: Be explicit about time boundaries
WHERE order_date >= '2024-01-01' AND order_date < '2024-04-01';

Mistake 4: Case Sensitivity in Text Filters

Problem: String comparisons might be case-sensitive depending on your database.

-- Might miss 'PREMIUM' or 'premium'
WHERE customer_tier = 'Premium';

-- SAFER: Use UPPER() or LOWER() for consistency
WHERE UPPER(customer_tier) = 'PREMIUM';

Performance Troubleshooting

Slow queries often result from:

  1. Missing indexes on filtered columns: If you frequently filter by customer_id, ensure there's an index on that column.
  2. Functions in WHERE clauses: WHERE YEAR(order_date) = 2024 prevents index use. Use WHERE order_date >= '2024-01-01' AND order_date < '2025-01-01' instead.
  3. Too many OR conditions: Consider breaking complex OR logic into UNION queries for better performance.

Quick performance check:

-- Add EXPLAIN to see the execution plan
EXPLAIN SELECT * FROM orders WHERE total_amount BETWEEN 500 AND 2000;

Look for "Seq Scan" (table scan) vs "Index Scan" in the output. Table scans on large datasets indicate missing indexes.

Summary & Next Steps

You've mastered the essential tools for filtering and sorting data in SQL. These techniques form the backbone of data analysis—every report, dashboard, and machine learning pipeline starts with selecting and organizing the right subset of data.

Key takeaways:

  • Use parentheses liberally when combining AND/OR to make logic explicit
  • IN and BETWEEN make your code more readable and often more performant than multiple conditions
  • Multi-column ORDER BY creates meaningful data hierarchies that reflect business priorities
  • Always consider NULL handling in your filter conditions
  • Performance matters: understand how your filters interact with database indexes

Your next learning priorities should be:

  1. Aggregation functions (GROUP BY, HAVING): Now that you can filter individual rows, learn to summarize data across groups
  2. JOINs: Combine data from multiple tables to answer more complex business questions
  3. Window functions: Perform advanced calculations like rankings and running totals within your filtered datasets
  4. Query optimization: Learn to use EXPLAIN plans and understand indexing strategies for large datasets

The filtering and sorting patterns you've learned here will appear in every advanced SQL technique you encounter. Master these fundamentals, and you'll find complex queries much easier to understand and debug.

Learning Path: SQL Fundamentals

Previous

SQL Filtering and Sorting: Master WHERE, AND, OR, IN, BETWEEN, ORDER BY

Next

Advanced SQL Filtering and Sorting: Master WHERE, Logical Operators, and ORDER BY

Related Articles

SQL⚡ Practitioner

NULL Handling in SQL: IS NULL, COALESCE, and NULLIF

19 min
SQL🌱 Foundation

String Functions and Date Functions in SQL: Complete Data Transformation Guide

15 min
SQL🔥 Expert

Advanced String and Date Functions in SQL: Production-Ready Data Processing

25 min

On this page

  • Prerequisites
  • The Foundation: WHERE Clause Essentials
  • Building Complex Logic with AND and OR
  • The AND Operator: All Conditions Must Be True
  • The OR Operator: Any Condition Can Be True
  • Combining AND and OR: The Power of Parentheses
  • The IN Operator: Elegant List Matching
  • NOT IN: The Exclusion Tool
  • The BETWEEN Operator: Range Queries Made Simple
  • Date Ranges with BETWEEN
  • Single Column Sorting
  • Multi-Column Sorting: The Power of Hierarchy
  • Sorting by Calculated Fields
  • Advanced Filtering Techniques
  • Combining All Operators for Complex Business Logic
  • Pattern Matching with LIKE
  • NULL Handling in Filters
  • Hands-On Exercise: E-commerce Sales Analysis
  • Common Mistakes & Troubleshooting
  • Mistake 1: Operator Precedence Confusion
  • Mistake 2: NULL Value Surprises
  • Mistake 3: Date Range Edge Cases
  • Mistake 4: Case Sensitivity in Text Filters
  • Performance Troubleshooting
  • Summary & Next Steps
  • NOT BETWEEN for Exclusions
  • ORDER BY: Sorting for Meaningful Results
  • Single Column Sorting
  • Multi-Column Sorting: The Power of Hierarchy
  • Sorting by Calculated Fields
  • Advanced Filtering Techniques
  • Combining All Operators for Complex Business Logic
  • Pattern Matching with LIKE
  • NULL Handling in Filters
  • Hands-On Exercise: E-commerce Sales Analysis
  • Common Mistakes & Troubleshooting
  • Mistake 1: Operator Precedence Confusion
  • Mistake 2: NULL Value Surprises
  • Mistake 3: Date Range Edge Cases
  • Mistake 4: Case Sensitivity in Text Filters
  • Performance Troubleshooting
  • Summary & Next Steps