Search code examples
sqlsql-servercountsql-server-2012subquery

How do I count subquery CASE WHEN categories with an outer query?


I have a sample set of data in this db<>fiddle. The data represents a batch of wells that fall into different well-type categories based on some criteria. I am trying to group the wells by the category they fall into and then count how many wells are in each category based on another set of criteria.

My current query partially works but only correctly counts wells that are higher in the CASE WHEN clause hierarchy. This is because the first CASE WHEN has the chance to assign well categories to all of the wells in the data set. However, as it goes through each CASE WHEN clause, the query "see's" fewer wells because it runs out of wells it can assign a category to. By the time it reaches the end, almost all of the wells have already had a category assigned to them, preventing some category counts from occurring at all.

Here is the current query I have, which is also in the db<>fiddle link above:

SELECT
    dt.WellCategory,
    ISNULL(SUM(CASE WHEN dt.LeaseType IN ('F','I','S','P') OR dt.LeaseType NOT IN ('F', 'I', 'S', 'P', 'U') THEN 1 END), 0) AS [Number of Wells], -- Federal + Indian + State + Fee + Multi-Lease
    ISNULL(SUM(CASE WHEN dt.LeaseType = 'F' THEN 1 END), 0) AS [Federal], -- Sums up how many wells from the subquery have a leasetype of "Federal"
    ISNULL(SUM(CASE WHEN dt.LeaseType = 'I' THEN 1 END), 0) AS [Indian],
    ISNULL(SUM(CASE WHEN dt.LeaseType = 'S' THEN 1 END), 0) AS [State],
    ISNULL(SUM(CASE WHEN dt.LeaseType = 'P' THEN 1 END), 0) AS [Fee (Private)],
    ISNULL(SUM(CASE WHEN dt.LeaseType NOT IN ('F', 'I', 'S', 'P', 'U') THEN 1 END), 0) AS [Multiple Lease Types]
