Search code examples
sqlsql-servercasewindowing

SQL - Flattening a table using windowing and case statements


First time asker -- I'm having some problems combining case logic and windowing in SqlServer 2012. I need to flatten the data structure shown below, so I'll be running MAX statements against these results afterwards. I'm using case/when logic to specify an 'Owner' for each xID. My challenge is I'm consistently getting the wrong results and I haven't been able to determine what I'm doing wrong.

Here is the case/when logic and the table structure. For the 'Owner' column I need to evaluate three conditions and was trying to use FIRST_VALUE() to select the output of the case statement. The logic in the case statement works correctly, but when I try to apply the value to each of the xIDs using the windowing function it's returning the wrong value.

FIRST_VALUE(case
when [partnerType] = 'Giver' and [partnerAgree] = 'True' then [partnerGroup]
when [partnerType] = 'Impacted' and [partnerAgree] = 'True' then [partnerGroup]
when [Stakeholder No#] = 1 and [partnerAgree] <> 'True' then [partnerGroup]
else Null end) over (partition by [xID] order by [yID])
as 'Owner'

Desired Results --------------------------

|xID|yID| Owner     |partnerType| partnergrp|partnerAgree
|100|  1| grp_Banana|Taker      |grp_Apple  |TRUE
|100|  2| grp_Banana|Giver      |grp_Banana |TRUE
|100|  3| grp_Banana|Taker      |grp_Banana |FALSE
|101|  1| grp_Carrot|Taker      |grp_Carrot |TRUE
|101|  2| grp_Carrot|Giver      |grp_Danish |FALSE
|101|  3| grp_Carrot|Taker      |grp_Banana |TRUE
|101|  4| grp_Carrot|Taker      |grp_Danish |FALSE

Results I'm getting --------------------------

|xID|yID| Owner     |partnerType| partnergrp|partnerAgree
|100|  1| grp_Apple |Taker      |grp_Apple  |TRUE
|100|  2| grp_Apple |Giver      |grp_Banana |TRUE
|100|  3| grp_Apple |Taker      |grp_Banana |FALSE
|101|  1| grp_Carrot|Taker      |grp_Carrot |TRUE
|101|  2| grp_Carrot|Giver      |grp_Danish |FALSE
|101|  3| grp_Carrot|Taker      |grp_Banana |TRUE
|101|  4| grp_Carrot|Taker      |grp_Danish |FALSE

The first table shows the results as I would expect them, but the code produces the values in the second table. Example: for xID=100 I would expect the Owner to be grp_Banana, but my code returns grp_Apple. For xID=101 I'm getting the correct answer, but for the wrong reason. The windowing function seems to be taking the first yID for any result set.

Thanks, any help is appreciated. Also, I'm open to not using a windowing function it just seem the right direction.


Solution

  • This is tricky unless your database supports the ignore nulls argument. You can do this with two window functions:

    max(case when yid = yid_special then partnerGroup end) over (partition by xid) as Owner
    from (select . . . ,
                 min(case when partnerType = 'Giver' and [partnerAgree] = 'True' then yid
                          when partnerType = 'Impacted' and [partnerAgree] = 'True' then yid
                          when [Stakeholder No#] = 1 and [partnerAgree] <> 'True' then yid
                     end) over (partition by xid) as yid_special
    

    You may also be able to write this using first_value():

    first_value(partnerGroup) over
        (partition by xid
         order by (case when partnerType = 'Giver' and [partnerAgree] = 'True' then yid
                        when partnerType = 'Impacted' and [partnerAgree] = 'True' then yid
                        when [Stakeholder No#] = 1 and [partnerAgree] <> 'True' then yid
                        else 999999
                    end) 
        ) as owner