ORDER BY Alias not working - sql

UPDATING QUESTION:
ERROR: column "Fruits" does not exist
Running Postgres 7.4(Yeah we are upgrading)
Why can't I ORDER BY the column alias? wants tof."TypeOfFruits" in the ORDER BY as well, why?
SELECT (CASE
WHEN tof."TypeOfFruits" = 'A' THEN 'Apple'
WHEN tof."TypeOfFruits" = 'P' THEN 'Pear'
WHEN tof."TypeOfFruits" = 'G' THEN 'Grapes'
ELSE 'Other' END) AS "Fruits",
SUM(CASE WHEN r.order_date
BETWEEN DATE_TRUNC('DAY', LOCALTIMESTAMP) AND DATE_TRUNC('DAY', LOCALTIMESTAMP) + INTERVAL '1 DAY'
THEN 1 ELSE 0 END) AS daily,
SUM(CASE WHEN r.order_date
BETWEEN DATE_TRUNC('MONTH', LOCALTIMESTAMP) AND DATE_TRUNC('MONTH', LOCALTIMESTAMP) + INTERVAL '1 MONTH'
THEN 1 ELSE 0 END) AS monthly,
SUM(CASE WHEN r.order_date
BETWEEN DATE_TRUNC('YEAR', LOCALTIMESTAMP) AND DATE_TRUNC('YEAR', LOCALTIMESTAMP) + INTERVAL '1 YEAR'
THEN 1 ELSE 0 END) AS yearly,
SUM(CASE WHEN r.order_date >= '01-01-2011 00:00:00' THEN 1 ELSE 0 END) AS lifetime
FROM reports AS r, "TypeOfFruits" AS tof
WHERE r.id = tof."ID"
GROUP BY "Fruits"
ORDER BY CASE
WHEN "Fruits" = 'Apple' THEN 1
WHEN "Fruits" = 'Pear' THEN 2
WHEN "Fruits" = 'Grapes' THEN 3
ELSE 4
END
Results as of now
Fruits;daily;monthly;yearly;lifetime
"Apple";17;1174;3136;3136
"Pear";28;94;94;94
"Grapes";0;191;490;490
"Other";0;2;27;27
"Other";0;0;1;1
"Other";0;0;27;27
"Other";0;6;28;28
"Other";0;58;229;229
"Other";0;3;3;3
"Other";0;0;1;1
Desired results would be one row with the "Other" total, so four rows altogether
(x would be the total)
Fruits;daily;monthly;yearly;lifetime
"Apple";17;1174;3136;3136
"Pear";28;94;94;94
"Grapes";0;191;490;490
"Other";x;x;x;x

You can use ORDER BY 1 to order by the first field, which is "Fruits". The same is valid for GROUP BY
Update
For the order, instead of doing the case in the order by, create a new column in.. say.. the second position:
(CASE
WHEN "Fruits" = 'Apple' THEN 1
WHEN "Fruits" = 'Pear' THEN 2
WHEN "Fruits" = 'Grapes' THEN 3
ELSE 4 ) as Order
Then in you ORDER BY 2.

The reason for this can be found in the documentation:
Each expression [in the ORDER BY list] can be the name or ordinal number of an output column (SELECT list item), or it can be an arbitrary expression formed from input-column values.
(my emphasis)
The reason for this is that old versions of the SQL standard (SQL-92) only allowed sorting by output column name or number, whereas newer versions allow sorting by arbitrary expressions, but those expressions are formed from input column values.
Other answers already contain various suitable workarounds for your case.

The alias is assigned after the order by so you can't use it in the order by. Use this instead:
(CASE
WHEN tof."TypeOfFruits" = 'A' THEN 'Apple'
WHEN tof."TypeOfFruits" = 'P' THEN 'Pear'
WHEN tof."TypeOfFruits" = 'G' THEN 'Grapes'
ELSE 'Other' END)

Consider something like this:
SELECT * FROM (SELECT (CASE
WHEN tof."TypeOfFruits" = 'A' THEN 'Apple'
WHEN tof."TypeOfFruits" = 'P' THEN 'Pear'
WHEN tof."TypeOfFruits" = 'G' THEN 'Grapes'
ELSE 'Other' END) AS "Fruits",
(CASE
WHEN tof."TypeOfFruits" = 'A' THEN 1
WHEN tof."TypeOfFruits" = 'P' THEN 2
WHEN tof."TypeOfFruits" = 'G' THEN 3
ELSE 4 END) as NUM
FROM ..... <rest of your query without group by and order by .....
)
GROUP BY Fruits
ORDER BY NUM

