Home > OS >  Operations with aggregate functions - SQL Server
Operations with aggregate functions - SQL Server

Time:07-09

I don't get how to make a subquery with aggregate functions, about how to handle the aggregate functions and use it with others.

This is my code

SET DATEFORMAT DMY;
GO

SELECT
      NumeroD AS 'Bill '
    , Descrip1 AS 'Product Description' 
    , MAX(ExistAnt) AS 'Ex. Act.'
    , MIN((ExistAnt)) AS 'Ex. Act.'
    , SUM(CASE
        WHEN TipoFac = 'A' THEN Amount
        WHEN TipoFac = 'B' THEN -Amount END) AS 'Total Amount'

    , CASE
        WHEN TipoFac = 'A' THEN (Amount*Price) END AS 'Total Sale'
    , CASE
        WHEN TipoFac = 'A' THEN (Amount*Cost) END AS 'Total Cost'
    , CASE
        WHEN TipoFac = 'A' THEN 'Total Sale' - 'Total Cost'  END AS 'Total Utility'
    , CASE
        WHEN TipoFac = 'A' THEN ('Total Utility' * 100) / 'Total Sale' END AS '% Util' 
    , NroLineaC
    , DEsComp

FROM
    SAITEMFAC
WHERE
    DateE BETWEEN '05/07/2022' AND '05/07/2022 23:59:59'
    AND codubic = '0010-01' 
    AND CodItem = '11001'
GROUP BY
    Coditem
    , NumeroD
    , Descrip1
    , TipoFac
    , Cantidad
    , Precio 
    , Costo

When I click the F5 key, I get an error:

The data types varchar and varchar are incompatible in the subtract operator

Could somebody please guide me about what steps to follow?

I appreciate your help.

PS: maybe using a CTE?

CodePudding user response:

A simple approach is to re-type your logic instead of using aliases (which won't work without a CTE or derived table), ie replace this:

, CASE
    WHEN TipoFac = 'A' THEN (Amount*Price) END AS 'Total Sale'
, CASE
    WHEN TipoFac = 'A' THEN (Amount*Cost) END AS 'Total Cost'
, CASE
    WHEN TipoFac = 'A' THEN 'Total Sale' - 'Total Cost'  END AS 'Total Utility'

With this:

, CASE
    WHEN TipoFac = 'A' THEN (Amount*Price) END AS 'Total Sale'
, CASE
    WHEN TipoFac = 'A' THEN (Amount*Cost) END AS 'Total Cost'
, CASE
    WHEN TipoFac = 'A' THEN (Amount*Price) - (Amount*Cost)  END AS 'Total Utility'

CodePudding user response:

Firstly, to repeat a lot of my comments.

Don't use literal strings for aliases; this is a bad habit and it doesn't work as people think. Take the query SELECT [name] AS 'DatabaseName' FROM sys.databases ORDER BY 'DatabaseName'; This does not ORDER BY the column aliased as 'DatabaseName', instead it orders by the literal varchar value 'DatabaseName'; thus is has no order, as the literal value is the same for every row. Ideally, don't use object and aliases that require delimit identifying. If you must then either use T-SQL's delimit identify, brackets ([]), or the ANSI-SQL delimit identifier, double quotes (").

Next, you can't reference a column by it's alias in the same query scope apart from in the ORDER BY; SELECT 1 AS A, A 1 AS B; will give an invalid column error, as the column A isn't defined. You would need to repeat the expression again to use that expression in the same scope. If you need to reference a column by its alias, you need to use a derived table or a CTE:

--Derived table
SELECT A,
       A   1
FROM (SELECT 1 AS A) DT;

--CTE
WITH CTE AS (
    SELECT 1 AS A)
SELECT A,
       A 1 AS B
FROM CTE;

Finally, DateE BETWEEN '05/07/2022' AND '05/07/2022 23:59:59' is bad for a couple of reasons; it misses off the last second of the day, and its an ambiguous date format. Use >= and < logic and an unambiguous format; for T-SQL that is yyyyMMdd and yyyy-MM-ddThh:mm:ss(.nnnnnnn). So instead you have DateE >= '20220705' AND DateE < '20220706'.


Now, onto your problem. As I mentioned, there are a couple of options open to use. Personally, I would suggest just repeating the expressions here, as they aren't complicated:

SELECT NumeroD AS Bill, --Don't use literal strings for aliases
       Descrip1 AS ProductDescription, --Don't use object names/aliases that require delimit
       MAX(ExistAnt) AS [Ex. Act.],    --If you must, then use brackets ([]) or double quotes (")
       MIN(ExistAnt) AS [Ex. Act.],  --but, ideally use camelCase, PascalCase, or snake_case instead (I prefer PascalCase)
       --The above 2 columns have the same alias; that can't be right
       SUM(CASE WHEN TipoFac = 'A' THEN Amount
                WHEN TipoFac = 'B' THEN -Amount
           END) AS TotalAmount,
        CASE WHEN TipoFac = 'A' THEN (Amount*Price) END AS TotalSale,
        CASE WHEN TipoFac = 'A' THEN (Amount*Cost) END AS TotalCost,
        CASE WHEN TipoFac = 'A' THEN (Amount*Price) - (Amount*Cost) END AS TotalUtility, --Repeat the expression
        CASE WHEN TipoFac = 'A' THEN (((Amount*Price) - (Amount*Cost)) * 100) / (Amount*Price)  END AS PercentageUtil,
        NroLineaC,
        DEsComp
FROM dbo.SAITEMFAC --Always schema qualify your objects
WHERE DateE >= '20220705' AND DateE < '20220706' --Use better date boundaries, not BETWEEN
  AND codubic = '0010-01'
  AND CodItem = '11001'
GROUP BY Coditem,
         NumeroD,
         Descrip1,
         TipoFac,
         Cantidad,
         Precio,
         Costo;

If you "had to" reference them by their alias, or for more complicated expressions, I would use a CTE. This isn't needed for what you have here, but I'll show you anyway. As you have a nested reference, you'll actually need to use two CTEs:

WITH SalesCost AS(
    SELECT NumeroD AS Bill, 
           Descrip1 AS ProductDescription,
           MAX(ExistAnt) AS ExAct1,   
           MIN(ExistAnt) AS ExAct2, --These cannot have the same name in a CTE/Derived table
           SUM(CASE WHEN TipoFac = 'A' THEN Amount
                    WHEN TipoFac = 'B' THEN -Amount
               END) AS TotalAmount,
            CASE WHEN TipoFac = 'A' THEN (Amount*Price) END AS TotalSale,
            CASE WHEN TipoFac = 'A' THEN (Amount*Cost) END AS TotalCost,
            NroLineaC,
            DEsComp
    FROM dbo.SAITEMFAC --Always schema qualify your objects
    WHERE DateE >= '20220705' AND DateE < '20220706' --Use better date boundaries, not BETWEEN
      AND codubic = '0010-01'
      AND CodItem = '11001'
    GROUP BY Coditem,
             NumeroD,
             Descrip1,
             TipoFac,
             Cantidad,
             Precio,
             Costo),
Utility AS(
    SELECT Bill,
           ProductDescription,
           ExAct1,
           ExAct2,
           TotalAmount,
           TotalSale,
           TotalCost,
           TotalSale - TotalCost AS TotalUtility, --No need for the CASE here, as it's already handled
           NroLineaC,
           DEsComp
    FROM SalesCost)
SELECT Bill,
       ProductDescription,
       ExAct1,
       ExAct2,
       TotalAmount,
       TotalSale,
       TotalCost,
       TotalUtility,
       (TotalUtility * 100) / TotalSales AS PercentageUtility,
       NroLineaC,
       DEsComp
FROM Utility;
  • Related