When else with partition by isn't working in redshift queries - sql

I would like to exclude the categories sub_tag1, sub_tag2 and sub_tag3 of tag from the TAG_SALES_by_month but the rest whatever i mentioned in the where condition need to be included in the count. I couldn't achieve the desired result.can anyone help me to achieve the same, which would be very much appreciated.
select o.tag,
o.SOME, o.THING, o.ILIKE, o.date, c.THE, c.MOST,
date_part(month, o.date) as Month,
date_part(day, o.date) as day,
count(o.id) over (partition by day, CUST_Id) as SALE_NO,
count(o.id) over (partition by Month, CUST_Id) as SALE_NO_by_month,
count(case when (tag <> 'sub_tag1' AND tag <> 'sub_tag2' AND tag <> 'sub_tag3') then o.id else 0 END) over (partition by Month, CUST_Id) as TAG_SALES_by_month,
c.id as CUST_Id
from order_info o
left join config c on o.SOME = c.SOME
where date >= '05/01/2021' AND tag in ('sub_tag1', 'sub_tag2', 'sub_tag3', 'sub_tag4', 'sub_tag5',
'sub_tag6') AND ILIKE = 'JACK'
group by o.tag, o.SOME, o.THING, o.ILIKE, o.date, c.THE, c.MOST, CUST_Id, o.id
order by date

Per the comments, the issue here is the that COUNT will return 1 for any value, it counts existence vs not existence of a value/row.
So COUNT(CASE WHEN... ELSE 0...) will still count 1 on the ELSE condition, since 0 is a value that exists.
The solution is to use ELSE NULL or omit the ELSE clause which will default to NULL, because NULL will not be counted.

Related

SQL Lag and LEAD query

I need data which is in Output column. When 1st column status is P then we need value from Filled date. But once status is anything from P then we need date from last P status. Pls. let me know if i am not able to explain. Thanks in advance.
In standard SQL, you can use:
select (case when status = 'P'
then filled_dt
else lag(case when status = 'P' then filled_dt end) over (partition by mbr_id order by filled_dt ignore nulls)
end) as imputed_filled_dt
This is standard SQL; however, not all databases support ignore nulls. This probably does what you want:
select (case when status = 'P'
then filled_dt
else max(case when status = 'P' then filled_dt end) over (partition by mbr_id order by filled_dt)
end) as imputed_filled_dt

Conditional CASE WHEN select snowflake SQL

I am stuck on a conditional snowflake select sql. I am trying to count the IDs when they have the corresponding categorial value. I would appreciate some help.
Thanks
SELECT
YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
COUNT(CASE WHEN ID THEN CATEGORY = 'A')
from table
group by week, year;
Here is one method:
SELECT YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
SUM(CASE WHEN CATEGORY = 'A' THEN 1 ELSE 0 END) as num_a
FROM table
GROUP BY week, year;
Snowflake supports COUNT_IF:
Returns the number of records that satisfy a condition.
Aggregate function
COUNT_IF( <condition> )
SELECT YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
COUNT_IF(CATEGORY = 'A') AS num_a
FROM tab
GROUP BY week, year;
You should / can use IFF() since case when is more suitable when there are multiple conditions.
SELECT
YEAR(DATETIME) AS YEAR,
WEEKOVERYEAR(DATETIME) AS WEEK,
COUNT(IFF(CATEGORY = 'A',ID,NULL)) as count
from table
group by week, year;
COUNT() counts the number of rows that are not null.
If you are want when ID is not null AND CATEGORY = 'A' then
COUNT(CASE WHEN ID IS NOT NULL AND CATEGORY = 'A' THEN TRUE ELSE NULL END)
will give you that, or you can use a SUM like in Gordon's answer
SUM(CASE WHEN ID IS NOT NULL AND CATEGORY = 'A' THEN 1 ELSE 0 END)
or you can use the snowflake IFF as a shorter form for the same thing, which is how I do it
SUM( IFF( ID IS NOT NULL AND CATEGORY = 'A', 1, 0))

SQL query to find Date, TranscationAmount_US,TransactionAmount_UK

