"IS NOT NULL" not producing the required results in mySQL - mysql

I am trying to build a report based of my table. Here is my table:
Following is the SQL query to get desired results.
SELECT
`user`,
SUM(
CASE
WHEN `event_time` >= '2021-04-01 00:00:00'
AND `event_time` <= '2021-04-16 23:59:59'
AND `sub_status` = 'TB'
THEN pause_sec
END
) AS 'Training Break',
SUM(
CASE
WHEN `event_time` >= '2021-04-01 00:00:00'
AND `event_time` <= '2021-04-16 23:59:59'
AND `sub_status` = 'SB1'
THEN pause_sec
END
) AS 'Short Break 1',
SUM(
CASE
WHEN `event_time` >= '2021-04-01 00:00:00'
AND `event_time` <= '2021-04-16 23:59:59'
AND `sub_status` = 'SB2'
THEN pause_sec
END
) AS 'Short Break 2',
SUM(
CASE
WHEN `event_time` >= '2021-04-01 00:00:00'
AND `event_time` <= '2021-04-16 23:59:59'
AND `sub_status` = 'LB'
THEN pause_sec
END
) AS 'Long Break'
FROM
`vicidial_agent_log`
GROUP BY `user`
I am looking to exclude entries which have all null values and following is my syntax but it is not working and produces the same results.
FROM
`vicidial_agent_log`
WHERE 'Training Break' IS NOT NULL
AND 'Short Break 1' IS NOT NULL
AND 'Short Break 2' IS NOT NULL
AND 'Long Break' IS NOT NULL
GROUP BY `user`
Kindly help here or share a post that can help. I have been trying to find but

Don't use single quotes for column names/aliases, because for example 'Training Break' in an expression like 'Training Break' IS NOT NULL is interpreted as a string literal which of course is not null.
In MySql you can use backticks to surround column names.
Also, the columns Training Break, Short Break 1, Short Break 2 and Long Break are the result of aggregate functions so they can't be used in a WHERE clause. Instead use a HAVING clause.
Finally, since you want the rows where at least 1 of these columns is not null you should use the operator OR instead of AND:
FROM
`vicidial_agent_log`
GROUP BY `user`
HAVING `Training Break` IS NOT NULL
OR `Short Break 1` IS NOT NULL
OR `Short Break 2` IS NOT NULL
OR `Long Break` IS NOT NULL
Or:
FROM
`vicidial_agent_log`
GROUP BY `user`
HAVING COALESCE(`Training Break`, `Short Break 1`, `Short Break 2`, `Long Break`) IS NOT NULL

First, move the common part of the SUM() expressions to the WHERE clause.
Second, fix the date logic so you are not missing a second.
I also strongly, strongly recommend using column names that do not need to be escaped. So:
SELECT user,
SUM(CASE WHEN sub_status` = 'TB' THEN pause_sec END) AS Training_Break,
SUM(CASE WHEN sub_status = 'SB1' THEN pause_sec END) AS Short_Break_1,
SUM(CASE WHEN sub_status = 'SB2' THEN pause_sec END) AS Short_Break_2,
SUM(CASE WHEN sub_status = 'LB' THEN pause_sec END) AS Long_Break
FROM vicidial_agent_log
WHERE event_time >= '2021-04-01' AND
event_time < '2021-04-17'
GROUP BY user;
This will probably eliminate the NULL values and speed up the query. However, if there are still NULL values in an entire row, it is because there are rows that don't have one of these statuses. So, just check for them:
SELECT user,
SUM(CASE WHEN sub_status` = 'TB' THEN pause_sec END) AS Training_Break,
SUM(CASE WHEN sub_status = 'SB1' THEN pause_sec END) AS Short_Break_1,
SUM(CASE WHEN sub_status = 'SB2' THEN pause_sec END) AS Short_Break_2,
SUM(CASE WHEN sub_status = 'LB' THEN pause_sec END) AS Long_Break
FROM vicidial_agent_log
WHERE event_time >= '2021-04-01' AND
event_time < '2021-04-17' AND
sub_status IN ('TB', 'SB1', 'SB2', 'LB')
GROUP BY user;
SQL is very powerful in letting you filter before or after aggregation. However, it is usually much better (from a performance perspective) to filter before aggregating.
I also advise you to use identifiers that do not need to be escaped. And, never use single quotes for anything other than string and date literals.

