1

I am trying in the below query (which is going to be a subquery of another larger query) to convert a VARCHAR to a value which can be measured as either <0 or >0 - not sure exactly what value to use. I am getting the error "operand data type is invalid in the sum operator" this is the very first time I encountered this. The ultimate goal is to say "when the value is 'L' then return 0, else return 1" additionally DEL_INDICATOR is a field in thePO_ITEM table but I omitted in the SELECT.

Here is the Updated code:

 SELECT    G.order_no AS 'GPS_ORDER_#', 
          G.order_status AS 'GPS_ORDER_STATUS', 
          G.cst_order_no AS 'GPS_CUSTOMER_PO_#',
          H.PO_NUMBER AS 'SAP_PO_#',
          P.PO_ITEM_NUMBER, 
          P.DEL_INDICATOR 

FROM   

          (SELECT    order_no, 
                     order_status, 
                     cst_order_no

           FROM      asagdwpdx_prod.dbo.SimoxOrder1

           UNION ALL

           SELECT    order_no, 
                     order_status, 
                     cst_order_no

           FROM      asagdwpdx_prod.dbo.SimoxOrder2

           UNION ALL 

           SELECT    order_no, 
                     order_status, 
                     cst_order_no

           FROM      asagdwpdx_prod.dbo.SimoxOrder3) G 

JOIN      PDX_SAP_USER.dbo.VW_PO_HEADER H ON G.order_no = H.AHAG_NUMBER

JOIN      PDX_SAP_USER.dbo.VW_PO_ITEM P ON H.PO_NUMBER = P.PO_NUMBER 

WHERE     G.order_status = '10'

AND       NOT EXISTS  ( 
                                SELECT P1.PO_NUMBER,
                                       P1.PO_ITEM_NUMBER, 
                                       SUM(CASE 
                                            WHEN CAST(P1.DEL_INDICATOR AS INT) = 'L' 
                                            THEN '0'
                                            ELSE '1'
                                       END AS [SUM_DEL]) AS [SUM]

                                FROM   PDX_SAP_USER.dbo.VW_PO_ITEM P1

                                WHERE  P1.PO_NUMBER = H.PO_NUMBER

                                GROUP BY P1.PO_NUMBER,
                                         P1.PO_ITEM_NUMBER,
                                         CASE 
                                            WHEN P1.DEL_INDICATOR = 'L' 
                                            THEN '0'
                                            ELSE '1'
                                       END

                                HAVING SUM  (CASE 
                                            WHEN CAST(P1.DEL_INDICATOR AS INT) = 'L' 
                                            THEN '0'
                                            ELSE '1'
                                       END)  > '0')
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
user3496218
  • 185
  • 3
  • 5
  • 19

2 Answers2

2

I do not think you have adequately described your desired results, but here are two options that might get you on the right path:

Using group by with the having clause to only return po_type > 0:

select 
    po_number
  , po_item_number
  , po_type = sum(case when del_indicator = 'L' then 0 else 1 end)
from vw_po_item
group by 
    po_number
  , po_item_number
having sum(case when del_indicator = 'L' then 1 else 0 end) > 0

Using a common table expression (a derived table would work just as well) with the sum() over() aggregation window function:

;with cte as (
  select 
      po_number
    , po_item_number
    , del_indicator_calc = case when del_indicator = 'L' then 0 else 1 end
    , po_type = sum(case when del_indicator = 'L' then 0 else 1 end)
        over (partition by po_number, po_item_number)
  from vw_po_item
)
select *
from cte
where po_type > 0

Depending on how you plan on using this as a "subquery of another larger query" you may want to consider using an exists() or not exists() clause instead:

select ...
from ...
where ...
   /* only return records that have an item with del_indicator = 'L' */
  and exists (
    select 1 
    from po_item i
    where i.po_number = t.po_number -- `t` being an alias for a table in your `from` clause
      -- if you are using the subquery per item instead of just `po_number`
      and i.po_item_number = t.po_item_number  

      and del_indicator = 'L'
    )

Example of using not exists()

