luvv to helpDiscover the Best Free Online Tools
Topic 5 of 9

Managing Conflicts In SQL Models

Learn Managing Conflicts In SQL Models for free with explanations, exercises, and a quick test (for Analytics Engineer).

Published: December 23, 2025 | Updated: December 23, 2025

Who this is for

  • Analytics Engineers and BI Developers collaborating on SQL models in Git.
  • Anyone merging feature branches that touch the same SQL files (dbt models, views, stored procedures).
  • Teams formalizing code review, CI, and stable releases for analytics.

Prerequisites

  • Basic Git workflow: clone, branch, commit, merge, pull, push.
  • Comfort reading and editing SQL (SELECT, JOIN, WHERE, CTEs).
  • Familiarity with your project structure (e.g., models directory, macros, seeds).

Why this matters

  • Conflicts happen when multiple people change the same SQL model. Resolving them quickly keeps releases moving.
  • Incorrect conflict resolutions cause silent logic bugs (double filters, missing columns) that break dashboards.
  • A clear process reduces rework and production fire drills.

Concept explained simply

A merge conflict occurs when Git cannot automatically combine two edits to the same lines. In SQL models, this often means different filters, column aliases, or CTE structures.

Mental model: The 3-way comparison

  • Base: the original version both branches started from.
  • Ours: your branch changes.
  • Theirs: the branch you're merging in (or main during rebase).

Your job: choose the correct logic from each side and create a single, consistent SQL result. Think "compose the right query", not just "delete conflict markers".

Workflow to resolve conflicts quickly

  1. Open the conflicted file. Look for markers:
    <<<<<<< HEAD
    -- ours
    =======
    -- theirs
    >>>>>>> branch_name
  2. Understand intent.
    Fast intent checklist
    • What business rule changed? (filters, joins, aggregations)
    • Which columns/aliases are canonical?
    • Any downstream models depending on these names?
  3. Compose the right SQL.
    • Pick or merge filters (prefer inclusive but correct logic).
    • Unify column names and expressions (avoid duplicates).
    • Keep consistent style (aliases, indentation, CTE order).
  4. Test locally.
    • Run the query or build the model.
    • Spot-check row counts and a few sample rows.
  5. Stage and commit the resolution:
    git add path/to/model.sql
    git commit -m "Resolve conflict in model: unify filters and column aliases"
  6. Push and have a teammate review the logic.

Worked examples

Example 1: Filter and column expression conflict

Conflict:

SELECT
  o.order_id,
  o.customer_id,
<<<<<<< HEAD
  0.0 AS discount,
  o.amount AS order_total
=======
  COALESCE(o.discount, 0) AS discount,
  o.amount AS order_total
>>>>>>> feature/discount
FROM raw.orders o
WHERE
<<<<<<< HEAD
  o.status = 'completed'
=======
  o.status IN ('completed','shipped')
>>>>>>> feature/discount

Resolution: Keep the safer expression and the broader but correct filter.

SELECT
  o.order_id,
  o.customer_id,
  COALESCE(o.discount, 0) AS discount,
  o.amount AS order_total
FROM raw.orders o
WHERE o.status IN ('completed','shipped')

Example 2: Alias and join change conflict

Conflict:

WITH customers AS (
  SELECT id, email FROM raw.customers
),
orders AS (
  SELECT customer_id, amount FROM raw.orders
)
SELECT
<<<<<<< HEAD
  c.id AS customer_id,
  o.amount
=======
  customers.id AS customer_id,
  orders.amount
>>>>>>> feature/alias_cleanup
FROM customers c
JOIN orders o ON o.customer_id = c.id

Resolution: Use short aliases consistently.

WITH customers AS (
  SELECT id, email FROM raw.customers
),
orders AS (
  SELECT customer_id, amount FROM raw.orders
)
SELECT
  c.id AS customer_id,
  o.amount
FROM customers c
JOIN orders o ON o.customer_id = c.id

Example 3: CTE restructure conflict

Conflict:

WITH base AS (
  SELECT * FROM raw.events
),
<<<<<<< HEAD
filtered AS (
  SELECT * FROM base WHERE event_type = 'purchase'
),
agg AS (
  SELECT user_id, COUNT(*) AS purchases FROM filtered GROUP BY 1
)
=======
agg AS (
  SELECT user_id, COUNT(*) AS purchases FROM base WHERE event_type = 'purchase' GROUP BY 1
)
>>>>>>> feature/ctes
SELECT * FROM agg