You could try something like this ... untested, but I've seen similar queries.
Let me know if it works...
SELECT "Fruits",
SUM(CASE WHEN r.order_date
BETWEEN DATE_TRUNC('DAY', LOCALTIMESTAMP) AND DATE_TRUNC('DAY', LOCALTIMESTAMP) + INTERVAL '1 DAY'
THEN 1 ELSE 0 END) AS daily,
SUM(CASE WHEN r.order_date
BETWEEN DATE_TRUNC('MONTH', LOCALTIMESTAMP) AND DATE_TRUNC('MONTH', LOCALTIMESTAMP) + INTERVAL '1 MONTH'
THEN 1 ELSE 0 END) AS monthly,
SUM(CASE WHEN r.order_date
BETWEEN DATE_TRUNC('YEAR', LOCALTIMESTAMP) AND DATE_TRUNC('YEAR', LOCALTIMESTAMP) + INTERVAL '1 YEAR'
THEN 1 ELSE 0 END) AS yearly,
SUM(CASE WHEN r.order_date >= '01-01-2011 00:00:00' THEN 1 ELSE 0 END) AS lifetime
FROM reports AS r
,(SELECT "ID",
CASE
WHEN tof."TypeOfFruits" = 'A' THEN 'Apple'
WHEN tof."TypeOfFruits" = 'P' THEN 'Pear'
WHEN tof."TypeOfFruits" = 'G' THEN 'Grapes'
ELSE 'Other'
END AS "Fruits" FROM "TypeOfFruits" ) AS "tof"
WHERE r.id = tof."ID"
GROUP BY "Fruits"
ORDER BY CASE
WHEN "Fruits" = 'Apple' THEN 1
WHEN "Fruits" = 'Pear' THEN 2
WHEN "Fruits" = 'Grapes' THEN 3
ELSE 4
END

