← Back to list

Asking for a Query vs. Asking for Help Designing a Query

Before asking for the implementation, collaborate on a design.

Wrangler · 2026-05-08 11:41 · 1 claps · 17.8 min read paywalled
#prompt-engineering #data-engineering #systems-design-thinking #semantic-debt #ai-responsibility
Open on Medium ↗
Wiki topics: PRD · Product Design 🔧 · Data Engineering 📊 · Economic Policy

Asking for a Query vs. Asking for Help Designing a Query

Before asking for the implementation, collaborate on a design.

Photo by Sunder Muthukumaran on Unsplash

Photo by Sunder Muthukumaran on Unsplash

The Spark

A recent T-SQL request gave us the whole lesson in miniature:

The first prompt asked the chatbot for SQL.

The better prompt would have asked the chatbot to behave like a data engineer first.

The business need was reasonable: understand task backlog trends over time. Specifically, calculate the average number of open tasks per day, summarized by month.

The initial prompt was basically this:

There is a [Task] table with [Created On] and [Completed On]. 
Write a query to calculate the average number of daily open 
tasks from month to month.

The chatbot produced a structured query. It was reasonable. It was almost correct. It was not the query we would want running against a large operational table spanning more than twenty years.

That is the lesson. The problem was not that someone used a chatbot. The problem was that the interaction started at the implementation layer.

Let’s give ourselves a break. Most people are still learning how to work with GenAI. A better framing is often to start by asking for help designing the query.

The Core Comparison

Asking for a Query

Write a T-SQL query to calculate the average number of 
daily open tasks by month.
The table is [Task].
The columns are [Task Number], [Created On] and [Completed On].

This asks the chatbot to produce an artifact before it has enough information to understand the problem.

It may still produce something that looks good. In fact, that is part of the danger. A polished query can hide undefined business rules, performance risks, and data quality assumptions.

Asking for Help Designing a Query

I need to understand task backlog trends over time.
Before writing SQL, help me design the right T-SQL approach.
The source table is dbo.[Task], with:
- [Task Number]
- [Created On]
- [Completed On]
The goal is to calculate the average number of open tasks per day, summarized by month.
Please help me identify the business rules, assumptions, data quality checks, performance risks, and implementation options before writing the final query.

This asks the chatbot to help shape the problem first.

Why the First Prompt Was Under-Specified

The chatbot was given only a concept and two date columns. That leaves too much unresolved.

A good data engineer would immediately ask questions like:

  • Do we already have a calendar table?
  • How many rows are in [Task]?
  • How many years of data are involved?
  • Does [Task Number] uniquely identify each task?
  • Is there a primary key?
  • What does “open” mean?
  • Should same-day created/completed tasks count?
  • Are we ignoring time of day?
  • Are time zones relevant?
  • Can [Created On] ever be after [Completed On]?
  • Can tasks be reopened?
  • Is this a one-time analysis or a recurring report?
  • Do we need sanity-check columns in the result?

Those questions are not over-engineering. They are the work. The SQL comes after understanding the problem and designing the solution.

Calendar Table: Small Detail, Big Consequence

One of the first questions should have been:

Do we already have a calendar table?

With the first query, the chatbot generated a calendar inside the query using a CTE. That is not automatically wrong. If this were a small, one-time analysis over a narrow date range, generating dates inside the query might be perfectly acceptable.

But that changes when the source table has more than one million rows and spans roughly twenty years. For recurring reporting, a real calendar table is usually better. It can be indexed, reused, tested, and extended with useful reporting attributes.

A good calendar table can support:

  • calendar date
  • month start
  • month end
  • fiscal period
  • business day flag
  • holiday flag
  • week number
  • reporting period
  • timezone-adjusted reporting dates, if needed

The chatbot did not know whether that table existed, so it invented one. That was a reasonable fallback, but it can introduce performance issues.

Uniqueness: Defensive SQL Can Become Expensive SQL

Another issue: the chatbot added unnecessary DISTINCT and deduplication-oriented GROUP BY logic because it could not confirm whether [Task] had unique rows. That is an understandable defensive move.

If the table had duplicate task records, then counting rows directly could inflate the result. A cautious query might try to deduplicate tasks before calculating open counts. But in this case, [Task Number] exists and the table has a primary key. If each task is already unique, extra deduplication can become expensive noise.

