Using COUNT CASE WHEN MONTH Statement in MariaDB 10.2.15 - sql

I created a query to calculate the Amount of Id in a table using COUNT, CASE, WHEN and MONTH ..
Code:
SELECT
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN LogsFormatted.Id ELSE 0 END ) AS '1',
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 2 THEN LogsFormatted.Id ELSE 0 END ) AS '2'
FROM
HrAttLogsFormatted AS LogsFormatted
WHERE
LogsFormatted.DateIn BETWEEN '2019-01-01' AND '2019-02-31'
AND LogsFormatted.Late != ''
Output :
| 1 | 2 |
| 1378 | 1378 |
The output I want to make is to calculate the Id in each month, namely Month 1 and Month 2
| 1 | 2 |
| 792 | 586 |
The data above is a fact
Using the above query instead adds up between the results of calculating month 1 and month 2

You should be counting NULL when the criteria in your CASE expression does not match. Also, I prefer counting 1 unless you really want to the count the Ids themselves. This version should work:
SELECT
COUNT(CASE WHEN MONTH(lf.DateIn) = 1 THEN 1 END) AS '1',
COUNT(CASE WHEN MONTH(lf.DateIn) = 2 THEN 1 END) AS '2'
FROM HrAttLogsFormatted AS lf
WHERE
lf.DateIn BETWEEN '2019-01-01' AND '2019-02-31' AND
lf.Late != '';
Note carefully that the current counts you are seeing sum up to the individual counts, that is:
1378 = 792 + 586
The reason for this is the the COUNT function "counts" any non NULL value as 1, and any NULL value as zero. Your current CASE expression will always count 1, for every record in the table.

remove else part from case when expression - if you use else with 0 then count takes that also in consideration which gives u actually wrong ouput
SELECT
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN LogsFormatted.Id END ) AS '1',
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 2 THEN LogsFormatted.Id END ) AS '2'
FROM
HrAttLogsFormatted AS LogsFormatted
WHERE
LogsFormatted.DateIn BETWEEN '2019-01-01' AND '2019-02-31'
AND LogsFormatted.Late != ''

If you are using MariaDB, I would just use SUM() with a boolean:
SELECT SUM( MONTH(lf.DateIn) = 1 ) as month_1,
SUM( MONTH(lf.DateIn) = 2 ) as month_2
FROM HrAttLogsFormatted lf
WHERE lf.DateIn >= '2019-01-01' AND
lf.DateIn < '2020-01-01' AND
lf.Late <> '';
This assumes that the id that you are counting is never NULL (a reasonable assumption for an id).
Note other changes:
The column aliases do not need to be escaped. Don't use non-standard names, unless you need them -- for some reason -- for downstream processing.
This uses a shorter table alias, so the query is easier to write and to read.
The date comparisons use inequalities, so this works both for dates and datetimes.
<> is the standard SQL comparison operator for inequality.

Related

SQL query select returns different result than expected

I'm querying on this 2 tables:
TIMBRATURE
ASSELEMGEN
This is my query:
select convert(varchar(11),convert(date,datav),105) as data,
sum (CASE WHEN idterminale=3 and DATEPART(hour,datav)>='9' and DATEPART(hour,datav)<='16' THEN 1 ELSE 0 END) as pranzoP,
sum (CASE WHEN idterminale=3 and DATEPART(hour,datav)>='17' and DATEPART(hour,datav)<='23' THEN 1 ELSE 0 END) as cenaP,
sum (CASE WHEN idterminale=3 THEN 1 ELSE 0 END) as totaleP
from TIMBRATURE where DATAV>=#dataDa and DATAV<#primoGGmeseSuccessivo and TIMBRATURE.IDDIP
in (select iddip from ASSELEMGEN where IDELEM=1001)
group by convert(date,datav)
order by convert(date,datav)
For this purpose consider this argumets:
declare #datada as date='20230201'
declare #primoggmesesuccessivo as date='20230207'
The result I get:
Iddip is user ID, problem is when user have 2 entries for the same day, one with datav hour part between 9 and 16, the other between 17 and 23. In this case I have to count 2 for it, but my query only count it once. For example in the result above on 1th February I expect PranzoP=93 and totaleP=130.

