Search code examples
sql-servert-sqlgroup-bysumcase-when

Creating Columns in a View That Use an Aggregate Method in a Case...When Expression


I am trying to create a view in SQL Server that renders columns from 3 tables and that creates 4 NEW columns. These new columns use the SQL "Case...When" syntax to conditionally render the SUM of existing fields depending on the values of an adjacent column [Portfolio Report Category] in the same table.

The objective is to display a single account's total market value for 4 distinct asset categories. Right now an [Account Number] can have MANY records with MANY [Investment Amount] for a single [Portfolio Report Category]. So obviously this view should allow an [Account Number] to have only ONE record with 4 summed monetary values that meet the "Case...When" conditions.

I have read similar threads, but none that addresses an aggregate method after the "Then" keyword in the conditional block. When I place the Sum() before "Case" and remove it after the "Then," there is no error but the calculations are wrong: In many cases, they are much higher. I was under the impression that a simple Try_Convert to money would be sufficient, and indeed if I execute SUM(Try_Convert(money, [Market_Value]) Where [Account Number] = 'x', the result is correct. But not when I'm creating the view.

Here is the error when testing the following code with SELECT:

Column 'KTHoldings.Portfolio Report Category' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

Further below is the KTHoldings table returning all records for a single account. I basically need the [Other Total Value] column of my view to sum its [Market Value] data.

CREATE VIEW VW_KTAccountsADR AS (
    SELECT adr.[Account Number], adr.[Account_ Display Name], adr.[Account Category Code], adr.[Division Code], adr.[Division Name], adr.[Market Value Amount],
    CASE WHEN kth.[Portfolio Report Category] = '705' THEN SUM((TRY_CONVERT(money, kth.[Market Value]))) ELSE '0.00' END AS [Crypto Total Value], 
    CASE WHEN kth.[Portfolio Report Category] = '691' THEN SUM((TRY_CONVERT(money, kth.[Market Value]))) ELSE '0.00' END AS [Precious Metal Total Value],
    CASE WHEN kth.[Portfolio Report Category] IN ('010', '011', '020', '025', '030') THEN SUM((TRY_CONVERT(money, kth.[Market Value]))) ELSE '0.00' END AS [Stock Total Value],
    CASE WHEN kth.[Portfolio Report Category] NOT IN ('010', '011', '020', '025', '030', '691', '705') THEN SUM((TRY_CONVERT(money, kth.[Market Value]))) ELSE '0.00' END AS [Other Total Value],
    ktc.[Available Cash Amount] AS [CASH TOTAL]
    FROM KTAccountsADR adr
    INNER JOIN KTHoldings kth
    ON adr.[Account Number] = kth.[Account Number]
    INNER JOIN KTCash ktc 
    ON adr.[Account Number] = ktc.[Account Number]
    GROUP BY adr.[Account Number], adr.[Account_ Display Name], adr.[Account Category Code], adr.[Division Code], adr.[Division Code], adr.[Division Name], adr.[Market Value Amount], ktc.[Available Cash Amount]
)

KTAccountsADR Table

Account Number          Display Name      Division Code   Market Value Amount
07007835        Frank C Thomas Roth IRA           27             390410.98
07007835        Frank C Thomas Roth IRA           27             390410.98
07007835        Frank C Thomas Roth IRA           27             390410.98
07007835        Frank C Thomas Roth IRA           27             390410.98
001000          Carl S Sykes Roth IRA             27           196338.1292            
001000          Carl S Sykes Roth IRA             27           196338.1292
001000          Carl S Sykes Roth IRA             27           196338.1292

KTHoldings Table

Account Number  Display Name    Market Value    Portfolio Report Category
001000  Carl S Sykes Roth IRA   9998.4792        600
001000  Carl S Sykes Roth IRA   43467.09         600
001000  Carl S Sykes Roth IRA   84524.71         600

KTCash Table

Account Number  Available Cash Amount
001000               58347.85

Solution

  • You need to put your case inside your sum:

    SUM(CASE WHEN kth.[Portfolio Report Category] = '705' THEN TRY_CONVERT(money, kth.[Market Value]) ELSE '0.00' END) AS [Crypto Total Value], 
    

    Here is a working example:

    declare @Adr table ([Account Number] varchar(6), [Account_ Display Name] varchar(64), [Account Category Code] varchar, [Division Code] varchar, [Division Name] varchar, [Market Value Amount] money);
    declare @Kth table ([Account Number] varchar(6), [Portfolio Report Category] varchar(3), [Market Value] money);
    declare @Ktc table ([Account Number] varchar(6), [Available Cash Amount] money);
    
    insert into @Adr ([Account Number], [Account_ Display Name])
    values ('001000', 'Carl S Sykes Roth IRA');
    
    insert into @Kth ([Account Number], [Market Value], [Portfolio Report Category])
    values ('001000', 9998.4792, '600'),
    ('001000', 43467.09, '600'),
    ('001000', 84524.71, '600');
    
    insert into @Ktc ([Account Number])
    values ('001000');
    
    SELECT adr.[Account Number], adr.[Account_ Display Name], adr.[Account Category Code], adr.[Division Code], adr.[Division Name], adr.[Market Value Amount]
      , SUM(CASE WHEN kth.[Portfolio Report Category] = '705' THEN (TRY_CONVERT(money, kth.[Market Value])) ELSE 0 END) AS [Crypto Total Value] 
      , SUM(CASE WHEN kth.[Portfolio Report Category] = '691' THEN (TRY_CONVERT(money, kth.[Market Value])) ELSE 0 END) AS [Precious Metal Total Value]
      , SUM(CASE WHEN kth.[Portfolio Report Category] IN ('010', '011', '020', '025', '030') THEN (TRY_CONVERT(money, kth.[Market Value])) ELSE 0 END) AS [Stock Total Value]
      , SUM(CASE WHEN kth.[Portfolio Report Category] NOT IN ('010', '011', '020', '025', '030', '691', '705') THEN (TRY_CONVERT(money, kth.[Market Value])) ELSE 0 END) AS [Other Total Value]
      , ktc.[Available Cash Amount] AS [CASH TOTAL]
    FROM @Adr adr
    INNER JOIN @Kth kth ON adr.[Account Number] = kth.[Account Number]
    INNER JOIN @Ktc ktc  ON adr.[Account Number] = ktc.[Account Number]
    GROUP BY adr.[Account Number], adr.[Account_ Display Name], adr.[Account Category Code], adr.[Division Code], adr.[Division Code], adr.[Division Name], adr.[Market Value Amount], ktc.[Available Cash Amount];
    

    Which returns (un-necessary columns removed):

    Account Number  Crypto Total Value  Precious Metal Total Value  Stock Total Value   Other Total Value   CASH TOTAL
    001000          0.00                0.00                        0.00                137990.2792         NULL
    

    Edit: As you have now added sample data, the reason you were getting incorrect values is because you had duplicate rows in your KTAccountsADR which then multiple the rows in the KTHoldings table. Resolve the duplicates and you will get the correct values when using case inside sum.

    Note, its best practice to ensure you are returning the same datatype from all branches of a case expression.