Shopify SQL Queries: Analyze Your Store Data Like a Pro
Content
Shopify SQL Queries: Analyze Your Store Data Like a Pro
Shopify SQL Queries: Analyze Your Store Data Like a Pro
Shopify SQL Queries: Analyze Your Store Data Like a Pro
Shopify's built-in analytics show you the basics: total sales, top products, returning customers. But when your store scales, you need answers that dashboards can't give you.
Which marketing channel produces the highest lifetime value customers? What's your real refund rate by product variant? Which discount codes actually drive profit vs just revenue?
The answer is SQL. Once your Shopify data lives in a database, these queries unlock insights that no dashboard provides.
Getting Your Shopify Data into a SQL Database
Shopify is a SaaS platform, so you don't get direct database access. But there are several ways to get your data into a queryable format:
Shopify Data Export: CSV exports from Admin → Analytics → Reports
ETL Tools: Stitch, Fivetran, or Airbyte sync Shopify data to your warehouse automatically
Shopify API: Pull data programmatically into MySQL, PostgreSQL, or BigQuery
ShopifyQL Notebooks: Available on Shopify Plus for direct querying
Third-party apps: Panoply, Coupler.io, or Supermetrics
Once synced, your Shopify data typically lands in these tables:
Table
What It Contains
orders
Order ID, customer, totals, dates, status
order_line_items
Products within each order
products
Product catalog with titles, types, vendors
variants
Product variants (size, color, SKU)
customers
Customer profiles, emails, addresses
transactions
Payment details
refunds
Refund records
discount_codes
Coupon usage
inventory_levels
Stock quantities by location
Revenue & Sales Queries
1. Daily Revenue with Order Count
SELECTDATE(created_at)as order_date,COUNT(*)as total_orders,SUM(total_price)as revenue,AVG(total_price)as avg_order_value,SUM(total_discounts)as total_discounts_given
FROM ordersWHERE financial_status = 'paid'AND cancelled_at IS NULLAND created_at >= DATE_SUB(CURRENT_DATE,INTERVAL 30DAY)GROUP BY DATE(created_at)ORDER BY order_date DESC;
2. Revenue by Traffic Source
SELECTCOALESCE(referring_site,'Direct')as traffic_source,COUNT(*)as orders,SUM(total_price)as revenue,AVG(total_price)as avg_order_value,COUNT(DISTINCT customer_id)as unique_customers
FROM ordersWHERE financial_status = 'paid'AND cancelled_at IS NULLAND created_at >= DATE_SUB(CURRENT_DATE,INTERVAL 90DAY)GROUP BY referring_siteORDER BY revenue DESCLIMIT 15;
3. Revenue by Product Type
SELECTp.product_type,COUNT(DISTINCT o.id)as orders,SUM(li.quantity)as units_sold,SUM(li.price * li.quantity)as revenue,AVG(li.price)as avg_price
FROM order_line_items liJOIN orders o ON li.order_id = o.idJOIN products p ON li.product_id = p.idWHERE o.financial_status = 'paid'AND o.cancelled_atIS NULLAND o.created_at >= DATE_SUB(CURRENT_DATE,INTERVAL 30DAY)GROUP BY p.product_typeORDER BY revenue DESC;
Customer Queries
4. Customer Lifetime Value (Top 20)
SELECTc.email,c.first_name,c.last_name,c.city,c.country,COUNT(o.id)as total_orders,SUM(o.total_price)as lifetime_value,MIN(o.created_at)as first_order,MAX(o.created_at)as last_order,DATEDIFF(MAX(o.created_at),MIN(o.created_at))as customer_age_days
FROM customers cJOIN orders o ON c.id = o.customer_idWHERE o.financial_status = 'paid'AND o.cancelled_atIS NULLGROUP BY c.idORDER BY lifetime_value DESCLIMIT 20;
5. Customer Cohort: First Purchase Month
SELECTDATE_FORMAT(first_order,'%Y-%m')as cohort_month,COUNT(DISTINCT customer_id)as new_customers,SUM(first_order_value)as cohort_revenue,AVG(first_order_value)as avg_first_order
FROM(SELECT
customer_id,MIN(created_at)as first_order,(SELECT total_price FROM orders o2WHERE o2.customer_id = o.customer_idORDER BY created_at LIMIT 1)as first_order_valueFROM orders oWHERE financial_status = 'paid'AND cancelled_at IS NULLGROUP BY customer_id)cohortsGROUP BY cohort_monthORDER BY cohort_month DESC;
6. Customers at Risk of Churning
SELECTc.email,c.first_name,COUNT(o.id)as past_orders,SUM(o.total_price)as total_spent,MAX(o.created_at)as last_order,DATEDIFF(CURRENT_DATE,MAX(o.created_at))as days_since_last_order
FROM customers cJOIN orders o ON c.id = o.customer_idWHERE o.financial_status = 'paid'GROUP BY c.idHAVING COUNT(o.id) >= 2AND days_since_last_order > 90ORDER BY total_spent DESC;
Product Performance Queries
7. Best-Selling Products with Variant Breakdown
SELECTp.titleas product,v.titleas variant,v.sku,SUM(li.quantity)as units_sold,SUM(li.price * li.quantity)as revenue,AVG(li.price)as avg_selling_price
FROM order_line_items liJOIN products p ON li.product_id = p.idJOIN variants v ON li.variant_id = v.idJOIN orders o ON li.order_id = o.idWHERE o.financial_status = 'paid'AND o.cancelled_atIS NULLAND o.created_at >= DATE_SUB(CURRENT_DATE,INTERVAL 30DAY)GROUP BY p.id,v.idORDER BY units_sold DESCLIMIT 20;
8. Product Refund Rate
SELECTp.title,COUNT(DISTINCT o.id)as total_orders,COUNT(DISTINCT r.id)as refunded_orders,ROUND(COUNT(DISTINCT r.id) * 100.0 / NULLIF(COUNT(DISTINCT o.id),0),1)as refund_rate_pct,SUM(COALESCE(rli.subtotal,0))as refund_amount
FROM products pJOIN order_line_items li ON p.id = li.product_idJOIN orders o ON li.order_id = o.idLEFT JOIN refund_line_items rli ON li.id = rli.line_item_idLEFT JOIN refunds r ON rli.refund_id = r.idWHERE o.created_at >= DATE_SUB(CURRENT_DATE,INTERVAL 90DAY)GROUP BY p.idHAVING total_orders >= 10ORDER BY refund_rate_pct DESC;
Discount & Marketing Queries
9. Discount Code Performance
SELECTdc.code,COUNT(DISTINCT o.id)as times_used,SUM(o.total_price)as total_revenue,SUM(o.total_discounts)as total_discount_given,AVG(o.total_price)as avg_order_value,ROUND(SUM(o.total_discounts) * 100.0 / NULLIF(SUM(o.total_price + o.total_discounts),0),1)as discount_pct
FROM discount_codes dcJOIN orders o ON dc.order_id = o.idWHERE o.financial_status = 'paid'AND o.cancelled_atIS NULLGROUP BY dc.codeORDER BY times_used DESC;
10. Repeat Purchase Rate
SELECTtotal_customers,repeat_customers,ROUND(repeat_customers * 100.0 / total_customers,1)as repeat_rate_pct,single_purchase,ROUND(single_purchase * 100.0 / total_customers,1)as one_time_pct
FROM(SELECT
COUNT(DISTINCT customer_id)as total_customers,SUM(CASE WHEN order_count > 1THEN 1ELSE 0END)as repeat_customers,SUM(CASE WHEN order_count = 1THEN 1ELSE 0END)as single_purchase
FROM(SELECT customer_id,COUNT(*)as order_count
FROM ordersWHERE financial_status = 'paid'AND cancelled_at IS NULLGROUP BY customer_id)per_customer)summary;
Skip the SQL Learning Curve
Every query above took expertise to write. The JOINs, date functions, CASE statements, and subqueries are complex even for experienced developers.
With AI2SQL, you describe what you want in plain English:
"Show me revenue by country for the last 3 months" → done
"Which customers bought more than 3 times but haven't ordered in 60 days?" → done
"Compare discount code performance this quarter" → done
Try it free at ai2sql.io and turn your Shopify data into actionable insights.