Using Window Functions With Comparison Operator - sql

I have the following query within a cte.
SELECT [item_id]
FROM [AWS_Stage]
WHERE [yr] IN('2020')
GROUP BY [item_id]
HAVING SUM(ISNULL([frcst_qty], 0)) >= 0
In the past I just needed all item_id's in 2020 greater than 0.
I now need to have all item id's greater than 0 by customer groupings. A line of code like the following makes sense:
SUM([frcst_qty]) OVER (PARTITION BY [item_id], [keycust4]) >= 0
I can't use a window function in a HAVING clause, and I can't have a comparison operator in the SELECT statement.
Any advice on how to make this work?

Could you please try using a subquery like below? This calculated the sum using window function and then using filter on the result. I removed year condition.
SELECT
[item_id]
FROM
(
SELECT
[item_id],
SUM([frcst_qty]) OVER (PARTITION BY [item_id], [keycust4] ORDER BY [item_id], [keycust4]) sum_qty
FROM
[AWS_Stage] ) subq
WHERE
sum_qty >=0
GROUP BY
[item_id]

select [keycust4], [item_id]
from [aws_stage]
where [yr] = '2020'
group by [keycust4], [item_id]
having SUM(ISNULL([frcst_qty], 0)) >= 0
try this. not sure though if i understood your question correctly

Related

Adding an aggregate condition to get total count of sub-group

