SUM with CASE and LEFT Syntax - sql

With the code below, I'm trying to get the total Net_Value and Eaches for the three years 2015-2017. However I cannot, for the life of me figure out what the syntax error is
[Microsoft][SQL Server Native Client 10.0][SQL Server]Incorrect syntax near '2015'.
SELECT
SUM(CASE WHEN LEFT(STR(FP_Based_on_Comm_Date),4) = '2015' THEN Net_Value ELSE 0 END) AS 2015Sales,
SUM(CASE WHEN LEFT(STR(FP_Based_on_Comm_Date),4) = '2016' THEN Net_Value ELSE 0 END) AS 2016Sales,
SUM(CASE WHEN LEFT(STR(FP_Based_on_Comm_Date),4) = '2017' THEN Net_Value ELSE 0 END) AS 2017Sales,
SUM(CASE WHEN LEFT(STR(FP_Based_on_Comm_Date),4) = '2015' THEN Eaches ELSE 0 END) AS 2015Eaches,
SUM(CASE WHEN LEFT(STR(FP_Based_on_Comm_Date),4) = '2016' THEN Eaches ELSE 0 END) AS 2016Eaches,
SUM(CASE WHEN LEFT(STR(FP_Based_on_Comm_Date),4) = '2017' THEN Eaches ELSE 0 END) AS 2017Eaches
FROM DB;
The field itself is a numerical field, and am trying to use it as a string. Those values typically come through as 2015001 where the format is yyyymmm

The problem are the column alias you're using - a valid SQL Server / T-SQL identifier cannot begin with a number.
If you really want to keep this, you must put these in square brackets like this:
SELECT
SUM(CASE
WHEN LEFT(FP_Based_on_Comm_Date, 4) = 2015
THEN Net_Value
ELSE 0
END) AS [2015Sales],
and you need to apply this to all your column aliases.

Assuming your dates are stored as dates, why aren't you using the YEAR() function. I would also suggest putting the year at the end of the column alias.
SELECT SUM(CASE WHEN YEAR(FP_Based_on_Comm_Date) = 2015 THEN Net_Value ELSE 0 END) AS Sales_2015
I think it is bad idea to use identifiers that need to be escaped -- they just make queries harder to write and to read.
And, if you just care about the results, why not use GROUP BY:
select year(FP_Based_on_Comm_Date), sum(net_value) as sales, sum(eaches) as eaches
from db
group by year(FP_Based_on_Comm_Date),
order by year(FP_Based_on_Comm_Date);

Related

PostgreSQL WHERE cause to filter out results with 0 causes a syntax error

I'm trying to produce a SQL query where I have my IDs on the first column, then sum of page hits of accounts in column 2 and page hits of payments in column 3. The below code works, except for one point. I want to exclude any rows which have 0 in accounts column. When I add in the WHERE clause
SELECT
EVAR3 AS MYID
sum(case when web.Page.name = 'Accounts:Overview' then 1 else 0 end) as accounts
sum(case when web.Page.name = 'Payment:Overview' then 1 else 0 end) as payments
FROM mytable
WHERE
timestamp >= to_timestamp('2021-09-01') AND timestamp <= to_timestamp('2021-09-02')
AND
(sum(case when web.Page.name = 'Accounts:Overview' then 1 else 0 end) > 0)
GROUP BY EVAR3
ORDER BY EVAR3 ASC
This gives me an invalid expression error on my where clause saying generate expressions are not valid in the where clause. When I change WHERE to HAVING I get a syntax error saying it expected EOF rather than GROUP.
How do I correctly implement a filter to remove results with 0 in accounts to otherwise working code?
Since it is an aggregate function it should be HAVING but it should appear after the GROUP BY. the structure is SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY
SELECT
EVAR3 AS MYID,
sum(case when web.Page.name = 'Accounts:Overview' then 1 else 0 end) as accounts,
sum(case when web.Page.name = 'Payment:Overview' then 1 else 0 end) as payments
FROM mytable
WHERE
timestamp >= to_timestamp('2021-09-01') AND timestamp <= to_timestamp('2021-09-02')
GROUP BY EVAR3
HAVING (sum(case when web.Page.name = 'Accounts:Overview' then 1 else 0 end) > 0)
ORDER BY EVAR3 ASC

SQL Case and Cast in Count function