On a small table, an unnecessary DISTINCT may not matter. On a table with more than one million rows, it can matter a lot. This is the key distinction: Defensive SQL is helpful when the data model is unknown. Once the data model is known, unnecessary defenses should be removed.

This does not mean GROUP BY is bad. Grouping is exactly the right tool when we are aggregating daily events, monthly counts, or reporting metrics. The waste comes from grouping or using DISTINCT only to remove duplicate task rows when the model already guarantees those duplicates cannot exist.

The chatbot was not wrong to be cautious. It was missing context. A better prompt would include:

[Task Number] uniquely identifies a task. 
The [Task] table has a primary key, 
so do not add DISTINCT or deduplication logic 
unless you explain why it is still necessary.

That one sentence can prevent a lot of unnecessary work.

What Does “Open” Mean?

This is the heart of the problem. The phrase “open task” sounds simple until we have to calculate it.

Possible definitions include:

  • Open at beginning of day: Task was already open when the day started
  • Open at end of day: Task was still open when the day ended
  • Open at any time during the day: Task existed in an open state at any point during that date
  • Inclusive date span: Task counts for every date from created date through completed date

In this case, same-day created/completed tasks should count. That means a task created on May 1 and completed on May 1 should contribute to May 1’s open task count. That business rule changes the SQL. For an event/delta approach, the logic becomes:

  • add +1 on [Created Date]
  • add -1 on the day after [Completed Date]

Not on [Completed Date] itself. If we subtract on the completed date, same-day created/completed tasks cancel out and disappear from the daily count. Sometimes that is the right definition. In this case, it is not.

That is why the chatbot should be asked to clarify the definition before writing the query.

Time of Day and Time Zones

The source columns are datetime values:

[Created On]
[Completed On]

But the reporting question is daily. So another question should be:

Should this report use full datetime precision, or should it ignore time of day and calculate by calendar date?

In this case, we ignore time of day. That means the query should intentionally convert datetime values into date values. The conversion is not an accident. It is a business rule.

In other cases, time of day could matter. For example:

  • SLA reporting
  • end-of-business-day reporting
  • overnight task queues
  • global teams
  • systems storing timestamps in UTC
  • reporting by local office or region

Timezone handling can become especially important when a task is created late at night in one timezone and reported in another.

For this scenario, the assumption is acceptable if documented: This report ignores time of day and treats created/completed values as calendar dates. Timezone effects are out of scope for this version.

That is a good assumption because it is visible. Silent assumptions are where the bugs breed.

Do Not Assume Created Date Is Before Completed Date

A clean logical model says:

[Created On] <= [Completed On]

Real operational data does not always obey clean logical models. Bad date sequences can happen because of:

  • data migrations
  • automation bugs
  • manual corrections
  • old system behavior
  • reopened tasks
  • timezone conversion problems
  • default dates
  • partial backfills
  • prior workflow defects

So the chatbot should not assume every completed task has a valid date sequence. It should ask:

Can [Created On] ever be after [Completed On]?

If the answer is yes, or even “we are not sure,” the query needs a policy. Possible policy:

  • exclude invalid date sequences from the backlog calculation
  • count them separately as [Invalid Date Count]
  • make the issue visible in the monthly output

That way, the report does not quietly turn bad data into bad insight.

Can Tasks Be Reopened?

Another hidden assumption is that each task has a single lifecycle:

created → completed

But many task systems allow tasks to be reopened:

created → completed → reopened → completed again

If tasks can be reopened, then [Created On] and [Completed On] may not be enough to reconstruct true historical open counts. To model reopened tasks correctly, we would need lifecycle history, such as:

  • status history
  • audit records
  • workflow transition events
  • reopened date
  • reclosed date
  • effective-dated state intervals

That does not mean we must always model reopening. It means we should be honest.

A practical note for the article: If tasks can be reopened, a simple created/completed query may still be useful for trend analysis, but it is not a complete historical lifecycle model. Handling reopened tasks correctly requires additional history and should only be added if the value justifies the cost.

Performance: Correct Eventually Is Not Good Enough

A query can be logically close and still operationally dangerous. One common approach is to join every task to every calendar day where the task was open. Conceptually, that looks like this:

SELECT
    [Calendar].[Calendar Date],
    [Open Task Count] = COUNT_BIG(*)