FROM
(
    SELECT -- Subquery labels wells according to their wellstatus, welltype, etc.
        c.LeaseType,
        CASE 
            WHEN w.WellStatus = 'p' AND w.WellType = 'gw' OR ((w.WellStatus = 'pai' OR w.WellStatus = 'pii') AND (w.WellType = 'gwi' OR w.WellType = 'ggi' OR w.WellType = 'gwd')) THEN 'Producing Gas Wells'
            WHEN w.WellStatus = 'p' AND w.WellType = 'ow' OR ((w.WellStatus = 'pai' OR w.WellStatus = 'pii') AND (w.WellType = 'owi' OR w.WellType = 'ogi' OR w.WellType = 'owd')) THEN 'Producing Oil Wells'
            WHEN w.WellStatus = 's' AND w.WellType = 'ow' OR ((w.WellStatus = 'sai' OR w.WellStatus = 'sii') AND (w.WellType = 'owi' OR w.WellType = 'ogi' OR w.WellType = 'owd')) THEN 'Shut-In Oil Wells'
            WHEN w.WellStatus = 's' AND w.WellType = 'gw' OR ((w.WellStatus = 'sai' OR w.WellStatus = 'sii') AND (w.WellType = 'gwi' or w.WellType = 'ggi' or w.WellType = 'gwd')) THEN 'Shut-In Gas Wells'
            WHEN w.WellStatus = 'a' AND w.WellType = 'wi' THEN 'Active Water Injection Wells'
            WHEN w.WellStatus = 'a' AND w.WellType = 'gi' THEN 'Active Gas Injection Wells'
            WHEN w.WellStatus = 'a' AND w.WellType = 'wd' THEN 'Active Water Disposal Wells'
            WHEN w.WellStatus = 'a' AND w.WellType = 'gs' THEN 'Active Gas Storage Wells'
            WHEN w.WellStatus = 'a' AND w.WellType = 'ws' THEN 'Active Water Source Wells'
            WHEN w.WellStatus = 'a' AND w.WellType = 'tw' THEN 'Active Test Holes'
            WHEN w.WellStatus = 'i' AND w.WellType = 'wi' THEN 'Inactive Water Injection Wells'
            WHEN w.WellStatus = 'i' AND w.WellType = 'gi' THEN 'Inactive Gas Injection Wells'
            WHEN w.WellStatus = 'i' AND w.WellType = 'wd' THEN 'Inactive Water Disposal Wells'
            WHEN w.WellStatus = 'i' AND w.WellType = 'gs' THEN 'Inactive Gas Storage Wells'
            WHEN w.WellStatus = 'i' AND w.WellType = 'ws' THEN 'Inactive Water Source Wells'
            WHEN w.WellStatus = 'i' AND w.WellType = 'tw' THEN 'Inactive Test Holes'
            WHEN w.WellStatus = 'ta' THEN 'Temporarily-Abandoned Wells'
            WHEN w.WellStatus = 'pa' THEN 'Plugged and Abandoned Wells'
            WHEN c.LateralStatus = 'NEW' THEN 'New Permits - Not Yet Approved'
            WHEN c.LateralStatus = 'APD' THEN 'Permits Approved - Not Yet Commenced'
            WHEN c.LateralStatus = 'DRL' THEN 'Drilling Commenced - Not Yet Completed'
            WHEN c.LateralStatus = 'OPS' THEN 'Drilling Operations Suspended'
            WHEN w.WellStatus IN ('drl','ops','p','s','ta','pai','pii','sai','sii','a','i') AND w.Operator = 101600 THEN 'Open Orphan Wells (no known operator)'
            WHEN w.WellStatus IN ('drl','ops') THEN 'Total Holes Not Yet Completed'
            WHEN ((w.WellStatus = 'p' or w.WellStatus = 's') AND w.WellType <> 'LI') OR (w.WellStatus = 'pai' OR w.WellStatus = 'pii' OR w.WellStatus = 'sai' OR w.WellStatus = 'sii') THEN 'Total Wells Capable of Production'
            WHEN (w.WellStatus = 'drl' OR w.WellStatus = 'ops' OR ((w.WellStatus = 'p' or w.WellStatus = 's') AND w.WellType <> 'LI') OR w.WellStatus = 'ta' OR w.WellStatus = 'pai' OR w.WellStatus = 'pii' OR w.WellStatus = 'sai' OR w.WellStatus = 'sii' OR w.WellStatus = 'a' OR w.WellStatus = 'i') THEN 'Total Non-Plugged Wells'
            WHEN (w.WellStatus = 'drl' or w.WellStatus = 'ops' or ((w.WellStatus = 'p' or w.WellStatus = 's') and w.WellType <> 'LI') or w.WellStatus = 'ta' or w.WellStatus = 'pai' or w.WellStatus = 'pii' or w.WellStatus = 'sai' or w.WellStatus = 'sii' or w.WellStatus = 'a' or w.WellStatus = 'i' or w.WellStatus = 'pa') THEN 'Total Wells Drilled'
        END AS WellCategory
    FROM HWell w
        LEFT JOIN HConstruct c ON c.WellKey = w.PKey
) dt
GROUP BY dt.WellCategory
ORDER BY dt.WellCategory DESC

Here is the table the query above produces:

Table #1

WellCategory Number of Wells Federal Indian State Fee (Private) Multiple Lease Types
Total Wells Capable of Production 2 2 0 0 0 0
Temporarily-Abandoned Wells 1 1 0 0 0 0
Shut-In Oil Wells 21 10 10 0 1 0
Shut-In Gas Wells 26 19 2 4 1 0
Producing Oil Wells 59 18 25 6 10 0
Producing Gas Wells 90 59 1 25 5 0
Plugged and Abandoned Wells 113 60 15 19 19 0
Permits Approved - Not Yet Commenced 14 4 2 2 4 2
New Permits - Not Yet Approved 1 0 1 0 0 0
Inactive Water Injection Wells 18 11 5 2 0 0
Drilling Operations Suspended 4 1 3 0 0 0
Drilling Commenced - Not Yet Completed 4 1 1 0 2 0
Active Water Injection Wells 6 1 5 0 0 0
Active Water Disposal Wells 1 0 0 1 0 0
NULL 140 83 28 14 15 0

Here is the table I know the same data set can produce and the one I want to replicate:

Table #2