I am trying to count records from two fields only if they meet a specific criteria.
This [Is it possible to specify condition in Count()? ] post was helpful, but it doesn't account for casting varchar to int.
Here is my code:
SELECT Mailing_Id ,Mailing_Nm,Subject_Line,Campaign_Nm,Start_Ts,End_Ts, Mailed_Cnt, Invalid_Cnt ,Actual_Sent_Cnt ,Bounce_Cnt ,Open_Cnt ,Click_Cnt
,count(case ag.logtype when '7' then 1 end) as Unsubs
,count(case ag.category when '1' then 1 end) as Block
,count(case ag.category when '2' then 1 end) as Hard
,count(case ag.category when '3' then 1 end) as Soft
,count(case ag.category when '4' then 1 end) as Tech
,count(case ag.category when '9' then 1 end) as Unknown
FROM [StrongMailTracking].[dbo].[SM_MAILING_SUMMARY] ms left join sm_aggregate_log ag on ms.mailing_id = ag.mailingid
WHERE datepart(year,start_ts) = 2015 and (mailing_nm not like '%delivery report%' and mailing_nm not like '%daily helpdesk%' and mailing_nm not like '%test%')
GROUP BY Mailing_Id ,Mailing_Nm ,Subject_Line ,Campaign_Nm ,Start_Ts ,End_Ts ,Mailed_Cnt ,Invalid_Cnt ,Actual_Sent_Cnt ,Bounce_Cnt ,Open_Cnt ,Click_Cnt
ORDER BY mailing_id asc
Please draw your attention to the 6 case statements. Logtype is int, Category is varchar.
I've tried:
removing the single quotes
adding ... case cast( - as int) when ...
removing single quotes while casting
casting as numeric first then int
But I keep getting this error: "Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'dynamic-preview-7179' to data type int."
Does anyone have ideas on what to do?
According to your description of the data types, this should work:
count(case ag.logtype when 7 then 1 end) as Unsubs,
count(case ag.category when '1' then 1 end) as Block,
count(case ag.category when '2' then 1 end) as Hard,
count(case ag.category when '3' then 1 end) as Soft,
count(case ag.category when '4' then 1 end) as Tech,
count(case ag.category when '9' then 1 end) as Unknown
Numbers should be compared to numeric constants; strings to string constants.
Although equivalent, I would write the logic as:
sum(case when ag.logtype = 7 then 1 else 0 end) as Unsubs,
Why? Two reasons that are merely preferences:
I prefer the more general case statement because I find that when modifying code, I often need to add in new conditions. I prefer IN to using multiple WHENs.
I prefer sum() over count() because count(2) = count(1).
as I commented before, ms.Mailing_Id is an int and ag.mailingid is a varchar. a colleague helped me out with this:
FROM [StrongMailTracking].[dbo].[SM_MAILING_SUMMARY] ms left join sm_aggregate_log ag on CAST(ms.mailing_id As varchar(255)) = ag.mailingid

Avoiding a divide by zero error in a case statement

I am getting a divide by zero error in my script.
Can anyone please help.
I am trying to divide two records and one of them has zero in it. I dont want to lose the row, please advise.
select DATEPART(Year,Request_date) as "Year",
DATEPART(Month,Request_date) as "Month",
COUNT([MONTH_OF_SUSPENSION]) as "Request" ,
sum(case when [PAYMENT_<=24HRS] = 'Y' then 1 else 0 end) as "Paid in 24hrs",
COUNT([MONTH_OF_SUSPENSION])/sum(case when [PAYMENT_<=24HRS] = 'Y' then 1 else 0 end) as "Achieved"
FROM suspension_br
where REQUEST_STATUS = 'OTHERS'
GROUP BY DATEPART(Year,Request_date),DATEPART(Month,Request_date)
You can introduce a second case to check the result of the sum:
case
when sum(case when [PAYMENT_<=24HRS] = 'Y' then 1 else 0 end) > 0
then COUNT([MONTH_OF_SUSPENSION])/sum(case when [PAYMENT_<=24HRS] = 'Y' then 1 else 0 end)
else 0 /* a default value that makes sense to you */
end as "Achieved"
Looking at your code I could assume you are using MSSQL and therefore you could use nullif which returns null if two arguments are equal. So for example your code could look like :
COUNT([MONTH_OF_SUSPENSION])/nullif(sum(case when [PAYMENT_<=24HRS] = 'Y' then 1 else 0 end),0) as "Achieved"
What it does is if the value of the sum operator is equal 0 then the divisor is turn from zero into null and that will result in the entire equation to become null.
use another case statement to check the result of your sum
select DATEPART(Year,Request_date) as "Year",
DATEPART(Month,Request_date) as "Month",
COUNT([MONTH_OF_SUSPENSION]) as "Request" ,
sum(case
when [PAYMENT_<=24HRS] = 'Y' then 1
else 0
end) as "Paid in 24hrs",
case
when sum(case
when [PAYMENT_<=24HRS] = 'Y' then 1
else 0
end) = 0 then 'whatever you want in this case'
else COUNT([MONTH_OF_SUSPENSION])/sum(case
when [PAYMENT_<=24HRS] = 'Y' then 1
else 0
end) as "Achieved"
FROM suspension_br
where REQUEST_STATUS = 'OTHERS'
GROUP BY DATEPART(Year,Request_date),DATEPART(Month,Request_date)
although this is pretty nasty looking, so you could tidy it up a bit with a sub-select:
select
year,
month,
request,
PaidIn24hrs,
case
when PaidIn24hrs = 0 then 'whatever you want in this case'
else request/PaidIn24hrs
end as "Achieved"
from
(
select DATEPART(Year,Request_date) as "Year",
DATEPART(Month,Request_date) as "Month",
COUNT([MONTH_OF_SUSPENSION]) as "Request" ,
sum(case
when [PAYMENT_<=24HRS] = 'Y' then 1
else 0
end) as "PaidIn24hrs"
FROM suspension_br
where REQUEST_STATUS = 'OTHERS'
GROUP BY DATEPART(Year,Request_date),DATEPART(Month,Request_date)
)