Related

SQL to summarise solar power generated by quarter

I have a single MySQL table with 10 years of solar panel generation data (every 10 minutes, where generation is > zero).
I have no idea how to construct a query that will give me four values per year row, each value representing the summed total for each quarter in that year.
My source table (DTP) schema looks like this:
#, Field, Type, Null, Key, Default, Extra
1, 'PWR', 'decimal(5,3)', 'NO', '', NULL, ''
2, 'idDTP', 'int(11)', 'NO', 'PRI', NULL, 'auto_increment'
3, 'DT', 'datetime', 'NO', '', NULL, ''
One example I though might point me in the right direction looks like this (bear in mind this is partly modified from the source):
SELECT Year,SUM(Quarter1) AS Quarter1,SUM(Quarter2) AS Quarter2,SUM(Quarter3) AS Quarter3,SUM(Quarter4) AS Quarter4
FROM
(
SELECT YEAR(DT) AS 'Year' ,
Quarter1 = CASE(DATEPART(q, DTP.DT))
WHEN 1 THEN SUM(DTP.DT)
ELSE 0
END,
Quarter2 = CASE(DATEPART(q, DTP.DT))
WHEN 2 THEN SUM(DTP.DT)
ELSE 0
END,
Quarter3 = CASE(DATEPART(q, DTP.DT))
WHEN 3 THEN SUM(DTP.DT)
ELSE 0
END,
Quarter4 = CASE(DATEPART(q, DTP.DT))
WHEN 4 THEN SUM(DTP.DT)
ELSE 0
END
FROM DTP LEFT JOIN PWR ON DTP.DT = Customers.CustomerID
LEFT JOIN [Order Details] ON [Order Details].OrderID = Orders.OrderID
GROUP BY CompanyName, YEAR(OrderDate), DATEPART(q, OrderDate)
)C
GROUP BY CompanyName,Year
I started trying to convert it to my scenario but without understanding it, I've landed myself in a half finished (assuming this is even on the right lines) mess and I'm not sure how to fix it. Any well explained hints or links would be welcome - flames, less so. ;oP
If I've got the wrong forum, please politely point out the right one - couldn't see a better alternative in the Stack-Echange list.
Thanks
Small sample from first year of source data (Feb-Mar)
'160851', '2012-02-29 08:00:00', '0.030'
'160852', '2012-02-29 08:10:00', '0.066'
'160853', '2012-02-29 08:20:00', '0.072'
'160854', '2012-02-29 08:30:00', '0.060'
'160855', '2012-02-29 08:40:00', '0.090'
'160856', '2012-02-29 08:50:00', '0.102'
'160857', '2012-02-29 09:00:00', '0.084'
'160858', '2012-02-29 09:10:00', '0.132'
'160859', '2012-02-29 09:20:00', '0.144'
'160860', '2012-02-29 09:30:00', '0.138'
'160861', '2012-02-29 09:40:00', '0.150'
'160862', '2012-02-29 09:50:00', '0.174'
'160863', '2012-02-29 10:00:00', '0.174'
'160864', '2012-02-29 10:10:00', '0.162'
I can't enter a years worth of data, as it unsurprisingly exceeds the allowed count but it proceeds in a similar vein.
There's no meaningful output because I got out of my depth well before I'd approached anything like viable code.
Define a new year-quarter column, named yq.
Fill it with values like "2021-q4" and "2022-q1".
Now your problem is simple.
Just compute SUM( ... )
with GROUP BY yq.
You are missing some tables, but i kept them in the subquery.
the subwquery, would show the numbers for all quaters , for every company and year.
The outer query, would, sum them up into years without companies..
As you need more columns you can add them from comapanies orders or order details and add them in the inner SELECT and then add them also in the outer, to group them for them examle by orders or such
SELECT Year,SUM(Quarter1) AS Quarter1,SUM(Quarter2) AS Quarter2,SUM(Quarter3) AS Quarter3,SUM(Quarter4) AS Quarter4
FROM
(
SELECT CompanyName,YEAR(DT) AS 'Year' ,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 1 THEN DTP.PWR ELSe 0 END) Quarter1,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 2 THEN DTP.PWR ELSe 0 END) Quarter2,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 3 THEN DTP.PWR ELSe 0 END) Quarter3,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 4 THEN DTP.PWR ELSe 0 END) Quarter4
FROM DTP LEFT JOIN PWR ON DTP.DT = Customers.CustomerID
LEFT JOIN [Order Details] ON [Order Details].OrderID = Orders.OrderID
GROUP BY CompanyName, YEAR(OrderDate)
) C
GROUP BY Year
Without the customer and orders
SELECT CompanyName,YEAR(DT) AS 'Year' ,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 1 THEN DTP.PWR ELSe 0 END) Quarter1,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 2 THEN DTP.PWR ELSe 0 END) Quarter2,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 3 THEN DTP.PWR ELSe 0 END) Quarter3,
SUM(CASE WHEN DATEPART(q, DTP.DT) = 4 THEN DTP.PWR ELSe 0 END) Quarter4
FROM DTP
GROUP BY YEAR(DT)

