Wicked Smart Data
LearnInsightsAboutContact
Sign InLet's Build
LearnInsightsAboutContact
Sign InLet's Build
Wicked Smart Data

Intelligence, automation, and expert execution — plus an elite library of free knowledge. We turn complexity into competitive advantage.

Start a conversation

Platform

  • Learning Paths
  • Insights
  • RSS Feed

Company

  • About
  • Contact
  • Work With Us

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Wicked Smart Data. All rights reserved.

Intelligence · Automation · Advantage

All Insights
SQL

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

Master SQL's most essential tools for data selection and organization. Learn to build complex filter logic, handle ranges and lists elegantly, and sort results that reflect real business priorities.

⚡ Practitioner13 min readApr 28, 2026Updated Aug 29, 2026
SQL Filtering and Sorting Mastery: WHERE, AND, OR, IN, BETWEEN, ORDER BY
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
  • 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
  • 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.

    Work With Us

    From insight to implementation

    Reading is the start. When you're ready to build the data, automation, or AI systems behind it, our team turns strategy into shipped results.

    Let's Build

    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 Insights

    SQLExpert

    Deterministic and Nondeterministic Functions in SQL: Understanding Volatility Categories and Their Impact on Query Planning, Caching, and Index Usage

    30 min
    SQLPractitioner

    Multitenancy Query Patterns in SQL: Schema-Per-Tenant, Shared Tables, and Efficient Cross-Tenant Reporting Strategies

    21 min
    SQLFoundation

    Mastering SQL Joins with Aggregates: Combining GROUP BY, COUNT, and SUM Across Related Tables

    16 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
    • 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