FROM dbo.[Calendar] AS [Calendar]
JOIN dbo.[Task] AS [Task]
    ON CONVERT(date, [Task].[Created On]) <= [Calendar].[Calendar Date]
    AND
    (
        [Task].[Completed On] IS NULL
        OR CONVERT(date, [Task].[Completed On]) >= [Calendar].[Calendar Date]
    )
GROUP BY
    [Calendar].[Calendar Date];

That is easy to understand. It may also be very expensive.

If we have over one million tasks and roughly twenty years of dates, we have created a large interval-join problem. SQL Server may eventually return the correct result, but “eventually correct” is not a good enough standard for recurring reporting.

For this type of backlog calculation, a daily event/delta model is often a better starting point. Instead of expanding each task across every day it was open, we record changes without exploding task-by-calendar:

  • +1 when a task enters the open population
  • -1 when it leaves the open population
  • running total by date
  • monthly average over those daily totals

There is also a date-handling tradeoff worth making explicit. Converting datetime values to dates makes the business rule visible, but doing that conversion repeatedly inside predicates can make it harder for SQL Server to use ordinary indexes efficiently. For a recurring report, it may be better to support the query with persisted computed date columns, indexed reporting-date columns, or a pre-aggregated reporting table. The right choice depends on the table size, update pattern, reporting frequency, and how much schema change the team can safely make.

Sanity Checks Belong in the Result

The output should not only return the average daily open task count. It should include movement and quality checks.

Suggested output columns:

  • [Month Start]
  • [Average Daily Open Tasks]
  • [Minimum Daily Open Tasks]
  • [Maximum Daily Open Tasks]
  • [Tasks Created During Month]
  • [Tasks Completed During Month]
  • [Tasks Created And Completed Same Day]
  • [Invalid Date Count]
  • [Invalid Tasks Missing Created Date]
  • [Starting Open Task Count]
  • [Ending Open Task Count]

Why? Because the average backlog number is easier to trust when we can see what flowed in and what flowed out.

  • If tasks are being created and closed at about the same rate, a stable backlog makes sense.
  • If created tasks greatly exceed completed tasks, a rising backlog may be real.
  • If completed tasks suddenly drop to almost nothing, that may be operational insight. Or it may be a data issue.
  • If invalid date counts spike after a migration, that is not just a query problem. That is a data quality signal.

Good reporting surfaces the moments when the answer deserves another look.

Human-Readable SQL Should Be Part of the Ask

Another problem we need to address is that many people run the query they get from a chatbot without really understanding it. If the numbers look reasonable, they assume the query is good enough. That is risky.

A query can return plausible numbers while still being wrong, expensive, fragile, or built on hidden assumptions. We cannot make everyone a SQL expert overnight. But we can ask the chatbot to produce SQL that is easier for a human to inspect.

That means asking for:

  • comments that explain each CTE
  • comments that explain the business rule being implemented
  • comments that explain date boundary choices
  • comments that explain how same-day created/completed tasks are handled
  • comments that explain why a calendar table is being used
  • comments that explain any excluded or flagged records
  • comments that explain the reporting date range and whether it is month-aligned
  • readable aliases and output column names
  • a short explanation after the query describing how the pieces work together

The prompt should say this explicitly:

When you write the final T-SQL, make it human-readable and reviewable.
Please include comments explaining:
- the purpose of each CTE
- how the open-task definition is implemented
- why completed tasks subtract on the day after completion
- how NULL [Completed On] values are handled
- how invalid date sequences are handled
- how records with missing created dates are surfaced
- why the report period is aligned to complete months
- why the calendar table is used
- where the monthly sanity-check columns come from
Use clear aliases and avoid clever SQL that is hard to review.
After the query, explain the query section by section in plain 
English so someone can validate the logic before running it.

This is not about style. Comments are a safety feature. They make it harder for a polished-looking query to smuggle in a bad assumption.

The Better Prompt

The full prompt can be long. If that feels like too much, start with this:

Before writing SQL, ask me the questions a data engineer would ask.
Help me clarify the business definition, date rules, data quality risks,
performance tradeoffs, and reporting checks first.
Then recommend an approach before writing the final query.

That shorter prompt is not perfect, but it moves the conversation to the right layer.

Here is the kind of fuller prompt that should have started the process.