select 
    G.order_no        as [gps_order_#]
  , G.order_status    as [gps_order_status]
  , G.cst_order_no    as [gps_customer_po_#]
  , H.po_number       as [sap_po_#]
  , P.po_item_number
  , P.del_indicator
from (
  select order_no, order_status, cst_order_no 
  from asagdwpdx_prod.dbo.SimoxOrder1
  union all
  select order_no, order_status, cst_order_no 
  from asagdwpdx_prod.dbo.SimoxOrder2
  union all
  select order_no, order_status, cst_order_no 
  from asagdwpdx_prod.dbo.SimoxOrder3
  ) G
  inner join pdx_sap_user.dbo.vw_po_header H
    on G.order_no = H.ahag_number
  inner join pdx_sap_user.dbo.vw_po_item P
    on H.po_number = P.po_number
where G.order_status = '10'
   and not exists (
    select 1
    from pdx_sap_user.dbo.vw_po_item i
    where i.po_number = H.po_number
      and (i.del_indicator <> 'L' or i.del_indicator is null)
  )
SqlZim
  • 37,248
  • 6
  • 41
  • 59
  • Thank you for the ideas and input. I have updated the original code as this is indeed part of a larger query. I made the suggested changes using NOT EXISTS and using the GROUP by. I am still getting the message : Msg 245, Level 16, State 1, Line 1 Conversion failed when converting the varchar value 'DEL_INDICATOR_CALC' to data type int. Hopefully seeing the full query may help – user3496218 Sep 25 '17 at 01:48
  • also on a different issue - SQL Server is now redlining all the instances of columns referenced under P. - i.e. P.PO_NUMBER - not sure exactly why – user3496218 Sep 25 '17 at 02:31
  • One other thing - the values in DEL_INDICATOR are either 'L', 'S' or " " - a blank - not sure if that affects the conversion/cast? – user3496218 Sep 25 '17 at 02:37
  • @user3496218 stop using string literals as aliases, wrap them in square brackets. You can not sum string literals, e.g. `'del_indicator_calc'`, repeat the expression you are trying to reference as shown in my answer. Also, you are using `not exists()` without a correlating reference. Please review my answer and try again. – SqlZim Sep 25 '17 at 11:45
  • thank you for that advice - appreciate it! I have edited the code above to do the SUM and CAST in the CASE statement - this seems logical to me but I am now getting the SUM is not a recognized function error - assuming this is to do with my ordering but not familiar. Also added the correlating reference - but I haven't used an exist/not exist statement for a long time, hoping that is correct. – user3496218 Sep 26 '17 at 02:23
  • For that second solution works great, I am not familiar with the WITH part, could you explain how i would add that to my outer query? Appreciate all the help – user3496218 Sep 26 '17 at 02:31
  • @user3496218 Again, stop using string literals as aliases (single quotes around aliases is deprecated), wrap them in square brackets. You can not sum string literals, e.g. 'del_indicator_calc', repeat the case expression inside the `sum()` e.g. sum(case when del_indicator = 'L' then 0 else 1 end). I have updated my answer with what I think you are trying to do. – SqlZim Sep 26 '17 at 11:57
  • apologies - old habits die hard appreciate the input. That seems to have solved it. For reference and because I am not quite sure how that not exists worked - what does that part SELECT 1 do? It was somehow able to avoid using SUM. I really appreciate the help - learning a lot – user3496218 Sep 28 '17 at 00:42
  • @user3496218 [**`exists()`**](https://technet.microsoft.com/en-us/library/ms189259(v=sql.105).aspx) does not return rows, it only tests if there are rows that would be returned based on the `from` and `where` (and/or `having`) parts of the query. Based on your `having` clause, you are just verifying that no row exists for a given `po_number` that has a `del_indicator` that isn't `'L'`, so we can skip the aggregation. If it required at least 2 rows or more, then you would still need to do aggregation with a `having` clause in the `not exists()` – SqlZim Sep 28 '17 at 11:22
  • @user3496218 I use `select 1` out of habit; Both `exists()` and `not exists()` do not return rows, so you could use `select 1`, `select null`, `select *`, or even `select 1/0`. From this article [`exists` Subqueries: `select 1` vs. `select *` - Conor Cunningham](http://www.sqlskills.com/blogs/conor/exists-subqueries-select-1-vs-select/) using `select 1` will avoid having to examine any unneeded metadata for that table during query compilation. But [Martin Smith in his answer here](https://stackoverflow.com/a/6140367/2333499) ran tests that show no actual difference in performance – SqlZim Sep 28 '17 at 11:26
  • Thanks for the great explanation. Just to make sure I am understanding that second to last comment correctly - in my case, I can have PO's that have more than 1 line, the goal as stated is to not return any PO's where even one line is not L, return only the PO's where all lines are L - from that do you think I would still need to do aggregation – user3496218 Oct 03 '17 at 01:02
  • just making sure I understand and explained correctly – user3496218 Oct 03 '17 at 01:03
  • my understanding of your response is that the not exists checks a PO and if any of the_del indicator are L on any of the rows then it excludes those PO's - is that correct? – user3496218 Oct 03 '17 at 01:12
  • @user3496218 if any `del_indicator` are __not__ `'L'` they are excluded. Your `case` expression in the question assigns a value of `0` to rows with `del_indicator='L'` and only excludes a `po_number` (via `not exists()`) `having sum(...) > 0`. The code in my answer simplifies this to exclude a `po_number` if any of its rows have a `del_indicator<>'L' or del_indicator is null` which in your `case` expression would have been assigned a value of `1`, resulting in the same rows being excluded in my version as would be in yours. – SqlZim Oct 03 '17 at 12:06
0

1) You need to convert the VARCHAR to numeric BEFORE trying to sum the value. 2) You need a GROUP BY clause if you want to perform an aggregation on just 1 column. 3) You cannot reference an expression alias in the WHERE clause, you'll need to use a HAVING clause instead. 4) You DO NOT want to use the FLOAT data type unless you are aware of the problems associated with use of "approximate" number data type. Depending in the data in DEL_INDICATOR_CALC, consider using either INT, DECIMAL or NUMERIC.

With that in mind, see how the following works for you...

SELECT
    vpi.PO_NUMBER,
    vpi.PO_ITEM_NUMBER,
    CASE WHEN vpi.DEL_INDICATOR = 'L' THEN '0' ELSE '1' END AS 'DEL_INDICATOR_CALC',
    SUM(TRY_CONVERT(INT, vpi.DEL_INDICATOR_CALC)) AS 'PO_TYPE'
FROM
    dbo.VW_PO_ITEM vpi
GROUP BY
    vpi.PO_NUMBER,
    vpi.PO_ITEM_NUMBER,
    CASE WHEN vpi.DEL_INDICATOR = 'L' THEN 0 ELSE 1 END
HAVING 
    SUM(TRY_CONVERT(INT, vpi.DEL_INDICATOR_CALC)) = 1;
Jason A. Long
  • 4,382
  • 1
  • 12
  • 17
  • thank you for the reply. That example returns the result: Msg 207, Level 16, State 1, Line 13 Invalid column name 'DEL_INDICATOR_CALC'. Msg 207, Level 16, State 1, Line 5 Invalid column name 'DEL_INDICATOR_CALC'. – user3496218 Sep 24 '17 at 04:05
  • That column name was taken directly from your original code... Just replace the name with the correct name in in the view. Also, I made the assumption (perhaps falsely) that your default schema is dbo... If I was incorrect, replace dbo with the correct schema. – Jason A. Long Sep 24 '17 at 05:39
  • thanks again - now getting the error that TRY_CONVERT is not a recognized built-in function - does that have something to do with the system? – user3496218 Sep 26 '17 at 02:55
  • @user3496218 - TRY_CONVERT (and TRY_CAST) were introduced in SQL Server 2012. If you're on 2008R2 or earlier, it won;t work for you... Which is why you should ALWAYS let people know what version you're using when you ask a question. – Jason A. Long Sep 26 '17 at 04:56
  • I am on SQL Server 2016 – user3496218 Sep 26 '17 at 06:37
  • TRY_CONVERT shouldn't have any issues in 2016. Unless you post your table structure and some test data so that I can actually test the execution, there's not much I can do beyond this... – Jason A. Long Sep 26 '17 at 06:52