Try using backticks (`) instead of single/double quotes to wrap your alias name.
Had the same issue with MySQL; backticks fixed the problem.

Related

SQL CASE WHEN THEN logics of calculating the types of a column

Have a tableA like this:
I wanna receive a tableŠ˜ like this (group by startTime and endTime, count of Severity in cnt column and count of every type of Severity in a distinct column):
The simple count (cnt column) works fine. But with the other I tired CASE WHEN THEN logics and it seems not working (line 10 for example). Can you please assist me with SQL query in this case.
You need conditional aggregation :
select starttime, endtime, count(*),
sum(case when severity = 'low' then 1 else 0 end),
sum(case when severity = 'med' then 1 else 0 end),
sum(case when severity = 'high' then 1 else 0 end)
from table t
group by starttime, endtime;
Try below query: with case when
select starttime, endtime, count(severity) as cnt, count(case when severity='LOW' then 1 end) cnt_low,count(case when severity='MED' then 1 end) cnt_med,count(case when severity='HIGH' then 1 end) as cnt_high
from tablename
group by starttime, endtime
use case when and aggregate function sum
select startTime , endTime,count(*) as Cnt,
sum( case when Severity='MED' then 1 else 0 end) as cntMed,
sum( case when Severity='LOW' then 1 else 0 end) as cntLow,
sum( case when Severity='HIGH' then 1 else 0 end) as cntHIGH from yourtable
group by startTime , endTime

Group By Week on Redshift

I'm trying to count my table by week but the DATE_TRUNCT('week',date) function considers Monday as the start of the week but I need for the week to start in Sunday.
This is the query, which runs properly but with starting in Mondays...
SELECT DATE_TRUNC('week',myDate) AS Reference,
column1 AS Item1,
column2 AS Item2,
COUNT(*) AS Volume,
COUNT(CASE WHEN status = 'status1' THEN 1 END) AS Status1,
COUNT(CASE WHEN status = 'status2' THEN 1 END) AS Status2,
COUNT(CASE WHEN status = 'status2' AND fase = '1' THEN 1 END) AS Fase1,
COUNT(CASE WHEN status = 'status2' AND fase = '2' THEN 1 END) AS Fase2,
COUNT(CASE WHEN status = 'status2' AND fase = '3' THEN 1 END) AS Fase3
FROM myTable
WHERE DATE_TRUNC('week',myDate) = DATE_TRUNC('week',TO_DATE('12/25/2016 00:00:00','MM/dd/yyyy'))
GROUP BY 1,
2,
3;
So far I only tried another query which doesnt even run and I dont know why, it just says "syntax error at or near "integer" :
SELECT DATE_TRUNC('week',myDate) - integer '1' AS Reference,
column1 AS Item1,
column2 AS Item2,
COUNT(*) AS Item3,
COUNT(CASE WHEN status = 'status1' THEN 1 END) AS Status1,
COUNT(CASE WHEN status = 'status2' THEN 1 END) AS Status2,
COUNT(CASE WHEN status = 'status2' AND fase = '1' THEN 1 END) AS Fase1,
COUNT(CASE WHEN status = 'status2' AND fase = '2' THEN 1 END) AS Fase2,
COUNT(CASE WHEN status = 'status2' AND fase = '3' THEN 1 END) AS Fase3
FROM myTable
WHERE myDate between ( DATE_TRUNC('week', TO_DATE('12/25/2016 00:00:00','MM/dd/yyyy' ) - integer '1' ) and ( DATE_TRUNC('week', TO_DATE('12/25/2016 00:00:00','MM/dd/yyyy' ) ) + integer '5' )
GROUP BY 1,
2,
3;
Also, even if this query runned propely, it would show the count of the week 18/Dec - 24/Dec and not the week 25/Dec - 31/Dec in the case 25/Dec. The same would happen in other days if they are Sundays.
EDIT:
I just found the solution in this blog:
https://blog.modeanalytics.com/date-trunc-sql-timestamp-function-count-on/
It was introducing the date_trunct function and someone asked the same question in the comments. This is my solved query for future reference to others:
SELECT date_trunc('WEEK',(myDate + interval '1 day'))- interval '1 day' AS Reference
column1 AS Item1,
column2 AS Item2,
COUNT(*) AS Volume,
COUNT(CASE WHEN status = 'status1' THEN 1 END) AS Status1,
COUNT(CASE WHEN status = 'status2' THEN 1 END) AS Status2,
COUNT(CASE WHEN status = 'status2' AND fase = '1' THEN 1 END) AS Fase1,
COUNT(CASE WHEN status = 'status2' AND fase = '2' THEN 1 END) AS Fase2,
COUNT(CASE WHEN status = 'status2' AND fase = '3' THEN 1 END) AS Fase3
FROM myTable
WHERE ( date_trunc('WEEK',(myDate + interval '1 day'))- interval '1 day') = ( DATE_TRUNC('week',TO_DATE('12/24/2016 00:00:00','MM/dd/yyyy') + interval '1 day' ) - interval '1 day' )
GROUP BY 1,
2,
3;
I couldn't find any simple way to set week as Sunday to Saturday. But you can try this:
select date_trunc('week', myDate + 1) - 1 as Reference,
...
from myTable
where ...
group by date_trunc('week', myDate + 1), ...
The trick here is just shift by one day while doing group by.

SQL Server Drop NULLS in pivot

I have a base table. Base table
I want to put in linear form that is why I use pivot to have it. And my query goes like:
select
[report_date] AS 'Date'
,[30],[percent_30] AS '%30'
,[45],[percent_45] AS '%45'
,[60],[percent_60] AS '%60'
,[75],[percent_75] AS '%75'
,[90],[percent_90] AS '%90'
,[105],[percent_105] AS '%105'
,[120],[percent_120] AS '%120'
,[TOTAL] AS 'Total Sales'
--,[total_percentage] AS 'Total Percent'
from database.dbo.delivery_report_logs
PIVOT(SUM(Sale_Count)
FOR description IN ([30],[45],[60],[75],[90],[105],[120],[TOTAL])) AS pvz
But the result looks like this.
I want to remove all nulls. Any help? Thank you.
Here is my expected output:
Sample Output
select
[report_date] AS 'Date'
,[30],[percent_30] AS '%30'
,[45],[percent_45] AS '%45'
,[60],[percent_60] AS '%60'
,[75],[percent_75] AS '%75'
,[90],[percent_90] AS '%90'
,[105],[percent_105] AS '%105'
,[120],[percent_120] AS '%120'
,[TOTAL] AS 'Total Sales'
--,[total_percentage] AS 'Total Percent'
from database.dbo.delivery_report_logs
PIVOT ISNULL(SUM(Sale_Count),0)
FOR description IN ([30],[45],[60],[75],[90],[105],[120],[TOTAL])) AS pvz
This is solved. I use temporary table to pivot the data and insert it to a log table and it gives me the result I need.
you can also use a query like this that uses and aggregate with a case expression
SELECT report_date,
SUM(CASE WHEN Description = '30' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = '30' THEN percent_30 END) AS [percent_30],
SUM(CASE WHEN Description = '45' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = '45' THEN percent_45 END) AS [percent_45],
SUM(CASE WHEN Description = '60' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = '60' THEN percent_60 END) AS [percent_60],
SUM(CASE WHEN Description = '75' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = '75' THEN percent_70 END) AS [percent_70],
SUM(CASE WHEN Description = '90' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = '90' THEN percent_90 END) AS [percent_90],
SUM(CASE WHEN Description = '105' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = '105' THEN percent_105 END) AS [percent_105],
SUM(CASE WHEN Description = '120' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = '120' THEN percent_120 END) AS [percent_120],
SUM(CASE WHEN Description = 'TOTAL' THEN sale_count END) AS [30],
SUM(CASE WHEN Description = 'TOTAL' THEN total_percentage END) AS [total_percentage]
FROM database.dbo.delivery_report_logs
GROUP BY report_date
you can also use MAX or MIN in place of SUM if there is only one value per report_date/percentate

Limit SQL query to days

I use this SQL query to make status report by day:
CREATE TABLE TICKET(
ID INTEGER NOT NULL,
TITLE TEXT,
STATUS INTEGER,
LAST_UPDATED DATE,
CREATED DATE
)
;
Query:
SELECT t.created,
COUNT(CASE WHEN t.status = '1' THEN 1 END) as cnt_status1,
COUNT(CASE WHEN t.status = '2' THEN 1 END) as cnt_status2,
COUNT(CASE WHEN t.status = '3' THEN 1 END) as cnt_status3,
COUNT(CASE WHEN t.status = '4' THEN 1 END) as cnt_status4
FROM ticket t
GROUP BY t.created
How I can limit this query to last 7 days?
Also I would like to get the results split by day. Fow example I would like to group the first dates for 24 hours, second for next 24 hours and etc.
Expected result:
This might help:
SELECT TO_CHAR(t.created, 'YYYY-MM-DD') AS created_date,
COUNT(CASE WHEN t.status = '1' THEN 1 END) as cnt_status1,
COUNT(CASE WHEN t.status = '2' THEN 1 END) as cnt_status2,
COUNT(CASE WHEN t.status = '3' THEN 1 END) as cnt_status3,
COUNT(CASE WHEN t.status = '4' THEN 1 END) as cnt_status4
FROM ticket t
WHERE t.created >= SYSDATE-7
GROUP BY TO_CHAR(t.created, 'YYYY-MM-DD')
ORDER BY created_date;
I used the oracle function for date conversion. I'm sure you'll find the corresponding one for postgresql.

One date check for entire query

I have the following query:
select
fp.id,
fr.id,
sum(case
when to_date(fp.offered_date) BETWEEN TO_DATE( :ad_startdate, 'YYYY-MM-DD')
AND TO_DATE(:ad_enddate, 'YYYY-MM-DD') and fp.result <> 'E'
then 1
else 0
end) total,
sum(case when fp.result = 'G'
and to_date(fp.offered_date) >= :ad_startdate
and to_date(fp.offered_date) <= :ad_enddate then 1 else 0 end) colorgreen,
sum(case when fp.resultat = 'R'
and to_date(fp.offered_date) >= :ad_startdate
and to_date(fp.offered_date) <= :ad_enddate then 1 else 0 end) colorred
FROM
fruit_properties fp, fruit fr
WHERE
fp.id = fr.id
GROUP BY
fp.id, fr.id
I'm checking dates 1 time for each sum column and have a feeling this can be made once somehow? Right now if I check only once at the total column, then colorgreen + colorred might be larger than the total since it counts no matter what date they have.
Can my query be enhanced somehow?
you can simplify like this. but PLEASE check your SQL. you're mixing TO_DATE and CHAR datatypes. this will only end in disaster.
eg you have:
when to_date(fp.offered_date) BETWEEN TO_DATE( :ad_startdate, 'YYYY-MM-DD')
AND TO_DATE(:ad_enddate, 'YYYY-MM-DD')
vs
sum(case when fp.result = 'G'
and to_date(fp.offered_date) >= :ad_startdate
in one case you are TO_DATE'ing ad_startdate but not another (so is it a date already or not?). you are also TO_DATEing the column but crucially WITHOUT a format mask. is the column really a VARCHAR datatype? if so you really should not store dates as anything but DATEs.
anyway assuming the column is a DATE datatype and the binds are of type DATE..
select fruit_prop_Id,fruit_id,
sum(case when result != 'E' then within_offer else 0 end) total,
sum(case when result = 'R' then within_offer else 0 end) colorred,
sum(case when result = 'G' then within_offer else 0 end) colorgreen
from (select fp.id fruit_id,
fr.id fruit_prop_Id,
fp.result,
case
when fp.offered_date >= :ad_startdate
and fp.offered_date <= :ad_enddate then 1 else 0 end within_offer
from fruit_properties fp, fruit fr
where fp.id = fr.id)
group by fruit_id, fruit_prop_Id
You can put the date check in the where clause:
select
fp.id,
fr.id,
sum(case when and fp.result <> 'E' then 1 else 0 end) total,
sum(case when fp.result = 'G' then 1 else 0 end) colorgreen,
sum(case when fp.resultat = 'R' then 1 else 0 end) colorred
FROM
fruit_properties fp, fruit fr
WHERE
fp.id = fr.id
AND to_date(fp.offered_date) >= :ad_startdate
AND to_date(fp.offered_date) <= :ad_enddate
GROUP BY
fp.id, fr.id
Edit: as pointed out in the comments, this query will filter out ids which doesn't have any offer dates in the given interval.