Well Statuses Number Of Wells Federal Indian State Fee (Private) Multiple Lease Types
Producing Oil Wells 59 18 25 6 10 0
Producing Gas Wells 90 59 1 25 5 0
Shut-In Oil Wells 21 10 10 0 1 0
Shut-In Gas Wells 26 19 2 4 1 0
Active Water Injection Wells 6 1 5 0 0 0
Active Gas Injection Wells 0 0 0 0 0 0
Active Water Disposal Wells 1 0 0 1 0 0
Active Gas Storage Wells 0 0 0 0 0 0
Active Water Source Wells 0 0 0 0 0 0
Active Test Holes 0 0 0 0 0 0
Inactive Water Injection Wells 18 11 5 2 0 0
Inactive Gas Injection Wells 0 0 0 0 0 0
Inactive Water Disposal Wells 0 0 0 0 0 0
Inactive Gas Storage Wells 0 0 0 0 0 0
Inactive Water Source Wells 0 0 0 0 0 0
Inactive Test Holes 0 0 0 0 0 0
Temporarily-Abandoned Wells 1 1 0 0 0 0
Plugged and Abandoned Wells 113 60 15 19 19 0
New Permits - Not Yet Approved 1 0 1 0 0 0
Permits Approved - Not Yet Commenced 14 4 2 2 4 2
Drilling Commenced - Not Yet Completed 4 1 1 0 2 0
Drilling Operations Suspended 4 1 3 0 0 0
Open Orphan Wells (no known operator) 1 1 0 0 0 0
Total Holes Not Yet Completed 8 2 4 0 2 0
Total Wells Capable of Production 198 108 38 35 17 0
Total Non-Plugged Wells 232 123 52 38 19 0
Total Wells Drilled 345 183 67 57 38 0

Table #2 is generated using a much longer query that uses several subqueries and temp tables to count how many wells belong to each category using their DISTINCT w.WellID. In addition, the full data set has a lot more entries, usually each category has at least some wells in it and not so many "0's".

I don't know how to tell the query to count wells more than once if they fall into several well categories without making the query super long and start introducing temp tables, which I would prefer not to do. *NOTE I do not want to count a well twice for same category, only count it once per category.


