The below query produces the following error message: "Cannot perform an aggregate function on an expression containing an aggregate or a subquery."
I'm trying to get a count of elements (loans) from a table based on certain criteria that I've put into a case statement. I'm using a case statement instead of simply inserting the criteria into the WHERE clause because I'm pulling multiple metrics with this single query and these criteria only apply to this specific metric and not the others. How can I fix it?
SELECT COUNT(
CASE
WHEN (SELECT CONVERT(DATE, MAX(Dates)) FROM (VALUES (S.SchedClosingDate), (S.SchedClosingDate)) AS SchedDates (Dates)) BETWEEN '05/01/18' AND '05/31/18' THEN FD.FileName
END
) AS [Scheduled to Close]
FROM FileData AS FD
JOIN Status AS S ON FD.FileDataID = S.FileDataID
Note: I've removed the other metrics from the query for readability.
Move the calculation to the FROM clause using APPLY:
SELECT SUM(CASE WHEN v.dte >= '2018-05-01' AND v.dte < '2018-06-01' AND
FD.FileName IS NOT NULL
THEN 1 ELSE 0 END
END) AS [Scheduled to Close]
FROM FileData FD JOIN
Status S
ON FD.FileDataID = S.FileDataID CROSS APPLY
(SELECT MAX(dte)
FROM (VALUES (S.SchedClosingDate), (S.SchedClosingDate)) SchedDates(dte)
) as v(dte);
Related
I need to display a zero where "Silo Wt" is null, and display the sum of the two values in the Total column even if "Silo Wt" is null.. may not require any changes if I can get a zero in the Silo column
SELECT DISTINCT (coffee_type) AS "Coffee_Type",
(SELECT ItemName
FROM [T01_Item_Name_TBL]
WHERE Item = B.Coffee_Type) AS "Description",
(SELECT COUNT(Green_Inventory_ID)
FROM [Green_Inventory] AS A
WHERE A.Coffee_Type = B.Coffee_Type
AND current_Quantity > 0) AS "Current Units",
SUM((Unit_Weight) * (Current_Quantity)) AS "Green Inv Wt",
(SELECT SUM(TGWeight)
FROM [P04_Green_STotal_TBL] AS C
WHERE TGItem = Coffee_type) AS "Silo Wt",
(SUM((Unit_Weight) * (Current_Quantity)) +
(SELECT SUM(TGWeight)
FROM [P04_Green_STotal_TBL] AS C
WHERE TGItem = Coffee_type)) AS Total
FROM
[Green_Inventory] AS B
WHERE
Pallet_Status = 0
GROUP BY
Coffee_Type
SS of query results now
You just need to wrap them in ISNULL.
However, your query could do with some serious cleanup and simplification:
DISTINCT makes no sense as you are grouping by that column anyway.
Two of the subqueries can be combined using OUTER APPLY, although this requires moving the grouped Green_Inventory into a derived table.
Another subquery, the self-join on Green_Inventory, can be transformed into conditional aggregation.
Not sure whether I've got the logic right, as the subquery did not have a filter on Pallet_Status, but it looks like you would also need to move that condition into conditional aggregation for the SUM, and use a HAVING. It depends exactly on your requirements.
Don't use quoted table or column names unless you have to.
Use meaningful table aliases, rather than A B C.
Specify table names when referencing columns, especially when using subqueries, or you might get unintended results.
SELECT
gi.Coffee_Type,
(SELECT ItemName
FROM T01_Item_Name_TBL AS n
WHERE n.Item = gi.coffee_Type
) AS Description,
ISNULL(gst.TGWeight, 0) AS SiloWt,
ISNULL(gi.GreenInvWt, 0) + ISNULL(gst.TGWeight, 0) AS Total
FROM (
SELECT
gi.Coffee_Type,
COUNT(CASE WHEN gi.current_Quantity > 0 THEN 1 END) AS CurrentUnits,
SUM(CASE WHEN gi.Pallet_Status = 0 THEN gi.Unit_Weight * gi.Current_Quantity END) AS GreenInvWt
FROM
Green_Inventory AS gi
GROUP BY
gi.Coffee_Type
HAVING
SUM(CASE WHEN gi.Pallet_Status = 0 THEN gi.Unit_Weight * gi.Current_Quantity END) > 0
) AS gi
OUTER APPLY (
SELECT SUM(gst.TGWeight) AS TGWeight
FROM P04_Green_STotal_TBL AS gst
WHERE gst.TGItem = gi.Coffee_Type
) AS gst;
I have a script that gathers data from a lot of different tables and pull data as I want. This script is long and very sensitive, if I group by anything we might miss on any data being pulled. Is there a way we can use these functions and not have to Group every single value?
Here is the aggregate functions I am trying to use:
CONVERT (INT, ROUND (AVG (CONVERT ( DECIMAL, score)), 0))
This part also uses where clause, in simpler script I usually just have a separate select statement to grab this data but in this case it ties into a lot of other LEFT JOINS so I cant put a Where clause as well.
Here is how I am grabbing this field in single script:
SELECT
CONVERT (INT, ROUND (AVG (CONVERT (DECIMAL, score)), 0)) AS AverageScore
FROM
tbIDs scm
LEFT JOIN
tbIds2 m ON m.ID = scm.ID
WHERE
(Score <> 0) AND (m.Complete= 0)
How can I have this whole statement in another SELECT query?
For example here is how I want to grab this data within another query
SELECT
Firstname, LastName,
CONVERT (INT, ROUND (AVG (CONVERT (DECIMAL, score)), 0)) AS AverageScore
FROM
tbppl P
LEFT JOIN
tbIds ID1 ON P.PPLID = ID1.PPlID
LEFT JOIN
tbIDs2 ID2 ON ID1.ID = ID2.ID
WHERE
(Score <> 0) AND (m.Complete= 0)
When I run it I get an error
is invalid in the select list because it is not contained in either an or the GROUP BY clause.
How can I do this?
Your query should look like:
SELECT
Firstname, LastName,
CONVERT (INT, ROUND (AVG (CONVERT (DECIMAL, score)) OVER (PARTITION BY Firstname, LastName), 0)) AS AverageScore
FROM
tbppl P
LEFT JOIN
tbIds ID1 ON P.PPLID = ID1.PPlID
LEFT JOIN
tbIDs2 ID2 ON ID1.ID = ID2.ID
WHERE
(Score <> 0) AND (m.Complete= 0)
The difference to your query is the part:
OVER (PARTITION BY Firstname, LastName)
which your AVG invoke lacked.
If you specify more columns when using aggregate functions, you need to use their window alternatives (see over clause in SQL) and in PARTITION BY specify additional columns (or use standard AVG function and add GROUP BY clause).
Use over and partition by instead. It applies the aggregate function while preserving the row structure. The format is as follows:
avg(x) over (partition by Group1, Group2, Group3)
Given that you want the average over the full data without any "groups", you simply remove the partition by part.
Your query (without the data type conversions) is as follows:
SELECT
Firstname, LastName,
AVG (score) over () AS AverageScore
FROM
tbppl P
LEFT JOIN
tbIds ID1 ON P.PPLID = ID1.PPlID
LEFT JOIN
tbIDs2 ID2 ON ID1.ID = ID2.ID
WHERE
(Score <> 0) AND (m.Complete= 0)
Edit in response to comment:
If you want to take the average over a subset of rows subject to a certain condition you need to use the following:
avg(case when <conditional logic> then x else null end)
If you only want the output rows populated where the condition is met then use:
case when <conditional logic> then avg(x) end
Combining all of the above for your case gives us:
case when (Score <> 0) AND (m.Complete= 0) then avg(case when when (Score <> 0) AND (m.Complete= 0) then Score else null end) over ()
I have the following CTE that I am using in a larger query and I receive two different error messages based on how I group.
I am on Redash and using Amazon Athena. I can group by tenant_id or I can group by tenant_id & my case statement that is named "active". Either way I will receive an error.
active_billpay AS
(SELECT o.tenant_id as tenant_id, CASE WHEN o.created_date >= min(mbpc.created_date)
THEN true else false end as active
FROM reporting.t_order o
LEFT JOIN reporting.t_me_bill_pay_charge mbpc ON o.tenant_id = mbpc.tenant_id
WHERE o.retired_date is null
AND mbpc.retired_date is null
GROUP by 1),
If I group by only tenant_id:
Error running query: SYNTAX_ERROR: line 13:32: '(CASE WHEN
("o"."created_date" >= "min"("mbpc"."created_date")) THEN true ELSE
false END)' must be an aggregate expression or appear in GROUP BY
clause
If I group by both tenant_id and active:
Error running query: SYNTAX_ERROR: line 13:32: GROUP BY clause cannot
contain aggregations or window functions:
["min"("mbpc"."created_date")]
Thank you in advance.
I think you just want to aggregate by tenant_id and created_date:
SELECT o.tenant_id as tenant_id,
(CASE WHEN o.created_date >= MIN(mbpc.created_date) THEN true ELSE false
END) as active
FROM reporting.t_order o LEFT JOIN
reporting.t_me_bill_pay_charge mbpc
ON o.tenant_id = mbpc.tenant_id
where o.retired_date is null
and mbpc.retired_date is null
group by o.tenant_id, o.created_date
In order to apply aggregate functions like min, SQL requires you to be very specific about what set of data that aggregate applies to. Even if SQL allowed the query you've written, you'd still only get the minimum created_date for each row, not each tenant_id.
In order to do what I think you're trying to do, you should use a sub-query to get the minimum created_date for each tenant_id, then use that value inform your active field.
SELECT o.tenant_id AS tenant_id,
CASE WHEN o.created_date >= min_created_date THEN TRUE ELSE FALSE END AS active
FROM reporting.t_order o
LEFT JOIN
(SELECT tenant_id, MIN (created_date) AS min_created_date
FROM reporting.t_me_bill_pay_charge
WHERE retired_date IS NULL) mbpc
ON o.tenant_id = mbpc.tenant_id
WHERE o.retired_date IS NULL
Generally, if you find yourself trying to cheat the SQL syntax requirements by doing something like group by 1, that's a strong indication that your approach is flawed.
I have two tables, an Orders table which contains a list of a users orders and a OrderShippingCosts table which contains a price for shipping each item based on the OrderTypeID in the Orders table.
I am running a query like below to calculate the total shipping costs:
SELECT
SUM(CASE
WHEN OR.OrderTypeID = 1
THEN (SELECT CostOfShippingSmallParcel
FROM OrderShippingCosts)
ELSE (SELECT CostOfShippingBigParcel
FROM OrderShippingCosts)
END) AS TotalShippingCost
FROM
Orders AS OR
But I'm getting the following error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery
Does anyone know what is wrong with my query?
Function SUM takes an expression on input, which evaluates into single data value, not a dataset. Expression definition from MSDN:
Is a combination of symbols and operators that the SQL Server Database Engine evaluates to obtain a single data value.
You trying to pass to SUM function a dataset (which is result of subquery), not a single data value. This is simplification of what you trying to query:
SELECT SUM(SELECT Number FROM SomeTable)
It is not valid. The valid query would be:
SELECT SUM(Value) FROM SomeTable
In your particular case looks like you missing JOIN. Your original logic will result in summary of entire OrderShippingCosts table for each row of Orders table. I think, it should be something like this:
SELECT
SUM
(
CASE
WHEN ord.OrderTypeID = 1 THEN ship.CostOfShippingSmallParcel
ELSE ship.CostOfShippingBigParcel
END
) TotalShippingCost
FROM Orders AS ord
JOIN OrderShippingCosts ship ON /* your search condition, e.g.: ord.OrderID = ship.OrderID */
By the way, it is not a good idea to use reserved symbols as aliases, names and so on. In your query you use OR as alias for Orders table. Symbol OR is reserved for logical or operation. If you really need to use reserved symbol, wrap it into [ and ] square braces. Look here and here for more details.
The error message is clear, you can avoid it with a join:
SELECT
SUM(CASE WHEN [OR].OrderTypeID = 1
THEN CostOfShippingSmallParcel
ELSE CostOfShippingBigParcel END) AS TotalShippingCost
FROM Orders [OR]
CROSS JOIN OrderShippingCosts
You can try like this...
SELECT
CASE WHEN OR.OrderTypeID = 1
THEN (SELECT SUM(CostOfShippingSmallParcel) FROM OrderShippingCosts)
ELSE (SELECT SUM(CostOfShippingBigParcel) FROM OrderShippingCosts) END AS TotalShippingCost
FROM Orders AS OR
Let me know
select sum (or.TotalShippingCost)
FROM
SELECT
(CASE WHEN OR.OrderTypeID = 1
THEN (SELECT CostOfShippingSmallParcel FROM OrderShippingCosts)
ELSE (SELECT CostOfShippingBigParcel FROM OrderShippingCosts) END) AS TotalShippingCost
FROM Orders AS OR
Try this
SELECT
ISNULL
(
SUM
(
CASE
WHEN O.OrderTypeID = 1 THEN C.CostOfShippingSmallParcel
ELSE C.CostOfShippingBigParcel END
), 0
) AS TotalShippingCost
FROM
Orders AS O LEFT JOIN
OrderShippingCosts C ON O.Id = C.OrderId -- Your releation id
I don't understand the purpose of using syntax code "CASE GROUPING"?
Unfortunately, I don't have the database to review the sourcecode below.
SELECT
CASE GROUPING(st.stor_name) WHEN 0 THEN st.stor_name ELSE 'ALL' END AS Store,
CASE GROUPING(s.type) WHEN 0 THEN s.type ELSE 'ALL TYPES' END AS Type,
SUM(s.qty) AS TotalSold
FROM
(SELECT DISTINCT st.stor_id, t.type, 0 AS qty
FROM stores st CROSS JOIN titles t
UNION ALL
SELECT
s.stor_id,
t.type, s.qty
FROM sales s JOIN titles t ON s.title_id=t.title_id) s
JOIN stores st ON (s.stor_id=st.stor_id)
GROUP BY st.stor_name, s.type WITH CUBE
CASE is a conditional expression, like an if statement.
GROUPING is a function that:
Indicates whether a specified column expression in a GROUP BY list is aggregated or not. GROUPING returns 1 for aggregated or 0 for not aggregated in the result set. GROUPING can be used only in the SELECT list, HAVING, and ORDER BY clauses when GROUP BY is specified.