I need help designing a T-SQL solution, but do not write the final query yet.
Business goal:
I am trying to understand task backlog trends month to month. 
Specifically, I want the average number of open tasks per day, 
summarized by month.
Source table:
dbo.[Task]
Relevant columns:
- [Task Number]
- [Created On] datetime
- [Completed On] datetime nullable
Environment:
- SQL Server / T-SQL
- The table may contain over 1 million rows
- The data spans roughly 20 years
- This may become a recurring report, not just a one-time query
- [Task Number] uniquely identifies a task
- The table has a primary key, so do not add DISTINCT 
  or deduplication logic unless there is a specific reason
Known business rules and assumptions:
- A NULL [Completed On] means the task is still open
- We ignore time of day for this report and calculate by calendar date
- Same-day created/completed tasks should count as open for that day
- Do not assume [Created On] is always before or equal to [Completed On]
- Tasks may have been affected by migrations, automations, prior bugs, 
  or bad historical data
- Tasks might be reopenable, but we may not have enough history 
  to model that correctly
Before writing SQL, help me clarify:
1. Whether this requires an existing calendar table or a generated date set
2. What definition of “open” should be used
3. How same-day created/completed tasks should be counted
4. How invalid date sequences should be handled
5. Whether reopened tasks can be accurately represented with only these columns
6. What performance risks exist with different approaches
7. Whether DISTINCT, GROUP BY, or deduplication logic is actually needed
Then compare these implementation options:
1. Calendar table joined directly to task intervals
2. Daily created/completed deltas with a running total
3. Pre-aggregated reporting table
For each option, explain:
- how it works
- when it is appropriate
- performance risks
- assumptions
- recommended indexes or supporting tables
After that, recommend the best approach for a 
recurring monthly backlog trend report.
The final output should include:
- [Month Start]
- [Average Daily Open Tasks]
- [Minimum Daily Open Tasks]
- [Maximum Daily Open Tasks]
- [Tasks Created During Month]
- [Tasks Completed During Month]
- [Tasks Created And Completed Same Day]
- [Invalid Date Count]
- [Invalid Tasks Missing Created Date]
- [Starting Open Task Count]
- [Ending Open Task Count]
Finally, review the query for correctness, 
performance, and hidden assumptions.

The Process We Want People to Learn

The important comparison is not one chatbot versus another. Instead, it is about a “just give me the query” prompt and a “let’s design this query” prompt:

  • Ask for a query → Ask for help designing a query
  • Start with implementation → Start with the business goal
  • Accept hidden assumptions → Surface assumptions explicitly
  • Let the chatbot choose definitions → Define terms like “open” before coding
  • Optimize for a polished answer → Optimize for a usable answer
  • Trust the first draft → Ask for review, test cases, and sanity checks
  • Treat SQL as text generation → Treat SQL as data engineering

The real skill is not magic. It is the old engineering discipline wearing a new interface.

  1. First understand the problem.
  2. Then design the approach.
  3. Then write the query.

The chatbot can help at every step, but only if we invite it.

Closing Thought

A chatbot can generate SQL quickly. But speed without context can turn a reasonable business question into an expensive database meditation session.

The better habit is simple: Do not ask the chatbot to start by writing the query. Ask it to help you design the query.

That is how we move from code-shaped answers to engineering-shaped thinking.

Same tool. Better layer. Fewer knots in the weave.

Appendix: What the Commented T-SQL Might Look Like

With the answers we have so far, the query should be written as something a human can review, not just something SQL Server can execute.

Assumptions for this example:

  • SQL Server / T-SQL
  • dbo.[Task] has one row per task
  • [Task Number] uniquely identifies a task
  • no DISTINCT or deduplication is needed
  • dbo.[Calendar] already exists
  • dbo.[Calendar].[Calendar Date] contains one row per calendar date
  • time of day is ignored
  • same-day created/completed tasks count as open for that day
  • a task is considered open for every date from created date through completed date, inclusive
  • NULL [Completed On] means the task is still open
  • invalid date sequences are excluded from the open-task calculation and surfaced separately
  • tasks with missing created dates are surfaced separately because they cannot honestly be assigned to a created month
  • reopened tasks are not modeled because [Created On] and [Completed On] alone are not enough to reconstruct reopen history
  • the report period is month-aligned so starting and ending open counts represent complete calendar months.
DECLARE @StartMonth date = '2025-01-01';
DECLARE @EndMonth date = '2025-12-01';