Dynamically Creating Column Counts Per Day of Month

OK, so I have this query to find number of types per day like below. (Courtesy of MarkD here Counting Items/Rows in DB as Columns Grouped by Another Column)
select type,
sum(case when MyDate = '' then 1 else 0 end) as "10/1",
sum(case when MyDate = '' then 1 else 0 end) as "10/2
...etc
from MyTabe
group by type
However, I want to dynamically create date columns and have the count generated for each column, otherwise I would end up manually adding a new column for each day of the month.
I guess the best way to get the output I wanted was to do the following:
define startDate = to_date('2013-10-01', 'yyyy-mm-dd')
select type,
sum(case when MyDate = &&startDate then 1 else 0 end) as "1"
, sum(case when MyDate = &&startDate +1 then 1 else 0 end) as "2"
, sum(case when MyDate = &&startDate +2 then 1 else 0 end) as "3"
, sum(case when MyDate = &&startDate +3 then 1 else 0 end) as "4"
...etc for as many days of current month I am running
, sum(case when MyDate = &&startDate +29 then 1 else 0 end) as "30"
, sum(case when MyDate = &&startDate +30 then 1 else 0 end) as "31"--This would be commented out for Nov
from MyTabe
group by type
order by type
;
This way if I want to run this for Nov, Dec, Jan, and so on, I can just change the variable at the top and run the query. This is what I was looking for; however, I still wonder if it would be possible to generate the columns dynamically, but the more I look at it, the more I think that would require a pivot table.

MySQL using calculated return values to calculate other values

Let's say I have a select Statement with the following:
SUM(st.tafPoints/200) as totalFriendReferrals,
SUM(CASE WHEN st.gender = "m" THEN st.tafPoints/200 ELSE 0 END) as maleFriendReferrals,
SUM(CASE WHEN st.gender = "f" THEN st.tafPoints/200 ELSE 0 END) as femaleFriendReferrals
I need to calculate the percentage of maleFriendReferrals based on the totalFriendReferrals. These are both derived (ie, not originally in the list of columns) values. So if I try to do something like:
CONCAT( (maleFriendReferrals/totalFriendReferrals) *100 , '%') as malePercentReferrals
But I get an error:
Unknown column 'maleFriendReferrals' in 'field list' – 1 ms
Is there a way to do this easily? I need to be able to do this within the query itself, and not have to loop through my results to calculate percentages.
You should put the original query to a subquery
SELECT CONCAT( (maleFriendReferrals/totalFriendReferrals) *100 , '%') as malePercentReferrals
FROM
(SELECT
SUM(st.tafPoints/200) as totalFriendReferrals,
SUM(CASE WHEN st.gender = "m" THEN st.tafPoints/200 ELSE 0 END) as maleFriendReferrals,
SUM(CASE WHEN st.gender = "f" THEN st.tafPoints/200 ELSE 0 END) as femaleFriendReferrals
FROM st) AS subquery