We have 2 tables:
Transaction (AccountId,Date,TransactionAmount)
Master(Aid,Country)
We need to find Date, TotalTAmt_US, Total_TAmt_UK
My solution:
Select
Date,
CASE WHEN Country in ('US') THEN SUM(TransAmt) ELSE '0'END AS TotalTAmt_US,
CASE WHEN Country in ('UK') THEN SUM(TransAmt) ELSE '0'END AS TotalTAmt_UK
FROM
(
SELECT
T.Date As Date,
M.Country As Country,
SUM(T.TransAmt) As TransAmt,
FROM
Transaction T JOIN Master M On T.Aid = M.Aid
WHERE Country in ('US','UK')
group by Date,Country
) As T1
group by Date;
Is this right?
Can we use Country in CASE WHEN without pulling it as I do not want to pull it and then group by it.
Advice please.
Thanks.
You have to declare in GROUP BY section all columns that you use in select statement.
So just put your cases to the grouping.
Select
Date,
CASE WHEN Country in ('US') THEN TransAmt ELSE 0 END AS TotalTAmt_US,
CASE WHEN Country in ('UK') THEN TransAmt ELSE 0 END AS TotalTAmt_UK
FROM
(
SELECT
T.Date As Date,
M.Country As Country,
SUM(T.TransAmt) As TransAmt,
FROM
Transaction T JOIN Master M On T.Aid = M.Aid
WHERE Country in ('US','UK')
GROUP BY Date,Country
) As T1
GROUP BY Date, CASE WHEN Country in ('US') THEN TransAmt ELSE 0 END AS
TotalTAmt_US, CASE WHEN Country in ('UK') THEN TransAmt ELSE 0 END
AS TotalTAmt_UK
Additionaly, remove the SUM() function in your case conditions. If you put them to the GROUP BY you can get error:
ORA-00934: Group function is not allowed here.
And, in the end, remove the ticks in zeros in ELSE conditions. You can get another error about incosistens data types.
I believe you wanted to do a conditional aggregation getting the sum for all US based transaction for a day and the sum of all the UK based transactions for the same day. Then you had to move the CASE into the sum(), adding the amount only if the country is the one you look for, otherwise zero.
Also your subquery isn't necessary.
SELECT t.date,
sum(CASE m.country
WHEN 'US' THEN
t.transamt
ELSE
0
END) totaltamt_us,
sum(CASE m.country
WHEN 'UK' THEN
t.transamt
ELSE
0
END) totaltamt_uk
FROM transaction t
INNER JOIN master m
ON m.aid = t.accountid
WHERE m.county IN ('US',
'UK')
GROUP BY t.date;
If you don't insist on having the different sums in different columns, but would also accept rows, it can be as simple as:
SELECT m.country,
t.date,
sum(t.transamt) totaltamt
FROM transaction t
INNER JOIN master m
ON m.aid = t.accountid
WHERE m.county IN ('US',
'UK')
GROUP BY m.country,
t.date;
I think you want conditional aggregation:
SELECT Date,
SUM(CASE WHEN Country in ('US') THEN TransAmt ELSE 0 END) AS TotalTAmt_US,
SUM(CASE WHEN Country in ('UK') THEN TransAmt ELSE 0 END) AS TotalTAmt_UK
FROM Transaction T JOIN
Master M
On T.Aid = M.Aid
WHERE Country in ('US', 'UK')
GROUP BY Date;
Notes:
You do not need two levels of aggregation.
Numbers should not be enclosed in single quotes, so 0, not '0'.

sorting month in ascending order

I have looked at related Qs on Stack.
I am trying to sort my query to display month in ascending order. A similar post on Stack said that I must indicate "ASC", but this doesn't work for me. I have written an Order By. I cannot figure out why it is not working.
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date]
FROM Submission
GROUP BY DATENAME( MONTH,(Submission.SubmissionDate)) ORDER BY [Date] ASC;
This is the output:
Month
August
February
September
Note: this is just a part of my query, I didnt think it would necessary to show the rest, which relates to other attributes from my table (Not month related)
Edit: This is my Entire Query: (The Initial One)
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date],
SUM( CASE WHEN Submission.Status='Under review' THEN 1 ELSE 0 END) [Under_Review],
SUM( CASE WHEN Submission.Status='Accepted' THEN 1 ELSE 0 END) [Accepted],
SUM( CASE WHEN Submission.Status='Rejected' THEN 1 ELSE 0 END) [Rejected],
SUM( CASE WHEN Submission.Status='In print' THEN 1 ELSE 0 END) [In_print],
SUM( CASE WHEN Submission.Status='Published' THEN 1 ELSE 0 END) [Published]
FROM Submission INNER JOIN ((Faculty INNER JOIN School ON Faculty.FacultyID = School.[FacultyID]) INNER JOIN (Researcher INNER JOIN ResearcherSubmission ON Researcher.ResearcherID = ResearcherSubmission.ResearcherID) ON School.SchoolID = Researcher.SchoolID) ON Submission.SubmissionID = ResearcherSubmission.SubmissionID
GROUP BY DATENAME( MONTH,(Submission.SubmissionDate))
ORDER BY DATENAME( MONTH,(Submission.SubmissionDate));
SELECT DATENAME( MONTH, Submission.SubmissionDate) AS [Date]
FROM Submission
ORDER BY datepart(mm,Submission.SubmissionDate)
You don't need a group by (for the query shown). Also, when you order by month name it would return results in the alphabetical order of month name. You should not use previously defined aliases in the where,order by having and group by clauses.
Edit: The problem is with the join conditions. You should correct them as per the comments in line.
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date],
SUM( CASE WHEN Submission.Status='Under review' THEN 1 ELSE 0 END) [Under_Review],
SUM( CASE WHEN Submission.Status='Accepted' THEN 1 ELSE 0 END) [Accepted],
SUM( CASE WHEN Submission.Status='Rejected' THEN 1 ELSE 0 END) [Rejected],
SUM( CASE WHEN Submission.Status='In print' THEN 1 ELSE 0 END) [In_print],
SUM( CASE WHEN Submission.Status='Published' THEN 1 ELSE 0 END) [Published]
FROM Faculty
INNER JOIN School ON Faculty.FacultyID = School.[FacultyID]
INNER JOIN Researcher ON School.SchoolID = Researcher.SchoolID
INNER JOIN ResearcherSubmission ON Researcher.ResearcherID = ResearcherSubmission.ResearcherID
INNER JOIN SUBMISSION ON Submission.SubmissionID = ResearcherSubmission.SubmissionID
GROUP BY DATENAME( MONTH,(Submission.SubmissionDate))
ORDER BY DATEPART( MONTH,(Submission.SubmissionDate))
A simple option is to add MONTH(Submission.SubmissionDate) to your group by clause, and order by that as well:
SELECT DATENAME( MONTH,(Submission.SubmissionDate)) AS [Date]
FROM Submission
GROUP BY MONTH(Submission.SubmissionDate), DATENAME( MONTH,(Submission.SubmissionDate))
ORDER BY MONTH(Submission.SubmissionDate)
This will work for your real query as well.
You can use MIN() or MAX():
SELECT DATENAME(MONTH,(Submission.SubmissionDate)) AS [Date]
FROM Submission
GROUP BY DATENAME(MONTH,(Submission.SubmissionDate))
ORDER BY MIN([Date]) ASC;
This chooses an arbitrary date from each group and orders by that.
By the way, you probably should care about the year as well as the month. If so:
SELECT YEAR(s.SubmissionDate), DATENAME(MONTH, s.SubmissionDate) AS [Date]
FROM Submission s
GROUP BY YEAR(s.SubmissionDate), DATENAME(MONTH, s.SubmissionDate)
ORDER BY MIN([Date]) ASC;