DECLARE @StartDate date =
    DATEFROMPARTS(YEAR(@StartMonth), MONTH(@StartMonth), 1);

DECLARE @EndDateInclusive date =
    EOMONTH(@EndMonth);

DECLARE @EndDateExclusive date =
    DATEADD(day, 1, @EndDateInclusive);

/*
    Purpose:
    Calculate average daily open tasks by month.

    Date range:
    The caller provides a starting month and ending month. The query normalizes
    those inputs to the first day of the starting month and the last day of the
    ending month, then uses an exclusive next-day boundary for date predicates.

    Definition of open for this version:
    A task counts as open on every calendar date from its created date
    through its completed date, inclusive.

    That means a task created and completed on the same day counts as open
    for that day.

    Important modeling limitation:
    This query does not model reopened tasks. If tasks can be completed,
    reopened, and completed again, accurate historical reporting requires
    task lifecycle/status history, not just [Created On] and [Completed On].
*/

WITH [Normalized Task] AS
(
    /*
        Convert datetime values to date values once so the rest of the query
        works at the reporting grain: calendar day.

        We are intentionally ignoring time of day for this report.
        If timezone or end-of-business-day reporting matters, this CTE is
        where that rule should be handled explicitly.
    */
    SELECT
        [Task].[Task Number],
        [Created Date] = CONVERT(date, [Task].[Created On]),
        [Completed Date] = CONVERT(date, [Task].[Completed On])
    FROM dbo.[Task] AS [Task]
),
[Invalid Task] AS
(
    /*
        Surface records that cannot safely participate in the backlog math.

        The most important invalid pattern here is a completed date before
        the created date. That can happen because of migrations, automations,
        timezone conversion issues, manual corrections, or prior system bugs.

        These rows are excluded from the open-task calculation but counted
        later as a data-quality signal.
    */
    SELECT
        [Normalized Task].[Task Number],
        [Normalized Task].[Created Date],
        [Normalized Task].[Completed Date]
    FROM [Normalized Task]
    WHERE
        [Normalized Task].[Created Date] IS NULL
        OR
        (
            [Normalized Task].[Completed Date] IS NOT NULL
            AND [Normalized Task].[Completed Date] < [Normalized Task].[Created Date]
        )
),
[Invalid Task Without Created Date] AS
(
    /*
        Count invalid records that do not have a created date.

        These cannot be assigned to a created month, so they are surfaced as
        a separate data-quality column rather than hidden inside a monthly
        invalid-date count.
    */
    SELECT
        [Invalid Tasks Missing Created Date] = COUNT_BIG(*)
    FROM [Invalid Task]
    WHERE
        [Invalid Task].[Created Date] IS NULL
),
[Valid Task] AS
(
    /*
        Keep only rows that can safely be used for the open-task calculation.

        No DISTINCT is used because [Task Number] uniquely identifies a task
        and the table has a primary key.
    */
    SELECT
        [Normalized Task].[Task Number],
        [Normalized Task].[Created Date],
        [Normalized Task].[Completed Date]
    FROM [Normalized Task]
    WHERE
        [Normalized Task].[Created Date] IS NOT NULL
        AND
        (
            [Normalized Task].[Completed Date] IS NULL
            OR [Normalized Task].[Completed Date] >= [Normalized Task].[Created Date]
        )
),
[Starting Open Tasks] AS
(
    /*
        Count tasks that were already open on the first day of the report.

        Because completed dates are inclusive, a task completed on @StartDate
        is still counted as open on @StartDate.
    */
    SELECT
        [Starting Open Task Count] = COUNT_BIG(*)
    FROM [Valid Task]
    WHERE
        [Valid Task].[Created Date] < @StartDate
        AND
        (
            [Valid Task].[Completed Date] IS NULL
            OR [Valid Task].[Completed Date] >= @StartDate
        )
),
[Task Event] AS
(
    /*
        Add +1 when a task enters the open population.
    */
    SELECT
        [Event Date] = [Valid Task].[Created Date],
        [Task Delta] = CONVERT(bigint, 1)
    FROM [Valid Task]
    WHERE
        [Valid Task].[Created Date] >= @StartDate
        AND [Valid Task].[Created Date] < @EndDateExclusive

    UNION ALL

    /*
        Add -1 on the day AFTER completion.

        This is the key rule that makes same-day created/completed tasks
        count as open on their completed date.

        Example:
        Created Date   = 2025-01-10
        Completed Date = 2025-01-10

        Events:
        2025-01-10: +1
        2025-01-11: -1

        Result:
        The task contributes to the open count on 2025-01-10.
    */
    SELECT
        [Event Date] = DATEADD(day, 1, [Valid Task].[Completed Date]),
        [Task Delta] = CONVERT(bigint, -1)
    FROM [Valid Task]
    WHERE
        [Valid Task].[Completed Date] IS NOT NULL
        AND DATEADD(day, 1, [Valid Task].[Completed Date]) >= @StartDate
        AND DATEADD(day, 1, [Valid Task].[Completed Date]) < @EndDateExclusive
),
[Daily Task Delta] AS
(
    /*
        Collapse task-level events into one net change per day.

        This keeps the query from joining every task to every calendar date.
    */
    SELECT
        [Task Event].[Event Date],
        [Task Delta] = SUM([Task Event].[Task Delta])
    FROM [Task Event]
    GROUP BY
        [Task Event].[Event Date]
),
[Daily Open Tasks] AS
(
    /*
        Use the calendar table as a date scaffold.

        This is different from joining every task to every date. At this point,
        the task table has already been reduced to daily deltas.

        The running total gives the open task count for each calendar date.
    */
    SELECT
        [Calendar].[Calendar Date],
        [Open Task Count] =
            [Starting Open Tasks].[Starting Open Task Count]
            + SUM(ISNULL([Daily Task Delta].[Task Delta], 0)) OVER
            (
                ORDER BY [Calendar].[Calendar Date]
                ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
            )
    FROM dbo.[Calendar] AS [Calendar]
    CROSS JOIN [Starting Open Tasks]
    LEFT JOIN [Daily Task Delta]
        ON [Daily Task Delta].[Event Date] = [Calendar].[Calendar Date]
    WHERE
        [Calendar].[Calendar Date] >= @StartDate
        AND [Calendar].[Calendar Date] < @EndDateExclusive
),
[Monthly Flow] AS
(
    /*
        Calculate sanity-check metrics by month.

        These columns help validate whether the backlog trend makes sense.
        If created and completed counts diverge sharply, that may be insight
        or it may reveal a data-quality or process issue.
    */
    SELECT
        [Month Start] = DATEFROMPARTS(YEAR([Valid Task].[Created Date]), MONTH([Valid Task].[Created Date]), 1),
        [Tasks Created During Month] = COUNT_BIG(*),
        [Tasks Created And Completed Same Day] =
            SUM
            (
                CASE
                    WHEN [Valid Task].[Completed Date] = [Valid Task].[Created Date]
                    THEN CONVERT(bigint, 1)
                    ELSE CONVERT(bigint, 0)
                END
            )
    FROM [Valid Task]
    WHERE
        [Valid Task].[Created Date] >= @StartDate
        AND [Valid Task].[Created Date] < @EndDateExclusive
    GROUP BY
        DATEFROMPARTS(YEAR([Valid Task].[Created Date]), MONTH([Valid Task].[Created Date]), 1)
),
[Monthly Completion] AS
(
    /*
        Count completed tasks by completion month.
    */
    SELECT
        [Month Start] = DATEFROMPARTS(YEAR([Valid Task].[Completed Date]), MONTH([Valid Task].[Completed Date]), 1),
        [Tasks Completed During Month] = COUNT_BIG(*)
    FROM [Valid Task]
    WHERE
        [Valid Task].[Completed Date] >= @StartDate
        AND [Valid Task].[Completed Date] < @EndDateExclusive
    GROUP BY
        DATEFROMPARTS(YEAR([Valid Task].[Completed Date]), MONTH([Valid Task].[Completed Date]), 1)
),
[Monthly Invalid Task] AS
(
    /*
        Count invalid tasks by created month when possible.

        This is a data-quality signal, not part of the backlog calculation.
        Invalid tasks with no created date are counted separately because
        they cannot be honestly assigned to a month.
    */
    SELECT
        [Month Start] = DATEFROMPARTS(YEAR([Invalid Task].[Created Date]), MONTH([Invalid Task].[Created Date]), 1),
        [Invalid Date Count] = COUNT_BIG(*)
    FROM [Invalid Task]
    WHERE
        [Invalid Task].[Created Date] >= @StartDate
        AND [Invalid Task].[Created Date] < @EndDateExclusive
    GROUP BY
        DATEFROMPARTS(YEAR([Invalid Task].[Created Date]), MONTH([Invalid Task].[Created Date]), 1)
),
[Monthly Open Tasks] AS
(
    /*
        Aggregate daily open task counts to the monthly reporting grain.
    */
    SELECT
        [Month Start] = DATEFROMPARTS(YEAR([Daily Open Tasks].[Calendar Date]), MONTH([Daily Open Tasks].[Calendar Date]), 1),
        [Average Daily Open Tasks] = AVG(CONVERT(decimal(18, 2), [Daily Open Tasks].[Open Task Count])),
        [Minimum Daily Open Tasks] = MIN([Daily Open Tasks].[Open Task Count]),
        [Maximum Daily Open Tasks] = MAX([Daily Open Tasks].[Open Task Count]),
        [Starting Open Task Count] =
            MAX
            (
                CASE
                    WHEN [Daily Open Tasks].[Calendar Date] = DATEFROMPARTS(YEAR([Daily Open Tasks].[Calendar Date]), MONTH([Daily Open Tasks].[Calendar Date]), 1)
                    THEN [Daily Open Tasks].[Open Task Count]
                END
            ),
        [Ending Open Task Count] =
            MAX
            (
                CASE
                    WHEN [Daily Open Tasks].[Calendar Date] = EOMONTH([Daily Open Tasks].[Calendar Date])
                    THEN [Daily Open Tasks].[Open Task Count]
                END
            )
    FROM [Daily Open Tasks]
    GROUP BY
        DATEFROMPARTS(YEAR([Daily Open Tasks].[Calendar Date]), MONTH([Daily Open Tasks].[Calendar Date]), 1)
)
SELECT
    [Monthly Open Tasks].[Month Start],
    [Monthly Open Tasks].[Average Daily Open Tasks],
    [Monthly Open Tasks].[Minimum Daily Open Tasks],
    [Monthly Open Tasks].[Maximum Daily Open Tasks],
    [Tasks Created During Month] = ISNULL([Monthly Flow].[Tasks Created During Month], 0),
    [Tasks Completed During Month] = ISNULL([Monthly Completion].[Tasks Completed During Month], 0),
    [Tasks Created And Completed Same Day] = ISNULL([Monthly Flow].[Tasks Created And Completed Same Day], 0),
    [Invalid Date Count] = ISNULL([Monthly Invalid Task].[Invalid Date Count], 0),
    [Invalid Tasks Missing Created Date] = [Invalid Task Without Created Date].[Invalid Tasks Missing Created Date],
    [Monthly Open Tasks].[Starting Open Task Count],
    [Monthly Open Tasks].[Ending Open Task Count]