Error Code: 1054: Unknown column 'table_name.column_name' in 'field list' when Table exist

I am trying to run mySQL query to fetch data from my Database. Following is my query:
SELECT user,
SUM(CASE WHEN sub_status = 'TB' THEN pause_sec END) AS Training_Break,
SUM(CASE WHEN sub_status = 'SB1' THEN pause_sec END) AS Short_Break_1,
SUM(CASE WHEN sub_status = 'SB2' THEN pause_sec END) AS Short_Break_2,
SUM(CASE WHEN sub_status = 'LB' THEN pause_sec END) AS Long_Break
FROM vicidial_agent_log
WHERE event_time >= '2021-04-01' AND
event_time < '2021-04-17' AND
sub_status IN ('TB', 'SB1', 'SB2', 'LB')
GROUP BY user
HAVING COALESCE(`Training Break`, `Short Break 1`, `Short Break 2`, `Long Break`) IS NOT NULL
This produces desired results:
Table Output on the results.
I want to show names of USERS but they are in another table. So, I used join here like as follow:
SELECT `vicidial_agent_log.user`,
`vicidial_users.full_name`,
SUM(CASE WHEN `vicidial_agent_log.sub_status` = 'TB' THEN `vicidial_agent_log.pause_sec` END) AS Training_Break,
SUM(CASE WHEN `vicidial_agent_log.sub_status` = 'SB1' THEN `vicidial_agent_log.pause_sec` END) AS Short_Break_1,
SUM(CASE WHEN `vicidial_agent_log.sub_status` = 'SB2' THEN `vicidial_agent_log.pause_sec` END) AS Short_Break_2,
SUM(CASE WHEN `vicidial_agent_log.sub_status` = 'LB' THEN `vicidial_agent_log.pause_sec` END) AS Long_Break
FROM `vicidial_agent_log`
INNER JOIN `vicidial_users` ON `vicidial_users.user`=`vicidial_agent_log.user`
WHERE `vicidial_agent_log.event_time` >= '2021-04-01' AND
`vicidial_agent_log.event_time` < '2021-04-17' AND
`vicidial_agent_log.sub_status` IN ('TB', 'SB1', 'SB2', 'LB')
GROUP BY `vicidial_agent_log.user`
HAVING COALESCE(`Training_Break`, `Short_Break_1`, `Short_Break_2`, `Long_Break`) IS NOT NULL
And it produces following error:
Error Code: 1054 Unknown column 'vicidial_agent_log.user' in 'field list'
But the table exists.
Also, if I remove back ticks in my code, it generates this error:
Error Code: 1054 Unknown column 'vicidial_agent_log.user' in 'group statement'
Following are fields of my two Tables and their columns:
>Columns of vicidial_agent_log: [agent_log_id, user, ...]
>Columns of vicidial_users: [user_id, user, pass, full_name, ...]
My desired output should look like https://i.stack.imgur.com/GVeIz.png only with an addition of one column with a name on it.
This is the first professional project I am building and any sort of help will do. I must be making a beginners mistake that might be small. I am not sure if there is problem with my logic or concept. I have researched into other posts but it doesn't seem to work for me. Any sort of help will go a long way.
Don't escape your identifiers! And use aliases! I think I mentioned this in an earlier answer. So:
SELECT al.user, u.full_name,
SUM(CASE WHEN al.sub_status = 'TB' THEN al.pause_sec END) AS Training_Break,
SUM(CASE WHEN al.sub_status = 'SB1' THEN al.pause_sec END) AS Short_Break_1,
SUM(CASE WHEN al.sub_status = 'SB2' THEN al.pause_sec END) AS Short_Break_2,
SUM(CASE WHEN al.sub_status = 'LB' THEN al.pause_sec END) AS Long_Break
FROM vicidial_agent_log al JOIN
vicidial_users u
ON u.user = al.user
WHERE al.event_time >= '2021-04-01' AND
al.event_time < '2021-04-17' AND
al.sub_status IN ('TB', 'SB1', 'SB2', 'LB')
GROUP BY al.user, u.full_name;
The HAVING clause is not needed with this version, unless al.pause_sec could be NULL. If that is possible, just add al.pause_sec IS NOT NULL to the WHERE clause.
The problem with your query is that
`vicidial_agent_log.user`
refers to a column name that has a . in it. You intend:
`vicidial_agent_log`.`user`
But why bother with the backticks when these are equivalent to:
vicidial_agent_log.user
Or:
al.user
when the table alias is defined.