How to use a SQL sub query?

I am attempting to use a sub query to query our order database and return 3 columns for example:
Date Orders Replacements
09-MAY-14 100 5
... ... ...
Each order that is created can be given a reason, which basically means that it is a replacement product i.e. orders without a reason are new orders and orders with a reason are replacement orders.
I am using the below query in an attempt to get this information, but I'm getting lots of error messages, and each time I think I've fixed one I create another 10, so assume I completely have the wrong idea here.
SELECT Orders.EntryDate AS "Date", COUNT(Orders.OrderNo) AS "Orders",
(SELECT COUNT(Orders.OrderNo) AS "Replacements"
FROM Orders
WHERE Orders.Reason IS NOT NULL
AND Orders.EntryDate = '09-MAY-2014'
AND Orders.CustomerNo = 'A001'
GROUP BY Orders.EntryDate
)
FROM Orders
WHERE Orders.Reason IS NULL
AND Orders.EntryDate = '09-MAY-2014'
AND Orders.CustomerNo = 'A001'
GROUP BY Orders.EntryDate
;
Why the sub query use a case!
SELECT Orders.EntryDate AS "Date", COUNT(Orders.OrderNo) AS "Orders",
sum(CASE WHEN Orders.reason is null then 1 else 0 end) as "Replacements"
FROM Orders
WHERE Orders.Reason IS NULL
AND Orders.EntryDate = '09-MAY-2014'
AND Orders.CustomerNo = 'A001'
GROUP BY Orders.EntryDate
The subquery has to execute each time, since you need to evaluate each record the case can do that for you and then sum the results. If you need to get a count of -non replacement orders then just do a different case instead of a count.
You could could sum a case expression instead of having a another subquery with another where clause:
SELECT Orders.EntryDate AS "Date",
SUM (CASE WHEN Orders.Reason IS NULL THEN 1 ELSE 0 END) AS "Orders",
SUM (CASE WHEN Orders.Reason IS NOT NULL THEN 1 ELSE 0 END) AS "Replacements"
FROM Orders
WHERE Orders.EntryDate = '09-MAY-2014'
AND Orders.CustomerNo = 'A001'
GROUP BY Orders.EntryDate
Your errors were probably due to the fact that you did not include the subquery in your group by clause. You can try that approach but this one is simpler:
select entrydate "date"
, count(orderno) "orders"
, sum(case when reason is not null then 1 else 0 end) "replacements"
etc
group by entrydate
Is this what you are trying to do?
SELECT Orders.EntryDate
, COUNT(case when Orders.reason is null then 1 end) AS orders
, COUNT(case when Orders.reason is not null then 1 end) AS Replacements
FROM Orders
WHERE Orders.EntryDate = '09-MAY-2014'
AND Orders.CustomerNo = 'A001'
GROUP BY Orders.EntryDate
The Replacements expression can be simplified to:
COUNT(Orders.reason)