Return the latest date based from a range of columns

I have a query that counts the number of completed tasks as well as returning the original values.
I'd like to add a new column which returns the most recent date (in this case task_1_completed_date or task_2_completed_date but in reality there are 20 task fields)
(CASE WHEN task_1_completed_date IS NOT NULL THEN 1 ELSE 0 END +
CASE WHEN task_2_completed_date IS NOT NULL THEN 1 ELSE 0 END
) AS task_completed_total
from (select JSON_EXTRACT_SCALAR(data, '$.task1.date') as task_1_completed_date
JSON_EXTRACT_SCALAR(data, '$.task2.date') as task_2_completed_date
from table
WHERE pet_store = 'london'
)
Not sure how to proceed, should I use a subquery here to order the task completion dates?
Use order by
(CASE WHEN task_1_completed_date IS NOT
NULL THEN 1 ELSE 0 END +
CASE WHEN task_2_completed_date IS NOT
NULL THEN 1 ELSE 0 END
) AS task_completed_total
from (select JSON_EXTRACT_SCALAR(data,
'$.task1.date') as task_1_completed_date
JSON_EXTRACT_SCALAR(data,
'$.task2.date') as task_2_completed_date
from table
WHERE pet_store = 'london'
)where rownum=1 order by
task_completed_total desc
-- if rownum doesn't work use Limit 1
I think you could use GROUP BY and MAX to get the most recent date. see here: https://learn.microsoft.com/en-us/sql/t-sql/queries/select-group-by-transact-sql?view=sql-server-ver15

Using SUM SEC_TO_TIME in MariaDB

Reference from How to sum time using mysql
I want to SUM Field LogsFormatted.Late Every month with query :
SELECT
SUM(CASE
WHEN MONTH (LogsFormatted.DateIn) = 1
THEN SEC_TO_TIME( SUM( TIME_TO_SEC(LogsFormatted.Late)))
ELSE 0 END
) AS '1'
FROM
HrAttLogsFormatted AS LogsFormatted
But the result is
1111 - Invalid use of group function
Where is the problem with the query? resulting in an error output.. Thank you in advance
[EDIT-SOLVED] It's Solved with simply apply
Change format SUM at the beginning of the query
SEC_TO_TIME(SUM(
CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN
TIME_TO_SEC(LogsFormatted.Late) END)
) AS '1'
You don't need to call the sum() so many times. You can also move the case condition to the WHERE clause:
SELECT SUM(TIME_TO_SEC(lf.Late))
FROM HrAttLogsFormatted lf
WHERE MONTH(lf.DateIn) = 1 ;
If you want conditional aggregation, then do:
SELECT SUM(CASE WHEN MONTH(lf.DateIn) = 1 THEN TIME_TO_SEC(lf.Late) END)
FROM HrAttLogsFormatted lf;

select query output not as expected