mysql SUM column on WHERE condition

Im trying to get a total sum of all the columns that meet a condition. Here is my current setup
SELECT
COUNT(order_type = 'BUY') AS buy_fill,
COUNT(order_type = 'SELL') AS sell_fill,
SUM(btc_total) AS fill_sum
FROM fill_orders
WHERE coin_id = '$coin'
AND time_stamp >= DATE_SUB(NOW(), INTERVAL 55 SECOND)
This is what I have now and its counting how many types of buy and sell orders I have and is also giving me sum of all orders but I need to the break the sum of the orders into BUY orders and SELL orders.
Heres the code that I'm trying trying to make work. I've added (btc_total WHERE order_type = 'BUY') and SUM(btc_total WHERE order_type = 'SELL')
SELECT
COUNT(order_type = 'BUY') AS buy_fill,
COUNT(order_type = 'SELL') AS sell_fill,
SUM(btc_total) AS fill_sum,
SUM(btc_total WHERE order_type = 'BUY') AS buy_total
SUM(btc_total WHERE order_type = 'SELL') AS sell_total
FROM fill_orders
WHERE coin_id = '$coin'
AND time_stamp >= DATE_SUB(NOW(), INTERVAL 55 SECOND)
Change this:
SUM(btc_total WHERE order_type = 'BUY') AS buy_total
to this:
SUM(IF(order_type='BUY',btc_total,NULL)) AS buy_total
The MySQL IF() function evaluates the first argument as a boolean, if that's TRUE, it returns the second argument, else it returns the third argument.
The IF() will be evaluated for each row, and the return from that expression will get totaled up by the SUM() aggregate.
or, use the more ANSI-standard equivalent to achieve the same result:
SUM(CASE WHEN order_type = 'BUY' THEN btc_total END) AS buy_total
This pattern is commonly referred to as "conditional aggregation".
For the "counts" we can replace COUNT with SUM, like this:
SUM(order_type = 'BUY') AS buy_fill
MySQL evaluates the equality comparison as a boolean, which returns 1, 0 or NULL, which are then totaled up by the SUM aggregate. (A COUNT of that would include zeros and ones, not just the ones.)
The above is equivalent to
SUM( CASE
WHEN order_type = 'BUY' THEN 1
WHEN order_type <> 'BUY' THEN 0
ELSE NULL
END
) AS buy_fill
If we want to use a COUNT aggregate, we could do it like this:
COUNT(IF(order_type = 'Buy',1,NULL)) AS buy_fill
(We could use any non-null value in place of 1, and get an equivalent result.)
"conditional aggregates" conventionally contain a case expression
SELECT
COUNT(CASE WHEN order_type = 'BUY' THEN order_type END) AS buy_fill
, COUNT(CASE WHEN order_type = 'SELL' THEN order_type END) AS sell_fill
, SUM(btc_total) AS fill_sum
, SUM(CASE WHEN order_type = 'BUY' THEN btc_total ELSE 0 END) AS buy_total
, SUM(CASE WHEN order_type = 'SELL' THEN btc_total ELSE 0 END) AS sell_total
FROM fill_orders
WHERE coin_id = '$coin'
AND time_stamp >= DATE_SUB(NOW(), INTERVAL 55 SECOND)