Thanks for the help on this matter, I'm new with SQL. I'm trying to get a sub-count of Jedi who had more than 2 padawans last month. I tried putting the condition in WHERE but I get an error saying I can't include aggregates in it. I also tried using a CASE but kept getting a syntax error there too. Any help on this would be incredible. Thank you so much!
SELECT COUNT(DISTINCT old_republic.jedi_id), old_republic.region_id
FROM jedi_archives.old_repulicdata old_republic
WHERE old_republic.republic_date >= '2022-06-01' AND old_republic.republic_date <= '2022-06-30' AND COUNT(old_republic.padawan)>2
GROUP BY old_republic.region_id
ORDER BY old_republic.region_id
SELECT old_republic.jedi_id CASE (
WHEN Count(old_republic.padawan)>2
THEN 1
ELSE 0 End), old_republic.region_id
FROM jedi_archives.old_repulicdata old_republic
WHERE old_republic.republic_date >= '2022-06-01' AND old_republic.republic_date <= '2022-06-30'
GROUP BY old_republic.region_id
ORDER BY old_republic.region_id
I can't comment to ask for a fiddle, but from what you've written, you're probably looking for the HAVING clause.
Assuming that padawan denotes the number of Padawans:
SELECT region_id, jedi_id, sum(padawan)
FROM jedi_archives.old_republicdata
WHERE republic_date >= '2022-06-01'
AND republic_date <= '2022-06-30'
GROUP BY region_id, jedi_id
HAVING sum(padawan) > 2;
This query will return the sum of Padawans for each Jedi per region who had more than two Padawans last month in one region (if you don't want to take the region into account, remove it from the SELECT and GROUP BY clause). Other Jedis won't appear in the result.
You can use the CASE expression, too, in order to indicate whether a Jedi had more than two padawans:
SELECT region_id, jedi_id,
CASE WHEN sum(padawan) > 2 THEN 1 ELSE 0 END AS more_than_2_padawans
FROM jedi_archives.old_republicdata
WHERE republic_date >= '2022-06-01'
AND republic_date <= '2022-06-30'
GROUP BY region_id, jedi_id;
I'm not entirely sure without sample data. But I think using the HAVING clause could solve your question.
SELECT COUNT(jedi_id) as jedi_id, region_id FROM tableA
WHERE republic_date between '2022-05-20' and '2022-05-25'
GROUP BY region_id
having padawan > 2
db fiddle

HAVING Clause Issues in SQL Server 2008

I am experiencing issues with my HAVING clause.
Explanation: Each orderno has at least one rxnum tied to it. But each rxnum can have multiple scripts (scriptitemcnt)'s. My problem is that I am trying to use a HAVING clause to ONLY pull orderno's that have the SUM of items less than or equal to 8. The query will execute, but it is still pulling orders that have item sum greater than 8. Here is my code:
SELECT
oh.orderno,
od.rxnum,
SUM(od.scriptitemcnt) as scriptitemcnt,
od.ndctopick,
od.drugdesc,
od.unitno,
od.status,
od.datetimefilled,
od.packingunit,
od.datetimepacked,
oh.totesideinorder
FROM
mck_hvs.oldorderdetails od with( nolock ),
mck_hvs.oldorderheader oh with( nolock )
WHERE
oh.orderno = od.orderno
and od.status != 5
and ( #dateFrom is NULL or od.datetimepacked >= cast( #dateFrom as datetime ) )
and ( #dateTo is NULL or od.datetimepacked < cast( #dateTo as datetime ) + 1 )
and oh.totesideinorder = 'N'
and od.packingunit NOT IN (695, 696, 697, 698)
GROUP BY
oh.orderno,
od.rxnum,
od.scriptitemcnt,
od.ndctopick,
od.drugdesc,
od.rxnum,
od.unitno,
od.status,
od.datetimefilled,
od.packingunit,
od.datetimepacked,
oh.totesideinorder
HAVING
SUM(od.scriptitemcnt) <= '8'
ORDER BY
oh.orderno asc,
od.rxnum asc
I see two possible problems. First, you're comparing the count against the string '8' instead of the number 8. Depending on how SQL server interprets things, that could result in numbers like 10 being treated as less than 8.
Now that's the simple issue. From your description it sounds like you want orders that have a total count not greater than 8. If that's right, then the bigger issue follows...
When you use GROUP BY, aggregates (like sum()) count within a group. Because you're showing (i.e. SELECTing) - and therefore grouping by - the RXNUM, the sum is per RXNUM, not per order. If you want to pull a record for each RXNUM but filter based on an aggregate for the entire ORDERNO it's a little trickier than what you're doing here.
You can use SUM() OVER() instead of SUM() as a starting point. But IIRC this can't be used directly for filtering in sql-server, so you end up needing a subquery. Something like
select *
from (select oh.orderno, od.rxnum
, sum(od.scriptitemcnt) over(partition by orderno) as order_scriptitemcnt
-- other data
from -- ...
)
where order_scriptitemcnt <= 8

First date when certain condition was met

I'm trying to find a first date when a condition was met. So the logic is below:
use
[AdventureWorksDW2012]
go
;WITH sales AS (
select d.OrderDateKey,
SalesAmount = SUM(d.SalesAmount)
from [dbo].[FactInternetSales] d
group by d.OrderDateKey
having SUM(d.SalesAmount)>10000
)
select FirstOrderDateKey = MIN(OrderDateKey)
from sales
The only problem is that in my data is too complex and too huge to calculate value for each date and then choose the min date when the condition is met. Is there any quick way of finding first date when Internet sales amount exceeded 10000? Is there some kind of loop required?
You can do it in single statement also. The performance can be improved with this if you have proper index on orderdatekey.
select MIN(s.OrderDateKey) as FirstOrderDateKey
from [dbo].[FactInternetSales] d
group by d.OrderDateKey
having SUM(d.SalesAmount)>10000
SELECT TOP 1
d.OrderDateKey
,S.RunningTotal
FROM [dbo].[FactInternetSales] d
CROSS APPLY ( SELECT SUM(SalesAmount) AS RunningTotal
FROM [dbo].[FactInternetSales]
WHERE OrderDateKey <= d.OrderDateKey
) S
WHERE S.RunningTotal < -- Condition
ORDER BY d.OrderDateKey DESC

Oracle sub error on query

Following code I added to the SQL Server query and now have to do the same in Oracle. I need to do grouping in the view rather than in the C#. I get this error message:
ORA-01747 Invalid user.table.column or column specification.
How must I code this to work in Oracle?
SELECT CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS, COUNT(*) AS [COUNT]
FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS;
at the beginning of query I have this full code here:
CREATE OR REPLACE VIEW DBD_V_CDL_CHANGES AS
WITH CTE AS
(
SELECT TR.FACILITY_KEY
, MV.VALUE_CODE
, CAST(COUNT(*) AS NUMERIC(9, 0)) COUNT
FROM OPTC.THS_T_TRANSACTIONS1 TR
JOIN OPTC.THS_M_MENU2 M
ON M.MENU_ID = TR.MENU_ID
JOIN OPTC.THS_M_VALUES MV
ON MV.MENU_ID = TR.MENU_ID_VALUE
JOIN OPTC.THS_M_VALUES MV2
ON MV2.MENU_ID = TR.PREVIOUS_MENU_ID_VALUE
JOIN OGEN.GEN_M_PATIENT_MAST PM
ON PM.PAT_NUMBER = TR.PAT_NUMBER
WHERE TR.TR_DATETIME BETWEEN TRUNC(SYSDATE)
AND TRUNC(SYSDATE) + 86399 / 86400
AND TR.EDIT_NO < 0
AND MV.VALUE_TYPE IS NULL
AND MV2.VALUE_TYPE IS NULL
AND MV.VALUE_CODE >= 0
AND MV2.VALUE_CODE >= 0
AND M.SUB_SYS_EXT = 'G1'
AND ABS(MV.VALUE_CODE - MV2.VALUE_CODE) > 1
AND (PM.DISCHARGE_DATE IS NULL OR PM.DISCHARGE_DATE < SYSDATE)
GROUP BY TR.FACILITY_KEY, MV.VALUE_CODE)
SELECT CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS, COUNT(*) AS [COUNT] FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS;
I see a few things wrong with your code.
First, you are selecting the following three columns FACILITY_KEY, VALUE_CODE and the count in the CTE:
SELECT TR.FACILITY_KEY ,
MV.VALUE_CODE ,
COUNT(*) as Count -- note there is no need to CAST(COUNT(*) AS NUMERIC(9, 0)) this
FROM OPTC.THS_T_TRANSACTIONS1 TR
But then when you select from the CTE you are selecting columns that you are not returning in the CTE:
with cte as
(
-- your query here does not return DATE or PATIENT_STATUS
)
SELECT CTE.FACILITY_KEY,
CTE.DATE,
CTE.PATIENT_STATUS,
COUNT(*) AS COUNT
FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS;
Where do PATIENT_STATUS and Date come from since you are not including them in your CTE? So these do not exist when you are trying to select them.
I replicated your error by including columns in the list that were not select in the CTE query.
The second issue is the CTE.DATE column. DATE is a reserved word, place that is double quotes CTE."DATE"
...AS [COUNT], ...AS NUMERIC(9, 0)) is not Oracle syntax and will never work. Simply remove [ ] and use NUMBER instead of NUMERIC. There is no need to CAST Count(). The Count() function will always return number, e.g. 0-zero or some number.
This is valid syntax in Oracle:
SELECT deptno, count(*) total_count_by_dept -- no need to cast or AS --
FROM scott.emp
GROUP BY deptno
/
Try not to use reserved words as COUNT for aliases:
SELECT CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS, COUNT(*) AS total_cnt -- 'AS' is for clarity only, not required
FROM CTE
GROUP BY CTE.FACILITY_KEY, CTE.DATE, CTE.PATIENT_STATUS
/

ORDER BY, set a specific case to the first index?

Using SQL Server 2008...
I'm having some troubles in trying to order my rows in a specific order that I would like them to be ordered by. I've found a few examples that use the ORDER BY CASE clause, but am unsure whether using this method will produce the result that I want it to, thus I come to the community!
Here's what I have:
First, I select, if it exists, a distinct year that is equal to the current year:
IF EXISTS(SELECT DISTINCT [Year]
FROM Assessment WHERE ProjectCode = #ProjectCode AND [Year] = DATENAME(YEAR, GETDATE()))
SELECT DISTINCT [Year]
FROM Assessment WHERE ProjectCode = #ProjectCode
But, then I find some confusion in ordering the results. I'd like to set the current year to the first row returned using the ORDER BY clause, then order the rest of the returned years in a descending order, here's what I have so far:
ORDER BY (CASE WHEN [Year] = (DATENAME(YEAR, GETDATE())) THEN 1
ELSE 100 END) ASC, [Year] desc
Next, if the current year is not contained in the query, select each year and order by year descending.
ELSE
SELECT DISTINCT [Year]
FROM Assessment WHERE ProjectCode = #ProjectCode
ORDER BY [Year] desc
Thanks, in advance!
You don't need conditional statements here at all:
SELECT *
FROM (
SELECT DISTINCT [Year]
FROM Assessment
WHERE projectCode = #projectCode
) q
ORDER BY
CASE [Year] WHEN YEAR(GETDATE()) THEN 1 ELSE 2 END,
[Year]
will output the current year (if exists) first, the other later.
You're question isn't very clear because you don't specify what is broken or where you're having issues. From what I gather, however, you don't need an IF/ELSE. Instead you could do something like ...
SELECT DISTINCT [Year],
CASE [Year]
WHEN DATENAME(Year, GETDATE()) THEN 9999
ELSE [Year] END AS GarbageSoDistinctWorks
FROM Assessment
WHERE ProjectCode = #ProjectCode
ORDER BY
CASE [Year]
WHEN DATENAME(Year, GETDATE()) THEN 9999
ELSE [Year] END DESC
FYI ... i added the case to the select list as a throw away column to avoid the error I assume you're getting.. There are other ways, like a derived table, but for now this should work..
Msg 145, Level 15, State 1, Line 2
ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
HTH,
-eric
Your example code appears to do what you describe. What problems are you having?
As a side note: You don't need the IF statement. By using the ORDER BY from your first example (with the CASE statement), you will get the correct results for both scenarios.
- If "this year" is in your data, it comes first. Everything else comes next in DESC order
- If "this year" isn't in your data, you just get everything else in DESC order