FROM [Monthly Open Tasks]
CROSS JOIN [Invalid Task Without Created Date]
LEFT JOIN [Monthly Flow]
    ON [Monthly Flow].[Month Start] = [Monthly Open Tasks].[Month Start]
LEFT JOIN [Monthly Completion]
    ON [Monthly Completion].[Month Start] = [Monthly Open Tasks].[Month Start]
LEFT JOIN [Monthly Invalid Task]
    ON [Monthly Invalid Task].[Month Start] = [Monthly Open Tasks].[Month Start]
ORDER BY
    [Monthly Open Tasks].[Month Start];

This is longer than a naked chatbot query, but the length is doing useful work. The comments explain what each section is responsible for. The CTEs create reviewable checkpoints. The sanity-check columns help the reader decide whether the numbers make sense.

That matters because a query that returns reasonable-looking numbers can still be wrong. Readable SQL gives the human reviewer more places to pause, inspect, and ask better questions before the query becomes trusted.


메타데이터
post_id
99e8eb9645bc
slug
asking-for-a-query-vs-asking-for-help-designing-a-query-99e8eb9645bc
url
https://medium.com/@mike.besso/asking-for-a-query-vs-asking-for-help-designing-a-query-99e8eb9645bc
canonical_url
https://medium.com/@mike.besso/asking-for-a-query-vs-asking-for-help-designing-a-query-99e8eb9645bc
author_url
https://medium.com/@mike.besso
status
ok
fetched_at
2026-06-09 15:37:30