Resolution: Prefer fewer CTEs if readability stays high.

WITH base AS (
  SELECT * FROM raw.events
),
agg AS (
  SELECT user_id, COUNT(*) AS purchases
  FROM base
  WHERE event_type = 'purchase'
  GROUP BY 1
)
SELECT * FROM agg

Common mistakes and how to self-check

  • Keeping both conflicting lines (duplicate columns). Self-check: run the query; look for duplicate column names or ambiguous references.
  • Losing business logic by accepting one side blindly. Self-check: compare the resolved SQL to both sides; did you keep all intended conditions/columns?
  • Inconsistent aliases causing downstream errors. Self-check: ensure a single alias per table within the query.
  • Style-only conflicts because of formatting. Self-check: run a formatter before committing to reduce noisy diffs.
  • Forgetting to run the model. Self-check: build or run the SQL locally and verify row counts and sample rows.

Practical projects

  • Create a demo repo with two branches changing the same SQL model; practice resolving conflicts and writing clear commit messages.
  • Introduce a project-wide SQL style guide (aliases, capitalization, indentation) and reformat models to reduce future conflicts.
  • Add a lightweight checklist to your PR template: filters, aggregates, aliases, duplicates, downstream impacts.

Exercises

Do these in order. They mirror the interactive exercises below.

Exercise 1: Resolve a filter and expression conflict

Goal: Merge discount expression and broaden filter correctly. After resolving, stage and commit.

  • Resolve to COALESCE(o.discount, 0) and status IN ('completed','shipped').
  • Keep alias o consistently.
  • Commit with a clear message.
Exercise 2: Resolve a rename vs new column conflict

Goal: Keep the standardized column name and preserve the new column.

  • Adopt order_total as the canonical name for the amount field.
  • Retain tax as a separate column; ensure expressions compile.
  • Commit with a message describing both decisions.

Checklist before you commit

  • [ ] No conflict markers remain.
  • [ ] No duplicate or ambiguous column names.
  • [ ] Aliases are consistent throughout the query.
  • [ ] Filters reflect intended business logic from both sides.
  • [ ] Query runs and returns expected row counts.

Learning path

  1. Refresh core Git: branching, merging, rebasing, resolving conflicts.
  2. Adopt a SQL style guide and a formatter to minimize conflicts.
  3. Practice resolving realistic SQL conflicts (filters, joins, CTEs).
  4. Add reviews: a second set of eyes on logic after conflict resolution.
  5. Automate basic checks (build model, lint, simple data assertions) in CI.

Next steps

  • Practice on a small sandbox repo until you can resolve typical conflicts in minutes.
  • Add a PR checklist. Make it part of your team’s definition of done.
  • Document canonical column names to avoid repeated rename conflicts.

Mini challenge

Two branches changed the same model: one adds WHERE channel IN ('web','app'), the other adds WHERE country = 'US'. Compose the final WHERE so both are applied and ensure indexes/partitions still make sense for performance.

Quick Test: what to expect

Short multiple-choice test on conflict markers, merge vs rebase, and safe SQL resolutions. Available to everyone; log in to save your progress.

Practice Exercises

2 exercises to complete

Instructions

You are merging feature/discount into your branch. The file models/orders.sql has this conflict:

SELECT
  o.order_id,
  o.customer_id,
<<<<<<< HEAD
  0.0 AS discount,
  o.amount AS order_total
=======
  COALESCE(o.discount, 0) AS discount,
  o.amount AS order_total
>>>>>>> feature/discount
FROM raw.orders o
WHERE
<<<<<<< HEAD
  o.status = 'completed'
=======
  o.status IN ('completed','shipped')
>>>>>>> feature/discount

Tasks:

  • Resolve the conflict to keep COALESCE for discount and the broader status filter.
  • Use alias o consistently.
  • Stage and commit the resolved file with a clear message.
Expected Output
Final SQL selects COALESCE(o.discount, 0) AS discount and WHERE o.status IN ('completed','shipped'). Git commands: git add models/orders.sql then git commit -m "Resolve orders conflict: COALESCE discount + include shipped"

Managing Conflicts In SQL Models — Quick Test

Test your knowledge with 7 questions. Pass with 70% or higher.

7 questions70% to pass

Have questions about Managing Conflicts In SQL Models?

AI Assistant

Ask questions about this tool