Solution

  • Here's one way you could do it while keeping it somewhat easy to read:

    I broke the CASE statement into logical pieces. Parts that don't overlap but seem related go into their own query to determine the category. Then I UNION ALL them together to avoid de-duping.

    There's tons of ways this could be done, and this is just one of them...

    Also, like I mentioned in the chat session, if you are able to, I would highly recommend working on normalizing this data, creating lookup tables, etc.

    WITH cte_Wells AS (
        SELECT w.WellStatus, w.WellType, c.LateralStatus, c.LeaseType, w.Operator
        FROM dbo.HWell w
            LEFT JOIN dbo.HConstruct c ON c.WellKey = w.PKey
    )
    SELECT
        dt.WellCategory,
        ISNULL(SUM(CASE WHEN dt.LeaseType IN ('F','I','S','P') OR dt.LeaseType NOT IN ('F', 'I', 'S', 'P', 'U') THEN 1 END), 0) AS [Number of Wells], -- Federal + Indian + State + Fee + Multi-Lease
        ISNULL(SUM(CASE WHEN dt.LeaseType = 'F' THEN 1 END), 0) AS [Federal], -- Sums up how many wells from the subquery have a leasetype of "Federal"
        ISNULL(SUM(CASE WHEN dt.LeaseType = 'I' THEN 1 END), 0) AS [Indian],
        ISNULL(SUM(CASE WHEN dt.LeaseType = 'S' THEN 1 END), 0) AS [State],
        ISNULL(SUM(CASE WHEN dt.LeaseType = 'P' THEN 1 END), 0) AS [Fee (Private)],
        ISNULL(SUM(CASE WHEN dt.LeaseType NOT IN ('F', 'I', 'S', 'P', 'U') THEN 1 END), 0) AS [Multiple Lease Types]
    FROM (
        SELECT w.LeaseType, x.WellCategory
        FROM cte_Wells w
            CROSS APPLY (
                SELECT WellCategory = CASE 
                                        WHEN w.WellStatus = 'p' AND w.WellType = 'gw' OR (w.WellStatus IN ('pai','pii') AND w.WellType IN ('gwi','ggi','gwd')) THEN 'Producing Gas Wells'
                                        WHEN w.WellStatus = 'p' AND w.WellType = 'ow' OR (w.WellStatus IN ('pai','pii') AND w.WellType IN ('owi','ogi','owd')) THEN 'Producing Oil Wells'
                                        WHEN w.WellStatus = 's' AND w.WellType = 'gw' OR (w.WellStatus IN ('sai','sii') AND w.WellType IN ('gwi','ggi','gwd')) THEN 'Shut-In Gas Wells'
                                        WHEN w.WellStatus = 's' AND w.WellType = 'ow' OR (w.WellStatus IN ('sai','sii') AND w.WellType IN ('owi','ogi','owd')) THEN 'Shut-In Oil Wells'
                                        WHEN w.WellStatus = 'a' AND w.WellType = 'wi' THEN 'Active Water Injection Wells'
                                        WHEN w.WellStatus = 'a' AND w.WellType = 'gi' THEN 'Active Gas Injection Wells'
                                        WHEN w.WellStatus = 'a' AND w.WellType = 'wd' THEN 'Active Water Disposal Wells'
                                        WHEN w.WellStatus = 'a' AND w.WellType = 'gs' THEN 'Active Gas Storage Wells'
                                        WHEN w.WellStatus = 'a' AND w.WellType = 'ws' THEN 'Active Water Source Wells'
                                        WHEN w.WellStatus = 'a' AND w.WellType = 'tw' THEN 'Active Test Holes'
                                        WHEN w.WellStatus = 'i' AND w.WellType = 'wi' THEN 'Inactive Water Injection Wells'
                                        WHEN w.WellStatus = 'i' AND w.WellType = 'gi' THEN 'Inactive Gas Injection Wells'
                                        WHEN w.WellStatus = 'i' AND w.WellType = 'wd' THEN 'Inactive Water Disposal Wells'
                                        WHEN w.WellStatus = 'i' AND w.WellType = 'gs' THEN 'Inactive Gas Storage Wells'
                                        WHEN w.WellStatus = 'i' AND w.WellType = 'ws' THEN 'Inactive Water Source Wells'
                                        WHEN w.WellStatus = 'i' AND w.WellType = 'tw' THEN 'Inactive Test Holes'
                                        WHEN w.WellStatus = 'ta' THEN 'Temporarily-Abandoned Wells'
                                        WHEN w.WellStatus = 'pa' THEN 'Plugged and Abandoned Wells'
                                        ELSE NULL
                                    END
            ) x
        WHERE x.WellCategory IS NOT NULL
        -----------------
        UNION ALL
        -----------------
        SELECT w.LeaseType, x.WellCategory
        FROM cte_Wells w
            CROSS APPLY (
                SELECT WellCategory = CASE 
                                        WHEN w.LateralStatus = 'NEW' THEN 'New Permits - Not Yet Approved'
                                        WHEN w.LateralStatus = 'APD' THEN 'Permits Approved - Not Yet Commenced'
                                        WHEN w.LateralStatus = 'DRL' THEN 'Drilling Commenced - Not Yet Completed'
                                        WHEN w.LateralStatus = 'OPS' THEN 'Drilling Operations Suspended'
                                        ELSE NULL
                                    END
            ) x
        WHERE x.WellCategory IS NOT NULL
        -----------------
        UNION ALL
        -----------------
        SELECT w.LeaseType, WellCategory = 'Open Orphan Wells (no known operator)'
        FROM cte_Wells w
        WHERE w.WellStatus IN ('drl','ops','p','s','ta','pai','pii','sai','sii','a','i') AND w.Operator = 101600
        -----------------
        UNION ALL
        -----------------
        SELECT w.LeaseType, WellCategory = 'Total Holes Not Yet Completed'
        FROM cte_Wells w
        WHERE w.WellStatus IN ('drl','ops')
        -----------------
        UNION ALL
        -----------------
        SELECT w.LeaseType, WellCategory = 'Total Wells Capable of Production'
        FROM cte_Wells w
        WHERE (w.WellStatus IN ('p','s') AND w.WellType <> 'LI') OR w.WellStatus IN ('pai','pii','sai','sii')
        -----------------
        UNION ALL
        -----------------
        SELECT w.LeaseType, WellCategory = 'Total Non-Plugged Wells'
        FROM cte_Wells w
        WHERE (w.WellStatus IN ('p','s') AND w.WellType <> 'LI') OR w.WellStatus IN ('drl','ops','ta','pai','pii','sai','sii','a','i')
        -----------------
        UNION ALL
        -----------------
        SELECT w.LeaseType, WellCategory = 'Total Wells Drilled'
        FROM cte_Wells w
        WHERE (w.WellStatus IN ('p','s') AND w.WellType <> 'LI') or w.WellStatus IN ('drl','ops','ta','pai','pii','sai','sii','a','i','pa')
    ) dt
    GROUP BY dt.WellCategory
    ORDER BY dt.WellCategory DESC
    

    I've checked the results of this query against your sample data and proposed expected results, and they appear to match. So this will get you what you want. However, since I don't know the data, you'll need to inspect it for logic errors, and performance.

    NOTE: The results of this query do not produce counts for empty categories. If that is something you need, then comment on this solution and I can fix it. The fix is to add a table with all categories, then LEFT JOIN to that the results of the counts.