i need one single query which will give result like the one i give below
createddate recordcount acceptdate submitdate createddate
27-MAR-16 24 36 11
28-MAR-16 79 207 58
for reference i am providing some queries which i want to merge into one single query
select trim(date_created) createddate,count(*) recordcount
from man
where status IN ('CREATED')and date_created>sysdate-15
group by trim(date_created) ORDER BY TO_DATE(createddate,'DD/MM/YYYY');
this query will result like the following.
createddate recordcount
27-MAR-16 11
28-MAR-16 58
the second query
select trim(DATE_SUB) submitdate,count(*) recordcount
from man
where status IN ('SUBMITTED')and DATE_SUB>sysdate-15
group by trim(date_sub) ORDER BY TO_DATE(submitdate,'DD/MM/YYYY');
result of this query is like
submitdate recordcount
27-MAR-16 36
28-MAR-16 207
and the third query is like -
select trim(DATE_PUB) acceptdate,count(*) recordcount
from man
where status IN ('ACCEPTED')and DATE_PUB>sysdate-15
group by trim(DATE_PUB) ORDER BY TO_DATE(acceptdate,'DD/MM/YYYY');
acceptdate recordcount
27-MAR-16 24
28-MAR-16 79
how can i merger these three query so that i can get count for all in single query?which will give me result like
createddate recordcount acceptdate submitdate createddate
27-MAR-16 24 36 11
28-MAR-16 79 207 58
Your first query where clause has date but second query where clause has DATE_P.
Try like this
SELECT Trim(date) createddate,
COUNT(*) recordcount,
SUM(case when status = 'A' then 1 else 0 end) as a,
SUM(case when status = 'S' then 1 else 0 end) as s,
SUM(case when status = 'C' then 1 else 0 end) as c,
SUM(case when status = 'R' then 1 else 0 end) as r
FROM man
WHERE status IN ('A','S','C','R')and date >sysdate-15
GROUP BY trim(date) ORDER BY createddate;
You seem to want to get counts for each status type, for each day. The first step is generate all the dates you're interested in, which you can do with:
select trunc(sysdate) + 1 - level as dt
from dual
connect by level <= 15;
You can then (outer) join to your actual table where any of the three date columns match a generated date, and expand your case conditions to check which one you're looking at:
with t as (
select trunc(sysdate) + 1 - level as dt
from dual
connect by level <= 15
)
select t.dt,
count(*) as recordcount,
count(case when status = 'ACCEPTED' and trunc(m.date_pub) = t.dt
then 1 end) as acceptdate,
count(case when status = 'SUBMITTED' and trunc(m.date_sub) = t.dt
then 1 end) as submitdate,
count(case when status = 'CREATED' and trunc(m.date_created) = t.dt
then 1 end) as createddate
from t
left join man m
on (m.date_pub >= t.dt and m.date_pub < t.dt + 1)
or (m.date_sub >= t.dt and m.date_sub < t.dt + 1)
or (m.date_created >= t.dt and m.date_created < t.dt + 1)
group by t.dt
order by t.dt;
I've used range checks for the join conditions - it isn't clear if all your date columns are set at midnight, but it's safer to assume they might have other times and you cant everything from the matching day.
Each of the three count results is now only of those rows which match the status and where the specific date column matches, which I think is what you want. I've used trunc() here instead of a range comparison, as it doesn't have the potential performance penalty you can see in the where clause (from it potentially stopping an index being used).
This may throw out your recordcount though, depending on your actual data, as that will include rows that now might not match any of the case conditions. You can repeat the case conditions, or use an inline view to calculate the total of the three individual counts, depending on what you want it to include and what will be the easiest for you to maintain. If those are the only three statuses in your table then it may be OK with count(*) but check it gets the value you expect.

Case Function in Oracle - wrong return

I have an table with these fields:
id,
quarter,
description,
id_school.
These table have this data:
ID | quarter | description
12 A 1 YEAR
12 S 1 Year
12 A DONE
I want to get the serie_school based on the below query:
select NVL(
case
WHEN quarter = 'S' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%YEAR' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1
WHEN quarter = 'A' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%QUARTER' OR
UPPER(description ) LIKE '%STEP' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1
WHEN quarter = 'T' THEN -1
ELSE -1
end, -1) nvl_return from test
The return of this on my query is:
ID | quarter | description | nvl_return
12 A 1 YEAR 1
12 S 1 Year 1 *- (this column has the wrong answer)*
12 A DONE -1
The answer of line 2 is wrong, because the QUARTER field is 'S' and the description field have 'year', so it needs to be -1, but in Oracle is returning 1.
Is there anyone who can help me with this?
Thanks in advance
Alexandre,
Examine your statement, where I have added "<======" to point out the various possible amounts for this column.
select NVL(
case
WHEN quarter = 'S' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%YEAR' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1<=======
WHEN quarter = 'A' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%QUARTER' OR
UPPER(description ) LIKE '%STEP' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1<======
WHEN quarter = 'T' THEN -1 <======
ELSE -1 <=======
end, -1 <-this will never happen) nvl_return from test
You return "-1" in every single case. "-1" is the only value returned, by the only column returned in your query.
So when you say the results are:
ID | quarter | description | nvl_return
12 A 1 YEAR 1
12 S 1 Year 1 *- (this column has the wrong answer)*
12 A DONE -1
Unfortunately, your query cannot produce the results you've stated.
Try creating a SQL fiddle, to explain the issue. SQL Fiddle