What is wrong is this mysql statement with case?

SELECT COUNT(*)
FROM PATIENTS A, RECALLS_AFTER_RESERV C
LEFT JOIN PATIENT_LAST_RESERV D
ON D.PATIENT_ID = C.PATIENT_ID
WHERE C.PATIENT_ID = A.ID
AND C.OFFICE_ID = A.OFFICE_ID
AND C.OFFICE_ID = ?
AND YEAR(CONVERT_TZ(C.RECALLS_AT, 'UTC', 'Asia/Tokyo')) = ?
AND (CASE
WHEN ISNULL(D.STARTS_AT) THEN
A.CHECKED_IN_AT
ELSE
D.STARTS_AT
END) >= CONVERT_TZ('2015-01-27 00:00:00', 'Asia/Tokyo', 'UTC')) AND (CASE
WHEN ISNULL(D.STARTS_AT) THEN
A.CHECKED_IN_AT
ELSE
D.STARTS_AT
END) <= CONVERT_TZ('2015-01-27 23:59:59', 'Asia/Tokyo', 'UTC') AND C.RECALLED_AT IS NULL AND C.STARTS_AT IS NULL
I want to use the case to determine whether the D.starts_at have value,if not, I use A.checked_in_at to replace the D.starts_at.but it gives an error
(case when isnull(D.starts_at) then A.checked_in_at else D.starts_at end) >= CONVERT_TZ('2015-01-27 00:00:00', 'Asia/Tokyo', 'UTC') )
I finally find the problem, the case statement is right. the problem is the redundant ) in the above.

CASE Statement in SQL WHERE clause

I'm trying to fetch data from table where I'm using a CASE condition in the WHERE clause and currently I'm using following query:-
SELECT count(enq_id) AS total, sum(purchase_amount) AS purchase
FROM temp_stock
WHERE purchase_date <> '0000-00-00'
AND purchase_date < '2012-08-01'
AND (
STATUS = 'Sold'
OR STATUS = 'In Stock'
OR STATUS = 'Ref'
)
AND CASE WHEN (
STATUS = 'Sold'
)
THEN delivery_date >= '2012-08-01'
END
But it returns 0 for total and NULL for purchase.
From your comment.
I want to use Case Statement, could u pls clarify me about case statament in where clause
You can use CASE statement in WHERE like this:
SELECT count(enq_id) AS total, sum(purchase_amount) AS purchase
FROM temp_stock
WHERE purchase_date <> '0000-00-00'
AND purchase_date < '2012-08-01'
AND ( STATUS = 'Sold'
OR STATUS = 'In Stock'
OR STATUS = 'Ref')
AND CASE STATUS
WHEN 'Sold'
THEN delivery_date >= '2012-08-01'
ELSE 1=1
END
Here you need to use ELSE 1=1. otherwise you will not get desired result. For more explanation see this SQLFiddle
I don't think that CASE can work that way. What you want is a slightly more complex expression as your WHERE clause. Probably something like this:
SELECT count(enq_id) AS total, sum(purchase_amount) AS purchase
FROM temp_stock
WHERE purchase_date <> '0000-00-00'
AND purchase_date < '2012-08-01'
AND (
(STATUS = 'Sold' AND delivery_date >= '2012-08-01')
OR STATUS = 'In Stock'
OR STATUS = 'Ref'
)